SwiftUI(iOS13)でフルスクリーンモーダルを表示する
久しぶりの記事になります。おはこんばんにちは和尚です!
今日は一昨年Appleから発表されて話題となっているSwiftUIについての記事を書いていきたいと思います
SwiftUI 2はiOS13では使用できない件
現在(2021.3月時点)ではSwiftUIはver.2までリリースされておりますが、残念なことにSwiftUI 2でリリースされた機能はiOS13で使用することができません。SwiftUI 1では一般的なアプリに必要な機能が全然揃っておらず、2以上にライブラリやUIKitに頼ることになります。
必須級ライブラリ
SwiftUIX
↑ SwiftUIにまだ実装されていないものを補ってくれます。説明書がないものがチラホラあるのが残念...
さて、本題であるフルスクリーンのモーダルですが、公式からは「.fullScreenCover」というものが用意されていますがこちらはSwiftUI 2から登場したものとなっており残念ながらiOS13では使用することができません。
ので、今回iOS13でも使えるフルスクリーンのモーダルを作っていきましょう!
※ちなみに筆者、初めてのiOSアプリ実装がSwiftUIとなっておりUIKitも現在絶賛勉強中ですのでお手柔らかに(笑)
フルスクリーンのモーダル実装
1. UIApplicationの拡張
UIApplicationを拡張して一番上のコントローラーを取得するメソッドと、フルスクリーンのモーダルを閉じるメソッドを作成していきます。
extension UIApplication {
/// 一番上にあるコントローラーを取得する
public func getTopViewController() -> UIViewController? {
guard let window = UIApplication.shared
.connectedScenes
.filter({$0.activationState == .foregroundActive})
.map({$0 as? UIWindowScene})
.compactMap({$0})
.first?.windows.first else {
return nil
}
window.makeKeyAndVisible()
guard let rootViewController = window.rootViewController else {
return nil
}
var topController = rootViewController
while let newTopController = topController.presentedViewController {
topController = newTopController
}
return topController
}
/// フルスクリーンのモーダルを閉じる
public func closeModalView() {
UIApplication.shared.getTopViewController()?.dismiss(animated: true, completion: nil)
}
}
2. Viewの拡張
次はSwiftUIのViewでSheetViewやFullCreenCoverのようにフルスクリーンのモーダルが使用できるようにViewを拡張していきます。
extension View {
public func fullScreenView<Content>(
isPresented: Binding<Bool>,
@ViewBuilder content: @escaping () -> Content
) -> some View where Content: View {
if isPresented.wrappedValue {
let window = UIApplication.shared.windows.last
window?.isHidden = true
let view = content()
let viewController = UIHostingController(rootView: view)
viewController.modalPresentationStyle = .fullScreen
DispatchQueue.main.async {
guard let tvc = UIApplication.shared.getTopViewController() else {
return
}
tvc.present(viewController, animated: true, completion: nil)
isPresented.wrappedValue = false
}
}
return self
}
}
実際に使ってみよう!
実際にViewで使用してみましょう!今回適当なサンプルViewを用意してみました!
こちら参考にみなさん是非使ってみてください
struct ContentView: View {
@State private var showModal: Bool = false
var body: some View {
VStack {
Spacer()
Button(action: {
showModal.toggle()
}, label: {
Text("フルスクリーンのモーダルを表示する")
})
Spacer()
}
.fullScreenView(isPresented: $showModal) {
ModalView()
}
}
}
struct ModalView: View {
var body: some View {
ZStack {
Color.green.edgesIgnoringSafeArea(.all)
Button(action: {
UIApplication.shared.closeModalView()
}, label: {
Text("閉じる")
})
}
}
}
最後に
iOS13でSwiftUIとCombineを使用する際は、特にiOS13.0からiOS13.2までは気をつけましょう。バグがかなり多いです...
しかも、たちが悪いことにシュミレーションでは再現せず実機のみで再現するバグもいくつかあります。
実機がない場合は、SwiftUIをiOS13で使用することをおすすめしません
それでもiOS13を含めたい場合は最低でもiOS13.3以上にしましょう。
Let's Enjoy SwiftUI!!