iOS์์๋ ์ฌ์ฉ์๊ฐ ์๋์์ ์๋ก ์ค์์ดํ๋ ์ฑ ์ ํ๊ธฐ(App Switcher)๋ฅผ ์ด๋ฉด,
์์คํ
์ด ํ์ฌ ์ฑ์ ํ๋ฉด ์ค๋
์ท์ ์๋์ผ๋ก ์ฐ์ด ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํํ๋ก ๋ณด์ฌ์ค๋ค.
๋ฌธ์ ๋ ์ด๋ ๋ฏผ๊ฐ ์ ๋ณด๊ฐ ๊ทธ๋๋ก ๋ ธ์ถ๋ ์ ์๋ค๋ ์ ์ด๋ค.
์๋ฅผ ๋ค์ด ํฌ์ค ๋ฐ์ดํฐ, ์ฌ์ฉ์ ํ๋กํ, ๋ฉ์์ง ๋ฑ์ ๋ณด์์ ๊ทธ๋๋ก ๋ ธ์ถ๋๋ฉด ๊ณค๋ํ๋ค.
์ด๋ฒ ๊ธ์์๋,
์ ํ๊ธฐ๋ก ์ด๋ํ ๋ ์ฑ ํ๋ฉด ๋์ ๋ํดํธ ๋ก๊ณ ํน์ ์ํ๋ UI๋ฅผ ๊ตฌ์ฑํ์ฌ ํด๋น ๋ทฐ๊ฐ ๋ณด์ด๋๋ก ๊ฐ๋ฆฌ๋ ๋ฐฉ๋ฒ์ UIKit ๊ธฐ๋ฐ์ผ๋ก ๊ตฌํํด๋ณด๋ ค๊ณ ํ๋ค. (๋ง์ง๋ง์ ๊ฐ๋ตํ๊ฒ SwiftUI๋ ์์)
๋์ ์๋ฆฌ
์ฑ์ด active → inactive ๋๋ background ์ํ๋ก ์ ํ๋๋ฉด,
iOS๋ ํ์ฌ ์๋์ฐ๋ฅผ ๋ ๋๋งํ ๊ฒฐ๊ณผ๋ฅผ ์๋์ผ๋ก ์บก์ณํ๋ค.
์ด ์ค๋
์ท์ ๊ฐ๋ฐ์๊ฐ ์ง์ ๊ต์ฒดํ๊ฑฐ๋ ์ญ์ ํ ์ ์๊ณ ,
"๋ฌด์์ ์ฐํ๊ฒ ํ ๊ฒ์ธ์ง"๋ง ์ ์ดํ ์ ์๋ค.
์ฆ, ์ค๋
์ท ์ฐ๊ธฐ ์ ์ ์ ์ฒด๋ฅผ ๋ฎ๋ ๊ฐ์ง ํ๋ฉด์ ์ฌ๋ ค๋๊ธฐ(์ํ๋ ๋ทฐ)๋ฅผ ํ๋ฉด ๋๋ค.
๊ตฌํ ๋ฐฉ๋ฒ
1. ์ฑ์ด ๋นํ์ฑํ๋๊ธฐ ์ง์ (SceneDelegate์ sceneWillResignActive) ์์ ์ ์ ์ฒด๋ฅผ ๋ฎ๋ ์ปค๋ฒ ์๋์ฐ๋ฅผ ์ต์๋จ์ ๋์ฐ๊ธฐ
2. ์ปค๋ฒ์๋ ๋ก๊ณ ํน์ ์ํ๋ UI๋ฅผ ํ์ํ๊ธฐ
3. ์ฑ์ผ๋ก ๋ณต๊ท(SceneDelegate์ sceneDidBecomeActive)ํ์ฌ ์ปค๋ฒ๋ฅผ ์ฆ์ ์ ๊ฑฐํ๊ธฐ
์์ ๊ฐ์ด ๊ตฌํํ๋ฉด, iOS๊ฐ ์บก์ณํ๋ ํ๋ฉด์ ํญ์ "๋ก๊ณ (์ํ๋ UI)"๋ก ๊ณ ์ ๋๋ค.
๊ตฌํ ์ฝ๋
- ์ปค๋ฒ ๋ทฐ ๋ง๋ค๊ธฐ
import UIKit
import SnapKit
final class PrivacyCoverViewController: UIViewController {
// ์ค๋น๋ ์์๋ก๊ณ ๊ฐ ์๋ ๊ด๊ณ๋ก Label๋ก ํ์!
private let titleLabel: UILabel = {
let label = UILabel()
label.text = "yeggrrr"
label.textAlignment = .center
label.font = .systemFont(ofSize: 36, weight: .bold)
label.textColor = .black
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(titleLabel)
titleLabel.snp.makeConstraints { make in
make.center.equalToSuperview()
}
}
override var prefersStatusBarHidden: Bool { true } // ์ํ๋ฐ ์ ๊ฑฐ
}
- ์ปค๋ฒ๋ทฐ ๋งค๋์ ๊ตฌ์ฑ
import UIKit
final class PrivacyCoverManager {
static let shared = PrivacyCoverManager()
private init() {}
private var windows: [ObjectIdentifier: UIWindow] = [:]
func show(over scene: UIWindowScene) {
let key = ObjectIdentifier(scene)
guard windows[key] == nil else { return }
let w = UIWindow(windowScene: scene)
w.windowLevel = .alert + 1
w.rootViewController = PrivacyCoverViewController()
w.isHidden = false
w.layoutIfNeeded() // ์ค๋
์ท ์ง์ ์ฆ์ ๋ ๋๋ง
CATransaction.flush()
windows[key] = w
}
func hide(from scene: UIWindowScene) {
let key = ObjectIdentifier(scene)
guard let w = windows.removeValue(forKey: key) else { return }
UIView.animate(withDuration: 0.12, animations: {
w.alpha = 0
}, completion: { _ in
w.isHidden = true
})
}
}
- SceneDelegate์์์ ์ฒ๋ฆฌ
import UIKit
final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
// ์ ํ๊ธฐ ์ง์
์ง์
func sceneWillResignActive(_ scene: UIScene) {
guard let ws = scene as? UIWindowScene else { return }
PrivacyCoverManager.shared.show(over: ws)
}
// ์ฑ ๋ณต๊ท
func sceneDidBecomeActive(_ scene: UIScene) {
guard let ws = scene as? UIWindowScene else { return }
PrivacyCoverManager.shared.hide(from: ws)
}
}
์์ ๊ฐ์ด ๊ตฌ์ฑํ๋ฉด ๋!
๊ตฌํ๋ ํ๋ฉด
1. ์ฌ์ฉ์ ์ค์์ดํ → ์ฑ inactive ์ ํ
2. sceneWillResignActive ํธ์ถ
3. PrivacyCoverManager.show() → ์ปค๋ฒ ํ์
4. iOS๊ฐ ์ค๋
์ท ์ดฌ์ → ์ปค๋ฒ๋ง ์บก์ณ๋จ
5. ์ฑ ๋ณต๊ท → sceneDidBecomeActive ํธ์ถ
6. PrivacyCoverManager.hide() → ์ปค๋ฒ ์ ๊ฑฐ
๊ฒฐ๊ณผ์ ์ผ๋ก ์์ ๊ฐ์ด ์ ํ๊ธฐ ์ธ๋ค์ผ์๋ ํญ์ "yeggrrr" ํ ์คํธ๊ฐ ์๋ ๋ทฐ๋ง ๋ณด์ด๊ฒ ๋๋ค.
SwiftUI ์์๋?
swiftUI์์๋ "์ค๋
์ท/ํ๋ฉด ๋
นํ/App Switcher ๋ฏธ๋ฆฌ๋ณด๊ธฐ" ์์ ์์ ํน์ ๋ทฐ๋ฅผ ์๋์ผ๋ก ๋ง์คํน ํ ์ ์๋๋ก
privacySensitive(_:)๋ผ๋ modifier๊ฐ iOS 15 ์ด์๋ถํฐ ์ ๊ณต๋๋ค.
์ฆ, ์์ ์ค๋ช
ํ UIKit์ฒ๋ผ '์ง์ ์ปค๋ฒ ์ฌ๋ฆฌ๊ธฐ'๋ฅผ ํ์ง ์์๋,
์์คํ
์ด ์ค๋
์ท์ ์ฐ๋ ์๊ฐ์ ์์์ '์ด ๋ทฐ๋ ๋ฏผ๊ฐํ๋ ๊ฐ๋ ค์ผ ํจ'์ด๋ผ๊ณ ์ฒ๋ฆฌํ๋ค.
<์ฌ์ฉ ๋ฐฉ๋ฒ>
var body: some View {
MainView() // ์ค์ ์ฝํ
์ธ
.privacySensitive() // ← ์ด ํ ์ค
}
์์ ๊ฐ์ด .privacySensitive() ํ ์ค๋ก '๋ธ๋ฌ/๊ฐ๋ฆผ' ์ฒ๋ฆฌ๊ฐ ๋๋ค.
<์์ >
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Text("๋ฏผ๊ฐ ๋ฐ์ดํฐ ํ๋ฉด")
.font(.largeTitle)
.padding()
Text("์ด ํ๋ฉด์ App Switcher์์ ๊ฐ๋ ค์ง๋๋ค.")
.foregroundColor(.secondary)
}
.privacySensitive() // ํ ์ค๋ก ์ปค๋ฒ ๋์
}
}
์ฆ, SwiftUI๋ ์ด ๊ธฐ๋ฅ์ด ์ธ์ด ๋ ๋ฒจ์์ ๋ด์ฅ๋์ด ์๋ ์ ์ธ์ ๋ฐฉ์์ด๊ณ ,
UIKit์ ์ง์ ์๋์ฐ๋ฅผ ๋ง๋ค์ด ๋ฎ๋ ๋ช ๋ น์ ๋ฐฉ์์ผ๋ก ์ ๊ทผํ๋ค๊ณ ๋ณด๋ฉด ๋๋ค.
'๐' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[iOS] New Requirement for Apps Using Sign in with Apple for Account Creation (0) | 2025.10.15 |
---|---|
[Swift] ์ฑ ์ฒซ ์คํ ์ ํด์ผ ํ ์ด๊ธฐํ ์์ ์ ๋ฆฌ(AppDelagate vs SceneDelegate) (0) | 2025.10.05 |
[Swift] SplashView - ์คํ๋์ ํ๋ฉด (0) | 2025.09.28 |
[Swift] KeychainManager (0) | 2025.09.21 |
[Swift] Keychain (0) | 2025.09.14 |