IT박스

NSLayoutConstraint의 승수 속성을 변경할 수 있습니까?

itboxs 2020. 6. 30. 20:58
반응형

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 ...]

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

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
    }
}

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

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() 
}

승수의 값을 변경하려면 아래 단계를 따르십시오. 1. someConstraint와 같은 필요한 구속 조건을위한 배출구를 만듭니다. 2. someConstraint.constant * = 0.8 또는 승수 값과 같이 승수 값을 변경하십시오 .

그게 행복 코딩입니다.


NONE OF THE ABOVE CODE WORKED FOR ME SO AFTER TRYING TO MODIFY MY OWN CODE THIS CODE This code is working in Xcode 10 and 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() 
 }

Switch by changing the active constraint in code as suggested by many other answers did not work for me. So i created 2 constrains, one installed and the other not, bind both to the code, and then switch by removing one and adding the other.

For the sake of completeness, to bind the constrain drag the constrain to the code using mouse right button, just like any other graphic element:

enter image description here

I named one proportionIPad and the other proportionIPhone.

Then, add the following code, at viewDidLoad

override open func viewDidLoad() {
   super.viewDidLoad()
   if ... {

       view.removeConstraint(proportionIphone)
       view.addConstraint(proportionIpad) 
   }     
}

I am using xCode 10 and swift 5.0


Yes w can change multiplier values just make an extension of NSLayoutConstraint and use it like ->

   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)

Here is an answer based on @Tianfu's answer in C#. Other answers that require activation and deactivation of constraints did not work for me.

    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()
}

One can read:

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

on this documentation page. Doesn't that mean that one should be able to modify multiplier (since it is a var)?

참고URL : https://stackoverflow.com/questions/19593641/can-i-change-multiplier-property-for-nslayoutconstraint

반응형