C'è un evento di ridimensionamento di UIView?


102

Ho una vista che contiene righe e colonne di visualizzazioni di immagini.

Se questa visualizzazione viene ridimensionata, è necessario riorganizzare le posizioni delle visualizzazioni delle immagini.

Questa vista è una vista secondaria di un'altra vista che viene ridimensionata.

C'è un modo per rilevare quando questa visualizzazione viene ridimensionata?


Quando viene ridimensionata la vista? Quando il dispositivo ruota o quando l'utente lo ruota utilizzando il multi-touch?
Evan Mulawski

Questa vista viene ridimensionata quando l'utente tocca un pulsante in un'altra vista (vista principale).
live-love

Risposte:


98

Come ha commentato Uli di seguito, il modo corretto per farlo è sovrascrivere layoutSubviewse impaginare le ImageViews lì.

Se, per qualche motivo, non puoi creare sottoclassi e sovrascrivere layoutSubviews, l'osservazione boundsdovrebbe funzionare, anche quando sei un po 'sporco. Ancora peggio, c'è un rischio con l'osservazione: Apple non garantisce che KVO funzioni sulle classi UIKit. Leggi la discussione con l'ingegnere Apple qui: Quando viene rilasciato un oggetto associato?

risposta originale:

Puoi utilizzare l'osservazione di valori-chiave:

[yourView addObserver:self forKeyPath:@"bounds" options:0 context:nil];

e implementare:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (object == yourView && [keyPath isEqualToString:@"bounds"]) {
        // do your stuff, or better schedule to run later using performSelector:withObject:afterDuration:
    }
}

2
In realtà, la disposizione delle sottoview è esattamente lo scopo di layoutSubviews, quindi osservare i cambiamenti di dimensione, sebbene funzionale, è un cattivo design IMO.
Uliwitness

@uliwitness beh, continuano a cambiare questa roba. Comunque, ora dovresti usare viewWillTransitionecc. Ecc.
Dan Rosenstark

viewWillTransition di per UIViewControllers, non UIView.
Armand

È layoutSubviewsancora un metodo consigliato se, a seconda della dimensione corrente della vista, è necessario aggiungere / rimuovere diverse sottoview e devono essere aggiunti / rimossi diversi vincoli?
Chris Prince

1
Beh, penso di aver risposto solo per il mio caso. Quando eseguo la configurazione (a seconda delle dimensioni) di cui ho bisogno in un override dei limiti, funziona. Quando lo faccio in layoutSubviews non lo fa.
Chris Prince

93

In una UIViewsottoclasse, gli osservatori di proprietà possono essere utilizzati:

override var bounds: CGRect {
    didSet {
        // ...
    }
}

Senza sottoclassi, l'osservazione di valori-chiave con percorsi-chiave intelligente farà:

var boundsObservation: NSKeyValueObservation?

func beginObservingBounds() {
    boundsObservation = observe(\.bounds) { capturedSelf, _ in
        // ...
    }
}

2
@Ali Perché la pensi così?
Rudolf Adamkovič

sulle modifiche del frame di carico ma sembra che i limiti non lo facciano (lo so che è strano e forse sto sbagliando qualcosa)
Ali

3
@Ali: come è stato menzionato un paio di volte qui: frameè una proprietà calcolata in base al runtime. non sovrascriverlo, a meno che tu non abbia una ragione molto intelligente e consapevole per farlo. altrimenti: usa boundso (anche meglio) layoutSubviews.
auco

3
Un ottimo modo per creare un cerchio. Grazie! override var bounds: CGRect { didSet { layer.cornerRadius = bounds.size.width / 2 }}
SimplGy

4
Non è garantito che il rilevamento boundso la framemodifica funzionino, a seconda di dove si inserisce la visualizzazione nella gerarchia della visualizzazione. Sostituirò layoutSubviewsinvece. Vedi questo e questo risponde.
HuaTham

31

Crea una sottoclasse di UIView e sostituisci layoutSubviews


11
il problema è che le visualizzazioni secondarie non solo possono cambiare la loro dimensione, ma possono animare quel cambiamento di dimensione. Quando UIView esegue l'animazione, non chiama layoutSubviews ogni volta.
David Jeske

1
@DavidJeske UIViewAnimationOptions ha un'opzione chiamata UIViewAnimationOptionLayoutSubviews. Penso che questo possa aiutare. :)
Neal.Marlin

8

Swift 4 keypath KVO - Questo è il modo in cui rilevo la rotazione automatica e il passaggio al pannello laterale dell'iPad. Dovrebbe funzionare a qualsiasi vista. Ho dovuto osservare il livello di UIView.

