2014-06-12 11 views
21

In Objective-C, io normalmente usare qualcosa di simile a questo:L'aggiunta di osservatore per KVO senza puntatori utilizzando Swift

static NSString *kViewTransformChanged = @"view transform changed"; 
// or 
static const void *kViewTransformChanged = &kViewTransformChanged; 

[clearContentView addObserver:self 
         forKeyPath:@"transform" 
          options:NSKeyValueObservingOptionNew 
          context:&kViewTransformChanged]; 

Ho due metodi di overload da scegliere per aggiungere un osservatore per KVO con l'unica differenza l'argomento contesto:

clearContentView.addObserver(observer: NSObject?, forKeyPath: String?, options: NSKeyValueObservingOptions, context: CMutableVoidPointer) 
clearContentView.addObserver(observer: NSObject?, forKeyPath: String?, options: NSKeyValueObservingOptions, kvoContext: KVOContext) 

Con Swift non usare puntatori, io non sono sicuro di come dereference un puntatore da utilizzare per il primo metodo.

Se creo la mia costante KVOContext per l'uso con il secondo metodo, finisco con esso chiedere questo:

let test:KVOContext = KVOContext.fromVoidContext(context: CMutableVoidPointer) 

EDIT: Qual è la differenza tra CMutableVoidPointer e KVOContext? Qualcuno può darmi un esempio di come usarli entrambi e quando userei uno rispetto all'altro?

EDIT # 2: Un dev su Apple ha appena postato questo nei forum: KVOContext sta andando via; usando un riferimento globale come il tuo contesto è la strada da percorrere adesso.

+0

stai chiedendo come creare un CMutableVoidPointer? –

+0

Ho modificato il mio post per essere più preciso. –

risposta

15

Ora che KVOContext è passato in Xcode 6 beta 3, è possibile eseguire quanto segue. Definire un (cioè non una proprietà di classe) globale in questo modo:

let myContext = UnsafePointer<()>() 

Aggiungi un osservatore:

observee.addObserver(observer, forKeyPath: …, options: nil, context: myContext) 

nell'osservatore:

override func observeValueForKeyPath(keyPath: String!, ofObject object: AnyObject!, change: [NSObject : AnyObject]!, context: UnsafePointer<()>) { 
    if context == myContext { 
     … 
    } else { 
     super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context) 
    } 
} 
+3

'UnsafePointer <()>()' è un puntatore ** null **. Utilizzando un puntatore nullo come il contesto non ti dà un valore univoco in modo particolarmente affidabile. (Mi rendo conto che al giorno d'oggi non è aggiornato - così è la mia risposta - ho pensato che questo dovrebbe essere menzionato per completezza.) – jtbandes

49

V'è ora una tecnica ufficialmente recommended in the documentation, che è quello di creare una variabile mutabile privata e utilizzare il suo indirizzo come contesto.

(aggiornato per Swift 3 su 2017-01-09)

// Set up non-zero-sized storage. We don't intend to mutate this variable, 
// but it needs to be `var` so we can pass its address in as UnsafeMutablePointer. 
private static var myContext = 0 
// NOTE: `static` is not necessary if you want it to be a global variable 

observee.addObserver(self, forKeyPath: …, options: [], context: &MyClass.myContext) 

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) { 
    if context == &myContext { 
     … 
    } 
    else { 
     super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) 
    } 
} 
-1

Esempio completo con Swift:

// 
// AppDelegate.swift 
// Photos-MediaFramework-swift 
// 
// Created by Phurg on 11/11/16. 
// 
// Displays URLs for all photos in Photos Library 
// 
// @see http://stackoverflow.com/questions/30144547/programmatic-access-to-the-photos-library-on-mac-os-x-photokit-photos-framewo 
// 

import Cocoa 
import MediaLibrary 

// For KVO: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID12 
private var mediaLibraryLoaded = 1 
private var rootMediaGroupLoaded = 2 
private var mediaObjectsLoaded = 3 

@NSApplicationMain 
class AppDelegate: NSObject, NSApplicationDelegate { 

    @IBOutlet weak var window: NSWindow! 
    var mediaLibrary : MLMediaLibrary! 
    var allPhotosAlbum : MLMediaGroup! 


    func applicationDidFinishLaunching(_ aNotification: Notification) { 

     NSLog("applicationDidFinishLaunching:"); 

     let options:[String:Any] = [ 
      MLMediaLoadSourceTypesKey: MLMediaSourceType.image.rawValue, // Can't be Swift enum 
      MLMediaLoadIncludeSourcesKey: [MLMediaSourcePhotosIdentifier], // Array 
     ] 

     self.mediaLibrary = MLMediaLibrary(options:options) 
     NSLog("applicationDidFinishLaunching: mediaLibrary=%@", self.mediaLibrary); 

     self.mediaLibrary.addObserver(self, forKeyPath:"mediaSources", options:[], context:&mediaLibraryLoaded) 
     NSLog("applicationDidFinishLaunching: added mediaSources observer"); 

     // Force load 
     self.mediaLibrary.mediaSources?[MLMediaSourcePhotosIdentifier] 

     NSLog("applicationDidFinishLaunching: done"); 

    } 

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 
     NSLog("observeValue: keyPath=%@", keyPath!) 
     let mediaSource:MLMediaSource = self.mediaLibrary.mediaSources![MLMediaSourcePhotosIdentifier]! 

     if (context == &mediaLibraryLoaded) { 
      NSLog("observeValue: mediaLibraryLoaded") 
      mediaSource.addObserver(self, forKeyPath:"rootMediaGroup", options:[], context:&rootMediaGroupLoaded) 
      // Force load 
      mediaSource.rootMediaGroup 

     } else if (context == &rootMediaGroupLoaded) { 
      NSLog("observeValue: rootMediaGroupLoaded") 
      let albums:MLMediaGroup = mediaSource.mediaGroup(forIdentifier:"TopLevelAlbums")! 
      for album in albums.childGroups! { 
       let albumIdentifier:String = album.attributes["identifier"] as! String 
       if (albumIdentifier == "allPhotosAlbum") { 
        self.allPhotosAlbum = album 
        album.addObserver(self, forKeyPath:"mediaObjects", options:[], context:&mediaObjectsLoaded) 
        // Force load 
        album.mediaObjects 
       } 
      } 

     } else if (context == &mediaObjectsLoaded) { 
      NSLog("observeValue: mediaObjectsLoaded") 
      let mediaObjects:[MLMediaObject] = self.allPhotosAlbum.mediaObjects! 
      for mediaObject in mediaObjects { 
       let url:URL? = mediaObject.url 
       // URL does not extend NSObject, so can't be passed to NSLog; use string interpolation 
       NSLog("%@", "\(url)") 
      } 
     } 
    } 

} 
+2

Puoi spiegare questo esempio completo? – RamenChef

Problemi correlati