Open7
【iOS】UIKitからSwiftUIに移行するメモ(UaaL / WebViewを利用)
概要
- UIKitとStoryboardで作成されたiOSアプリをSwiftUIへ移行する。
- UaaL(Unity as a Library)とWebViewを利用している。
SwiftUIや@Stateについて
SwiftUIについて
チュートリアルが分かりやすい。UIKitからの移行についても書かれていてよい。
@Stateについて
- 値が更新されたときにビューも更新されるようにする
- その値が必要な最上位viewでprivateで宣言すること。subviewに対しては、readonlyとして直接渡すか、read-writeできるようにbindingとして渡すか。
実装
シンプルなState
struct PlayButton: View {
@State private var isPlaying: Bool = false
var body: some View {
Button(isPlaying ? "Pause" : "Play") {
isPlaying.toggle()
}
}
}
ボタンを押す前
ボタンを押した後
Bindingとしてsubviewにわたす
PlayerView.swift
struct PlayerView: View {
@State private var isPlaying: Bool = false
var body: some View {
Text(isPlaying ? "Playing now" : "Pausing now")
PlayButton(isPlaying: $isPlaying)
}
}
PlayButton.swift
struct PlayButton: View {
@Binding var isPlaying: Bool
var body: some View {
Button(isPlaying ? "Pause" : "Play") {
isPlaying.toggle()
}
}
}
プロジェクト全体をSwiftUIに対応させる
- ほぼこの記事を参考にした
- AppDelegateも引き続き使うことができる
UaaL部分の表示
- 参考はこの記事
- ただし、SwiftUIのAppの中から
Unity.shared.start
を実行すると、Metal関連のエラーがでた
'Texture Descriptor Validation MTLTextureDescriptor has width of zero.
- AppDelegateのapplicationの中で初期化し、pauseしておいて対処
AppDelegate.swift
class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
...略...
Unity.shared.start(application, didFinishLaunchingWithOptions: launchOptions)
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
Unity.shared.pause()
}
UnityView.swift
struct UnityView: UIViewRepresentable {
func makeUIView(context: Context) -> UIView {
return Unity.shared.view
}
func updateUIView(_ view: UIView, context: Context) {
}
}
Unity部分が表示されたときに強制的にlandscape向きに回転させる
- 参考
- UIKitのときには、以下のよぅに
supportedInterfaceOrientations
で向きを変更していたが、SwiftUIではこれは出来ない模様。
UnityViewController.swift
class UnityViewController: UIViewController {
...略...
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return [.landscapeLeft, .landscapeRight]
}
- また、iOS16以上では
UIDevice.current.setValue
は使えない
WebViewの表示
-
こちらの記事がとても参考になる
- ただ、CoordinatorクラスがWebViewのstructの中で定義されていない点を直した。
https://tech.yappli.io/entry/ios-webkit-with-swiftui
- ただ、CoordinatorクラスがWebViewのstructの中で定義されていない点を直した。
-
delegateなども含めて以下のように実装
WebView.swift
struct WebView: UIViewRepresentable {
@Binding var isLoading: Bool
func makeUIView(context: Context) -> WKWebView {
let url = URL(string: "https://example.com/")
let request = URLRequest(url: url!)
let webView = WKWebView()
let coordinator = context.coordinator
webView.navigationDelegate = coordinator
webView.scrollView.delegate = coordinator
webView.configuration.userContentController.add(coordinator, name: "event")
webView.load(request)
return webView
}
func updateUIView(_ uiView: WKWebView, context: Context) {}
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler, UIScrollViewDelegate {
private let parent: WebView
init(_ webView: WebView) {
self.parent = webView
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction) async
-> WKNavigationActionPolicy
{
...以下略(プロトコルに準拠した実装をしていく...
他参考
State、Observableなどの概念について参考