safearea safe remove delete before swift autolayout ios11 visual-format-language safearealayoutguide

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.