[ios] 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];

이제 multiplier 속성을 애니메이션으로 변경하고 싶지만 multipler 속성을 변경하는 방법을 알 수 없습니다. (헤더 파일 NSLayoutConstraint.h의 개인 속성에서 _coefficient를 찾았지만 개인입니다.)

멀티플렉서 속성을 어떻게 변경합니까?

내 해결 방법은 이전 제약 조건을 제거하고에 대한 다른 값으로 새 제약 조건을 추가하는 것입니다 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 ...]

스위프트 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


답변

Swift의 NSLayoutConstraint 확장 기능은 새로운 승수 설정을 매우 쉽게 만듭니다.

스위프트 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를 제거하고 새 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)


답변

Andrew Schreiber의 Objective-C 버전 답변

NSLayoutConstraint Class에 대한 카테고리를 작성하고 다음과 같이 .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];


답변

약간의 수학으로 동일한 목표를 달성하기 위해 “constant”속성을 대신 변경할 수 있습니다. 제약 조건의 기본 승수가 1.0f라고 가정합니다. 이것은 objective-c로 쉽게 번역 될 수있는 Xamarin C # 코드입니다.

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


답변

다른 답변에서 설명한 것처럼 : 제약 조건을 제거하고 새로운 제약 조건을 만들어야합니다.


전달 된 제약 조건을 다시 할당 할 수있는 NSLayoutConstraintwith 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() 
}