Scrollview 和 UI 组件的通用自动布局
Universal Autolayout for Scrollview & UI components
我知道自动布局,但我不熟悉具有自动布局的通用应用程序。我在为下图设置 Constrain
时遇到问题。
你能告诉我如何为 Universal App 设置自动布局吗?我尝试使用 constrain to margin & pin the leading, top, trailing space to container 但它不起作用。
我不知道自动布局,但下面的代码也能正常工作。我更喜欢代码中的约束,它更容易管理、修改和理解。我已经在所有设备尺寸上测试了下面的内容..
注意:您不必将所有内容都放在一个文件中。创建不同的视图并让每个视图处理自己的 constraints/layouts。
纵向:
横向:
//
// 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
实际上,自动布局在滚动视图中的工作方式与在其他视图中不同。在滚动视图中,前导、尾随、顶部和底部约束形成容器视图(在本例中为滚动视图)的超级视图,定义的不是间距,而是 "how much my scroll view should scroll to the left, right, top and bottom of this component".
的类型
所以,要在滚动视图中使用自动布局,您必须做一些棘手的事情:
- 设置从滚动视图到视图的前导、尾随、顶部和底部约束
- 向滚动视图添加一个子视图,您将用作引导视图。由于滚动视图的宽度因设备而异,因此您需要一个与滚动视图的宽度相关的视图,因此您可以设置与该视图相关的约束而不是滚动视图的左右边框(因为这不会定义大小, 但 "how much your scrollview scrolls to left or right").此视图的高度 = 0,位于 0,0,并且与滚动视图具有相同的宽度。
- 为该视图设置约束。 Height = 0. Leading space to container,Trailing space to container 设置为 0(这告诉你的滚动视图不要滚动到引导视图的两侧)。也向滚动视图添加顶部约束,因为您的引导视图将位于滚动视图的顶部。还要在你的滚动视图和你的引导视图之间添加一个等宽关系(这很重要,它会强制你的引导视图与滚动视图具有相同的宽度,因此水平滚动将不可用)。请注意,此时,您的滚动视图知道它必须向左、向右和顶部滚动多少,但不知道滚动到底部,因此您会在自动布局中看到一个错误。
- 现在你构建你的 UI,考虑到顶部、左侧和右侧的关系必须与引导视图相关,而不是与滚动视图相关(你想定义间距,而不是 "amount of scroll").很难把所有需要的约束放在这里,所以我在 github 上创建了一个示例项目:Download it here
当您打开项目时,请注意对象和滚动视图之间的所有约束并不反映间距(如我所说),而是反映您的滚动视图应滚动远离组件的程度。
希望这对您有所帮助,
我知道自动布局,但我不熟悉具有自动布局的通用应用程序。我在为下图设置 Constrain
时遇到问题。
你能告诉我如何为 Universal App 设置自动布局吗?我尝试使用 constrain to margin & pin the leading, top, trailing space to container 但它不起作用。
我不知道自动布局,但下面的代码也能正常工作。我更喜欢代码中的约束,它更容易管理、修改和理解。我已经在所有设备尺寸上测试了下面的内容..
注意:您不必将所有内容都放在一个文件中。创建不同的视图并让每个视图处理自己的 constraints/layouts。
纵向:
横向:
//
// 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
实际上,自动布局在滚动视图中的工作方式与在其他视图中不同。在滚动视图中,前导、尾随、顶部和底部约束形成容器视图(在本例中为滚动视图)的超级视图,定义的不是间距,而是 "how much my scroll view should scroll to the left, right, top and bottom of this component".
的类型所以,要在滚动视图中使用自动布局,您必须做一些棘手的事情:
- 设置从滚动视图到视图的前导、尾随、顶部和底部约束
- 向滚动视图添加一个子视图,您将用作引导视图。由于滚动视图的宽度因设备而异,因此您需要一个与滚动视图的宽度相关的视图,因此您可以设置与该视图相关的约束而不是滚动视图的左右边框(因为这不会定义大小, 但 "how much your scrollview scrolls to left or right").此视图的高度 = 0,位于 0,0,并且与滚动视图具有相同的宽度。
- 为该视图设置约束。 Height = 0. Leading space to container,Trailing space to container 设置为 0(这告诉你的滚动视图不要滚动到引导视图的两侧)。也向滚动视图添加顶部约束,因为您的引导视图将位于滚动视图的顶部。还要在你的滚动视图和你的引导视图之间添加一个等宽关系(这很重要,它会强制你的引导视图与滚动视图具有相同的宽度,因此水平滚动将不可用)。请注意,此时,您的滚动视图知道它必须向左、向右和顶部滚动多少,但不知道滚动到底部,因此您会在自动布局中看到一个错误。
- 现在你构建你的 UI,考虑到顶部、左侧和右侧的关系必须与引导视图相关,而不是与滚动视图相关(你想定义间距,而不是 "amount of scroll").很难把所有需要的约束放在这里,所以我在 github 上创建了一个示例项目:Download it here
当您打开项目时,请注意对象和滚动视图之间的所有约束并不反映间距(如我所说),而是反映您的滚动视图应滚动远离组件的程度。
希望这对您有所帮助,