Я создал два представления в одном супервизоре, а затем добавил ограничения между представлениями:
_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];
Теперь я хочу изменить свойство мультипликатора с помощью анимации, но не могу понять, как изменить свойство мультипликатора. (Я нашел _coefficient в частной собственности в файле заголовка NSLayoutConstraint.h, но это частное.)
Как изменить свойство мультиплексора?
Мой обходной путь - удалить старое ограничение и добавить новое с другим значением multipler
.
Ответы:
Если у вас есть только два набора множителей, которые нужно применить, начиная с iOS8 вы можете добавить оба набора ограничений и решить, какой из них должен быть активен в любое время:
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 ...]
Версия Swift 5.0
var standardConstraint: NSLayoutConstraint! var zoomedConstraint: NSLayoutConstraint! // ... // switch between constraints standardConstraint.isActive = false // this line should always be the first line. because you have to deactivate one before activating the other one. or they will conflict. zoomedConstraint.isActive = true self.view.layoutIfNeeded() // or using UIView.animate
источник
addConstraint(_:)
иremoveConstraint(_:)
в представлении, которое является ближайшим общим предком элементов, управляемых этим ограничением».Вот расширение NSLayoutConstraint в Swift, которое упрощает установку нового множителя:
В Swift 3.0+
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 } }
Использование демо:
@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() }
источник
NSLayoutConstraint.deactivateConstraints([self])
должно быть выполнено перед созданиемnewConstraint
, иначе это может привести к предупреждению: Невозможно одновременно удовлетворить ограничения . ТакжеnewConstraint.active = true
не требуется, посколькуNSLayoutConstraint.activateConstraints([newConstraint])
делает то же самое.cloneWithMultiplier
чтобы сделать ее более явной, что она не просто применяет побочные эффекты, а возвращает новое ограничение0.0
вы не сможете обновить его снова.multiplier
Свойство только для чтения. Вам необходимо удалить старый NSLayoutConstraint и заменить его новым, чтобы изменить его.Однако, поскольку вы знаете, что хотите изменить множитель, вы можете просто изменить константу, умножив ее самостоятельно, когда требуются изменения, что часто требует меньше кода.
источник
View2.bottomY
«s текущего значение для расчета ограничения в новой константе. Это некорректно, посколькуView2.bottomY
старое значение будет записано в константу, поэтому вам придется отказаться от декларативного стиля и вручную обновлять ограничение при каждомView2.bottomY
изменении. В этом случае вы также можете сделать макет вручную.Вспомогательная функция, которую я использую для изменения множителя существующего ограничения макета. Он создает и активирует новое ограничение и деактивирует старое.
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 } }
Использование, изменение множителя на 1,2:
constraint = MyConstraint.changeMultiplier(constraint, multiplier: 1.2)
источник
NSLayoutConstraint.deactivate
функция только iOS 8.0+.Версия Objective-C для ответа Эндрю Шрайбера
Создайте категорию для класса NSLayoutConstraint и добавьте метод в файл .h следующим образом
#import <UIKit/UIKit.h> @interface NSLayoutConstraint (Multiplier) -(instancetype)updateMultiplier:(CGFloat)multiplier; @end
В файле .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
Позже в ViewController создайте выход для ограничения, которое вы хотите обновить.
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *topConstraint;
и обновите множитель, где хотите, как показано ниже ..
self.topConstraint = [self.topConstraint updateMultiplier:0.9099];
источник
active = true
и активировать, и поэтому вызывает добавление дублирующего ограничения перед вызовом деактивации, это вызывает ошибку ограничений в некоторых сценариях.Вместо этого вы можете изменить свойство «константа», чтобы достичь той же цели с помощью небольшой математики. Предположим, ваш множитель по умолчанию для ограничения равен 1.0f. Это код Xamarin C #, который можно легко перевести на objective-c
private void SetMultiplier(nfloat multiplier) { FirstItemWidthConstraint.Constant = -secondItem.Frame.Width * (1.0f - multiplier); }
источник
Как объясняется в других ответах: вам нужно удалить ограничение и создать новое.
Вы можете избежать возврата нового ограничения, создав статический метод для параметра
NSLayoutConstraint
withinout
, который позволяет переназначить переданное ограничениеimport UIKit extension NSLayoutConstraint { static func setMultiplier(_ multiplier: CGFloat, of constraint: inout NSLayoutConstraint) { NSLayoutConstraint.deactivate([constraint]) 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 newConstraint.shouldBeArchived = constraint.shouldBeArchived newConstraint.identifier = constraint.identifier NSLayoutConstraint.activate([newConstraint]) constraint = newConstraint } }
Пример использования:
@IBOutlet weak var constraint: NSLayoutConstraint! override func viewDidLoad() { NSLayoutConstraint.setMultiplier(0.8, of: &constraint) // view.layoutIfNeeded() }
источник
НИ ОДИН ИЗ ВЫШЕГО КОДА НЕ РАБОТАЕТ ДЛЯ МЕНЯ ТАК ПОСЛЕ ПОПЫТКИ ИЗМЕНИТЬ МОЙ СОБСТВЕННЫЙ КОД ЭТОТ КОД Этот код работает в Xcode 10 и Swift 4.2
import UIKit extension NSLayoutConstraint { /** Change multiplier constraint - parameter multiplier: CGFloat - returns: NSLayoutConstraintfor */i 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 } } @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() }
источник
Да, w может изменять значения множителя, просто сделайте расширение 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 = shouldBeArchived newConstraint.identifier = identifier NSLayoutConstraint.activate([newConstraint]) return newConstraint } self.mainImageViewHeightMultiplier = self.mainImageViewHeightMultiplier.setMultiplier(375.0/812.0)
источник
Переключиться, изменив активное ограничение в коде, как было предложено многими другими ответами, для меня не сработало. Итак, я создал 2 ограничения, одно из которых установлено, а другое нет, привяжите оба к коду, а затем переключитесь, удалив одно и добавив другое.
Для полноты картины, чтобы привязать ограничение, перетащите ограничение в код с помощью правой кнопки мыши, как и любой другой графический элемент:
Я назвал одну пропорцию IPad, а другую - IPhone.
Затем добавьте следующий код в viewDidLoad
override open func viewDidLoad() { super.viewDidLoad() if ... { view.removeConstraint(proportionIphone) view.addConstraint(proportionIpad) } }
Я использую xCode 10 и Swift 5.0
источник
в Swift 5.x вы можете использовать:
extension NSLayoutConstraint { func setMultiplier(multiplier: CGFloat) -> NSLayoutConstraint { guard let firstItem = firstItem else { return self } 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 } }
источник
Вот ответ, основанный на ответе @Tianfu на C #. Другие ответы, требующие активации и деактивации ограничений, у меня не работали.
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() }
источник
Чтобы изменить значение множителя, выполните следующие действия: 1. Создайте выходы для требуемого ограничения, например someConstraint. 2. измените значение множителя, например someConstraint.constant * = 0.8 или любое значение множителя.
вот и все, счастливого кодирования.
источник
Можно прочитать:
var multiplier: CGFloat The multiplier applied to the second attribute participating in the constraint.
на этой странице документации . Разве это не означает, что нужно иметь возможность изменять множитель (поскольку это переменная)?
источник
var multiplier: CGFloat { get }
- это определение, которое означает, что у него есть только геттер.