delete - swift remove safe area
Guía rápida de diseño de área segura y lenguaje de formato visual (3)
Quiero usar el lenguaje de formato visual de Apple para restringir una vista a la nueva Guía de diseño del área segura
Usted no puede No hay acceso a la guía de diseño de área segura a través del lenguaje de formato visual. He archivado un error en esto, y te sugiero que hagas lo mismo.
Quiero usar el lenguaje de formato visual de Apple para restringir una vista a la nueva Guía de diseño del área segura en iOS 11. Sin embargo, obtengo una excepción:
- [NSLayoutYAxisAnchor nsli_superitem]: selector no reconocido enviado a la instancia 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)
La razón por la que me gusta el lenguaje de formato visual es que en algunos casos puede agregar muchas restricciones con menos código.
¿Algunas ideas?
Aquí hemos extendido un poco el lenguaje de formato visual, por lo que ahora puede anclar contra "<|" Cuando te refieres a SafeAreaLayoutGuide. Ojalá Apple hiciera algo así.
Por ejemplo, si tiene el siguiente código pre iOS 11:
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"V:[_button]-(normalPadding)-|"
options:0 metrics:metrics views:views
]];
Y ahora desea asegurarse de que el botón se encuentra por encima del margen inferior seguro en el iPhone X, luego haga esto:
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint
mmm_constraintsWithVisualFormat:@"V:[_button]-(normalPadding)-<|"
options:0 metrics:metrics views:views
]];
Eso es. Anclará el botón en la parte inferior de su supervisión en iOS 9 y 10, pero lo anclará en la parte inferior de su safeAreaLayoutGuide en iOS 11.
Tenga en cuenta que usar "|>" para fijar en la parte superior no excluirá la barra de estado en iOS 9 y 10.
// In @interface/@implementation NSLayoutConstraint (MMMUtil)
// ...
+(NSArray<NSLayoutConstraint *> *)mmm_constraintsWithVisualFormat:(NSString *)format
options:(NSLayoutFormatOptions)opts
metrics:(NSDictionary<NSString *,id> *)metrics
views:(NSDictionary<NSString *,id> *)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
Sé que no es VFL, pero hay una clase de fábrica llamada NSLayoutAnchor
que hace que la creación de restricciones sea un poco más clara y concisa.
Por ejemplo, pude sujetar el ancla superior de un UILabel al ancla superior del área segura con una línea compacta:
label.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
Tenga en cuenta que safeAreaLayoutGuide
requiere iOS 11. Para versiones anteriores, reemplace self.view.safeAreaLayoutGuide.topAnchor
por self.topLayoutGuide.bottomAnchor
.
Una vez más, sé que no es VFL, pero esto parece ser lo que tenemos por ahora.