滚动视图和 UI 组件的通用自动布局
Posted
技术标签:
【中文标题】滚动视图和 UI 组件的通用自动布局【英文标题】:Universal Autolayout for Scrollview & UI components 【发布时间】:2015-09-11 16:51:13 【问题描述】:我知道自动布局,但我不熟悉具有自动布局的通用应用程序。我在将Constrain
设置为下图时遇到问题。
您能告诉我如何为 Universal App 设置自动布局吗?我尝试限制边距并将前导、顶部、尾随空间固定到容器,但它不起作用。
【问题讨论】:
【参考方案1】:实际上,自动布局在滚动视图中的工作方式与在其他视图中不同。在滚动视图中,前导、尾随、顶部和底部约束形成容器视图的超级视图(在本例中为滚动视图)定义的不是间距,而是“我的滚动视图应该向左、向右、顶部和该组件的底部”。
因此,要在滚动视图中使用自动布局,您必须做一些棘手的事情:
-
设置从 Scrollview 到视图的前导、尾随、顶部和底部约束
-
向滚动视图添加一个子视图,您将使用它作为引导视图。由于滚动视图的宽度因设备而异,因此您需要一个与滚动视图的宽度相关的视图,因此您可以设置与该视图相关的约束,而不是滚动视图的左右边框(因为这不会定义大小,但是“您的滚动视图向左或向右滚动多少”)。此视图的高度 = 0,位于 0,0,并且与滚动视图具有相同的宽度。
-
为该视图设置约束。高度 = 0。容器的前导空间和容器的尾随空间设置为 0(这告诉您的滚动视图不要滚动到引导视图的两侧)。也为滚动视图添加一个顶部约束,因为您的引导视图将位于滚动视图的顶部。还要在滚动视图和引导视图之间添加等宽关系(这非常重要,它将强制引导视图与滚动视图具有相同的宽度,因此没有水平滚动可用)。请注意,此时,您的滚动视图知道它必须向左、向右和顶部滚动多少,而不是向下滚动,因此您会在自动布局中看到一个错误。
-
现在您构建您的 UI,考虑到顶部、左侧和右侧的关系必须与引导视图相关,而不是与滚动视图相关(您要定义间距,而不是“滚动量”)。在这里很难把所有需要的约束都放在这里,所以我在 github 上创建了一个示例项目:Download it here
当您打开项目时,请注意对象和滚动视图之间的所有约束不反映间距(如我所说),而是反映您的滚动视图应滚动远离组件的程度。
希望对你有帮助,
【讨论】:
【参考方案2】:我不知道自动布局,但以下代码也可以正常工作。我更喜欢代码中的约束,它更易于管理、修改和理解。我已经在所有设备尺寸上测试了以下代码。
注意:您不必将所有内容都放在一个文件中。创建不同的视图并让每个视图处理自己的约束/布局。
肖像:
风景:
//
// ViewController.m
// test
//
// Created by Brandon T on 2015-09-11.
// Copyright (c) 2015 Brandon T. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) UIView *paleometerView;
@property (nonatomic, strong) UIView *maleometerView;
@property (nonatomic, strong) UIView *influenceView;
@property (nonatomic, strong) UIButton *letCompanyKnowButton;
@property (nonatomic, strong) UIButton *shareWithFriendsButton;
@end
@implementation ViewController
- (instancetype)init
if (self = [super init])
[self initControls];
return self;
/*- (instancetype)initWithCoder:(NSCoder *)aDecoder
if (self = [super initWithCoder:aDecoder])
[self initControls];
return self;
*/
/*- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])
[self initControls];
return self;
*/
- (void)initControls
self.paleometerView = [[UIView alloc] init];
self.maleometerView = [[UIView alloc] init];
self.influenceView = [[UIView alloc] init];
self.letCompanyKnowButton = [[UIButton alloc] init];
self.shareWithFriendsButton = [[UIButton alloc] init];
[self.paleometerView setBackgroundColor:[UIColor lightGrayColor]];
[self.maleometerView setBackgroundColor:[UIColor grayColor]];
[self.influenceView setBackgroundColor:[UIColor yellowColor]];
[self.letCompanyKnowButton setBackgroundColor:[UIColor redColor]];
[self.shareWithFriendsButton setBackgroundColor:[UIColor greenColor]];
[self.view setBackgroundColor:[UIColor grayColor]];
[self.letCompanyKnowButton setTitle:@"Let Company Know" forState:UIControlStateNormal];
[self.shareWithFriendsButton setTitle:@"Share With Friends" forState:UIControlStateNormal];
- (void)viewDidLoad
[super viewDidLoad];
[self initControls];
[self layoutPaleMaleView];
[self layoutInfluenceView];
[self layoutAllViews];
- (void)layoutPaleMaleView
UILabel *paleLabel = [[UILabel alloc] init];
UILabel *maleLabel = [[UILabel alloc] init];
[paleLabel setText:@"Paleomenter\n85%\nPale"];
[maleLabel setText:@"Maleometer\n71%\nMale"];
[paleLabel setTextAlignment:NSTextAlignmentCenter];
[maleLabel setTextAlignment:NSTextAlignmentCenter];
[paleLabel setNumberOfLines:0];
[maleLabel setNumberOfLines:0];
[paleLabel setLineBreakMode:NSLineBreakByWordWrapping];
[maleLabel setLineBreakMode:NSLineBreakByWordWrapping];
[self.paleometerView addSubview:paleLabel];
[self.maleometerView addSubview:maleLabel];
[self.paleometerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[pale]-0-|" options:0 metrics:nil views:@@"pale":paleLabel]];
[self.paleometerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[pale]-0-|" options:0 metrics:nil views:@@"pale":paleLabel]];
[self.maleometerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[male]-0-|" options:0 metrics:nil views:@@"male":maleLabel]];
[self.maleometerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[male]-0-|" options:0 metrics:nil views:@@"male":maleLabel]];
[paleLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[maleLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
- (void)layoutInfluenceView
UIView *left = [[UIView alloc] init];
UIView *middle = [[UIView alloc] init];
UIView *right = [[UIView alloc] init];
[left setBackgroundColor:[UIColor blueColor]];
[middle setBackgroundColor:[UIColor yellowColor]];
[right setBackgroundColor:[UIColor purpleColor]];
[self.influenceView addSubview:left];
[self.influenceView addSubview:middle];
[self.influenceView addSubview:right];
//Left, right and middle all have the same width.
[self.influenceView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[left(==middle)]-0-[middle(==right)]-0-[right(==left)]-0-|" options:0 metrics:nil views:@@"left":left, @"middle":middle, @"right":right]];
[self.influenceView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[left]-0-|" options:0 metrics:nil views:@@"left":left]];
[self.influenceView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[middle]-0-|" options:0 metrics:nil views:@@"middle":middle]];
[self.influenceView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[right]-0-|" options:0 metrics:nil views:@@"right":right]];
[left setTranslatesAutoresizingMaskIntoConstraints:NO];
[middle setTranslatesAutoresizingMaskIntoConstraints:NO];
[right setTranslatesAutoresizingMaskIntoConstraints:NO];
- (void)layoutAllViews
[self.view addSubview:self.paleometerView];
[self.view addSubview:self.maleometerView];
[self.view addSubview:self.influenceView];
[self.view addSubview:self.letCompanyKnowButton];
[self.view addSubview:self.shareWithFriendsButton];
NSDictionary *views = @@"paleometer":self.paleometerView, @"maleometer":self.maleometerView, @"influence":self.influenceView, @"letCompanyKnow":self.letCompanyKnowButton, @"share":self.shareWithFriendsButton;
NSMutableArray *constraints = [[NSMutableArray alloc] init];
//Constrain *Horizontally* Paleometer and Maleometer to be equal widths, 0 spacing from the left and 0 spacing from the right, with 0 spacing between them..
[constraints addObject:@"H:|-0-[paleometer(==maleometer)]-0-[maleometer(==paleometer)]-0-|"];
//Constrain *Horizontally* InfluenceView to be 0 spacing from the left and 0 spacing from the right.
[constraints addObject:@"H:|-0-[influence]-0-|"];
//Constrain *Horizontally* the "letCompanyKnowButton" with 20 spacing on the left and 20 spacing on the right..
[constraints addObject:[NSString stringWithFormat:@"H:|-%d-[letCompanyKnow]-%d-|", 20, 20]];
//Constrain *Horizontally* the "shareButton" with 20 spacing on the left and 20 spacing on the right..
[constraints addObject:[NSString stringWithFormat:@"H:|-%d-[share]-%d-|", 20, 20]];
//Constrain *Vertically* the paleometer with 0 spacing from the top and 150 in height.
//Below it we have 0 spacing and then the influenceView which has a minimum height of 250 but is flexible so it will size to fit whenever needed.
//Next we have another 20 spacing below the influenceView and the letCompanyKnow button which has a height of 50 and has a 20 spacing above and below it.
//Finally the shareWithFriends button has the same height as the letCompanyKnow button.
[constraints addObject:@"V:|-0-[paleometer(150)]-0-[influence(250@500)]-20-[letCompanyKnow(50)]-20-[share(==letCompanyKnow)]-20-|"];
//Constrain *Vertically* the maleometer to be equal height with paleometer.
[constraints addObject:@"V:[maleometer(==paleometer)]"];
//Make influenceView flexible height for both shrinking and expanding vertically.
[self.influenceView setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
[self.influenceView setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
//add the constraints to the view.
for (NSString *constraint in constraints)
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:constraint options:0 metrics:nil views:views]];
for (UIView *view in self.view.subviews)
[view setTranslatesAutoresizingMaskIntoConstraints:NO];
- (void)didReceiveMemoryWarning
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
@end
【讨论】:
以上是关于滚动视图和 UI 组件的通用自动布局的主要内容,如果未能解决你的问题,请参考以下文章
UITextView 使用自动布局动态扩展为滚动视图内的文本
尽管添加了自动布局约束,但 UIScrollView 不滚动