gpt4 book ai didi

ios - 父 View 框架尺寸更改后更新约束

转载 作者:可可西里 更新时间:2023-11-01 03:07:27 25 4
gpt4 key购买 nike

我有一个非常简单的 UIViewController,我用它来尝试更好地理解约束、自动布局和框架。 View Controller 有两个 subview :两者都是 UIView,它们根据设备方向并排或顶部/底部。在每个 UIView 中,都存在一个应该在其父 View 中居中的标签。

当设备旋转时,UIView 会正确更新。我正在计算它们的框架尺寸和来源。但是,标签不会保持居中,并且不遵守 Storyboard 中定义的约束。

以下是显示问题的屏幕截图。如果我注释掉 viewDidLayoutSubviews 方法,标签将完全居中(但是 UIView 的大小不正确)。我意识到我可以手动调整每个标签的框架,但我正在寻找一种方法让它们在新调整大小的 super View 中尊重它们的约束。 enter image description here enter image description here这是代码:

#import "ViewController.h"

@interface ViewController ()
@property (nonatomic) CGFloat spacer;
@end

@implementation ViewController

@synthesize topLeftView, bottomRightView, topLeftLabel, bottomRightLabel;

- (void)viewDidLoad {
[super viewDidLoad];

topLeftLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
bottomRightLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

self.spacer = 8.0f;
}

- (void)viewDidLayoutSubviews
{
if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) {
[self setupTopLeftForLandscape];
[self setupBottomRightForLandscape];
} else {
[self setupTopLeftForPortrait];
[self setupBottomRightForPortrait];
}

}

- (void) setupTopLeftForPortrait {
CGRect frame = topLeftView.frame;
frame.origin.x = self.spacer;
frame.origin.y = self.spacer;
frame.size.width = self.view.frame.size.width - 2*self.spacer;
frame.size.height = (self.view.frame.size.height - 3*self.spacer) * 0.5;
[topLeftView setFrame:frame];
}

- (void) setupBottomRightForPortrait {
CGRect frame = bottomRightView.frame;
frame.origin.x = self.spacer;
frame.origin.y = topLeftView.frame.size.height + 2*self.spacer;
frame.size.width = topLeftView.frame.size.width;
frame.size.height = topLeftView.frame.size.height;
[bottomRightView setFrame:frame];
}

- (void) setupTopLeftForLandscape {
CGRect frame = topLeftView.frame;
frame.origin.x = self.spacer;
frame.origin.y = self.spacer;
frame.size.width = (self.view.frame.size.width - 3*self.spacer) * 0.5;
frame.size.height = self.view.frame.size.height - 2*self.spacer;
[topLeftView setFrame:frame];
}

- (void) setupBottomRightForLandscape {
CGRect frame = bottomRightView.frame;
frame.origin.x = self.topLeftView.frame.size.width + 2*self.spacer;
frame.origin.y = self.spacer;
frame.size.width = topLeftView.frame.size.width;
frame.size.height = topLeftView.frame.size.height;
[bottomRightView setFrame:frame];
}

@end

最佳答案

通常将框架与自动布局混合使用是个坏主意。 (异常(exception)情况是 View 层次结构使用包含不包含 View 的约束,然后从该点开始不使用任何约束[和其他警告])。一个大问题是约束系统通常不会从 setFrame 中获取任何信息。

另一个经验法则是,setFrame 和传统的布局树是在约束系统之前计算的。对于第一部分,这似乎违反直觉,但请记住 1) 在传统的布局树中, View 布局它们的 subview ,然后在它们上调用 layoutSubviews,所以每个人的父 View 框架在它自己布局之前设置,但 2) 在约束系统,它尝试从 subview 计算父 View 框架,自下而上。但是在自下而上获取信息后,每个 subview 都会报告信息,布局工作是自上而下完成的。


修复

那会给你留下什么?你是对的,你需要以编程方式设置它。在 IB 中无法指示您应该从上下切换到左右。以下是您可以如何做到这一点:

  1. 选择一个轮换并确保所有约束都已设置您在界面生成器中想要的方式-例如,每种颜色 View 从 super View 中放置 8 个点(您的间隔 View )。 “明确约束”和底部的“更新帧”按钮会帮助你,你会想要点击经常这样做是为了确保它是同步的。
  2. 非常重要的是,左上角的 View 只能连接到左(领先)和顶部以及右下角的 super View 仅由右侧(尾部)和底部连接。如果你清除尺寸设置固定的高度和宽度,这将产生一个警告。这是正常的,这种情况可以通过设置来解决“等宽”和“等高”以及必要时第 3 步的一部分。(请注意,要使值真正相等,常数必须为零。)在其他情况下,我们必须放置一个约束并将其标记为“占位符”使编译器静音,如果我们确定我们将填充信息但编译器不知道。
  3. 识别(或创建)链接右侧/底部的两个约束查看左侧和顶部的某物。您可能想使用 IB 左侧的对象浏览器。在中创建两个导出viewController.h 使用助理编辑器。看起来像:

    @property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomViewToTopConstraint;
    @property (weak, nonatomic) IBOutlet NSLayoutConstraint *rightViewToLeftConstraint;

  4. 在 viewController 中实现 updateConstraints。这里是逻辑会去:

.

-(void)updateViewConstraints 
{

//first remove the constraints

[self.view removeConstraints:@[self.rightViewToLeftConstraint, self.bottomViewToTopConstraint]];

if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) {

//align the tops equal
self.bottomViewToTopConstraint = [NSLayoutConstraint constraintWithItem:self.bottomRightView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.topLeftView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
//align to the trailing edge by spacer
self.rightViewToLeftConstraint = [NSLayoutConstraint constraintWithItem:self.bottomRightView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.topLeftView
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:self.spacer];
} else { //portrait

//right view atached vertically to the bottom of topLeftView by spacer
self.bottomViewToTopConstraint = [NSLayoutConstraint constraintWithItem:self.bottomRightView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.topLeftView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:self.spacer];

//bottom view left edge aligned to left edge of top view
self.rightViewToLeftConstraint = [NSLayoutConstraint constraintWithItem:self.bottomRightView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.topLeftView
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0];
}

[self.view addConstraints:@[self.rightViewToLeftConstraint, self.bottomViewToTopConstraint]];

[super updateViewConstraints];

由于添加约束后无法更改约束(常量除外),因此我们必须执行此删除-添加步骤。请注意 IB 中的那些也可能是占位符,因为我们每次都会删除它们(我们可以先检查)。我们可以将常量修改为某个偏移值,例如通过 spacer + topViewHight + spacer 与 superview 相关。但这意味着当自动布局去计算这个 View 时,你已经根据一些其他信息做出了假设,这些信息可能已经改变了。交换观点并改变它们相关的因素,这些因素旨在改变彼此相关的因素。

注意,因为Auto Layout在向上传递信息的时候会用到这里的约束,所以我们先修改它们,然后调用super。这是调用父类(super class)私有(private)实现来为这个 View 做计算,而不是 View 层次结构中这个 View 的父 View ,尽管实际上下一步将在树的更上层。

关于ios - 父 View 框架尺寸更改后更新约束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28287013/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com