web-dev-qa-db-ja.com

Swift 3でカスタム通知を作成するにはどうすればよいですか?

Objective-Cでは、カスタム通知は単なるNSStringですが、Swift 3のWWDCバージョンでは、それがどうあるべきかは明らかではありません。

102
hexdreamer

このためにプロトコルを使用することもできます

protocol NotificationName {
    var name: Notification.Name { get }
}

extension RawRepresentable where RawValue == String, Self: NotificationName {
    var name: Notification.Name {
        get {
            return Notification.Name(self.rawValue)
        }
    }
}

そして、通知名をenumとして任意の場所に定義します。例えば:

class MyClass {
    enum Notifications: String, NotificationName {
        case myNotification
    }
}

そして、それを次のように使用します

NotificationCenter.default.post(name: Notifications.myNotification.name, object: nil)

これにより、通知名はFoundation Notification.Nameから分離されます。また、Notification.Nameの実装が変更された場合にのみ、プロトコルを変更する必要があります。

26
halil_g

それを達成するためのクリーナー(私は思う)の方法があります

extension Notification.Name {

    static let onSelectedSkin = Notification.Name("on-selected-skin")
}

そして、あなたはこのようにそれを使用することができます

NotificationCenter.default.post(name: .onSelectedSkin, object: selectedSkin)
345
Cesar Varela

Notification.postは次のように定義されます:

public func post(name aName: NSNotification.Name, object anObject: AnyObject?)

Objective-Cでは、通知名はプレーンなNSStringです。 Swiftでは、NSNotification.Nameとして定義されています。

NSNotification.Nameは次のように定義されます:

public struct Name : RawRepresentable, Equatable, Hashable, Comparable {
    public init(_ rawValue: String)
    public init(rawValue: String)
}

私はそれが列挙型であり、それ以上の利益はないように見えるカスタム構造ではないと予想するので、これはちょっと奇妙です。

NSNotification.Nameの通知にはタイプエイリアスがあります。

public typealias Name = NSNotification.Name

紛らわしい部分は、通知とNSNotificationの両方がSwiftに存在することです

したがって、独自のカスタム通知を定義するには、次のようなことを行います。

public class MyClass {
    static let myNotification = Notification.Name("myNotification")
}

それからそれを呼び出すには:

NotificationCenter.default().post(name: MyClass.myNotification, object: self)
35
hexdreamer

より簡単な方法:

let name:NSNotification.Name = NSNotification.Name("notificationName")
NotificationCenter.default.post(name: name, object: nil)
13
Zoltan Varadi

NSNotification.Nameにカスタム初期化子を追加できます

extension NSNotification.Name {
    enum Notifications: String {
        case foo, bar
    }
    init(_ value: Notifications) {
        self = NSNotification.Name(value.rawValue)
    }
}

使用法:

NotificationCenter.default.post(name: Notification.Name(.foo), object: nil)
10
efremidze

@CesarVarelaが提案したものに似た別のオプションを提案するかもしれません。

extension Notification.Name {
    static var notificationName: Notification.Name {
        return .init("notificationName")
    }
}

これにより、通知を簡単に投稿および購読できます。

NotificationCenter.default.post(Notification(name: .notificationName))

これがお役に立てば幸いです。

6
Mikhail Glotov

私はあちこちで物事を混合して独自の実装を行いましたが、これが最も便利だと感じました。興味のある人のための共有:

public extension Notification {
    public class MyApp {
        public static let Something = Notification.Name("Notification.MyApp.Something")
    }
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(self.onSomethingChange(notification:)),
                                               name: Notification.MyApp.Something,
                                               object: nil)
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    @IBAction func btnTapped(_ sender: UIButton) {
        NotificationCenter.default.post(name: Notification.MyApp.Something,
                                      object: self,
                                    userInfo: [Notification.MyApp.Something:"foo"])
    }

    func onSomethingChange(notification:NSNotification) {
        print("notification received")
        let userInfo = notification.userInfo!
        let key = Notification.MyApp.Something 
        let something = userInfo[key]! as! String //Yes, this works :)
        print(something)
    }
}
4
inigo333
NSNotification.Name(rawValue: "myNotificationName")
3
Lee Probert

これは単なる参考です

// Add observer:
NotificationCenter.default.addObserver(self,
    selector: #selector(notificationCallback),
    name: MyClass.myNotification,
    object: nil)

    // Post notification:
    let userInfo = ["foo": 1, "bar": "baz"] as [String: Any]
    NotificationCenter.default.post(name: MyClass.myNotification,
        object: nil,
        userInfo: userInfo)
2
user6943269

列挙型を使用する利点は、名前が正しいことをコンパイラに確認させることです。潜在的な問題を減らし、リファクタリングを簡単にします。

通知名に引用符で囲まれた文字列の代わりに列挙を使用したい人のために、このコードはトリックを行います:

enum MyNotification: String {
    case somethingHappened
    case somethingElseHappened
    case anotherNotification
    case oneMore
}

extension NotificationCenter {
    func add(observer: Any, selector: Selector, 
             notification: MyNotification, object: Any? = nil) {
        addObserver(observer, selector: selector, 
                    name: Notification.Name(notification.rawValue),
                    object: object)
    }
    func post(notification: MyNotification, 
              object: Any? = nil, userInfo: [AnyHashable: Any]? = nil) {
        post(name: NSNotification.Name(rawValue: notification.rawValue), 
             object: object, userInfo: userInfo)
    }
}

その後、次のように使用できます。

NotificationCenter.default.post(.somethingHappened)

質問とは無関係ですが、引用符付きの文字列を入力しないように、ストーリーボードセグエでも同じことができます。

enum StoryboardSegue: String {
    case toHere
    case toThere
    case unwindToX
}

extension UIViewController {
    func perform(segue: StoryboardSegue) {
        performSegue(withIdentifier: segue.rawValue, sender: self)
    }
}

次に、View Controllerで次のように呼び出します。

perform(segue: .unwindToX)
1
Eneko Alonso

文字列のみのカスタム通知を使用する場合、String以外のクラスを拡張する理由はありません

    extension String {
        var notificationName : Notification.Name{
            return Notification.Name.init(self)
        }
    }
0
Quang Vĩnh Hà

@CesarVarelaの答えは良いですが、コードを少しきれいにするために、次のことができます:

extension Notification.Name {
    typealias Name = Notification.Name

    static let onSelectedSkin = Name("on-selected-skin")
    static let onFoo = Name("on-foo")
}
0
ThomasW