隐藏视图时如何使用自动布局来移动其他视图?
我已经在IBdevise了自定义Cell,将其分类,并将我的网点连接到我的自定义类。 在单元格内容中有三个子视图:UIView(cdView)和两个标签(titleLabel和emailLabel)。 根据每行可用的数据,有时我想要在我的单元格中显示UIView和两个标签,有时候只有两个标签。 我想要做的是设置约束,如果我把UIView属性设置为隐藏,或者我将从超级视图中移除它,两个标签将移动到左侧。 我试图设置UIView领先的约束为10px的Superview(单元格内容)和UILabels领先约束为10像素到下一个视图(UIView)。 后来在我的代码
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(IndexPath *)indexPath { ... Record *record = [self.records objectAtIndex:indexPath.row]; if ([record.imageURL is equalToString:@""]) { cell.cdView.hidden = YES; }
我隐藏我的cell.cdView,我想标签移动到左边,但他们留在细胞中的相同位置。 我试图从超级视图中删除cell.cdView,但它也没有工作。 我附上图片来澄清我的情况。
我知道如何以编程方式进行此操作,而我并不是在寻找解决scheme。 我想要的是在IB中设置约束,我期望如果其他视图被删除或隐藏,我的子视图将dynamic地移动。 是否有可能在自动布局的IB中做到这一点?
.....
这是可能的,但你将不得不做一些额外的工作。 首先有一些概念性的东西可以避开:
- 隐藏的视图,即使不绘制, 仍然参与自动布局,通常保留其框架 ,留下其他相关的意见。
- 从其超级视图中删除视图时, 所有相关的约束也从该视图层次中删除。
就你而言,这可能意味着:
- 如果您将左侧视图设置为隐藏状态,则标签会保留原位,因为左侧视图仍占用空间(尽pipe看不见)。
- 如果您删除了左侧视图,则标签可能会被模糊限制,因为您不再对标签的左侧边缘有限制。
你需要做的是明智地过度限制你的标签。 将现有的约束(10pts空间保留到另一个视图)单独添加,但添加另一个约束:使标签的左边缘距离其超级视图的左边缘10分钟,并且不需要优先级(默认的高优先级可能工作正常)。
然后,当你想让它们向左移动时,完全移除左边的视图。 左视图的强制性10pt约束与其相关的视图一起消失,并且只剩下一个高优先级的约束条件,即标签距离其超视图为10pt。 在下一个布局过程中,这应该会导致它们向左扩展,直到它们填充超视图的宽度,而是在边缘周围进行间隔。
一个重要的警告:如果您想要将左视图重新放入图片中,则不仅需要将其添加回视图层次结构中,还必须同时重新创build所有约束 。 这意味着当需要再次显示视图时,需要一种将视图和标签之间的10pt间距约束放回的方法。
在运行时添加或删除约束是一个重要的操作,可能会影响性能。 但是,有一个更简单的select。
对于你想要隐藏的视图,设置一个宽度约束。 限制其他观点与这个观点存在领先的横向差距。
要隐藏, .constant
宽度约束的.constant
更新为0.f. 其他视图将自动向左移动以承担位置。
看到我的其他答案在这里更多的细节:
如何在运行时更改标签约束?
对于仅支持iOS 8+的用户,有一个新的布尔属性处于活动状态 。 这将有助于dynamic地仅启用所需的约束
PS约束出口应该强大 ,不弱
UIStackView
其任何子视图(iOS 9 +)上的hidden
属性发生更改时自动重新定位其视图。
UIView.animateWithDuration(1.0) { () -> Void in self.mySubview.hidden = !self.mySubview.hidden }
在此WWDCvideo中跳到11:48进行演示:
自动布局之谜,第1部分
我的项目使用UILabel
的自定义@IBDesignable
子类(以确保颜色,字体,插入等的一致性),我已经实现了如下内容:
override func intrinsicContentSize() -> CGSize { if hidden { return CGSizeZero } else { return super.intrinsicContentSize() } }
这允许标签子类参与自动布局,但隐藏时不占用空间。
对于Google员工:build立在Max的答案上,为了解决许多人已经注意到的填充问题,我简单地增加了标签的高度,并使用该高度作为分隔符而不是实际的填充。 这个想法可以扩展到包含视图的任何场景。
这是一个简单的例子:
在这种情况下,我将作者标签的高度映射到适当的IBOutlet
:
@property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight;
当我将约束的高度设置为0.0f
,我们保留“填充”,因为“ 播放”button的高度允许。
将UI视图和标签之间的约束连接为IBOutlet,并在设置hidden = YES时将优先级成员设置为较小的值
我最终做的是创build2个xibs。 一个左视图,一个没有它。 我在控制器中注册了两个,然后决定在cellForRowAtIndexPath期间使用哪一个。
他们使用相同的UITableViewCell类。 缺点是xib之间的内容有一些重复,但是这些单元是非常基本的。 好处是我没有一堆代码来手动pipe理删除视图,更新约束等。
一般来说,这可能是一个更好的解决scheme,因为它们在技术上是不同的布局,因此应该有不同的xib。
[self.table registerNib:[UINib nibWithNibName:@"TrackCell" bundle:nil] forCellReuseIdentifier:@"TrackCell"]; [self.table registerNib:[UINib nibWithNibName:@"TrackCellNoImage" bundle:nil] forCellReuseIdentifier:@"TrackCellNoImage"]; TrackCell *cell = [tableView dequeueReusableCellWithIdentifier:(appDelegate.showImages ? @"TrackCell" : @"TrackCellNoImage") forIndexPath:indexPath];
在这种情况下,我将作者标签的高度映射到适当的IBOutlet:
@property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight;
当我将约束的高度设置为0.0f时,我们保留“填充”,因为“播放”button的高度允许。
cell.authorLabelHeight.constant = 0;
使用两个UIStackView水平和垂直,当一些子视图在堆栈隐藏其他堆栈子视图将被移动,使用分布 – >填充Proporionally与两个UILabels垂直堆栈,并需要设置宽度和高度第一UIView
如no_scenebuild议,你可以通过在运行时改变约束的优先级来做到这一点。 这对我来说容易得多,因为我有多个阻止视图,必须删除。
这里有一个使用ReactiveCocoa的片段:
RACSignal* isViewOneHiddenSignal = RACObserve(self.viewModel, isViewOneHidden); RACSignal* isViewTwoHiddenSignal = RACObserve(self.viewModel, isViewTwoHidden); RACSignal* isViewThreeHiddenSignal = RACObserve(self.viewModel, isViewThreeHidden); RAC(self.viewOne, hidden) = isViewOneHiddenSignal; RAC(self.viewTwo, hidden) = isViewTwoHiddenSignal; RAC(self.viewThree, hidden) = isViewThreeHiddenSignal; RAC(self.viewFourBottomConstraint, priority) = [[[[RACSignal combineLatest:@[isViewOneHiddenSignal, isViewTwoHiddenSignal, isViewThreeHiddenSignal]] and] distinctUntilChanged] map:^id(NSNumber* allAreHidden) { return [allAreHidden boolValue] ? @(780) : @(UILayoutPriorityDefaultHigh); }]; RACSignal* updateFramesSignal = [RACObserve(self.viewFourBottomConstraint, priority) distinctUntilChanged]; [updateFramesSignal subscribeNext:^(id x) { @strongify(self); [self.view setNeedsUpdateConstraints]; [UIView animateWithDuration:0.3 animations:^{ [self.view layoutIfNeeded]; }]; }];
在我的情况下,我将高度约束的常量设置为0.0f
,并将hidden
属性设置为YES
。
为了再次显示视图(和子视图),我做了相反的事情:我将高度常量设置为非零值,并将hidden
属性设置为NO
。
如果这有助于某人,我build立了使用视觉格式约束的助手类。 我正在使用它在我目前的应用程序。
AutolayoutHelper
这可能会根据我的需求而定制,但是您可能会发现它很有用,或者您可能想修改它并创build自己的帮助器。
我不得不感谢蒂姆上面的 答案,关于UIScrollView和本教程的 答案 。
以下是我将如何重新调整我的uiviews以获得您的解决scheme:
- 拖放一个UIImageView并将其放置在左侧。
- 拖放一个UIView并将其放置在UIImageView的右侧。
- 拖放两个UILabels,该UIView的前后约束都是零。
- 将包含2个标签的UIView的主要约束设置为superview而不是UIImagView。
- 如果UIImageView处于隐藏状态,则将超前查看的前导约束设置为10 px。 ELSE,将前导约束设置为10 px + UIImageView.width + 10 px。
我创build了我自己的拇指规则。 无论何时你必须隐藏/显示其约束可能受到影响的任何uiview,请在uiview中添加所有受影响/依赖的子视图,并以编程方式更新其前导/尾随/顶部/底部约束常量。
这是一个古老的问题,但我希望这会有所帮助。 从Android开始,在这个平台上,你有一个方便的方法isVisible
来隐藏它的视图,但也没有考虑到自动布局绘制视图时的框架。
使用扩展和“扩展”uiview你可以做一个类似的function在IOS(不知道为什么它不在UIKit已经)在这里一个实现在SWIFT 3:
func isVisible(_ isVisible: Bool) { self.isHidden = !isVisible self.translatesAutoresizingMaskIntoConstraints = isVisible if isVisible { //if visible we remove the hight constraint if let constraint = (self.constraints.filter{$0.firstAttribute == .height}.first){ self.removeConstraint(constraint) } } else { //if not visible we add a constraint to force the view to have a hight set to 0 let height = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal , toItem: nil, attribute: .notAnAttribute, multiplier: 0, constant: 0) self.addConstraint(height) } self.layoutIfNeeded() }