development

xib 파일에서보기와 상단 레이아웃 가이드 사이에 제약 조건을 추가하는 방법이 있습니까?

big-blog 2020. 10. 19. 08:20
반응형

xib 파일에서보기와 상단 레이아웃 가이드 사이에 제약 조건을 추가하는 방법이 있습니까?


iOS 7에서는 이제보기와 상단 레이아웃 가이드 사이에 제약 조건을 추가 할 수 있습니다. iOS7에서 상태 표시 줄 오프셋 문제를 해결하는 데 매우 유용하다고 생각합니다 (특히보기에 탐색 표시 줄이없는 경우).

스토리 보드 파일에서 이러한 종류의 제약 조건을 쉽게 추가 할 수 있습니다. 컨트롤 키를 누른 상태에서보기를 컨테이너로 드래그하면 "Top Space to Top Layout Guide"옵션이 표시됩니다.

여기에 이미지 설명 입력

그러나 xib 파일에서 동일한 작업을 수행하면이 옵션이 사라집니다.

여기에 이미지 설명 입력

그렇다면 xib 파일에 이러한 종류의 제약 조건을 추가하는 방법이 있습니까? 아니면 코드와 함께 추가해야합니까?


다음 예제를 참조해야합니다. 이것은 확실히 문제에 도움이 될 것입니다. http://developer.apple.com 에서 얻었습니다 .

[button setTranslatesAutoresizingMaskIntoConstraints: NO];
id topGuide = myViewController.topLayoutGuide;
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings (button, topGuide);

[myViewController.view addConstraints:
    [NSLayoutConstraint constraintsWithVisualFormat: @"V:[topGuide]-20-[button]"
    options: 0
    metrics: nil
    views: viewsDictionary]
];

여기 내 대체 솔루션이 있습니다.

UIView를 피벗으로 찾고 상단 레이아웃 제약 조건을 컨테이너 상단에 고정 된 수직 공간으로 설정합니다.

Control-이 레이아웃 제약을 IBOutlet으로 드래그합니다.

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *topLayoutConstraint;

마지막으로 다음과 같이 UIViewController의 viewWillLayoutSubviews 메서드를 재정의하십시오.

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];

    self.topLayoutConstraint.constant = [self.topLayoutGuide length] + YOUR_TOP_CONSTRSINT;
}

다른 모든 뷰의 상단 제약은이 피벗 뷰를 기반으로합니다.


iOS 9에서는 topLayoutGuide의 anchors다음을 사용 하여 더 간단하게 할 수도 있습니다 .

  • 스위프트 3

view.topAnchor.constraint(equalTo: self.topLayoutGuide.bottomAnchor).isActive = true

  • ObjC

[controller.view.topAnchor constraintEqualToAnchor:controller.topLayoutGuide.bottomAnchor].active = YES;


지금까지 XCode 6을 사용하더라도 .xib 파일의 상단 레이아웃 가이드에 컨트롤을 정렬 할 수 없습니다. 대신 다른 방법을 사용하고 있습니다.

첫째, 인터페이스 빌더에서 여전히 뷰 컨트롤러 뷰의 상단 테두리에 컨트롤을 정렬합니다.

그런 다음 viewDidLoad 메서드에서 몇 가지 제약 조건을 대체하여 기본 뷰 대신 상단 레이아웃 가이드에 정렬됩니다.

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSArray *constraints = self.view.constraints;
    for (NSLayoutConstraint *constraint in constraints) {
        if ( (constraint.firstItem == self.view) && (constraint.firstAttribute == NSLayoutAttributeTop) ) {
            NSLayoutConstraint *newConstraint = [self constraint:constraint replaceFirstItemBy:self.topLayoutGuide attribute:NSLayoutAttributeBottom];
            [self.view removeConstraint:constraint];
            [self.view addConstraint:newConstraint];
        } else if ( (constraint.secondItem == self.view) && (constraint.secondAttribute == NSLayoutAttributeTop) ) {
            NSLayoutConstraint *newConstraint = [self constraint:constraint replaceSecondItemBy:self.topLayoutGuide attribute:NSLayoutAttributeBottom];
            [self.view removeConstraint:constraint];
            [self.view addConstraint:newConstraint];
        }
    }
}

- (NSLayoutConstraint*)constraint:(NSLayoutConstraint*)constraint replaceFirstItemBy:(id)newItem attribute:(NSLayoutAttribute)newAttribute {
    UILayoutPriority priority = constraint.priority;
    NSLayoutRelation relation = constraint.relation;
    id secondItem = constraint.secondItem;
    NSLayoutAttribute secondAttribute = constraint.secondAttribute;
    CGFloat multiplier = constraint.multiplier;
    CGFloat constant = constraint.constant;

    NSLayoutConstraint *newConstraint = [NSLayoutConstraint constraintWithItem:newItem attribute:newAttribute relatedBy:relation toItem:secondItem attribute:secondAttribute multiplier:multiplier constant:constant];
    newConstraint.priority = priority;
    return newConstraint;
}

