UIScrollView的用粘页脚的UIView和动态内容的高度 [英] UIScrollView with sticky footer UIView and dynamic height content

查看:240
本文介绍了UIScrollView的用粘页脚的UIView和动态内容的高度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

挑战时间!

假设我们有2个内容的看法:

Imagine we have 2 content views:


  1. 与UIView的高度动态内容(可扩展的UITextView)= RED

  2. 的UIView作为页脚=蓝色

这内容是里面的UIScrollView = GEEN

This content is inside a UIScrollView = GEEN

我应该如何构建和处理与自动布局的制约归档所有下列情况?

How should I structure and handle the constraints with auto-layout to archive all the following cases?

我想接下来的基本结构入手:

I am thinking next basic structure to start with:

- UIScrollView (with always bounce vertically)
    - UIView - Container
       - UIView - DynamicHeightContent
       - UIView - Sticky Footer

键盘操作应该由code看通知来完成 UIKeyboardWillShowNotification UIKeyboardWillHideNotification 。我们可以选择键盘的结束帧的高度设置为容器的UIView下销约束或到UIScrollView的底部contentInset。

Keyboard handling should be done by code watching notifications UIKeyboardWillShowNotification and UIKeyboardWillHideNotification. We can chose to set the keyboard's end frame height to Container UIView bottom pin constraint or to the UIScrollView bottom contentInset.

现在,棘手的部分是粘页脚。

Now, the tricky part is the sticky footer.


  • 粘页脚的UIView我们如何确保停留在如果有更多可用的屏幕比整个容器视图的底部?

  • 我们如何知道可用的屏幕空间时显示/隐藏键盘?我们一定会需要它。

  • 是是不是这个结构我的目的?

感谢您。

推荐答案

的UITextView 相对较短,内容视图的子视图(如文本的文本内容查看和页脚)将无法通过约束来决定其内容视图的大小。这是因为,当文本内容是短,就需要通过滚动视图的大小来确定的内容视图的大小。

When the text content of the UITextView is relatively short, the content view's subviews (i.e., the text view and footer) will not be able to dictate the size of their content view through constraints. That's because when the text content is short, the content view's size will need to be determined by the scroll view's size.

更新:后者段落是不真实的。你既可以对内容视图本身或内容视图的视图层次的地方安装一个固定高度的限制。固定高度约束常数可能在code设置,以反映滚动视图的高度。后者款也反映在思维谬误。在纯粹的自动布局方法,内容视图的子视图并不需要决定了滚动视图 contentSize ;相反,它是内容视图本身最终必须决定了 contentSize

Update: The latter paragraph is untrue. You could install a fixed-height constraint either on the content view itself or somewhere in the content view's view hierarchy. The fixed-height constraint's constant could be set in code to reflect the height of the scroll view. The latter paragraph also reflects a fallacy in thinking. In a pure Auto Layout approach, the content view's subviews don't need to dictate the scroll view's contentSize; instead, it's the content view itself that ultimately must dictate the contentSize.

无论如何,我决定去与苹果的所谓混合模式,使用自动布局与的UIScrollView (见苹果的技术说明:<一href=\"https://developer.apple.com/library/ios/technotes/tn2154/_index.html\">https://developer.apple.com/library/ios/technotes/tn2154/_index.html)

Regardless, I decided to go with Apple's so-called "mixed approach" for using Auto Layout with UIScrollView (see Apple's Technical Note: https://developer.apple.com/library/ios/technotes/tn2154/_index.html)

在某些iOS技术作家,像埃里卡丧盾,使用pretty混合方法preFER多所有情况(iOS的自动布局揭秘,第2版)。

Some iOS technical writers, like Erica Sadun, prefer using the mixed approach in pretty much all situations ("iOS Auto Layout Demystified", 2nd Ed.).

在混合的方法,内容视图的框架和滚动视图内容大小中明确code设置。

In the mixed approach, the content view's frame and the scroll view's content size are explicitly set in code.

下面是我对这一挑战创建G​​itHub的回购:<一href=\"https://github.com/bilobatum/StickyFooterAutoLayoutChallenge\">https://github.com/bilobatum/StickyFooterAutoLayoutChallenge.这是一个有效的解决方案完成布局的变化动画。它适用于不同大小的设备。为简单起见,我禁用旋转为横向。

Here's the GitHub repo I created for this challenge: https://github.com/bilobatum/StickyFooterAutoLayoutChallenge. It's a working solution complete with animation of layout changes. It works on different sized devices. For simplicity, I disabled rotation to landscape.

对于那些不希望下载并运行该项目GitHub的谁,我已经包括下面的一些亮点(为完整的实现,你必须看看GitHub的项目):

For those who don't want to download and run the GitHub project, I have included some highlights below (for the complete implementation, you'll have to look at the GitHub project):

内容的看法是橙色,文本视图是灰色的,粘页脚是蓝色的。该文本是在滚动状态栏后面可见。其实我并不喜欢这一点,但它是罚款演示。

The content view is orange, the text view is gray, and the sticky footer is blue. The text is visible behind the status bar while scrolling. I don't actually like that, but it's fine for a demo.

在故事板实例化的唯一观点是滚动视图,这是全屏幕(即underlaps状态栏)。

The only view instantiated in storyboard is the scroll view, which is full-screen (i.e., underlaps status bar).

有关测试的目的,我连着一双击手势识别蓝页脚驳回键盘的目的。

For testing purposes, I attached a double tap gesture recognizer to the blue footer for the purpose of dismissing the keyboard.

- (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, 0, scrollViewSize.width, scrollViewSize.height);
    } else {
        self.contentView.frame = CGRectMake(0, 0, scrollViewSize.width, 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, 0, scrollViewSize.width, contentViewHeight);
        self.scrollView.contentSize = self.contentView.bounds.size;
        UIEdgeInsets insets = UIEdgeInsetsMake(0, 0, keyboardRect.size.height, 0);
        self.scrollView.contentInset = insets;
        self.scrollView.scrollIndicatorInsets = insets;

        [self.view layoutIfNeeded];

    } completion:^(BOOL finished) {

        [self scrollToCaret];
    }];
}

虽然这个演示应用程序的自动布局组件花了一些时间,我花了几乎同样多的时间相关的的UITextView 被嵌套的内部滚动问题的UIScrollView

Although the Auto Layout component of this demo app took some time, I spent almost as much time on scrolling issues related to a UITextView being nested inside of a UIScrollView.

这篇关于UIScrollView的用粘页脚的UIView和动态内容的高度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