Is there a UIView resize event?
IphoneIpadIosIphone Problem Overview
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?
Iphone Solutions
Solution 1 - Iphone
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: https://stackoverflow.com/questions/6039309/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:
}
}
Solution 2 - Iphone
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
// ...
}
}
Solution 3 - Iphone
Create subclass of UIView, and override layoutSubviews
Solution 4 - Iphone
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()
//...
}
Solution 5 - Iphone
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.
}
Solution 6 - Iphone
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:
.
Solution 7 - Iphone
If you're in a UIViewController instance, overriding viewDidLayoutSubviews
does the trick.
override func viewDidLayoutSubviews() {
// update subviews
}