弹出键盘时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 及以后的 topLayoutGuide
和 bottomLayoutGuide
上,我已将它们替换为 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无限滚动的主要内容,如果未能解决你的问题,请参考以下文章