Могу ли я изменить свойство множителя для 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)?