添加子视图停靠在超级视图底部(自定义键盘)

问题描述:

我试图创建一个自定义视图,显示完全像键盘,但我无法弄清楚如何使用框架操作和/或编程自动布局解决我的问题。添加子视图停靠在超级视图底部(自定义键盘)

一些上下文:我有一个消息应用程序样式视图控制器的文本视图和按钮停靠在屏幕的底部。所有的视图都被包装到一个漂亮的单一内容视图中,并设置了自动布局,这样当键盘出现时,它会将整个视图向上推,当视图消失时,会将整个视图向下推。这是我试图重现的行为。

我搞砸了,试图手动调整框架类似于我的键盘代码的大小,但最终抛弃了赞成基于自动布局的解决方案。这是我到目前为止有:

StickersCollectionViewController *stickerController = [self.storyboard instantiateViewControllerWithIdentifier:@"StickersCollectionViewController"]; 

[self addChildViewController:stickerController]; 
[self.view addSubview:stickerController.view]; 
[stickerController didMoveToParentViewController:self]; 

NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:stickerController.view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:self.view.bounds.size.height]; 
[self.view addConstraint:constraint]; 

NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:stickerController.view attribute:NSLayoutAttributeWidth 
          relatedBy:NSLayoutRelationEqual toItem:self.view 
          attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0]; 

NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:stickerController.view 
                attribute:NSLayoutAttributeHeight 
                relatedBy:NSLayoutRelationEqual 
                 toItem:nil 
                attribute:NSLayoutAttributeNotAnAttribute 
                multiplier:1.0 
                constant:240.0]; 

[self.view addConstraint:width]; 
[self.view addConstraint:height]; 


double delayInSeconds = 0.0; 
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 
    constraint.constant = 240.0; 
    [UIView animateWithDuration:0.3 
        animations:^{ 
         [self.view layoutIfNeeded]; 
        }]; 
}); 

到目前为止,这看起来不错:视图被实例化并添加只是关闭屏幕,然后动画进入视野。不过,我还想让我的超级观点(上面提到)也用这种观点生动。这是我需要帮助的一块。

任何人都可以在这个方向提供帮助吗?或者提供一个关于我可以走的另一条路线的建议?谢谢。

试试这个,不同的是使用“[self.view layoutIfNeeded];”在动画之前和动画之后,然后放置这个“constraint.constant = 240.0;”在动画中。

double delayInSeconds = 0.0; 
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 
    [self.view layoutIfNeeded]; 
     [UIView animateWithDuration:0.3 
         animations:^{ 
          constraint.constant = 240.0; 
          [self.view layoutIfNeeded]; 
         }]; 
    }); 

所以,试试这个还有:

这样的:

NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:stickerController.view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:self.view.bounds.size.height]; 

768,16大概是这样的:

NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:stickerController.view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]; 

,因为你是限制到视图的顶部和不限制在视图顶部+ self.view.bounds.size.height,不知道这是否有帮助,但这是一个想法。

第二种可能的解决方案是将所有UI元素包装到一个UIView中,并将此UIView设置为stickerController.view的视图,这并不是那么简单,因为从技术上讲,您应该将此视图指定为stickerController的视图.view在stickerController impelemntation文件负载视图方法然后键入铸stickerController.view因为这例如

Typcasting内部stickerController的实现文件

- (void)loadView 
{ 
    [self setView:[ContainerViewThatHasSubViews new]]; 
} 

- (ContainerViewThatHasSubViews*)contentView 
{ 
    return (id)[self view]; 
} 

然后,创建ContainerViewThatHasSubViews的像这样的子类

ContainerViewThatHasSubViews.h

@interface ContainerViewThatHasSubViews : UIView 
@property (nonatomic) UIView *subContainerOfContainerViewThatHasSubViews; 
@property (nonatomic) NSLayoutConstraint *tester; 
@end 

ContainerViewThatHasSubViews。米

@interface ContainerViewThatHasSubViews() 
@end 

@implementation ContainerViewThatHasSubViews { 
} 
    self = [super initWithFrame:frame]; 
    if (self) { 

     _subContainerOfContainerViewThatHasSubViews = [UIView new]; 
     [_subContainerOfContainerViewThatHasSubViews setTranslatesAutoresizingMaskIntoConstraints:false]; 
     [self addSubview:subContainerOfContainerViewThatHasSubViews]; 

     /// PSUEDO CODE HERE NOW 
     ALL OTHER UI ELEMENTS ARE ADDED TO THE UIVIEW "subContainerOfContainerViewThatHasSubViews" 

     like this *** [subContainerOfContainerViewThatHasSubViews addSubView:**another ui element***];*** 

     etc. etc. etc. 

     then use layout constraints like this: 

     NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:stickerController.view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:self.view.bounds.size.height]; 
     [subContainerOfContainerViewThatHasSubViews.view addConstraint:constraint]; 

     etc. etc., so the layout constraints are added to "subContainerOfContainerViewThatHasSubViews" 

     add separate constraints to this UIView for the items inside this view, and you can then animate these cosntraitns as well by declaring the constraints in your header file as propertyies like i did with the _tester constraint, you can animate these when you press a button in your UIViewController impelmentation file by addding a gesture recognizer or whatever you have to show the keyboard 

     then end with this: 

     NSDictionary* views = NSDictionaryOfVariableBindings(subContainerOfContainerViewThatHasSubViews); 
     NSDictionary* metrics = @{@"sp" : @(heightResizer(10)), @"sh" : @40}; //include sizing metrics here 

     [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_subContainerOfContainerViewThatHasSubViews]|" options:0 metrics:metrics views:views]]; 

     and this: 

     _tester =[NSLayoutConstraint constraintWithItem:_subContainerOfContainerViewThatHasSubViews attribute:NSLayoutAttributeCenterTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterTop multiplier:1.0f constant:0.0f]; 


     [self addConstraint:_tester]; 
    } 
    return self; 
} 
@end 

