2

I'm currently setting up the layout constraints of a web view like this:

UIView *theView = self.view;
UILayoutGuide *theGuide = theView.safeAreaLayoutGuide;

inWebView.translatesAutoresizingMaskIntoConstraints = NO;
[theView.topAnchor constraintEqualToAnchor:inWebView.topAnchor].active = YES;
[theGuide.leadingAnchor constraintEqualToAnchor:inWebView.leadingAnchor].active = YES;
[theGuide.trailingAnchor constraintEqualToAnchor:inWebView.trailingAnchor].active = YES;
[theView.bottomAnchor constraintEqualToAnchor:inWebView.bottomAnchor].active = YES;

Which leads to left and right margins when the view is displayed on an iPhone X in landscape mode. The web view is centered horizontally in the view. This is shown as (1) in the following sketch enter image description here where the web view is drawn as green area.

But I want to use the maximal width of the view, where the web view extends to the right (2) or left edge (3). This would require a dynamic layout guide for leading and trailing.

How can I achieve this?

clemens
  • 16,716
  • 11
  • 50
  • 65

1 Answers1

0

I found a solution by adding further constraints:

@property (nonatomic, strong) NSLayoutConstraint *leftFullConstraint;
@property (nonatomic, strong) NSLayoutConstraint *rightFullConstraint;

and initializing the view constraints:

UIView *theView = self.view;
UILayoutGuide *theGuide = theView.safeAreaLayoutGuide;
NSLayoutConstraint *theConstraint;

inWebView.translatesAutoresizingMaskIntoConstraints = NO;
[theView.topAnchor constraintEqualToAnchor:inWebView.topAnchor].active = YES;
theConstraint = [theGuide.leadingAnchor constraintEqualToAnchor:inWebView.leadingAnchor];
theConstraint.priority = UILayoutPriorityDefaultHigh;
theConstraint.active = YES;
theConstraint = [theGuide.trailingAnchor constraintEqualToAnchor:inWebView.trailingAnchor];
theConstraint.priority = UILayoutPriorityDefaultHigh;
theConstraint.active = YES;
[theView.bottomAnchor constraintEqualToAnchor:inWebView.bottomAnchor].active = YES;

self.leftFullConstraint = [theView.leftAnchor constraintEqualToAnchor:inWebView.leftAnchor];
self.rightFullConstraint = [theView.rightAnchor constraintEqualToAnchor:inWebView.rightAnchor];

Note: The new constraints using left and right instead of leading and trailing, because the writing direction is irrelevant! They have a higher priority than the constraints to the save area layout guide. So when the app activates it, they will overrule the constraints to the safe area.

Finally, the constraints are updated when the trait collection changes:

- (void)updateConstraints {
    UIDeviceOrientation theOrientation = [[UIDevice currentDevice] orientation];

    switch (theOrientation) {
        case UIDeviceOrientationLandscapeLeft:
            self.leftFullConstraint.active = NO;
            self.rightFullConstraint.active = YES;
            break;
        case UIDeviceOrientationLandscapeRight:
            self.leftFullConstraint.active = YES;
            self.rightFullConstraint.active = NO;
            break;
        default:
            self.leftFullConstraint.active = NO;
            self.rightFullConstraint.active = NO;
    }
}

- (void)traitCollectionDidChange:(UITraitCollection *)inPreviousTraitCollection {
    [super traitCollectionDidChange:inPreviousTraitCollection];
    [self updateConstraints];
}

The same for Swift 4. Defining the properties:

var leftFullConstraint: NSLayoutConstraint?
var rightFullConstraint: NSLayoutConstraint?

Initializing the constraints:

if let theView = self.view {
    let theGuide = theView.safeAreaLayoutGuide
    var theConstraint: NSLayoutConstraint!

    inWebView.translatesAutoresizingMaskIntoConstraints = false
    theView.topAnchor.constraint(equalTo: inWebView.topAnchor).isActive = true
    theConstraint = theGuide.leadingAnchor.constraint(equalTo: inWebView.leadingAnchor)
    theConstraint.priority = UILayoutPriorityDefaultHigh
    theConstraint.isActive = true
    theConstraint = theGuide.trailingAnchor.constraint(equalTo:inWebView.trailingAnchor)
    theConstraint.priority = UILayoutPriorityDefaultHigh
    theConstraint.isActive = true
    theView.bottomAnchor.constraint(equalTo: inWebView.bottomAnchor).isActive = true

    self.leftFullConstraint = theView.leftAnchor.constraint(equalTo: inWebView.leftAnchor)
    self.rightFullConstraint = theView.rightAnchor.constraint(equalTo: inWebView.rightAnchor)
}

and updating them:

func updateConstraints() {
    if let leftFullConstraint = self.leftFullConstraint, 
       let rightFullConstraint = self.rightFullConstraint {
        let theOrientation = UIDevice.current.orientation

        switch(theOrientation) {
        case .landscapeLeft:
            leftFullConstraint.isActive = false
            rightFullConstraint.isActive = true
            break;
        case .landscapeRight:
            leftFullConstraint.isActive = true
            rightFullConstraint.isActive = false
            break
        default:
            leftFullConstraint.isActive = false
            rightFullConstraint.isActive = false
        }
    }
}

override func traitCollectionDidChange(_ inPreviousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(inPreviousTraitCollection)
    updateConstraints()
}
clemens
  • 16,716
  • 11
  • 50
  • 65