想像一下我们有2个内容视图:
> UIView具有动态高度内容(可扩展UITextView)= RED
> UIView作为页脚= BLUE
该内容在UIScrollView = GEEN内
我应该如何使用自动布局来构建和处理约束以归档所有以下情况?
我正在考虑下一个基本结构:
- UIScrollView (with always bounce vertically) - UIView - Container - UIView - DynamicHeightContent - UIView - Sticky Footer
键盘处理应该通过代码监视UIKeyboardWillShowNotification和UIKeyboardWillHideNotification来完成.我们可以选择将键盘的端框高度设置为Container UIView底部引脚约束或UIScrollView底部contentInset.
现在,棘手的部分是粘性页脚.
>如果有超过整个集装箱视图的屏幕可用,我们如何确保粘性页脚UIView保持在底部?
>当键盘显示/隐藏时,我们如何知道可用的屏幕空间?我们一定会需要它
>这个结构是正确的吗?
谢谢.
解决方法
更新:后一段是不真实的.您可以在内容视图本身或内容视图的视图层次结构中的某个位置安装固定高度约束.固定高度约束的常数可以在代码中设置,以反映滚动视图的高度.后一段也反映了思想的谬误.在纯粹的自动布局方法中,内容视图的子视图不需要规定滚动视图的contentSize;相反,它的内容视图本身最终必须指定contentSize.
无论如何,我决定和苹果公司所谓的“混合方式”一起使用UIScrollView自动布局(参见苹果技术说明:https://developer.apple.com/library/ios/technotes/tn2154/_index.html)
一些iOS技术作家,如Erica Sadun,喜欢在几乎所有情况下使用混合方法(“iOS Auto Layout Demystified”,第2版).
在混合方法中,内容视图的框架和滚动视图的内容大小在代码中被明确设置.
这是我为这个挑战创建的GitHub回购:https://github.com/bilobatum/StickyFooterAutoLayoutChallenge.这是一个完整的布局更改动画的工作解决方案.它适用于不同尺寸的设备.为了简单起见,我禁止轮到风景.
对于那些不想下载和运行GitHub项目的人,我已经在下面列出了一些亮点(为了完整的实现,你必须看GitHub项目):
内容视图为橙色,文本视图为灰色,粘性页脚为蓝色.滚动时,状态栏后面会显示文本.我实际上并不喜欢,但是演示就好了.
在故事板中实例化的唯一视图是滚动视图,它是全屏(即,底部状态栏).
为了测试目的,我将一个双击手势识别器附加到蓝色的页脚,目的是解除键盘.
- (void)viewDidLoad { [super viewDidLoad]; self.scrollView.alwaysBounceVertical = YES; [self.scrollView addSubview:self.contentView]; [self.contentView addSubview:self.textView]; [self.contentView addSubview:self.stickyFooterView]; [self configureConstraintsForContentViewSubviews]; // Apple's mixed (a.k.a. hybrid) approach to laying out a scroll view with Auto Layout: explicitly set content view's frame and scroll view's contentSize (see Apple's Technical Note TN2154: https://developer.apple.com/library/ios/technotes/tn2154/_index.html) CGFloat textViewHeight = [self calculateHeightForTextViewWithString:self.textView.text]; CGFloat contentViewHeight = [self calculateHeightForContentViewWithTextViewHeight:textViewHeight]; // scroll view is fullscreen in storyboard; i.e.,it's final on-screen geometries will be the same as the view controller's main view; unfortunately,the scroll view's final on-screen geometries are not available in viewDidLoad CGSize scrollViewSize = self.view.bounds.size; if (contentViewHeight < scrollViewSize.height) { self.contentView.frame = CGRectMake(0,scrollViewSize.width,scrollViewSize.height); } else { self.contentView.frame = CGRectMake(0,contentViewHeight); } self.scrollView.contentSize = self.contentView.bounds.size; } - (void)configureConstraintsForContentViewSubviews { assert(_textView && _stickyFooterView); // for debugging // note: there is no constraint between the subviews along the vertical axis; the amount of vertical space between the subviews is determined by the content view's height NSString *format = @"H:|-(space)-[textView]-(space)-|"; [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:@{@"space": @(SIDE_MARGIN)} views:@{@"textView": _textView}]]; format = @"H:|-(space)-[footer]-(space)-|"; [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:@{@"space": @(SIDE_MARGIN)} views:@{@"footer": _stickyFooterView}]]; format = @"V:|-(space)-[textView]"; [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:@{@"space": @(TOP_MARGIN)} views:@{@"textView": _textView}]]; format = @"V:[footer(height)]-(space)-|"; [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:@{@"space": @(BOTTOM_MARGIN),@"height": @(FOOTER_HEIGHT)} views:@{@"footer": _stickyFooterView}]]; // a UITextView does not have an intrinsic content size; will need to install an explicit height constraint based on the size of the text; when the text is modified,this height constraint's constant will need to be updated CGFloat textViewHeight = [self calculateHeightForTextViewWithString:self.textView.text]; self.textViewHeightConstraint = [NSLayoutConstraint constraintWithItem:self.textView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:0 multiplier:1.0f constant:textViewHeight]; [self.textView addConstraint:self.textViewHeightConstraint]; } - (void)keyboardUp:(NSNotification *)notification { // when the keyboard appears,extraneous vertical space between the subviews is eliminated–if necessary; i.e.,vertical space between the subviews is reduced to the minimum if this space is not already at the minimum NSDictionary *info = [notification userInfo]; CGRect keyboardRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; keyboardRect = [self.view convertRect:keyboardRect fromView:nil]; double duration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]; CGFloat contentViewHeight = [self calculateHeightForContentViewWithTextViewHeight:self.textView.bounds.size.height]; CGSize scrollViewSize = self.scrollView.bounds.size; [UIView animateWithDuration:duration animations:^{ self.contentView.frame = CGRectMake(0,contentViewHeight); self.scrollView.contentSize = self.contentView.bounds.size; UIEdgeInsets insets = UIEdgeInsetsMake(0,keyboardRect.size.height,0); self.scrollView.contentInset = insets; self.scrollView.scrollIndicatorInsets = insets; [self.view layoutIfNeeded]; } completion:^(BOOL finished) { [self scrollToCaret]; }]; }
虽然此演示应用程序的自动布局组件需要花费一些时间,但我花了几乎相当多的时间来滚动与嵌套在UIScrollView内的UITextView相关的问题.