はじめに
iOS設計パターン入門を読んでいてNotificationCenterがわからなかったのでメモ
できるもの
ボタンでカウントアップして各ViewControllerに値が変わったことを伝える
カウント0のときボタンをタップしたら、
各VCに通知されカウントの値が反映される
CountModel.swiftを作成
Modelとなるものです。
今回はCountModelに通知を出すnotificationCenter.postを作成してます。
import Foundation
final class CountModel {
// 値を共有したいのでシングルトンにしてます
static var shared: CountModel = .init()
private init() {}
let notificationCenter = NotificationCenter()
// private(set)は変更できないようにアクセス制限するが読み取りは可能
// countに変更があったら通知を出す
private(set) var count = 0 {
didSet {
notificationCenter.post(name: .init(rawValue: "count"), object: nil, userInfo: ["count": count])
}
}
func countUp() {
count += 1
}
}
各ストーリーボードとViewControllerを作成
import UIKit
class RedViewController: UIViewController {
@IBOutlet private weak var countLabel: UILabel!
@IBOutlet private weak var countButton: UIButton!
@IBAction private func tapped(_ sender: Any) {
countModel?.countUp()
}
// MainTabBarControllerからViewControllerにモデルを渡しています
// MainTabBarControllerが呼ばれたらdidSetでregisterModel()も呼ばれるのでNotificationCenterクラスへの登録完了
// 登録してから通知を出さなきゃきけない
var countModel: CountModel? {
didSet {
registerModel()
}
}
private func registerModel() {
guard let model = countModel else { return }
model.notificationCenter.addObserver(forName: .init(rawValue: "count"), object: nil, queue: nil, using: { [weak self] notification in
if let count = notification.userInfo?["count"] as? Int {
self?.countLabel?.text = "\(count)"
}
})
}
override func viewDidLoad() {
super.viewDidLoad()
countLabel.text = countModel?.count.description
}
}
名前と色だけ変えた同じ構成のストーリーボードとViewControllerを3つ作成します。
MainTabBarControllerを作成
ここでViewControllerの外からModelを渡してるのがポイント
iOS設計パターン入門を読んでこの書き方を知りました!
import UIKit
class MainTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
initTabBar()
}
func initTabBar() {
guard let redVC = UIStoryboard(name: "Red", bundle: nil).instantiateInitialViewController() as? RedViewController else {
return
}
// ここでViewControllerにモデルを渡しています
redVC.countModel = CountModel.shared
redVC.tabBarItem = UITabBarItem(tabBarSystemItem: .bookmarks, tag: 0)
let redNVC = UINavigationController(rootViewController: redVC)
guard let blueVC = UIStoryboard(name: "Blue", bundle: nil).instantiateInitialViewController() as? BlueViewController else {
return
}
blueVC.countModel = CountModel.shared
blueVC.tabBarItem = UITabBarItem(tabBarSystemItem: .contacts, tag: 1)
let blueNVC = UINavigationController(rootViewController: blueVC)
guard let greenVC = UIStoryboard(name: "Green", bundle: nil).instantiateInitialViewController() as? GreenViewController else {
return
}
greenVC.countModel = CountModel.shared
greenVC.tabBarItem = UITabBarItem(tabBarSystemItem: .favorites, tag: 2)
let greenNVC = UINavigationController(rootViewController: greenVC)
setViewControllers([redNVC, blueNVC, greenNVC], animated: false)
}
}
MainTabBarControllerを起動画面にする
タブ画面から起動させるので下記の記事を参考に
https://qiita.com/Imael/items/e908aef1e6fc077f29fc
プロジェクトからMain.storyboardとViewControllerを削除
TARGETSのinfoのCustom iOS Target Propertiesの → Main storyboard file base nameを削除
TARGETSのinfoのCustom iOS Target PropertiesのApplication Scene Manifest → Scene Configuration→ Application SessionRole →Item 0(default Configuration)→Storyboard Nameを削除
SceneDelegate.swiftの変更
下記コードに書き換えます。
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// 起動時の画面をMainTabBarController()に設定
guard let scene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: scene)
window?.rootViewController = MainTabBarController()
window?.makeKeyAndVisible()
}
}
おわりに
間違い等ありましたら教えてください!
サンプルです
https://github.com/kabikira/SampleNotificationCenter
参考
iOS設計パターン入門
https://peaks.cc/books/iOS_architecture
iOS設計パターン入門のサンプルコード
https://github.com/peaks-cc/iOS_architecture_samplecode
[増補改訂第3版]Swift実践入門
https://qiita.com/st43/items/ab76494ce6e33a90371e
https://qiita.com/Tatsuya_ss0121/items/f1ae1e7d4c5bca08c70b