我可以在同一个视图上使用 setFrame 和 autolayout 吗?

Posted

技术标签:

【中文标题】我可以在同一个视图上使用 setFrame 和 autolayout 吗?【英文标题】:Can I use setFrame and autolayout on the same view? 【发布时间】:2012-10-22 14:53:59 【问题描述】:

我想为我的所有按钮添加内边距,因此我将 UIButton 子类化,并且在其他更改中,我想通过使用 setFrame 方法添加一个固定内边距。一切正常,除了 setFrame。我检查了一下,发现如果我取消选中该视图上的“使用 AutoLayout”,那么我可以使用 setFrame,它可以工作。有没有解决的办法?我真的很想使用自动布局,因为它有助于使应用程序在 iphone 5 和更早的设备上看起来都不错。但我也想在我的子类中使用 setFrame,让我的生活更轻松。

总结一下,我的问题是:我可以使用自动布局并以编程方式调整 UIView 的框架吗?

【问题讨论】:

【参考方案1】:

是的,这是可以做到的。

如果您设置视图的translatesAutoresizingMaskIntoConstraints = YES,则对setFrame: 的调用会在运行时自动转换为基于视图当前autoresizingMask 的布局约束。这让您可以混合使用基于框架的布局和基于约束的布局。

例如,您可以使用自动布局来定义视图的所有子视图的布局,但仍然调用setFrame: 来设置视图本身的大小和位置。从您的角度来看,您正在使用自动布局和直接框架操作混合进行布局。但系统实际上是使用约束来处理所有事情。

但是,使用translatesAutoresizingMaskIntoConstraints 有一个很大的警告。

当你这样做时,你仍然需要确保这些自动约束可以满足你的其余约束

因此,例如,假设已经存在确定视图大小和位置的约束,然后您设置translatesAutoresizingMaskIntoConstraints = YES 并调用setFrame:。对setFrame: 的调用将在视图上生成新的约束,这可能会与已经存在的约束发生冲突。

(事实上,这个错误经常发生。如果您曾经看到一条日志消息抱怨约束冲突,并且其中一个约束是NSAutoresizingMaskLayoutConstraint,那么您看到的是与自动约束的冲突。这个是一个容易犯的错误,因为translatesAutoresizingMaskIntoConstraints = YES 是默认值,所以如果您在代码中配置约束,如果您不想要这些自动约束,则需要记住将其关闭。)

相反,再次假设已经存在确定视图大小和位置的约束,但是在调用setFrame: 之前设置了translatesAutoresizingMaskIntoConstraints = NO。在这种情况下,您的setFrame: 调用不会产生新的约束,因此单独的约束之间不会发生冲突。但是,在这种情况下,约束和您设置的帧值之间仍然存在“冲突”。下次调用 Auto Layout 时,它会看到视图上已经存在的约束,计算它们需要的框架值,并将框架设置为本身所需的值,从而破坏您手动设置的值。

有关更多详细信息,请查看 Apple 的 Cocoa Auto Layout Guide 中的“采用自动布局”部分。

【讨论】:

这可行,但我在日志中看到很多警告。任何人都可以建议使用这种方法的正确方法是什么?还是我应该忽略警告?谢谢 如果您看到关于不可满足约束的警告,这些可能是有意义的。如果您在视图上使用translatedAutoresizingMaskIntoConstraints=true 操作时看到它们,那么可能发生的情况是您已经有一些限制(可能在 IB 中设置)试图控制该视图的框架。框架上的那些旧约束与自动调整大小掩码自动生成的约束相互作用。在这种情况下,答案是消除那些旧的约束。如果您在 IB 中添加它们只是为了让 IB 在设计时关闭,您可以将它们设置为“占位符”以丢失它们。 @algal 如何摆脱“旧”约束?并且让子视图只监听 setFrame: 值,这样它们两个就不会冲突了。 这仍然适用于 ios 10 吗?我正在尝试为自定义容器 VC 中的子 VC 执行此操作,但它不尊重我设置的框架。 我注意到,如果您这样做,在许多情况下,您会希望从 的其他视图的框架中派生出框架的 CGRects > 使用自动布局。在这种情况下,您需要其他视图完成其自动布局传递之后执行框架计算。在视图控制器中viewDidLayoutSubviews 是这个地方。在自定义视图中应使用layoutSubviews。在这两种情况下,您都应该将帧计算放在 super 调用之后。可能值得将此添加到答案中,但我希望无论如何这是一个有用的评论!【参考方案2】:

对我来说最简单的事情是从其父视图中删除我想要移动/调整大小的视图,设置其frame,然后将其添加回来。例如,在 UITableViewCell 子类中使用自定义 UILabel

[cell.myLabel removeFromSuperview];
cell.myLabel.frame = someFrameIGenerated;
[cell.contentView addSubview:cell.myLabel];

【讨论】:

没问题。虽然我觉得有义务告诉你我最终摆脱了这个并真正学习了如何在 IB 中使用约束。我不知道它们存在,现在制作复杂的布局似乎更加合理。 是的,我试过了,但无法在表头视图上获得约束。也许我应该再试一次。 通过这样做,您可以消除可能会搞砸其他事情的约束。累了。【参考方案3】:

对我有用的方法是简单地将IBOutlet 添加到视图控制器中作为我的约束,然后更改插座上的constant 属性。

例如,要更改视图的 x 原点,请设置从该视图的前导约束到控制器的出口。换句话说,创建一个插座@IBOutlet var labelXOrigin: NSLayoutConstraint!。然后,当您想调整 x 位置时,只需执行类似

的操作
self.labelXOrigin.constant = 20.0 // Or whatever origin you want to set.

【讨论】:

【参考方案4】:

设置子视图框架的最佳方法是 viewWillLayoutSubviews 方法 自动布局项目。

设置属性 translateAutoresizingMaskIntoConstraints = true this 将在视图确实出现或在按钮上设置选项卡后工作,就在之后 视图确实出现了。

【讨论】:

【参考方案5】:

如果您的应用不支持多种设备方向。

viewDidLoad 中设置要调整大小的视图的框架

- (void)viewDidLoad

    [super viewDidLoad];
    // Do any additional setup after loading the view.
    [aView setFrame:CGRectMake(15, 15, 100, 100)];

然后在viewDidLayoutSubviews中:

-(void)viewDidLayoutSubviews

    [self.view setTranslatesAutoresizingMaskIntoConstraints:NO];

我不确定,但我认为如果您使用此解决方案,将一个方向切换到另一个方向可能会导致问题

【讨论】:

以上是关于我可以在同一个视图上使用 setFrame 和 autolayout 吗?的主要内容,如果未能解决你的问题,请参考以下文章

NSWindow 宽度未使用 setFrame(frame, display, animate) 更新

bringSubviewToFront + setFrame 不起作用

在 UITextView 上滚动内阴影

-[NSView setFrame:] 由于无效约束而崩溃

在 XIB 中添加手势时,iOS 应用程序崩溃并显示消息“-[UITapGestureRecognizer setFrame:]”

setTranslatesAutoresizingMaskIntoConstraints和setFrame组合使用导致的异常