Могу ли я изменить свойство множителя для NSLayoutConstraint?
Я создал два представления в одном супервиде, а затем добавил ограничения между представлениями:
_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];
Теперь я хочу изменить свойство множителя с помощью анимации, но я не могу понять, как изменить свойство multipler. (Я нашел _coefficient в частной собственности в файле заголовка NSLayoutConstraint.h, но он закрыт.)
Как изменить свойство multipler?
Мое обходное решение - удалить старое ограничение и добавить новое значение с другим значением для multipler
.
Ответы
Ответ 1
Если у вас есть только два набора множителей, которые необходимо применять, начиная с 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 ...]
Ответ 2
Вот расширение NSLayoutConstraint в Swift, которое делает установку нового множителя довольно простой:
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
}
}
В 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()
}
Ответ 3
Свойство multiplier
доступно только для чтения. Вы должны удалить старый NSLayoutConstraint и заменить его новым, чтобы изменить его.
Однако, поскольку вы знаете, что хотите изменить множитель, вы можете просто изменить константу, умножив ее самостоятельно, когда потребуются изменения, что часто меньше кода.
Ответ 4
Вспомогательная функция, используемая для изменения множителя существующего ограничения компоновки. Он создает и активирует новое ограничение и деактивирует старый.
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)
Ответ 5
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];
Ответ 6
Вместо этого вы можете изменить свойство "константа", чтобы достичь той же цели с небольшой математикой. Предположим, что ваш множитель по умолчанию для ограничения равен 1.0f. Это код Xamarin С#, который можно легко перевести на target-c
private void SetMultiplier(nfloat multiplier)
{
FirstItemWidthConstraint.Constant = -secondItem.Frame.Width * (1.0f - multiplier);
}
Ответ 7
Как объясняется в других ответах: Вам нужно удалить ограничение и создать новое.
Вы можете избежать возврата нового ограничения, создав статический метод для NSLayoutConstraint
с параметром inout
, который позволяет переназначить переданное ограничение
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()
}
Ответ 8
Для изменения значения множителя выполните следующие действия: 1. создайте выходы для требуемого ограничения, например, someConstraint. 2. измените значение множителя, например someConstraint.constant * = 0,8 или любое значение множителя.
Вот и все, счастливое кодирование.
Ответ 9
Да, мы можем изменить значения множителя, просто сделайте расширение 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)
Ответ 10
Вот ответ, основанный на ответе @Tianfu на С#. Другие ответы, требующие активации и деактивации ограничений, не помогли мне.
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()
}
Ответ 11
предположим, что view1 в раскадровке имеет ограничение ширины к основному виду (self.view в коде), подобному этому
view1Width = view * 0.7 + constant
вместо создания 2 ограничений и переключения между ними путем изменения активного свойства ORRR [путем деактивации текущего ограничения со старым множителем и создания нового с новым множителем] оставить его 1 ограничение и играть с ним постоянным
Предположим, я хочу изменить множитель view1 равным 0,9 вместо 0,7
Я могу сделать это
self.view1Width.constant += self.view.frame.size.width * 0.2
то же самое вы делаете для вычитания
Ответ 12
Ни один из вышеперечисленного кода не работал для меня, так что после попытки изменить мой собственный код этот код Этот код работает в 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()
}
Ответ 13
Переключение путем изменения активного ограничения в коде, как предлагалось во многих других ответах, не сработало для меня. Итак, я создал 2 ограничения, одно установлено, а другое нет, связать оба кода, а затем переключиться, удалив одно и добавив другое.
Для полноты привязки ограничения перетащите ограничение в код с помощью правой кнопки мыши, как и любой другой графический элемент:
![enter image description here]()
Я назвал одну пропорцию iPad и другую пропорцию iPhone.
Затем добавьте следующий код в viewDidLoad
override open func viewDidLoad() {
super.viewDidLoad()
if ... {
view.removeConstraint(proportionIphone)
view.addConstraint(proportionIpad)
}
}
Я использую xCode 10 и Swift 5.0
Ответ 14
Можно прочитать:
var multiplier: CGFloat
The multiplier applied to the second attribute participating in the constraint.
на этой странице . Разве это не означает, что нужно иметь возможность модифицировать множитель (поскольку это var)?