简体   繁体   English

键盘弹出时iOS 11无限滚动

[英]iOS 11 scrolling infinite when keyboard pops up

As im working on with the login screen on iOS native app, app worked exactly fine till ios 10. on iOS 11 onwards the topLayoutGuide and bottomLayoutGuide , i have replace them with the safeAreaLayoutGuide . 由于我正在使用iOS本机应用程序的login screen ,因此应用程序运行良好,直到iOS 10上的iOS 10以及topLayoutGuidebottomLayoutGuide ,我已将它们替换为safeAreaLayoutGuide

But it still doesnt fix my issue, where on when keyboard pops up, it leads to infinite scroll of the view, because of the footerview. 但是它仍然不能解决我的问题,因为footerview,当键盘弹出时,它会导致视图无限滚动。 My view hierarchy is that 我的观点层次是

ScrollView

Username and Password fields with Login button

FooterEmptyView

Footer Label

Where in FooterEmptyView i have a constraint as just to make space from login button till the Footer Label , which will increase or decrease depending on size of device. FooterEmptyView我有一个约束,就是只能从登录按钮到Footer Label留出空间,该空间会根据设备的大小而增加或减少。 All the constraints are programmatically placed. 所有约束都以编程方式放置。

Is it something UIScrollView issue which i need to take care for iOS 11? 是否需要解决iOS 11引起的UIScrollView问题? Thanks in Advance!! 提前致谢!!

The repo you provided did not include most of features described in your question. 您提供的存储库不包含问题中描述的大多数功能。 Keyboard events being one of them. 键盘事件就是其中之一。 While running the code I also got error logs that constraints were in conflict so at least on of them has been removed in runtime. 在运行代码时,我还收到错误日志,指出约束发生冲突,因此至少在运行时已删除了约束。 Further more the app posted does nothing at all or shows what it should be doing beyond displaying 2 text fields and a button. 此外,发布的应用程序什么也不做,或者除了显示2个文本字段和一个按钮之外,还显示应执行的操作。

Since you have created a new project into which all you did was modified its autogenerated view controller there was no reason not to add this code into your question instead of posting a link to your repository. 由于您创建了一个新项目,将您所做的全部工作都修改到该项目中,因此自动生成的视图控制器也就没有理由不将此代码添加到您的问题中,而无需发布指向您存储库的链接。

Still I have I checked your code and tried to deduct what you were doing and found out I can not make any sense in your constrains. 尽管如此,我还是检查了您的代码,并试图扣除您在做什么,发现我对您的约束没有任何意义。 Maybe at least some comments would help. 也许至少有一些评论会有所帮助。 I have then recreated your system, putting 2 text fields and a button on the scroll view with constraints. 然后,我重新创建了系统,将2个文本字段和一个按钮置于带有约束的滚动视图上。 I have also added methods that may move fields so they are correctly offset from bottom which is designed to be used on events when keyboard frame has changed. 我还添加了可能会移动字段的方法,以便它们正确地从底部偏移,该方法旨在在键盘框架发生更改时用于事件。

I hope the code will help you fix your issue. 希望代码能帮助您解决问题。 Once you are done please delete this question. 完成后,请删除此问题。

#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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM