ios - programmatic - ¿Puedo cambiar la propiedad del multiplicador para NSLayoutConstraint?
programmatically constraints swift 4 (9)
Aquí hay una extensión NSLayoutConstraint en Swift que hace que establecer un nuevo multiplicador sea bastante fácil:
import Foundation
import UIKit
extension NSLayoutConstraint {
func setMultiplier(multiplier:CGFloat) -> NSLayoutConstraint {
NSLayoutConstraint.deactivateConstraints([self])
let newConstraint = NSLayoutConstraint(
item: firstItem,
attribute: firstAttribute,
relatedBy: relation,
toItem: secondItem,
attribute: secondAttribute,
multiplier: multiplier,
constant: constant)
newConstraint.priority = priority
newConstraint.shouldBeArchived = shouldBeArchived
newConstraint.identifier = identifier
NSLayoutConstraint.activateConstraints([newConstraint])
return newConstraint
}
}
En Swift 3.0
import Foundation
import UIKit
extension NSLayoutConstraint {
/**
Change multiplier constraint
- parameter multiplier: CGFloat
- returns: NSLayoutConstraint
*/
func setMultiplier(multiplier:CGFloat) -> NSLayoutConstraint {
NSLayoutConstraint.deactivate([self])
let newConstraint = NSLayoutConstraint(
item: firstItem,
attribute: firstAttribute,
relatedBy: relation,
toItem: secondItem,
attribute: secondAttribute,
multiplier: multiplier,
constant: constant)
newConstraint.priority = priority
newConstraint.shouldBeArchived = self.shouldBeArchived
newConstraint.identifier = self.identifier
NSLayoutConstraint.activate([newConstraint])
return newConstraint
}
}
Uso de demostración:
@IBOutlet weak var myDemoConstraint:NSLayoutConstraint!
override func viewDidLoad() {
let newMultiplier:CGFloat = 0.80
myDemoConstraint = myDemoConstraint.setMultiplier(newMultiplier)
//If later in view lifecycle, you may need to call view.layoutIfNeeded()
}
Creé dos vistas en una supervista y luego agregué restricciones entre las vistas:
_indicatorConstrainWidth = [NSLayoutConstraint constraintWithItem:self.view1 attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view2 attribute:NSLayoutAttributeWidth multiplier:1.0f constant:0.0f];
[_indicatorConstrainWidth setPriority:UILayoutPriorityDefaultLow];
_indicatorConstrainHeight = [NSLayoutConstraint constraintWithItem:self.view1 attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view2 attribute:NSLayoutAttributeHeight multiplier:1.0f constant:0.0f];
[_indicatorConstrainHeight setPriority:UILayoutPriorityDefaultLow];
[self addConstraint:_indicatorConstrainWidth];
[self addConstraint:_indicatorConstrainHeight];
Ahora quiero cambiar la propiedad del multiplicador por animación, pero no puedo descifrar cómo cambiar la propiedad del multiplicador. (Encontré _coefficient en propiedad privada en el archivo de encabezado NSLayoutConstraint.h, pero es privado.)
¿Cómo cambio la propiedad de multiplicador?
Mi solución es eliminar la restricción anterior y agregar la nueva con un valor diferente para el multipler
.
Aquí hay una respuesta basada en la respuesta de @ Tianfu en C #. Otras respuestas que requieren activación y desactivación de restricciones no funcionaron para mí.
var isMapZoomed = false
@IBAction func didTapMapZoom(_ sender: UIButton) {
let offset = -1.0*graphHeightConstraint.secondItem!.frame.height*(1.0 - graphHeightConstraint.multiplier)
graphHeightConstraint.constant = (isMapZoomed) ? offset : 0.0
isMapZoomed = !isMapZoomed
self.view.layoutIfNeeded()
}
En cambio, puede cambiar la propiedad "constante" para lograr el mismo objetivo con un poco de matemática. Suponga que su multiplicador predeterminado en la restricción es 1.0f. Este es el código Xamarin C # que se puede traducir fácilmente a objetivo-c
private void SetMultiplier(nfloat multiplier)
{
FirstItemWidthConstraint.Constant = -secondItem.Frame.Width * (1.0f - multiplier);
}
La propiedad del multiplier
es de solo lectura. Debe eliminar la antigua NSLayoutConstraint y reemplazarla por una nueva para modificarla.
Sin embargo, como sabes que quieres cambiar el multiplicador, puedes cambiar la constante multiplicándola tú mismo cuando se necesiten cambios, que a menudo es menos código.
Si solo tiene que aplicar dos conjuntos de multiplicadores, desde iOS8 en adelante puede agregar ambos conjuntos de restricciones y decidir cuál debería estar activo en cualquier momento:
NSLayoutConstraint *standardConstraint, *zoomedConstraint;
// ...
// switch between constraints
standardConstraint.active = NO; // this line should always be the first line. because you have to deactivate one before activating the other one. or they will conflict.
zoomedConstraint.active = YES;
[self.view layoutIfNeeded]; // or using [UIView animate ...]
Una función auxiliar que uso para cambiar el multiplicador de una restricción de diseño existente. Crea y activa una nueva restricción y desactiva la anterior.
struct MyConstraint {
static func changeMultiplier(_ constraint: NSLayoutConstraint, multiplier: CGFloat) -> NSLayoutConstraint {
let newConstraint = NSLayoutConstraint(
item: constraint.firstItem,
attribute: constraint.firstAttribute,
relatedBy: constraint.relation,
toItem: constraint.secondItem,
attribute: constraint.secondAttribute,
multiplier: multiplier,
constant: constraint.constant)
newConstraint.priority = constraint.priority
NSLayoutConstraint.deactivate([constraint])
NSLayoutConstraint.activate([newConstraint])
return newConstraint
}
}
Uso, cambiando el multiplicador a 1.2:
constraint = MyConstraint.changeMultiplier(constraint, multiplier: 1.2)
Uno puede leer:
var multiplier: CGFloat
The multiplier applied to the second attribute participating in the constraint.
en esta página de documentación . ¿Eso no significa que uno debería poder modificar el multiplicador (ya que es una var)?
Versión de Objective-C para Andrew Schreiber respuesta
Cree la categoría para NSLayoutConstraint Class y agregue el método en un archivo .h como este
#import <UIKit/UIKit.h>
@interface NSLayoutConstraint (Multiplier)
-(instancetype)updateMultiplier:(CGFloat)multiplier;
@end
En el archivo .m
#import "NSLayoutConstraint+Multiplier.h"
@implementation NSLayoutConstraint (Multiplier)
-(instancetype)updateMultiplier:(CGFloat)multiplier {
[NSLayoutConstraint deactivateConstraints:[NSArray arrayWithObjects:self, nil]];
NSLayoutConstraint *newConstraint = [NSLayoutConstraint constraintWithItem:self.firstItem attribute:self.firstAttribute relatedBy:self.relation toItem:self.secondItem attribute:self.secondAttribute multiplier:multiplier constant:self.constant];
[newConstraint setPriority:self.priority];
newConstraint.shouldBeArchived = self.shouldBeArchived;
newConstraint.identifier = self.identifier;
newConstraint.active = true;
[NSLayoutConstraint activateConstraints:[NSArray arrayWithObjects:newConstraint, nil]];
//NSLayoutConstraint.activateConstraints([newConstraint])
return newConstraint;
}
@end
Más adelante en ViewController crea la salida para la restricción que deseas actualizar.
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *topConstraint;
y actualiza el multiplicador donde quieras, como a continuación ...
self.topConstraint = [self.topConstraint updateMultiplier:0.9099];
supongamos que una vista1 en el guión gráfico tiene una restricción de ancho para la vista principal (auto.vista en código) como esta
view1Width = view * 0.7 + constant
en lugar de crear 2 restricciones y cambiar entre ellas cambiando la propiedad activa ORRR [desactivando la restricción actual con el multiplicador anterior y creando una nueva con un nuevo multiplicador] déjala 1 restricción y juega con su constante
supongamos que quiero cambiar el multiplicador de view1 a 0.9 en lugar de 0.7
Puedo hacer esto
self.view1Width.constant += self.view.frame.size.width * 0.2
lo mismo que haces para restar