Swift Safe Area Layout Guide and Visual Format Language

匿名 (未验证) 提交于 2019-12-03 01:27:01

问题:

I want to use Apples visual format language to constrain a view to the new Safe Area Layout Guide in iOS 11. However, I get an exception:

-[NSLayoutYAxisAnchor nsli_superitem]: unrecognized selector sent to instance 0x1c447ed40

    //Make View Dictionary     var views: [String: Any] = ["left": self.leftContainer]      //Check swift version and add appropriate piece to the view dictionary     if #available(iOS 11, *) {         views["topGuide"] = self.view.safeAreaLayoutGuide.topAnchor     }else{         views["topGuide"] = self.topLayoutGuide     }      //Make the constraint using visual format language     let leftVertical = NSLayoutConstraint.constraints(withVisualFormat: "V:[topGuide][left]|", options: [], metrics: nil, views: views)      //Add the new constraint     self.view.addConstraints(vertical) 

The reason I like visual format language is because you a can add lot of constraints with less code in some cases.

Any Ideas?

回答1:

I want to use Apples visual format language to constrain a view to the new Safe Area Layout Guide

You can't. There is no access to the safe area layout guide through the visual format language. I've filed a bug on this, and I suggest you do the same.



回答2:

I know it's not VFL, but there is a factory class called NSLayoutAnchor that makes creating constraints a bit more clean and concise.

For example, I was able to pin the top anchor of a UILabel to the top anchor of the safe area with one compact line:

label.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true 

Note that safeAreaLayoutGuide requires iOS 11. For older versions, replace self.view.safeAreaLayoutGuide.topAnchor by self.topLayoutGuide.bottomAnchor.

Again, I know it's not VFL, but this seems to be what we have for now.



回答3:

We've extended the visual formatting language here a bit, so now you can pin against "

For example, if you have the following pre iOS 11 code:

[NSLayoutConstraint activateConstraints:[NSLayoutConstraint      constraintsWithVisualFormat:@"V:[_button]-(normalPadding)-|"     options:0 metrics:metrics views:views ]]; 

And now you want to make sure that the button sits above the safe bottom margin on iPhone X, then do this:

[NSLayoutConstraint activateConstraints:[NSLayoutConstraint     mmm_constraintsWithVisualFormat:@"V:[_button]-(normalPadding)-

That's it. It'll anchor the button to the bottom of its superview on iOS 9 and 10, but anchor it to the bottom of its safeAreaLayoutGuide on iOS 11.

Please note that using "|>" to pin to the top won't exclude the status bar on iOS 9 and 10.

// In @interface/@implementation NSLayoutConstraint (MMMUtil) // ...  +(NSArray *)mmm_constraintsWithVisualFormat:(NSString *)format     options:(NSLayoutFormatOptions)opts     metrics:(NSDictionary *)metrics     views:(NSDictionary *)views {     if ([format rangeOfString:@""].location == NSNotFound ) {         // No traces of our special symbol, so do nothing special.         return [self constraintsWithVisualFormat:format options:opts metrics:metrics views:views];     }      if (![UIView instancesRespondToSelector:@selector(safeAreaLayoutGuide)]) {         // Before iOS 11 simply use the edges of the corresponding superview.         NSString *actualFormat = [format stringByReplacingOccurrencesOfString:@"" withString:@"|"];         return [NSLayoutConstraint constraintsWithVisualFormat:actualFormat options:opts metrics:metrics views:views];     }      //     // OK, iOS 11+ time.     // For simplicity we replace our special symbols with a reference to a stub view, feed the updated format string     // to the system, and then replace every reference to our stub view with a corresponding reference to safeAreaLayoutGuide.     //      UIView *stub = [[UIView alloc] init];     static NSString * const stubKey = @"__MMMLayoutStub";     NSString *stubKeyRef = [NSString stringWithFormat:@"[%@]", stubKey];     NSDictionary *extendedViews = [@{ stubKey : stub } mmm_extendedWithDictionary:views];      NSString *actualFormat = [format stringByReplacingOccurrencesOfString:@"" withString:stubKeyRef];      NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:actualFormat options:opts metrics:metrics views:extendedViews];      NSMutableArray *processedConstraints = [[NSMutableArray alloc] init];     for (NSLayoutConstraint *c in constraints) {         UIView *firstView = c.firstItem;         UIView *secondView = c.secondItem;         NSLayoutConstraint *processed;         if (firstView == stub) {             if (![secondView isKindOfClass:[UIView class]]) {                 NSAssert(NO, @"We only support UIView with  anchors, got %@", secondView.class);                 continue;             }             processed = [self                 constraintWithItem:secondView.superview.safeAreaLayoutGuide attribute:_MMMOppositeAttribute(c.firstAttribute)                 relatedBy:c.relation                 toItem:secondView attribute:c.secondAttribute                 multiplier:c.multiplier constant:c.constant                 priority:c.priority                 identifier:@"MMMSafeAreaFirstItemConstraint"             ];         } else if (secondView == stub && [firstView isKindOfClass:[UIView class]]) {             if (![firstView isKindOfClass:[UIView class]]) {                 NSAssert(NO, @"We only support UIView with  anchors, got %@", secondView.class);                 continue;             }             processed = [self                 constraintWithItem:firstView attribute:c.firstAttribute                 relatedBy:c.relation                 toItem:firstView.superview.safeAreaLayoutGuide attribute:_MMMOppositeAttribute(c.secondAttribute)                 multiplier:c.multiplier constant:c.constant                 priority:c.priority                 identifier:@"MMMSafeAreaSecondItemConstraint"             ];         } else {             processed = c;         }         [processedConstraints addObject:processed];     }      return processedConstraints; }  + (instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1     relatedBy:(NSLayoutRelation)relation     toItem:(id)view2 attribute:(NSLayoutAttribute)attr2     multiplier:(CGFloat)multiplier constant:(CGFloat)c     priority:(UILayoutPriority)priority     identifier:(NSString *)identifier {     NSLayoutConstraint *result = [NSLayoutConstraint constraintWithItem:view1 attribute:attr1 relatedBy:relation toItem:view2 attribute:attr2 multiplier:multiplier constant:c];     result.priority = priority;     result.identifier = identifier;     return result; }  // @end  static inline NSLayoutAttribute _MMMOppositeAttribute(NSLayoutAttribute a) {     switch (a) {         // TODO: support trailing/leading in the same way         case NSLayoutAttributeLeft:             return NSLayoutAttributeRight;         case NSLayoutAttributeRight:             return NSLayoutAttributeLeft;         case NSLayoutAttributeTop:             return NSLayoutAttributeBottom;         case NSLayoutAttributeBottom:             return NSLayoutAttributeTop;         // These two are special cases, we see them when align all X or Y flags are used.         case NSLayoutAttributeCenterY:             return NSLayoutAttributeCenterY;         case NSLayoutAttributeCenterX:             return NSLayoutAttributeCenterX;         // Nothing more.         default:             NSCAssert(NO, @"We don't expect other attributes here");             return a;     } }  @interface NSDictionary (MMMUtil) - (NSDictionary *)mmm_extendedWithDictionary:(NSDictionary *)d;     @end  @implementation NSDictionary (MMMUtil)  - (NSDictionary *)mmm_extendedWithDictionary:(NSDictionary *)d {      if (!d || [d count] == 0)         return self;      NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithDictionary:self];     [result addEntriesFromDictionary:d];     return result; }  @end 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!