- (NSLayoutConstraint*)constraint:(NSLayoutConstraint*)constraint replaceSecondItemBy:(id)newItem attribute:(NSLayoutAttribute)newAttribute {
    UILayoutPriority priority = constraint.priority;
    id firstItem = constraint.firstItem;
    NSLayoutAttribute firstAttribute = constraint.firstAttribute;
    NSLayoutRelation relation = constraint.relation;
    CGFloat multiplier = constraint.multiplier;
    CGFloat constant = constraint.constant;

    NSLayoutConstraint *newConstraint = [NSLayoutConstraint constraintWithItem:firstItem attribute:firstAttribute relatedBy:relation toItem:newItem attribute:newAttribute multiplier:multiplier constant:constant];
    newConstraint.priority = priority;
    return newConstraint;
}

Think this is not the best way because we replace objects that we define on interface builder. But it may be an alternative way that we can think about.


Of course, You can not only add constraint between a view and the top layout guide or bottom layout guide by programmatically but also you can remove and access constraint between a view and the top and bottom layout guide with the help of KVConstraintExtensionsMaster library.

// create containerView
UIView *containerView = [UIView prepareNewViewForAutoLayout];
[containerView setBackgroundColor:[UIColor brownColor]];
[self.view addSubview:containerView];

// To add Top and Bottom Layout Guide constraint of containerView
[self applyTopLayoutGuideConstraintToView:containerView withPadding:0];
[self applyBottomLayoutGuideConstraintToView:containerView withPadding:50];

// To access top Layout Guide Constraint and update it's constant value.
NSLayoutConstraint *topLayoutGuideConstraint = [self accessAppliedTopLayoutGuideConstraintFromView:containerView];
topLayoutGuideConstraint.constant = 50;

// To access bottom Layout Guide Constraint and update it's constant value with animation
NSLayoutConstraint *bottomLayoutGuideConstraint = [self accessAppliedBottomLayoutGuideConstraintFromView:containerView];
bottomLayoutGuideConstraint.constant = 80;
[self.view updateModifyConstraintsWithAnimation:NULL]; // call this for animation

// To remove Top and Bottom Layout Guide constraint of containerView
[self removeAppliedTopLayoutGuideConstraintFromView:containerView];
[self removeAppliedBottomLayoutGuideConstraintFromView:containerView ];

I think the reason they don't show up in the XIB is that there is no knowledge of the view controller hierarchy in that situation, whereas in a story board IB can tell where the guides are from the view controller hierarchy the view is in. I.e If it is contained in a UINavigationController, it can determine that the top layout guide is below the navigation toolbar.


Cao Huu Loc's answer with swift 4

extension NSLayoutConstraint {

    func constraintReplacing(firstItemWith newFirstItem: UILayoutSupport, withAttribute newFirstAttribute: NSLayoutAttribute) -> NSLayoutConstraint {
        return NSLayoutConstraint(item: newFirstItem, attribute: newFirstAttribute, relatedBy: relation, toItem: secondItem, attribute: secondAttribute, multiplier: multiplier, constant: constant)
    }

    func constraintReplacing(secondItemWith newSecondItem: UILayoutSupport, withAttribute newSecondAttribute: NSLayoutAttribute) -> NSLayoutConstraint {
        return NSLayoutConstraint(item: firstItem as Any, attribute: firstAttribute, relatedBy: relation, toItem: newSecondItem, attribute: newSecondAttribute, multiplier: multiplier, constant: constant)
    }
}

class YourViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        for constraint in view.constraints {
            if constraint.firstItem === view && constraint.firstAttribute == .top {
                let newConstraint = constraint.constraintReplacing(firstItemWith: topLayoutGuide, withAttribute: .bottom)
                view.removeConstraint(constraint)
                view.addConstraint(newConstraint)
            }
            if constraint.secondItem === view && constraint.secondAttribute == .top {
                let newConstraint = constraint.constraintReplacing(secondItemWith: topLayoutGuide, withAttribute: .bottom)
                view.removeConstraint(constraint)
                view.addConstraint(newConstraint)
            }
        }
    }
}

참고 URL : https://stackoverflow.com/questions/19078278/is-there-any-way-to-add-constraint-between-a-view-and-the-top-layout-guide-in-a

반응형