现在的位置: 首页 > 综合 > 正文

Auto Layout: IB和代码结合使用约束,以及约束在UIView动画中的改变

2017年08月17日 ⁄ 综合 ⁄ 共 4801字 ⁄ 字号 评论关闭

如果您对Auto Layout还不是很了解,对它的大致原理还比较模糊,可以先看一下我的上一篇译文,http://blog.csdn.net/shinancao666/article/details/36932101


问题描述:

我需要在viewController的view(下面用self.view代替)中添加一个子view(下面就用loginView代替吧),self.view使用代码创建的:

CGRect rect = [[UIScreen mainScreen]bounds];
self.view = [[UIView alloc] initWithFrame:rect];
self.view.backgroundColor = [UIColor blueColor];

而loginView是用IB创建的,我需要在设备无论横屏还是竖屏时loginView都能位于界面的中间。当loginView中的文本框获得焦点后键盘出现时,让loginView整体向上滑动,避免键盘遮挡住文本框。


解决过程:

在IB中拖拽出一个登录的界面,拖拽过程不细说了,如下所示:


要注意一下view的Use Auto Layout要勾选上,其实XCode5在创建view时这一项就已经选上了。经过摸索,我发现只要确保在给每一个控件添加完约束后,能够让这个控件无论在什么时候在什么情况下都能确定它的大小和位置,就没有问题了,这样既不会产生警告,也不会多添加了约束。当约束之间产生了矛盾就会报错,比如一个约束是让控件左偏移10个点,一个约束是让控件做偏移20个点,这时就会报错。

在IB中有两个地方可以给控件加约束,一个是状态栏—>Editor—>Align/Pin,一个是在编辑器右下方的。当选中一个控件时,
 

                                              
     

当选中多个控件时,

 
    

现在开始给刚才的loginView添加约束吧,了解了以上这些内容,添加起约束来,应该不会不知从何下手了吧^_^ 。我想让所有控件都居中对齐,所有控件的大小都固定并且宽度相等。这样添加完约束后如下:


报了如下错误:


想想看,根据刚添加的这些约束,编译器要如何确定控件在竖直方向上放在什么地方,所以再接着添加,我想让第一个文本框距离父视图15个点,两个文本框之间距离10个点,文本框与按钮之间距离15个点,按钮距离下方20个点,最后再添加一个第一个文本框距离左边52个点的约束,这样编译器就能准确的知道loginView有多大,每个控件的大小和位置了:


到此为止约束全部添加完毕,再创建一个继承UIView的类LoginView,将上面的这个View的Class设置为LoginView。在LoginView的init方法中加载该xib文件,如下:

- (id)init
{
    self = [[[NSBundle mainBundle]loadNibNamed:@"LoginView" owner:self options:nil] lastObject];
    if (self) {
        
    }
    return self;
}


需要注意一个地方,我们要把loginView的translatesAutoresizingMaskIntoConstraints属性设置为NO。因为我要用代码手动地来给loginView设置约束,不需要编译器自动地来转换。这个属性是针对自己的,不是针对它里面的子控件的。在上面的init方法中再添加一句话:self.translatesAutoresizingMaskIntoConstraints
=
NO;


现在再创建一个继承UIViewController的类LoginViewController。重写其loadView方法,像文章开头那样。然后在

viewDidLoad添加loginView,代码如下:

- (void)loadView
{
    CGRect rect = [[UIScreen mainScreen]bounds];
    self.view = [[UIView alloc] initWithFrame:rect];
    self.view.backgroundColor = [UIColor blueColor];
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    LoginView *loginView = [[LoginView alloc]init];
    [self.view addSubview:loginView];
    
    NSLayoutConstraint *constraintX = [NSLayoutConstraint constraintWithItem:loginView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.f];
    [self.view addConstraint:constraintX];
    
    NSLayoutConstraint *contraintY = [NSLayoutConstraint constraintWithItem:loginView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.f];
    [self.view addConstraint:contraintY];
    
}

运行结果如下:

  

现在要开始处理一下键盘出现时对文本框的遮挡问题,我在这里的处理是方法是,在键盘出现时,让loginView上移一定的距离,在键盘消失时,让loginView再移回来。先给loginView中的UITextField设置delegate,使其在点击return按钮时,使焦点消失。


没有使用Auto Layout之前,view的移动是通过改变它的frame实现的,但是使用了Auto Layout之后就不能这么做了,因为使用Auto Layout是不能规定死view的frame的。既然我是通过给loginView添加了两个其中心的X、Y分别与self.view中心的X、Y重合的约束,使得loginView固定在self.view的中间的,那么控制loginView中心的Y值与self.view中心的Y值之间的距离,不就改变了loginView在竖直方向的位置了吗。因此要把上面在viewDidLoad中添加的constraintY设置为全局变量。好了,现在开始行动吧!

<span style="font-family:Microsoft YaHei;font-size:18px;color:#333333;">@interface LoginViewController ()
{
    NSLayoutConstraint *contraintY;
}
@end
</span>


然后给LoginViewController添加键盘通知:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleWillShowKeyboard:)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleWillHideKeyboard:)
                                                 name:UIKeyboardWillHideNotification
                                               object:nil];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
	[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}


再实现handleWillShowKeyboard和handleWillHideKeyboard方法,在这两个方法中改变contraintY的constrain值,在参照物的上面或左边则取负值,反之则取正值,代码如下:

#pragma mark - Keyboard notifications
- (void)handleWillShowKeyboard:(NSNotification *)notification
{
    NSDictionary *info = [notification userInfo];
    NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    
    contraintY.constant = -10;
    [self.view setNeedsUpdateConstraints];
    
    [UIView animateWithDuration:animationDuration animations:^{
        [self.view layoutIfNeeded];
    }];
}
- (void)handleWillHideKeyboard:(NSNotification *)notification
{
    NSDictionary *info = [notification userInfo];
    NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    
    contraintY.constant = 0;
    [self.view setNeedsUpdateConstraints];
    
    [UIView animateWithDuration:animationDuration animations:^{
        [self.view layoutIfNeeded];
    }];
}


在赋值之后要调用setNeedsUpdateContraints方法,这个方法告诉Auto Layout system去调用UIView.UpdateContraints,这个方法将会计算新的约束参数。layoutIfNeeded将强制self.view如果有必要则立即重新布局其子视图。在这里contraintY.constant调整的值我写了一个固定值,在我这种情况下没有办法动态的来获得这个值,如果您有好的办法,表要忘了告诉我呀^_^


还有一个点想说一下,我在一开始做的时候,以为在键盘出现时可以给loginView多加一个距离self.view底部一定距离的约束,结果报了很多警告,原因是多加的约束和之前添加的约束会产生冲突。


好啦,所有的内容都介绍完毕了,代码在这github

抱歉!评论已关闭.