弹出键盘时iOS 11无限滚动

Posted

技术标签:

【中文标题】弹出键盘时iOS 11无限滚动【英文标题】:iOS 11 scrolling infinite when keyboard pops up 【发布时间】:2018-01-16 07:27:35 【问题描述】:

当我在 ios 原生应用程序上使用 login screen 时,应用程序在 ios 10 之前运行良好。在 iOS 11 及以后的 topLayoutGuidebottomLayoutGuide 上,我已将它们替换为 safeAreaLayoutGuide

但它仍然不能解决我的问题,当键盘弹出时,由于页脚视图,它会导致视图无限滚动。 我的视图层次结构是这样的

ScrollView

Username and Password fields with Login button

FooterEmptyView

Footer Label

FooterEmptyView 中,我有一个限制,即从登录按钮到Footer Label 之间留出空间,这将根据设备的大小而增加或减少。 所有约束都以编程方式放置。

这是我需要为 iOS 11 处理的 UIScrollView 问题吗? 提前致谢!!

【问题讨论】:

由于所有这些都是以编程方式设置的,您可以简单地分享您的代码吗?事情出错的可能原因有很多。布局指南与安全区域应该没有区别。我认为该错误一直存在,但是您很幸运,以前的版本结果仍然可以。 感谢@MaticOblak .. 我正在创建一个示例项目并通过 github 链接与您分享。 我的远程仓库链接https://github.com/shivaiahmahesha/iOS11_Issues 我相信你粘贴到 repo 的一个文件夹太深了。项目文件丢失,但源文件在那里...... 对不起,请使用以下链接https://github.com/shivaiahmahesha/ScrollIssues 【参考方案1】:

您提供的存储库不包括您问题中描述的大部分功能。键盘事件就是其中之一。在运行代码时,我还收到了约束冲突的错误日志,因此至少其中一个已在运行时被删除。此外,发布的应用程序根本不做任何事情或显示除了显示 2 个文本字段和一个按钮之外它应该做的事情。

由于您创建了一个新项目,您所做的只是修改了其自动生成的视图控制器,因此没有理由不将此代码添加到您的问题中,而不是发布到您的存储库的链接。

我仍然检查了你的代码并试图推断你在做什么,发现我在你的约束下没有任何意义。也许至少有一些 cmets 会有所帮助。然后我重新创建了您的系统,在带有约束的滚动视图上放置了 2 个文本字段和一个按钮。我还添加了可以移动字段的方法,以便它们正确地从底部偏移,设计用于在键盘框架发生变化时用于事件。

我希望代码能帮助您解决问题。完成后请删除此问题。

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIView *scrollViewContentView;

@property (nonatomic, strong) UITextField *usernameTextField;
@property (nonatomic, strong) UITextField *passwordTextField;
@property (nonatomic, strong) UIButton *loginButton;

@property (nonatomic, strong) NSLayoutConstraint *usernameBottomConstraint; // Restrict for keyboard
@property (nonatomic, strong) NSLayoutConstraint *passwordBottomConstraint; // Restrict for keyboard

@end

@implementation ViewController

- (UIScrollView *)scrollView 
    // Lazy load
    if(_scrollView == nil) 
        _scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
        [self.view addSubview:_scrollView];
        _scrollView.translatesAutoresizingMaskIntoConstraints = NO;
        [self attachBorderConstraint:_scrollView to:self.view];
    
    return _scrollView;


- (UIView *)scrollViewContentView 
    // Lazy load
    if(_scrollViewContentView == nil) 
        _scrollViewContentView = [[UIView alloc] initWithFrame:self.view.bounds];
        [self.scrollView addSubview:_scrollViewContentView];
        _scrollViewContentView.translatesAutoresizingMaskIntoConstraints = NO;
        NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:_scrollViewContentView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self.scrollView attribute:NSLayoutAttributeTop multiplier:1.0 constant:10.0];
        topConstraint.priority = (UILayoutPriority)800; // Needs a bit lower constraints when keyboard shows and the whole thing goes a bit off the screen
        [self.scrollView addConstraint:topConstraint];
        [self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:_scrollViewContentView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self.scrollView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:10.0]];
        [self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:_scrollViewContentView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self.scrollView attribute:NSLayoutAttributeLeft multiplier:1.0 constant:10.0]];
        [self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:_scrollViewContentView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationLessThanOrEqual toItem:self.scrollView attribute:NSLayoutAttributeRight multiplier:1.0 constant:10.0]];
    
    return _scrollViewContentView;


