2015-03-06 13 views
28

Quando si accede UIapplication's finestra principale viene restituito come un UIWindow??Perché la finestra principale di tipo double opzionale?

let view = UIApplication.sharedApplication().delegate?.window // view:UIWindow?? 

Perché è tornando come un doppio opzionale e cosa significa e se messo in un if let devo aggiungere una ! dopo?

if let view = UIApplication.sharedApplication().delegate?.window! 

Il mio primo pensiero è stato quello di sostituire ? con un ! dopo delegato, ma che non era la soluzione.

+1

Non lo è: 'finestra var opzionale: UIWindow? {get set} '- il tuo' delegate' è opzionale-chained (usando '?'), che si traduce in un altro livello di optionals. –

+0

Ma anche quando metto! a destra del delegato invece, dà ancora ?? – Arbitur

+0

Domanda simile qui: http://stackoverflow.com/questions/25175544/uiwindow-does-not-have-member-named-bounds. –

risposta

23

@ Matt ha i dettagli, ma c'è una (un po 'orribile po' impressionante,) soluzione. (Vedi modifica sotto, però)

let window = app.delegate?.window??.`self`() 

Lascerò la comprensione di questa riga di codice come esercizio per il lettore.

OK, mento, abbattiamolo.

app.delegate?.window 

OK, finora tutto bene. A questo punto abbiamo il UIWindow?? che ci sta dando un mal di testa (e credo sia un bug in Swift disconnetti tra Swift e Cocoa). Vogliamo collassarlo due volte. Possiamo farlo con il concatenamento opzionale (?.), ma che si apre e riavvolge, quindi siamo tornati da dove siamo partiti. È possibile anche double-optional-chain, con ??. che è bizzarro, ma funziona.

Questo è ottimo, ma ?? non è un operatore di suffisso legale. Devi effettivamente concatenare a qualcosa. Bene, vogliamo ricondurre a se stesso (cioè "identità"). Il protocollo NSObject ci fornisce un metodo di identità: self.

self è un metodo su NSObject, ma è anche una parola riservata in Swift, quindi la sintassi perché è `self`()

E così otteniamo la nostra follia sopra. Fatelo come volete

Nota che dal ??. funziona, questo non è tecnicamente necessario. Puoi semplicemente accettare che view è UIWindow?? e utilizzare ??. su di esso come view??.frame. È un po 'rumoroso, ma probabilmente non crea problemi reali per i pochi posti in cui dovrebbe essere necessario.

(*) Ero solito considerare questo come un bug in Swift, ma non è risolvibile direttamente tramite concatenamento opzionale. Il problema è che non esiste alcun concatenamento opzionale dopo lo window. Quindi non sono sicuro di dove sia il posto giusto per sistemarlo. Swift potrebbe consentire a un suffisso - ? di "appiattire" senza richiedere il concatenamento, ma ciò sembra strano. Immagino che l'operatore giusto sarebbe interrobang delegate?.window‽: D Sono sicuro che non causerebbe alcuna confusione.

EDIT:

Joseph Lord pointed out the better solution (che è molto simile alle tecniche ho usato per evitare banali se-lasciare, ma non aveva pensato a questo modo prima):

let window = app.delegate?.window ?? nil // UIWindow? 

Io sono d'accordo con lui che questa è la risposta giusta.

+0

Sembra il lavoro per un buon vecchio 'flatMap'ping – DeFrenZ

+0

,'? .' è 'flatMap', quindi non sono sicuro che lo risolva. –

+0

Tuttavia, ciò non viene compilato (Swift 1.1) in un parco giochi. Probabilmente perché non sa se dovrebbe restituire un 'UIWindow ??' o un 'UIWindow?' (Entrambi sarebbero validi) – DeFrenZ

12

È perché la proprietà window è in dubbio (è facoltativa). Pertanto, è necessario un punto interrogativo perché potrebbe esserci o meno una proprietà della finestra e un altro punto interrogativo poiché il valore restituito dalla proprietà della finestra è di per sé facoltativo. Quindi otteniamo un Opzionale con doppio avvolgimento (come ho spiegato nel mio tutorial: scorri fino alla casella Suggerimento dove parlo di cosa succede quando una proprietà facoltativa ha un valore Opzionale).

Così, un modo per esprimere questo sarebbe in due fasi - una per lanciare (e scartare che opzionale), e uno per andare a prendere la finestra (e scartare che opzionale):

if let del = UIApplication.sharedApplication().delegate as? AppDelegate { 
    if let view = del.window { 

Ora view è una UIWindow.

Ovviamente, se sei sicuro del terreno (che probabilmente sei), puoi forzare il cast nella prima riga e lo scartare nella seconda riga. Così, in Swift 1.2:

let del = UIApplication.sharedApplication().delegate as! AppDelegate 
let view = del.window! 
+2

Almeno in 6.3, 'delegate' è di tipo' UIApplicationDelegate? '. Il concatenamento opzionale dovrebbe sbarazzarsi di quello strato aggiuntivo di opzionale. –

+1

@RobNapier Ri-espresso. - Non ho mai gradito questo particolare doppio opzionale. Swift va un po 'lontano qui, a mio avviso. – matt

+0

concordato. Ma sarebbe corretto se il concatenamento opzionale supportasse objc-protocol-optional (che considero un bug che non funziona). –

0

Con avvento delle Swift2 per me una soluzione di consueto in questo tipo di casi è

if let _window = UIApplication.sharedApplication().delegate?.window, window = _window { 
    // Some code... i.e. 
    let frame = window.frame 
} 
1

Oh il doppio facoltativo! A volte è possibile utilizzare un doppio-bang (due punti esclamativi) ma non è possibile eseguire il cast in questo modo con il binding facoltativo. Quindi ... il mio remix di tutti gli altri codice che ottiene un oggetto chiamato UIWindow window del tipo non-optional:

guard let w = UIApplication.shared.delegate?.window, let window = w else { return } 

Ma non perdiamo tempo e basta usare

let window = UIApplication.shared.delegate!.window!! 

e sia fatta .

Problemi correlati