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

теперь я хочу изменить свойство мультипликатора с анимацией, но я не могу понять, как изменить свойство мультипликатора. (Я нашел _coefficient в частной собственности в заголовочном файле NSLayoutConstraint.h, но это личное.)

Как изменить свойство мультипликатора?

мое решение состоит в том, чтобы удалить старое ограничение и добавить новое с другим значением для multipler.

10 108

10 ответов:

Если у вас есть только два набора множителей, которые должны быть применены, с 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 ...]

вот расширение 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() 
}

на multiplier свойство доступно только для чтения. Вы должны удалить старый NSLayoutConstraint и заменить его на Новый, чтобы изменить его.

однако, поскольку вы знаете, что хотите изменить множитель, вы можете просто изменить константу, умножив ее самостоятельно, когда необходимы изменения, которые часто меньше кода.

вспомогательная функция, которую я использую для изменения множитель существующего ограничения макета. Он создает и активирует новое ограничение и отключение старой.

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)

Objective-C версия для ответа Эндрю Шрайбера

создайте категорию для класса NSLayoutConstraint и добавьте метод .h файл, как это

    #import <UIKit/UIKit.h>

@interface NSLayoutConstraint (Multiplier)
-(instancetype)updateMultiplier:(CGFloat)multiplier;
@end

в рамках .файл м

#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];

вы можете изменить свойство "константа" вместо того, чтобы достичь той же цели с небольшой математикой. Предположим, что ваш множитель по умолчанию для ограничения равен 1.0 f. это код Xamarin C#, который можно легко перевести в objective-c

private void SetMultiplier(nfloat multiplier)
{
    FirstItemWidthConstraint.Constant = -secondItem.Frame.Width * (1.0f - multiplier);
}

вот ответ, основанный на ответе @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()
}

предположим, что view1 в раскадровке имеет ограничение ширины к главному виду (self.посмотреть в коде) вот так

  view1Width = view * 0.7 + constant 

вместо создания 2 ограничений и переключения между ними путем изменения активного свойства ORRR [путем деактивации текущего ограничения со старым множителем и создания нового с новым множителем] оставьте его 1 ограничение и играйте с его константой

предположим, я хочу изменить множитель view1 на 0,9 вместо 0,7

Я сделай это

  self.view1Width.constant += self.view.frame.size.width * 0.2

то же самое вы делаете для вычитания

для изменения значения множителя выполните следующие действия : 1. создайте выходы для требуемого ограничения, например someConstraint. 2. измените значение множителя, например someConstraint.постоянная *= 0.8 или любое значение множителя.

вот оно, счастливое кодирование.

можно прочесть:

var multiplier: CGFloat
The multiplier applied to the second attribute participating in the constraint.

на страница документация. Разве это не означает, что нужно иметь возможность изменять множитель (так как это var)?