Ios 具有隐藏视图的自动布局?
我觉得根据业务逻辑显示/隐藏Ios 具有隐藏视图的自动布局?,ios,objective-c,autolayout,Ios,Objective C,Autolayout,我觉得根据业务逻辑显示/隐藏ui视图,通常是ui标签,这是一种相当常见的范例。我的问题是,使用AutoLayout响应隐藏视图的最佳方式是什么,就好像它们的帧是0x0一样。下面是1-3个功能的动态列表示例 现在我有一个10px的顶部空间,从按钮到最后一个标签,当标签被隐藏时,它显然不会向上滑动。从现在起,我创建了此约束的出口,并根据显示的标签数量修改常量。这显然有点老套,因为我使用负常量值在隐藏帧上按下按钮。这也很糟糕,因为它不受实际布局元素的约束,只是根据其他元素的已知高度/填充进行偷偷的静
ui视图
,通常是ui标签
,这是一种相当常见的范例。我的问题是,使用AutoLayout响应隐藏视图的最佳方式是什么,就好像它们的帧是0x0一样。下面是1-3个功能的动态列表示例
现在我有一个10px的顶部空间,从按钮到最后一个标签,当标签被隐藏时,它显然不会向上滑动。从现在起,我创建了此约束的出口,并根据显示的标签数量修改常量。这显然有点老套,因为我使用负常量值在隐藏帧上按下按钮。这也很糟糕,因为它不受实际布局元素的约束,只是根据其他元素的已知高度/填充进行偷偷的静态计算,显然是在与AutoLayout的用途作斗争
很明显,我可以根据我的动态标签创建新的约束,但这需要大量的微观管理和大量的冗长,因为我试图折叠一些空白。有更好的方法吗?更改帧大小0,0并让AutoLayout在不操纵约束的情况下完成其工作?完全删除视图
老实说,从隐藏视图的上下文中修改常量只需要一行代码和简单的计算。使用constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:
重新创建新约束似乎太重了
编辑2018年2月:查看Ben关于
UIStackView
s的回答。我个人对显示/隐藏视图的偏好是创建具有适当宽度或高度约束的IBOutlet
然后,我将常量
值更新为0
以隐藏,或者更新任何应该显示的值
这种技术的最大优点是可以保持相对约束。例如,假设视图A和视图B的水平间隙为x。当视图A宽度常数
设置为0.f
时,视图B将向左移动以填充该空间
不需要添加或删除约束,这是一个重量级操作。只需更新约束的
常量
就可以了。我构建类别以轻松更新约束:
[myView1 hideByHeight:YES];
请回答:
我感到惊讶的是,
UIKit
没有为这种期望的行为提供更优雅的方法。这似乎是一件很平常的事情,希望能够做到
由于将约束连接到IBOutlets
并将其常量设置为0
让人感觉不舒服(并且在视图有子视图时导致NSLayoutConstraint
警告),因此我决定创建一个扩展来提供
它仅隐藏视图并删除外部约束。再次显示视图时,会将约束添加回视图。唯一需要注意的是,您需要为周围的视图指定灵活的故障切换约束
编辑
此答案针对iOS 8.4及以下版本。在iOS 9中,只需使用
UIStackView
方法。隐藏时使用常量0
,再次显示时使用另一个常量的解决方案是有效的,但如果内容大小灵活,则无法令人满意。您需要测量您的灵活内容,并设置一个常数。这感觉是错误的,如果内容因服务器或UI事件而改变大小,则会出现问题
我有一个更好的解决办法
其思想是在隐藏元素时,将0高度规则设置为具有高优先级,以便它不占用自动布局空间
以下是您如何做到这一点:
1。在interface builder中以低优先级设置宽度(或高度)0。
Interface Builder不会大声抱怨冲突,因为优先级较低。通过将优先级临时设置为999来测试高度行为(1000禁止以编程方式进行变异,因此我们不会使用它)。Interface builder现在可能会大喊冲突约束。您可以通过将相关对象的优先级设置为900左右来修复这些问题
2。添加一个插座,以便在代码中修改宽度约束的优先级:
cell.alertTimingView.hidden = place.closingSoon != true
cell.alertTimingWidth.priority = place.closingSoon == true ? 250 : 999
3。隐藏元素时调整优先级:
cell.alertTimingView.hidden = place.closingSoon != true
cell.alertTimingWidth.priority = place.closingSoon == true ? 250 : 999
这可能是iOS 9+的发展方向。它不仅处理隐藏视图,如果设置正确,还将删除额外的间距和边距。对视图进行子类化,并覆盖
func intrinsicContentSize()->CGSize
。如果视图是隐藏的,只需返回CGSizeZero
。我也会提供我的解决方案,以提供多样性。)我认为为每个项目的宽度/高度加上间距创建一个出口是荒谬的,会破坏代码、可能的错误和复杂性
我的方法删除所有视图(在我的例子中是UIImageView实例),选择需要添加回的视图,并在循环中添加回每个视图并创建新的约束。这其实很简单,请坚持到底。下面是我快速而肮脏的代码:
// remove all views
[self.twitterImageView removeFromSuperview];
[self.localQuestionImageView removeFromSuperview];
// self.recipients always has to be present
NSMutableArray *items;
items = [@[self.recipients] mutableCopy];
// optionally add the twitter image
if (self.question.sharedOnTwitter.boolValue) {
[items addObject:self.twitterImageView];
}
// optionally add the location image
if (self.question.isLocal) {
[items addObject:self.localQuestionImageView];
}
UIView *previousItem;
UIView *currentItem;
previousItem = items[0];
[self.contentView addSubview:previousItem];
// now loop through, add the items and the constraints
for (int i = 1; i < items.count; i++) {
previousItem = items[i - 1];
currentItem = items[i];
[self.contentView addSubview:currentItem];
[currentItem mas_remakeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(previousItem.mas_centerY);
make.right.equalTo(previousItem.mas_left).offset(-5);
}];
}
// here I just connect the left-most UILabel to the last UIView in the list, whichever that was
previousItem = items.lastObject;
[self.userName mas_remakeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(previousItem.mas_left);
make.leading.equalTo(self.text.mas_leading);
make.centerY.equalTo(self.attachmentIndicator.mas_centerY);;
}];
//删除所有视图
[self.twitterImageView从SuperView移除];
[self.localQuestionImageView从SuperView移除];
//自助接受者必须始终在场
NSMutableArray*项;
items=[@[self.recipients]mutableCopy];
//可以选择添加twitter图像
if(self.question.sharedOnTwitter.boolValue){
[items addObject:self.twitterImageView];
}
//(可选)添加位置图像
if(self.question.isLocal){
[items addObject:self.localQuestionImageView];
}
UIView*上一项;
UIView*当前项目;
previousItem=项目[0];
[self.contentView addSubview:previousItem];
//现在循环,添加项目和约束
对于(int i=1;icell.authorLabelHeight.constant = 0; //Hide
cell.authorLabelHeight.constant = 44; //Show
@property (strong, nonatomic) IBOutlet UIView *myContainer;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *myContainerHeight; //should be strong!!
-(void) showContainer
{
self.myContainerHeight.active = NO;
self.myContainer.hidden = NO;
[self.view layoutIfNeeded];
}
-(void) hideContainer
{
self.myContainerHeight.active = YES;
self.myContainerHeight.constant = 0.0f;
self.myContainer.hidden = YES;
[self.view layoutIfNeeded];
}
import UIKit
class ViewController: UIViewController {
@IBOutlet var ToBeHiddenLabel: UILabel!
@IBOutlet var hiddenConstraint: NSLayoutConstraint!
@IBOutlet var notHiddenConstraint: NSLayoutConstraint!
@IBAction func HideMiddleButton(_ sender: Any) {
ToBeHiddenLabel.isHidden = !ToBeHiddenLabel.isHidden
notHiddenConstraint.isActive = !notHiddenConstraint.isActive
hiddenConstraint.isActive = !hiddenConstraint.isActive
self.view.setNeedsDisplay()
}
}
boxView.optItems = [
firstLabel.boxed.useIf(isFirstLabelShown),
secondLabel.boxed.useIf(isSecondLabelShown),
button.boxed
]