问题
I have a need for floating autolayout. I find that its quite hard problem to solve but i think it can be done by using some of the tips described here: http://www.objc.io/issue-3/advanced-auto-layout-toolbox.html
Maybe someone have already tried solving such problem or would like to try it.
So here is the challenge:
nicely floated views which drops to new line if content width exceeded, like in next picture view1 width is bigger so view2 drops to new line. (same would happen if view2 width would become bigger)
All views come in sequence, views can have defined min width and max width should be the container width. views can stretch in height but then they always take full content width.
回答1:
I worked on this challenge tonight. I ran the code in the iPhone Simulator; it seems to work. However, I did not attempt to match the exact specifications of the OP, nor did I follow the link to tips on how to do this. I just wanted to see what I could knock out on my own in a couple of hours.
There's nothing to see in storyboard except an empty, yellow scroll view pinned to the sides of the root view.
The gray floating views are inside a yellow scroll view. The width of the scroll view's content size is the width of the root view; the height of the content size shrinks and expands to accommodate the varying number of rows.
The only place I didn't use Auto Layout was for the scroll view's content view (here, I used Apple's so-called "Mixed Approach").
The widths of the floating cells are randomly generated whenever viewWillLayoutSubviews is called. Hence, all floating cells change their width upon device rotation. I held the height of all floating cells to a constant.
@interface ViewController ()
@property (nonatomic, strong) NSMutableArray *floatingViews;
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@property (nonatomic, strong) UIView *contentView;
@end
@implementation ViewController
#define NUM_OF_VIEWS 18
#define HEIGHT 30.0f
#define HORIZONTAL_SPACER 20.0f
#define VERTICAL_SPACER 10.0f
- (void)viewDidLoad
{
[super viewDidLoad];
self.contentView = [[UIView alloc] initWithFrame:self.view.bounds];
[self.scrollView addSubview:self.contentView];
self.floatingViews = [[NSMutableArray alloc] init];
for (int i = 0; i < NUM_OF_VIEWS; i++) {
UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor grayColor];
view.translatesAutoresizingMaskIntoConstraints = NO;
[self.floatingViews addObject:view];
[self.contentView addSubview:view];
}
}
- (void)viewWillLayoutSubviews
{
[self configureSizeConstraintsForAllViews];
CGFloat superviewWidth = self.view.bounds.size.width;
int row = 0;
CGFloat leftMargin = 0.0f;
for (int i = 0; i < [self.floatingViews count]; i++) {
UIView *currentView = self.floatingViews[i];
// is there room for the current view on this row?
NSLayoutConstraint *widthConstaint = [self widthConstraintForView:currentView];
CGFloat currentViewWidth = widthConstaint.constant;
if ((leftMargin + currentViewWidth) > superviewWidth) {
row++;
leftMargin = 0.0f;
}
// position current view
[self configureTopConstraintForView:currentView forRow:row];
[self configureLeftConstraintForView:currentView withConstant:leftMargin];
// update leftMargin
leftMargin += currentViewWidth + HORIZONTAL_SPACER;
}
// update size of content view and scroll view's content size
CGRect rect = self.contentView.frame;
rect.size.width = superviewWidth;
rect.size.height = row * (HEIGHT + VERTICAL_SPACER) + HEIGHT;
self.contentView.frame = rect;
[self.scrollView setContentSize:rect.size];
}
- (void)configureSizeConstraintsForAllViews
{
static BOOL firstTime = YES;
if (firstTime) {
firstTime = NO;
[self configureHeightConstraintsForAllViews];
}
for (int i = 0; i < [self.floatingViews count]; i++) {
[self configureRandomWidthForView:self.floatingViews[i]];
}
}
- (void)configureRandomWidthForView:(UIView *)view
{
CGFloat maxWidth = self.view.bounds.size.width;
CGFloat minWidth = 30.0f;
CGFloat randomScale = (arc4random() % 101) / 100.0f; // 0.0 - 1.0
CGFloat randomWidth = minWidth + randomScale * (maxWidth - minWidth);
assert(randomWidth >= minWidth && randomWidth <= maxWidth);
NSLayoutConstraint *widthConstraint = [self widthConstraintForView:view];
if (!widthConstraint) {
widthConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:0.0f];
[view addConstraint:widthConstraint];
}
widthConstraint.constant = randomWidth;
}
- (NSLayoutConstraint *)widthConstraintForView:(UIView *)view
{
NSLayoutConstraint *widthConstraint = nil;
for (NSLayoutConstraint *constraint in view.constraints) {
if (constraint.firstAttribute == NSLayoutAttributeWidth) {
widthConstraint = constraint;
break;
}
}
return widthConstraint;
}
- (NSLayoutConstraint *)topConstraintForView:(UIView *)view
{
NSLayoutConstraint *topConstraint = nil;
for (NSLayoutConstraint *constraint in view.superview.constraints) {
if (constraint.firstItem == view || constraint.secondItem == view) {
if (constraint.firstAttribute == NSLayoutAttributeTop) {
topConstraint = constraint;
break;
}
}
}
return topConstraint;
}
- (NSLayoutConstraint *)leftConstraintForView:(UIView *)view
{
NSLayoutConstraint *leftConstraint = nil;
for (NSLayoutConstraint *constraint in view.superview.constraints) {
if (constraint.firstItem == view || constraint.secondItem == view) {
if (constraint.firstAttribute == NSLayoutAttributeLeft) {
leftConstraint = constraint;
break;
}
}
}
return leftConstraint;
}
- (void)configureHeightConstraintsForAllViews
{
assert(self.floatingViews);
for (int i = 0; i < [self.floatingViews count]; i++) {
UIView *view = self.floatingViews[i];
[view addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:HEIGHT]];
}
}
- (void)configureTopConstraintForView:(UIView *)view forRow:(NSUInteger)row
{
NSLayoutConstraint *topConstraint = [self topConstraintForView:view];
if (!topConstraint) {
topConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:view.superview attribute:NSLayoutAttributeTop multiplier:1.0f constant:0.0f];
[view.superview addConstraint:topConstraint];
}
topConstraint.constant = row * (HEIGHT + VERTICAL_SPACER);
}
- (void)configureLeftConstraintForView:(UIView *)view withConstant:(CGFloat)constant
{
NSLayoutConstraint *leftConstraint = [self leftConstraintForView:view];
if (!leftConstraint) {
leftConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:view.superview attribute:NSLayoutAttributeLeft multiplier:1.0f constant:0.0f];
[view.superview addConstraint:leftConstraint];
}
leftConstraint.constant = constant;
}
- (BOOL)prefersStatusBarHidden
{
return YES;
}
@end
回答2:
To simplify, take inspiration from the way Covoa text system works. Then a little from Core Text and auto layout.
Each of those views would go into an array. Each of them would have no content compression and lots of content hugging. Each line would be an NSStackView with horizontal orientation. All of those would go in an NSStackView with vertical orientation. That into a scroll view.
If a view doesn't fit in stack view line 1 you readjust down each stack view line.
It would work.
It might be slow if this gets really big.
来源:https://stackoverflow.com/questions/20568074/floating-autolayout-ios-osx