浮动自动布局 iOS/OSX

3

我需要浮动的自动布局。我发现这是一个相当困难的问题,但我认为可以通过使用以下一些提示来解决: http://www.objc.io/issue-3/advanced-auto-layout-toolbox.html

也许已经有人尝试过解决这样的问题,或者想要尝试。

所以这是挑战:

漂亮地浮动视图,如果内容宽度超出,则会换行,就像下一张图片中的view1宽度更大,所以view2会换到新行。 (如果view2的宽度变得更大,则会发生同样的情况)

enter image description here

所有视图按顺序排列,视图可以定义最小宽度,最大宽度应该是容器宽度。视图可以在高度上拉伸,但那时它们始终占据完整的内容宽度。


1
你应该使用集合视图来完成这个任务,内置的流式布局基本上已经为你做好了这个工作。 - jrturton
我相信这只适用于iOS。我需要它用于OS X。 - rskuja
1
gl;hf https://github.com/jwilling/JNWCollectionView - Nick Terry
@NickTerry gl;hf 这个词对我来说是新的。它是什么意思? - jrturton
Nick Terry,谢谢你提供的链接,我会尝试的 :) - rskuja
显示剩余3条评论
2个回答

2
我今晚研究了这个挑战。我在iPhone模拟器中运行了代码,看起来是可以工作的。然而,我没有尝试匹配OP的精确规格,也没有按照链接上的提示进行操作。我只是想看看我能在几个小时内独立完成什么。
在storyboard中除了一个固定在根视图边缘的空黄色滚动视图以外,没有什么可看的。
灰色浮动视图位于黄色滚动视图内。滚动视图内容尺寸的宽度是根视图的宽度;内容尺寸的高度会收缩和扩展,以适应变化的行数。
唯一没有使用自动布局的地方是滚动视图的内容视图(这里我采用了苹果所谓的“混合方法”)。
当viewWillLayoutSubviews被调用时,浮动单元格的宽度是随机生成的。因此,在设备旋转时所有浮动单元格都会改变其宽度。我将所有浮动单元格的高度保持不变。
图片链接如下: enter image description here enter image description here
@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

0
为简化起见,可以从Covoa文本系统的工作方式中获得灵感。然后再从Core Text和自动布局中获取一些灵感。
每个视图都将进入一个数组。每个视图都不会有内容压缩,而是有很多内容吸附。每行都将是一个水平方向的NSStackView。所有这些都将放在一个垂直方向的NSStackView中。然后放入一个滚动视图中。
如果一个视图无法适应堆栈视图的第一行,则需要调整每个堆栈视图行的位置。
这样做是可行的。但如果数据量非常大,可能会变慢。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接