Reputation: 91
I am building an iPad app with the main screen composed of a UIScrollView populated with a dynamic number of UIViews of fixed size. I am currently creating the UIViews programmatically and manually placing them by setting the center (setCenter) prior to adding to the scrollview. The functionality looks very much like a simplified version of the opening screen of Pages on the iPad. I am programmatically changing the vertical length of the scrollview container when each row is filled with UIViews. I have to manually place each newly created UIView since the number will be dynamically determined by the number of objects created by the user.
Since I also want to allow for orientation change, I am using the notification center to capture orientation change events. I want to leave the individual UIViews the same size regardless of orientation, but need to reorder them with orientation change since the portrait orientation will have one less UIView per row.
The only way I can think to reorder the UIViews within the scrollview is to programmatically change each of the UIView centers (setCenter), wrapping them in an animation.
Is there any other way to reorder the UIViews that I am not thinking about besides walking the subviews and adjusting their center positions?
Thanks!
Upvotes: 0
Views: 1429
Reputation: 385500
When you have a view that needs to lay out its subviews in a specific way, the general approach is to create your own view subclass and override its layoutSubviews
method. The system automatically sends layoutSubviews
to a view at various times, including whenever the view's size changes. If the system is autorotating, it will send you layoutSubviews
from inside the autorotation animation block, so you don't need to worry about animating the changes to your subviews' frames.
This is made slightly trickier when your view is a scroll view, because UIScrollView
sends itself the layoutSubview
messages a lot - every time it changes it contentOffset
. So if you subclass UIScrollView
, you want to be efficient. You need to keep track of the last size you laid out for, and only lay out again if the size has changed. Something like this:
@implementation MyScrollView {
CGSize _lastLayoutSize;
}
- (void)forceLayout {
_lastLayoutSize = CGSizeMake(-1, -1);
[self setNeedsLayout];
}
- (void)didAddSubview:(UIView *)view {
// UIView sends itself this whenever a subview is added.
[super didAddSubview:view];
[self forceLayout];
}
- (void)didRemoveSubview:(UIView *)view {
// UIView sends itself this whenever a subview is removed.
[super didAddSubview:view];
[self setNeedsLayout];
}
- (void)layoutSubviews {
[super layoutSubviews];
CGSize mySize = self.bounds.size;
if (CGSizeEqualToSize(mySize, _lastLayoutSize))
return;
_lastLayoutSize = mySize;
static CGFloat const margin = 8;
CGFloat yMax = 0;
CGPoint point = CGPointMake(margin, margin);
BOOL rowIsEmpty = YES;
for (UIView *subview in self.subviews) {
CGRect frame = subview.frame;
CGFloat xNext = point.x + frame.size.width + margin;
if (rowIsEmpty || xNext <= mySize.width) {
rowIsEmpty = NO;
point.x = xNext;
} else {
point.x = margin;
point.y = yMax + margin;
}
frame.origin = point;
subview.frame = frame;
yMax = MAX(yMax, CGRectGetMaxY(frame));
}
self.contentSize = CGSizeMake(mySize.width, yMax + margin);
}
Upvotes: 1