最近Swiftでアプリを開発しています。
UIKit(Storyboard)のスタイリングがとにかく苦痛で、途中まではコードベースでなんとか頑張っていましたが限界を迎えました。
UIKitの中でもSwiftUIを使うのは少し調べると対応できたのですが、データのバインディング周りで少し苦労したので共有します。
ボトムナビに以下のような戻る・進むアイコンを表示するものを作ります。
UIKitの中でSwiftUIを使う
SwiftUIのViewを作成
これだけでOK。UIKitに比べかなり分かりやすいですね。
@ObservedObject
はこのコンポーネント外の状態管理に使っています。
BottomNav.swift
import SwiftUI
import Combine
struct BottomNav: View {
@ObservedObject var historyViewModel: HistoryViewModel
var back: () -> Void
var forward: () -> Void
var body: some View {
HStack(spacing: 50) {
Button(action: { back() }
) {
Image(systemName: "arrow.uturn.backward")
}
.disabled(historyViewModel.isDisabledBack)
Button(action: { forward() }
) {
Image(systemName: "arrow.uturn.forward")
}
.disabled(historyViewModel.isDisabledForward)
}
}
}
ObservableObjectプロトコルを使った状態管理
HistoryViewModel.swift
import Combine
class HistoryViewModel: ObservableObject {
@Published var parentNodeBackHistory: [SCNNode] = []
@Published var parentNodeForwardHistory: [SCNNode] = []
var isDisabledBack: Bool {
return parentNodeBackHistory.isEmpty
}
func pushBackHistory(node: SCNNode) {
parentNodeBackHistory.append(node)
}
}
UIViewControllerのviewにSwiftUIを表示
XxxViewController.swift
class XxxViewController: UIViewController {
private let historyViewModel = HistoryViewModel()
override func viewDidLoad() {
super.viewDidLoad()
let vc: UIHostingController = UIHostingController(
rootView: BottomNav(
historyViewModel: historyViewModel,
back: popBackHistory,
forward: { print("forward") }
)
);
view.addSubview(vc.view);
vc.view.translatesAutoresizingMaskIntoConstraints = false
vc.view.heightAnchor.constraint(equalToConstant: 50).isActive = true
// 左右いっぱいに底から10pxの箇所に配置
vc.view.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
vc.view.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
vc.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -10.0).isActive = true
...
historyViewModel.pushBackHistory(node: node)
これでXxxViewController内からSwiftUIへデータが流れるようになりました。
逆にSwiftUIの変更をUIKitに流す場合は@Publishedアトリビュート変数の.sinkメソッドを使うと実現できます。
さいごに
今までアプリ開発はFlutterでいいのではと思っていたのですが、今回iOSの新機能使いたく、Swiftでの開発が必要になりました。
SwiftUIを使うことで開発体験がかなり向上したのと、何よりiOSの新機能を使っての開発が楽しいです!