我想使用Apples可视化格式语言将视图约束到iOS 11中的新安全区域布局指南.但是,我得到一个例外:
- [NSLayoutYAxisAnchor nsli_superitem]:无法识别的选择器发送到实例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)
我喜欢可视化格式语言的原因是因为在某些情况下你可以用更少的代码添加很多约束.
有任何想法吗?
我想使用Apples可视化格式语言将视图约束到新的"安全区域布局指南"
你不能.通过可视格式语言无法访问安全区域布局指南.我已经提交了一个错误,我建议你这样做.
我们在这里扩展了视觉格式化语言,所以现在你可以反对"<|" 当你的意思是safeAreaLayoutGuide.我希望Apple能做到这样的事情.
例如,如果您有以下iOS 11之前的代码:
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_button]-(normalPadding)-|" options:0 metrics:metrics views:views ]];
现在,您要确保按钮位于iPhone X上的安全底部边距上方,然后执行以下操作:
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint mmm_constraintsWithVisualFormat:@"V:[_button]-(normalPadding)-<|" options:0 metrics:metrics views:views ]];
而已.它会在iOS 9和10上将按钮锚定到其超级视图的底部,但是将其锚定到iOS 11上的safeAreaLayoutGuide的底部.
请注意,使用"|>"固定到顶部不会排除iOS 9和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 && [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:@"|"]; actualFormat = [actualFormat 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]; actualFormat = [actualFormat 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 <| and |> 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 <| and |> 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
我知道这不是VFL,但是有一个叫做的工厂类NSLayoutAnchor
可以让创建约束变得更加干净和简洁.
例如,我能够使用一条紧凑的线将UILabel的顶部锚点固定到安全区域的顶部锚点:
label.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
请注意,safeAreaLayoutGuide
需要iOS 11.对于旧版本,请替换self.view.safeAreaLayoutGuide.topAnchor
为self.topLayoutGuide.bottomAnchor
.
再说一次,我知道这不是VFL,但这似乎是我们现在所拥有的.