Why is the top layout guide moving in my iMessage extension
Asked Answered
K

3

8

I have an iMessage extension and I'm having some issues with the top layout guide. I have an MSMessagesAppViewController that handles changes between presentation styles. In my extension I have a button. When it is clicked I transition to expanded presentation style and then present a view controller modally. Here's the problem: my UI in the second VC is getting hidden behind the top navigation bar. I thought this was strange as I set my constraints to the top layout guide. So I dug through my code and started debugging the top layout guide. I noticed that after I transition to expanded presentation style, topLayoutGuide.length = 86. That's how it should be. But when I present the second view controller modally, the top layout guide is reset to 0. Why isn't it 86 as it should be? Here is my code:

In my main viewController:

@IBAction func addStickerButtonPressed(_ sender: AnyObject) {
    shouldPerformCreateSegue = true
    theSender = sender
    requestPresentationStyle(.expanded)

}    
override func didTransition(to presentationStyle: MSMessagesAppPresentationStyle) {
    if presentationStyle == .expanded {
        if shouldPerformCreateSegue == true {
            shouldPerformCreateSegue = false
            performSegue(withIdentifier: "CreateStickerSegue", sender: theSender)//here is where I present the new viewController
        } else {
            searchBar.becomeFirstResponder()
            searchBar.placeholder = nil
            searchBar.showsCancelButton = true
            searchBar.tintColor = UIColor.white
        }
    } else {
        searchBar.showsCancelButton = false
    }
    print(topLayoutGuide.length) //This prints out 86
}

In the other modally presented view controller:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.view.addConstraint(navBar.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor))
    print(topLayoutGuide.length) //This prints out 0
}
Knifeedged answered 22/8, 2016 at 0:38 Comment(8)
Same Problem hereArabian
Yeah same issue here also, I think you should really rise a bug report to Apple ;) I think they are getting bored of mine ^^Orcein
My current workaround is to change the top layout constraint to 66 in expandedKnifeedged
how did you change the top layout constraint?Arabian
@Arabian Sorry. I meant change my constraint to the top layout guideKnifeedged
@ATyshka how did you change constraint to the top layout guide?Tevis
@ATyshka yes, your solution works ok for now (until Apple fixes this?).Hamlen
You'd think this far in to iMessage apps they would have worked this out. I was positive they'd have it fixed before it was out of beta and here we are, 4 months later and no fix. I wonder how the many apps in the store without this problem have worked around it.Knifeedged
A
7

As a workaround I use UIPresentationController, which shifts the modal view controller by topLayoutGuide.length points:

class MyViewController: MSMessagesAppViewController {

    private func presentModalViewController() {
        let imagePicker = UIImagePickerController()
        imagePicker.delegate = self
        imagePicker.sourceType = .savedPhotosAlbum

        imagePicker.modalPresentationStyle = .custom
        imagePicker.transitioningDelegate = self

        present(imagePicker, animated: true, completion: nil)
    }
}
// MARK: - UIViewControllerTransitioningDelegate
extension MyViewController: UIViewControllerTransitioningDelegate {

    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        let vc = PresentationController(presentedViewController: presented, presenting: presenting)
        // I really don't want to hardcode the value of topLayoutGuideLength here, but when the extension is in compact mode, topLayoutGuide.length returns 172.0.
        vc.topLayoutGuideLength = topLayoutGuide.length > 100 ? 86.0 : topLayoutGuide.length
        return vc
    }
}


class PresentationController: UIPresentationController {

    var topLayoutGuideLength: CGFloat = 0.0

    override var frameOfPresentedViewInContainerView: CGRect {
        guard let containerView = containerView else {
            return super.frameOfPresentedViewInContainerView
        }
        return CGRect(x: 0, y: topLayoutGuideLength, width: containerView.bounds.width, height: containerView.bounds.height - topLayoutGuideLength)
    }
}

The only problem is when you're calling presentModalViewController from compact mode, topLayoutGuide.length is 172.0 for unknown reason. So I had to hardcode a value for that case.

Andie answered 31/8, 2016 at 12:40 Comment(1)
This is brilliant! The other thing I had to do was take orientation changes into account by closing and reopening this view controller in the overridden viewWillTransition method.Gath
R
1

I believe this was known bug on previous iOS 10 beta. I had same issue and top and bottom layout guide works as I expect after I upgraded iOS version to latest.

Ranna answered 26/8, 2016 at 3:7 Comment(1)
I'm using simulator. Will try latest Xcode beta thoughKnifeedged
M
0

I used a slightly varied version of Andrey's

class MyViewController: MSMessagesAppViewController {
    private func presentModalViewController() {
        let imagePicker = UIImagePickerController()
        imagePicker.delegate = self
        imagePicker.sourceType = .savedPhotosAlbum
        imagePicker.modalPresentationStyle = .custom
        imagePicker.transitioningDelegate = self
        present(
            imagePicker,
            animated: true,
            completion: nil
        )
    }
}

extension MyViewController: UIViewControllerTransitioningDelegate {
    func presentationController(
         forPresented presented: UIViewController,
         presenting: UIViewController?,
         source: UIViewController
    ) -> UIPresentationController? {
         let vc = PresentationController(
             presentedViewController: presented,
             presenting: presenting
         )
         vc.framePresented = modalBoundaries.frame
         return vc
    }
}

class PresentationController: UIPresentationController {
     var framePresented = CGRect.zero
     override var frameOfPresentedViewInContainerView: CGRect {
         return framePresented
    }
}

modalBoundaries being a dummy UIView constrained (via XIB in my case) to respect any TopLayoutGuide length.

Maximilien answered 5/7, 2017 at 22:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.