private var observer: NSKeyValueObservation?

override func viewDidLoad() {
    super.viewDidLoad()
    observer = view.layer.observe(\.bounds) { object, _ in
        print(object.bounds)
    }
    // ...
}
override func viewWillDisappear(_ animated: Bool) {
    observer?.invalidate()
    //...
}

Questa soluzione ha funzionato per me. Sono nuovo di Swift e non sono sicuro della sintassi \ .bounds che hai usato qui. Cosa significa esattamente?
WBuck


.layerha fatto il trucco! Sai perché l'utilizzo view.observenon funziona?
d4Rk

@ d4Rk - Non lo so. La mia ipotesi è che i limiti del livello siano meno ambigui dei frame UIView. Ad esempio, il contentOffset di UITableView influenzerà le coordinate finali delle sue viste secondarie. Non sono sicuro.
Warren Stringer

QUESTA È UNA GRANDE RISPOSTA PER ME. Il mio caso è che sto utilizzando AutoLayout per definire le mie visualizzazioni. MA UICollectionViewFlowLayout ti obbliga a dare un itemSize esplicito. ma quando la dimensione della tua collectionView cambia (come nel mio caso quando mostro una barra degli strumenti con AutoLayout), itemSize rimane lo stesso e genera = [l'osservatore è l'approccio più pulito che ho ottenuto finora. e il migliore !!
Yitzchak

7

È possibile creare una sottoclasse di UIView e sovrascrivere il

SETFRAME: (CGRect) Telaio

metodo. Questo è il metodo chiamato quando la cornice (cioè la dimensione) della vista viene modificata. Fai qualcosa del genere:

- (void) setFrame:(CGRect)frame
{
  // Call the parent class to move the view
  [super setFrame:frame];

  // Do your custom code here.
}

Sensibile e funzionale. Ottima scelta.
El Zorko

1
FYI Questo non funziona per la sottoclasse UIImageView. Maggiori informazioni qui: stackoverflow.com/questions/19216684/…
William Entriken

Inoltre, setFrame:non è stato chiamato nella mia UITextViewsottoclasse durante il ridimensionamento causato dall'autorotazione mentre lo layoutSubviews:era. Nota: sto utilizzando il layout automatico e iOS 7.0.
ma11hew28

3
non sovrascrivere mai setFrame:. frameè una proprietà derivata. Vedi la mia risposta
Adlai Holler

7

Abbastanza vecchio ma ancora una buona domanda. Nel codice di esempio di Apple e in alcune delle loro sottoclassi private UIView, sovrascrivono setBounds più o meno come:

-(void)setBounds:(CGRect)newBounds {
    BOOL const isResize = !CGSizeEqualToSize(newBounds.size, self.bounds.size);
    if (isResize) [self prepareToResizeTo:newBounds.size]; // probably saves 
    [super setBounds:newBounds];
    if (isResize) [self recoverFromResizing];
}

L'override setFrame:NON è una buona idea. frameè derivato da center, boundse transform, quindi iOS non chiamerà necessariamente setFrame:.


2
Questo è vero (in teoria). Tuttavia, setBounds:non viene chiamato neanche quando si imposta la proprietà frame (almeno su iOS 7.1). Questa potrebbe essere un'ottimizzazione aggiunta da Apple per evitare il messaggio aggiuntivo.
nschum

1
... e un cattivo design da parte di Apple per non chiamare i propri accessori.
osxdirk

1
In effetti, sia frame e boundssono derivati ​​dal sottostante della vista CALayer; chiamano semplicemente il getter del livello. E setFrame:imposta la cornice del livello, mentre setBounds:imposta i limiti del livello. Quindi non puoi semplicemente ignorare l'uno o l'altro. Inoltre, layoutSubviewsviene chiamato eccessivamente (non solo in caso di modifiche alla geometria), quindi potrebbe non essere sempre una buona opzione. Ancora guardando ...
big_m

-3

Se ti trovi in ​​un'istanza di UIViewController, l'override viewDidLayoutSubviewsfa il trucco.

override func viewDidLayoutSubviews() {
    // update subviews
}

Dimensione UIView modificata necessaria, non UIViewController.
Gastón Antonio Montes

@ GastónAntonioMontes grazie per questo! Potresti aver notato una relazione tra UIViewistanze e UIViewControlleristanze. Quindi, se hai UIViewun'istanza senza VC collegata, le altre risposte sono ottime, ma se ti capita di essere attaccato a una VC, questo è il tuo uomo. Spiacenti, non è applicabile al tuo caso.
Dan Rosenstark
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.