要点:

https://gist.github.com/anonymous/c601481d24ad1b98b219

https://gist.github.com/anonymous/b22b68d4bf8d7fa51d66

https://gist.github.com/anonymous/a9aaf922e0f5383256b6

https://gist.github.com/anonymous/fc6655ea8200cda9c0dd

有很多的这一个,但它只是^ h流程图的意见工作。我从来没有使用故事板,因为我觉得我有更多的控制权,以编程方式做所有事情,如果你可以处理你需要编写多少代码来实现这一点,那么你很好去,我知道这是因为我正在构建一个和你一样的应用程序,它可以完成与你想要的功能相同的功能,这对我来说很有用,因为我深入了解了这些视图。这是完全控制,只要确保你将其他NSLayoutContraints作为子类UIView的合法性,然后你可以在你的子类UIView的头文件中放置方法,指向UIView子类的实现文件,这些方法可以是动画方法您可以从您的视图控制器调用这样的:

[[self contentView] CALLYOURFUNCTION]; 

访问其他布局限制和UI元素从子类的UIView在你的浏览器这样做是为了给他们打电话:

[[self contentView] tester]; //this is the constraint from9 the header file of tbhe custom UIView 

设置在你的UIViewController中的常量做到这一点:

[[[self contentView] tester] setConstant:A_NUMBER]; 

问题是,您将所有视图封装到UIView子类中,而不是访问视图控制器本身的视图并尝试对视图进行动画处理。我从来没有见过有人尝试动画UIViewController本身的“视图”属性,我只看到UIView的动画,它是UIViewController视图的子视图。该点也就是你正迫使该UIView的是UIViewControllers视图的视图,然后您使用子类的UIView内的另一个UIView的动画视图的全部内容,所以它是这样的:

-UIViewController's view 
    Sub Class of UIView 
     UIView <=== this is the view that contains ALL THE OTHER UIElements of the View controller 

向包含所有其他UI元素的UIView添加一个约束,这个约束是我添加到自定义Sub类UIView头部的测试器约束。

然后,您可以添加额外的约束的,我已经提到其中包含的UIViewController中

所有其他UI元素当您在这些子项添加约束的UIView的子视图,您可以制作动画,以及。这种方法是先进的,所以问问题,如果你有他们,我的答案是有点混乱。

+1

嘿,谢谢你的回答,但我想我需要澄清一点。我的问题是与子视图上的动画不一致(它的动画效果很好),但我也试图将超视图移开。所以随着自定义视图从屏幕到屏幕上的动画效果,超视图应该动起来为其腾出空间。对不起,如果我不够清楚,但我很欣赏这个建议。 – FrostRocket

+0

太棒了,让我多想一想,我会回应。事实上,我要添加到我的答案,看看这个新东西 – Loxx

+0

我会弹出一个要点,告诉你我的意思是更彻底,一秒 – Loxx

爸爸warbucks答案确实让我接近我正在寻找的解决方案。对于那些用故事板构建了大部分UI骨架的人,我最终在视图控制器的底部添加了一个容器视图,并在按钮按下时修改了主内容视图和容器视图的中心值。

  1. 布局内容视图,你通常会。将Leading,Trailing和Top约束设置为内容视图,将Bottom约束设置为容器视图(如步骤2中所述)。

  2. 创建容器视图并将其停靠在内容视图下方。将等宽设置为内容视图,将顶部约束设置为内容视图的底部,并将高度设置为240(或您希望的任何内容)。注意:视图将被定位在视图控制器的关闭位置。如果你像我一样有点OCD,这会让你感到困扰,直到你意识到它是k。

  3. 下面是我用来将容器视图移动到位并使内容视图跟随它。请注意,我可能会清理它更多,但这是总体思路:

    if (_isStickersViewOpen) { 
        self.tableView.contentInset = UIEdgeInsetsZero; 
        self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero; 
    
        [UIView animateWithDuration:0.3 
             delay:0.0 
            options:UIViewAnimationOptionCurveEaseOut 
           animations:^{ 
            self.contentView.center = CGPointMake(self.scrollView.center.x, self.scrollView.center.y); 
            self.containerView.center = CGPointMake(self.scrollView.center.x, self.scrollView.bounds.size.height + self.containerView.bounds.size.height/2); 
           } 
           completion:^(BOOL finished){ 
            _isStickersViewOpen = NO; 
           }]; 
    } else { 
        UIEdgeInsets contentInsets = UIEdgeInsetsMake(self.containerView.frame.size.height, 0.0, 0.0, 0.0); 
    
        self.tableView.contentInset = contentInsets; 
        self.tableView.scrollIndicatorInsets = contentInsets; 
    
        [UIView animateWithDuration:0.3 
             delay:0.0 
            options:UIViewAnimationOptionCurveEaseOut 
           animations:^{ 
            self.contentView.center = CGPointMake(self.scrollView.center.x, self.scrollView.bounds.size.height/2 - self.containerView.bounds.size.height); 
            self.containerView.center = CGPointMake(self.scrollView.center.x, self.scrollView.bounds.size.height - self.containerView.bounds.size.height/2); 
           } 
           completion:^(BOOL finished){ 
            _isStickersViewOpen = YES; 
           }]; 
    } 
    

我不是说这是最有效的还是干净的解决方案,但它适合我的目的。

+0

这是一个很棒的答案,干净而且重要 – Loxx