2015-12-16 22 views
6

Sto cercando di inserire un tocco 3D in un'applicazione e ho incontrato un problema strano in cui l'assegno forceTouchCapability sta tornando nil su viewDidLoad ma non in viewWillAppear/viewDidAppear.forceTouchCapability ritorno nullo

Sono consapevole che questo è disponibile solo su iOS 9+ controlli, in modo che ho aggiunto per verificare che la proprietà traitCollection sul controller della vista risponde al forceTouchCapability come nell'esempio seguente:

- (void)loadView { 

    self.view = [[MyView alloc] init]; 
} 

- (void)viewDidLoad { 

    [super viewDidLoad]; 

    // Checking the force touch availability here 
    if ([self.traitCollection respondsToSelector:@selector(forceTouchCapability)] && 
     self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) { 

     // This won't get called because forceTouchCapability is returning nil 
     // which corresponds to UIForceTouchCapabilityUnknown 
     [self registerForPreviewingWithDelegate:self sourceView:self.view]; 
    } 
} 

In LLDB con un punto di interruzione nell'istruzione if, immettendo po [self.traitCollection forceTouchCapability] restituisce nil che corrisponde a UIForceTouchCapabilityUnknown. Tuttavia, lo stesso traitCollection non è nil.

Secondo la documentazione per UIForceTouchCapabilityUnknown:

UIForceTouchCapabilityUnknown: The availability of 3D Touch is unknown. For example, if you create a view but have not yet added it to your app’s view hierarchy, the view’s trait collection has this value.

ha la vista non è stato aggiunto alla gerarchia da questo punto?

Sono curioso di sapere se qualcuno ha già avuto a che fare con questo problema e come aggirare questo problema? Vorrei evitare di aggiungere questo nel viewDidAppear in quanto può essere chiamato un po '.

Se aiuta, sto correndo questo su un 6S su iOS 9.1 con Xcode 7.2

risposta

16

La vista non è stato aggiunto alla vista Gerarchia ancora. Si può vedere questo facilmente controllando per un superview nella console di debug

(lldb) po self.view.superview 
nil 

Se questo è quello che stai vedendo, la vista non è stato aggiunto ad una gerarchia ancora: quindi bisogna mettere il vostro assegno altrove.

Questo è un po 'di confusione perché nell'app di esempio di Apple ViewControllerPreview è in viewDidLoad. Ma in realtà dovrebbe essere in traitCollectionDidChange :, perché allora sei sicuro che la vista è stata aggiunta alla gerarchia dell'app.

Questo è il codice che uso (funziona su iOS 8, se non è necessario il supporto che si sentono liberi di spostare il condizionale esterno).

- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { 
    [super traitCollectionDidChange:previousTraitCollection]; 

    if ([self.traitCollection respondsToSelector:@selector(forceTouchCapability)]) { 
     if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) { 
      // retain the context to avoid registering more than once 
      if (!self.previewingContext) { 
       self.previewingContext = [self registerForPreviewingWithDelegate:self sourceView:self.view]; 
      } 
     } else { 
      [self unregisterForPreviewingWithContext:self.previewingContext]; 
      self.previewingContext = nil; 
     } 
    } 
} 

L'ulteriore vantaggio di questo è che la visualizzazione sarà registrato/non registrato se l'utente cambia le impostazioni touch 3D mentre l'applicazione è in esecuzione.

+0

Sei fantastico, grazie! – ajfigueroa

+0

@bpapa buona soluzione. Per favore considera di raccomandare l'uso di p invece di po. p guarda solo dentro, po è usato per l'esecuzione –

1

Ho visto questo problema e ho scoperto che il modo più semplice per verificare se il dispositivo può supportare il tocco forzato o meno è farlo tramite l'istanza dello schermo. Questo tipo ha senso perché la capacità è una proprietà dello schermo. Farlo in questo modo significa che non devi preoccuparti del ciclo di vita di un viewcontroller o di una vista.

func canForceTouch() -> Bool 
{ 
    if iOS9OrHigher // pseudocode, a function that makes sure u only do this check on ios9 or higher 
    { 
     return UIScreen.mainScreen().traitCollection.forceTouchCapability == .Available 
    } 
    return false 
} 
0

Come quello che @bpapa detto, Your view non ha aggiunto per visualizzare gerarchia ancora, ma la mia soluzione è diversa po ':

var token:dispatch_once_t = 0 
override func viewDidAppear(animated: Bool) { 
    dispatch_once(&token) { 
     // Force Touch Checking 
     if #available(iOS 9.0, *) { 
      if self.traitCollection.forceTouchCapability == .Available { 
       self.registerForPreviewingWithDelegate(self, sourceView: self.view) 
      } 
     } 
    } 
} 
+0

Perché iniziare a portare code diverse in questo?Realmente conforme all'SDK implementando un metodo opzionale è un approccio molto più pulito. – bpapa

+0

@bpapa Stiamo ancora utilizzando la coda principale (vedere questa risposta http://stackoverflow.com/a/25900466) ... Sto scrivendo questo codice in viewDidAppear per garantire che tutte le viste siano nella gerarchia .. E dispatching una volta perché viewDidAppear può essere chiamato più volte sai che :) – Husam