使UI适应iPhone X时从设备模型中抽象出来

仅使用“自动布局”使按钮具有自定义外观

我们的设计师最近要求在屏幕底部制作一个如下所示的按钮:

在这篇文章中,我将展示如何在不依赖设备模型查找的情况下实现这种自适应UI。

心态

每年,当新设备问世时,人们一直在问如何以编程方式检测模型的问题(2017年也不例外)。

尽管可以这样做,但该API有点奇怪。 这是因为Apple努力教会我们不要这样做。 取而代之的是,他们要求开发人员在进行功能测试时依赖可用性测试,并依靠自动布局来制作自定义界面。 换句话说,抽象远离特定的设备模型。 好吧,在一个可能可行的理想世界中……从这个角度看事物有时确实会有所帮助。

安全区

我要做的第一件事是更改按钮的约束,以遵守主视图控制器视图的安全区域。

这立即解决了我们在iPhone X Simulator和Xcode 9中首次启动该应用程序时遇到的问题。

  NSLayoutConstraint * buttonBottomConstraint = nil; 
如果(@available(iOS 11,*)){
buttonBottomConstraint = [self.button.bottomAnchor约束EqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor];
}
其他{
buttonBottomConstraint = [self.button.bottomAnchor约束EqualToAnchor:self.bottomLayoutGuide.topAnchor];
}

注意:如果您想知道bottomLayoutGuide.topAnchor ,这是因为bottomLayoutGuide具有id类型。 没有帮助吗? 好吧,这超出了这个故事的范围。 对不起that

然后,我决定重做按钮的所有约束,以坚持安全区域:

  NSLayoutConstraint * buttonBottomConstraint = nil; 
NSLayoutConstraint * buttonLeadingConstraint = nil;
NSLayoutConstraint * buttonTrailingConstraint = nil;
 如果(@available(iOS 11,*)){ 
buttonBottomConstraint = [self.button.bottomAnchor约束EqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor];
buttonLeadingConstraint = [self.button。LeadingAnchor约束EqualToAnchor:self.view.safeAreaLayoutGuide.leadingAnchor];
buttonTrailingConstraint = [self.button.trailingAnchor约束EqualToAnchor:self.view.safeAreaLayoutGuide.trailingAnchor];
}
其他{
buttonBottomConstraint = [self.button.bottomAnchor约束EqualToAnchor:self.bottomLayoutGuide.topAnchor];
buttonLeadingConstraint = [self.button。LeadingAnchor约束EqualToAnchor:self.view。Leading Anchor];
buttonTrailingConstraint = [self.button.trailingAnchor约束条件EqualToAnchor:self.view.trailingAnchor];
}
  buttonHeightConstraint = [NSLayoutConstraintconstraintWithItem:self.button属性:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual等于Item:nil属性:NSLayoutAttributeNotAnAttribute乘数:1.f常数:50.f]; 
  //我们稍后将需要引用这些约束 
self.buttonConstraints = @ [buttonBottomConstraint,buttonLeadingConstraint,buttonTrailingConstraint,buttonHeightConstraint];

这些约束仅在loadView例程期间创建一次,并添加到self.view

保证金和圆角

如果您仔细考虑一下, 真正使按钮看起来与众不同的是它与屏幕底部的距离,而不是设备型号:当按钮的底部边缘和屏幕之间没有空间时,可以它没有边距和圆角。

为了找出距离,我利用self.view.safeAreaInsets.bottom

  -(void)adjustButtonAppearanceIfNeeded { 
  //如果按钮不是非常[visual] 
//超级视图的底部(提示:iPhone X)
//然后将圆角
 如果(@available(iOS 11,*)){ 
CGFloat bottomPadding = self.view.safeAreaInsets.bottom;
如果(bottomPadding> 0){
如果(self.button.layer.cornerRadius!= 10.f){
self.button.layer.cornerRadius = 10.f;
self.button.layer.masksToBounds = YES;
self.buttonConstraints [1] .constant = 8; // 领导
self.buttonConstraints [2] .constant = -8; //尾随
[self.button setNeedsDisplay];
[self.view setNeedsLayout];
}
}
其他{
如果(self.button.layer.cornerRadius!= 0.f){
self.button.layer.cornerRadius = 0.f;
self.button.layer.masksToBounds = NO;
self.buttonConstraints [1] .constant = 0; // 领导
self.buttonConstraints [2] .constant = 0; //尾随
[self.button setNeedsDisplay];
[self.view setNeedsLayout];
}
}
}
}

为确保bottomPadding值正确,应从viewDidLayoutSubviews调用此方法:

  -(void)viewDidLayoutSubviews { 
[super viewDidLayoutSubviews];
  [self AdjustButtonAppearanceIfNeeded]; 
}

注意,条件将cornerRadius值与10.f0.f进行比较。 这是为了防止在[self.view setNeedsLayout]之后出现无限的布局循环。

结论

安全区域的概念比布局指南要干净得多。