- (void)viewDidLoad 
    [super viewDidLoad];

    // TODO: remove testing offsets gesture and its method
    [self.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTestGesture)]];

    // Set some colors for debugging and to see what happens. Later remove these
    self.scrollView.backgroundColor = [UIColor redColor];
    self.scrollViewContentView.backgroundColor = [UIColor greenColor];

    CGFloat margins = 20.0f;
    CGFloat fieldHeight = 44.0f;
    CGFloat separatorHeight = 12.0f;

    // Username text field
    
        UITextField *field = [[UITextField alloc] initWithFrame:CGRectMake(0, margins, self.view.frame.size.width-margins*2.0, fieldHeight)];
        field.placeholder = @"Enter User Name";
        field.backgroundColor = [UIColor whiteColor];
        field.translatesAutoresizingMaskIntoConstraints = NO;
        [self.scrollViewContentView addSubview:field];

        // We want it on top of content view and have fixed offset from borders
        [self.scrollViewContentView addConstraint:[NSLayoutConstraint constraintWithItem:field attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.scrollViewContentView attribute:NSLayoutAttributeTop multiplier:1.0 constant:margins]];
        [self.scrollViewContentView addConstraint:[NSLayoutConstraint constraintWithItem:field attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.scrollViewContentView attribute:NSLayoutAttributeLeft multiplier:1.0 constant:margins]];
        [self.scrollViewContentView addConstraint:[NSLayoutConstraint constraintWithItem:field attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.scrollViewContentView attribute:NSLayoutAttributeRight multiplier:1.0 constant:-margins]];
        [field addConstraint:[NSLayoutConstraint constraintWithItem:field attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:fieldHeight]];

        self.usernameTextField = field;
    

    // Password text field
    
        UITextField *field = [[UITextField alloc] initWithFrame:CGRectMake(0, margins, self.view.frame.size.width-margins*2.0, fieldHeight)];
        field.placeholder = @"Enter Password";
        field.backgroundColor = [UIColor whiteColor];
        field.translatesAutoresizingMaskIntoConstraints = NO;
        [self.scrollViewContentView addSubview:field];

        // We want it below username field and have fixed offset from borders
        [self.scrollViewContentView addConstraint:[NSLayoutConstraint constraintWithItem:field attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.usernameTextField attribute:NSLayoutAttributeBottom multiplier:1.0 constant:separatorHeight]];
        [self.scrollViewContentView addConstraint:[NSLayoutConstraint constraintWithItem:field attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.scrollViewContentView attribute:NSLayoutAttributeLeft multiplier:1.0 constant:margins]];
        [self.scrollViewContentView addConstraint:[NSLayoutConstraint constraintWithItem:field attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.scrollViewContentView attribute:NSLayoutAttributeRight multiplier:1.0 constant:-margins]];
        [field addConstraint:[NSLayoutConstraint constraintWithItem:field attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:fieldHeight]];

        self.passwordTextField = field;
    

    // Login button
    
        UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, margins, self.view.frame.size.width-margins*2.0, 50.0)];
        [button setTitle:@"Login" forState:UIControlStateNormal];
        [button setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        button.titleLabel.font = [UIFont systemFontOfSize:16.0];  //[UIFont fontWithName:@"MarkerFelt-Thin" size:16];
        button.translatesAutoresizingMaskIntoConstraints = NO;
        button.backgroundColor = [UIColor whiteColor];

        [self.scrollViewContentView addSubview:button];

        // We want it below password field and have fixed offset from borders
        [self.scrollViewContentView addConstraint:[NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.passwordTextField attribute:NSLayoutAttributeBottom multiplier:1.0 constant:separatorHeight]];
        [self.scrollViewContentView addConstraint:[NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.scrollViewContentView attribute:NSLayoutAttributeLeft multiplier:1.0 constant:margins]];
        [self.scrollViewContentView addConstraint:[NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.scrollViewContentView attribute:NSLayoutAttributeRight multiplier:1.0 constant:-margins]];
        [button addConstraint:[NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:50.0]];
        [self.scrollViewContentView addConstraint:[NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.scrollViewContentView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-margins]];

        self.loginButton = button;
    

    // Now to position the content view insde scroll view

    // Horizontally we want to constrain it to borders and restrict maximum width:
    
        // Must be centered
        NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.scrollViewContentView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0];
        [self.scrollView addConstraint:constraint];
    
    
        // Constraint width
        NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.scrollViewContentView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:400.0];
        constraint.priority = (UILayoutPriority)500; // Low priority so it will shrink when screen width will be too low
        [self.scrollViewContentView addConstraint:constraint];
    

    // Vertically we want to constrain it to borders and restrict maximum width:
    
        // Must be centered
        NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.scrollViewContentView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0];
        constraint.priority = (UILayoutPriority)500;
        [self.scrollView addConstraint:constraint];
    
    
        // Constraint height
        NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.scrollViewContentView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:400.0];
        constraint.priority = (UILayoutPriority)200; // Low priorty so it will autosize
        [self.scrollViewContentView addConstraint:constraint];
    

    self.usernameBottomConstraint = [NSLayoutConstraint constraintWithItem:self.usernameTextField attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationLessThanOrEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-0];
    self.passwordBottomConstraint = [NSLayoutConstraint constraintWithItem:self.passwordTextField attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationLessThanOrEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-0];
    [self.view addConstraints:@[self.usernameBottomConstraint, self.passwordBottomConstraint]];



