0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

NotificationCenterを使って複数のViewControllerに通知してみる

Posted at

はじめに

iOS設計パターン入門を読んでいてNotificationCenterがわからなかったのでメモ

できるもの

ボタンでカウントアップして各ViewControllerに値が変わったことを伝える
Simulator Screen Shot - iPhone 14 Pro - 2023-04-21 at 11.58.16.png
カウント0のときボタンをタップしたら、
Simulator Screen Shot - iPhone 14 Pro - 2023-04-21 at 11.58.16.pngSimulator Screen Shot - iPhone 14 Pro - 2023-04-21 at 11.58.16.png
各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を作成

カウントアップするボタンとラベルを置いてます。
スクリーンショット 2023-04-21 12.22.37.png

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?