I have a view that has rows and columns of imageviews in it.
If this view is resized, I need to rearrange the imageviews positions.
This view is a subview of another view that gets resized.
Is there a way to detect when this view is being resized?
As Uli commented below, the proper way to do it is override layoutSubviews
and layout the imageViews there.
If, for some reason, you can't subclass and override layoutSubviews
, observing bounds
should work, even when being kind of dirty. Even worse, there is a risk with observing - Apple does not guarantee KVO works on UIKit classes. Read the discussion with Apple engineer here: When does an associated object get released?
original answer:
You can use key-value observing:
[yourView addObserver:self forKeyPath:@"bounds" options:0 context:nil];
and implement:
- (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:
}
}
In a UIView
subclass, property observers can be used:
override var bounds: CGRect {
didSet {
// ...
}
}
Without subclassing, key-value observation with smart key-paths will do:
var boundsObservation: NSKeyValueObservation?
func beginObservingBounds() {
boundsObservation = observe(\.bounds) { capturedSelf, _ in
// ...
}
}
frame
is a derived and runtime calculated property. do not override this, unless you have a very smart and knowing reason to do so. otherwise: use bounds
or (even better) layoutSubviews
.
override var bounds: CGRect { didSet { layer.cornerRadius = bounds.size.width / 2 }}
Create subclass of UIView, and override layoutSubviews
Swift 4 keypath KVO -- This is how I detect autorotate and moving to iPad side panel. Should work work any view. Had to observe the UIView's layer.
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()
//...
}
.layer
did the trick! Do you know why using view.observe
doesn't work?
You can create a subclass of UIView and override the
setFrame:(CGRect)frame
method. This is the method called when the frame (i.e. the size) of the view is changed. Do something like this:
- (void) setFrame:(CGRect)frame
{
// Call the parent class to move the view
[super setFrame:frame];
// Do your custom code here.
}
setFrame:
wasn't called on my UITextView
subclass during resizing caused by autorotation whereas layoutSubviews:
was. Note: I'm using auto layout & iOS 7.0.
setFrame:
. frame
is a derived property. See my answer
Pretty old but still a good question. In Apple's sample code, and in some of their private UIView subclasses, they override setBounds roughly like:
-(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];
}
Overriding setFrame:
is NOT a good idea. frame
is derived from center
, bounds
, and transform
, so iOS will not necessarily call setFrame:
.
setBounds:
is not called either when setting the frame property (at least on iOS 7.1). This could be an optimization Apple added to avoid the additional message.
frame
and bounds
are derived from the view's underlying CALayer
; they just call through to the layer's getter. And setFrame:
sets the layer's frame, while setBounds:
sets the layer bounds. So you cannot just override one or the other. Also, layoutSubviews
gets called excessively (not just on geometry changes), so it may not always be a good option either. Still looking...
If you're in a UIViewController instance, overriding viewDidLayoutSubviews
does the trick.
override func viewDidLayoutSubviews() {
// update subviews
}
UIView
instances and UIViewController
instances. So if you have a UIView
instance with no VC attached, the other answers are great, but if you happen to be attached to a VC, this is your man. Sorry it's not applicable to your case.
Success story sharing
viewWillTransition
etc. etc.layoutSubviews
still a recommended method if, depending on the current size of the view, different subviews needed to be added/removed and different constraints need to be added/removed?