- (void)onTestGesture 
    static int testState = 0;
    testState++;

    switch (testState%3) 
        case 0:
            // Keyboard hidden
            [self restrictUsernameBottomOffsetTo:0.0];
            [self restrictPasswrodBottomOffsetTo:0.0];
            break;
        case 1:
            // Keyboard shown for username
            [self restrictUsernameBottomOffsetTo:350.0];
            break;
        case 2:
            // Keyboard shown for password
            [self restrictPasswrodBottomOffsetTo:350.0];
            break;
        default:
            break;
    

    [UIView animateWithDuration:0.3 animations:^
        [self.view layoutIfNeeded];
    ];


- (void)restrictUsernameBottomOffsetTo:(CGFloat)newOffset 
    self.passwordBottomConstraint.constant = -0.0f;
    self.usernameBottomConstraint.constant = -newOffset;

- (void)restrictPasswrodBottomOffsetTo:(CGFloat)newOffset 
    self.usernameBottomConstraint.constant = -0.0f;
    self.passwordBottomConstraint.constant = -newOffset;


- (NSArray<NSLayoutConstraint *> *)attachBorderConstraint:(UIView *)subview to:(UIView *)superview 
    NSMutableArray *constraints = [[NSMutableArray alloc] init];
    [constraints addObject:[NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0]];
    [constraints addObject:[NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0]];
    [constraints addObject:[NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0.0]];
    [constraints addObject:[NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeRight multiplier:1.0 constant:0.0]];
    [superview addConstraints:constraints];
    return constraints;



@end

【讨论】:

非常感谢,您提到的评论帮助我解决了这个问题,I assume the bug was there all along but you got lucky with previous versions that the result was still OK。我的布局在哪里非常复杂(仅以编程方式),我尝试重构它的代码。我现在可以做到的..很抱歉没有完全更新我的回购,因为专注于解决问题,现在工作正常..但是我更新我的回购并添加无限滚动视图问题,可能是我可以更好地解决我已修复的问题!!再次感谢!

以上是关于弹出键盘时iOS 11无限滚动的主要内容,如果未能解决你的问题,请参考以下文章

尝试使用 Swift 实现无限滚动时陷入无限循环

ios中具有自动滚动功能的无限Textview

加载内容后如何停止无限滚动

如何防止'GMSMapView'无限水平滚动?

Ag Grid 无限滚动

iOS无限循环滚动实现