TopAnchor of viewController changin in iMessage Extension between presentation modes - ios

I'm making a survey iMessage app (yeah I know) and having a problem moving between the presentation modes. The series of screenshots below shows that when the app launches, all is fine in the compact mode. When expanded everything is still correct but then when I go back to compact the content is shifted down by what looks like the same height as the large messages nav bar (86 I believe)
I've tried setting the top constraint to be -86 when switching back to the compact view, however, this either does nothing or sends it back to where is should be and then subtracts 86 so it dissapears too high up. I've based this project on the IceCream example project from app so not sure where this issue is coming from (probably autolayout but everything is pinned to the layout guides)
Here's the code that adds the view controller:
func loadTheViewController(controller: UIViewController) {
// Remove any existing child controllers.
for child in childViewControllers {
child.willMove(toParentViewController: nil)
child.view.removeFromSuperview()
child.removeFromParentViewController()
}
// Embed the new controller.
addChildViewController(controller)
controller.view.frame = view.bounds
controller.view.translatesAutoresizingMaskIntoConstraints = true
view.addSubview(controller.view)
controller.view.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
controller.view.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
controller.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
controller.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
controller.didMove(toParentViewController: self)
}
I've been working on this for what feels forever so any suggestions welcome.

You are setting up constraints on view but you have set translatesAutoresizingMaskIntoConstraints to true. The autoresizing mask constraints will likely conflict with the constraints you are adding, cause unexpected results. You should change to:
controller.view.translatesAutoresizingMaskIntoConstraints = false
Also rather than pinning to view.topAnchor, you should pin to the topLayoutGuide, which will take the top navigation bar into account.
controller.view.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true
Similarly,
controller.view.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor).isActive = true

Related

UISplitViewController - Expand & Collapse Master View in iPad Portrait

My universal app displays both master and detail views in iPad with preferredDisplayMode = .allVisible. I need to expand the master view into full screen and hide the detail view on a button click. I know there is functionality to expand detail into full screen hiding master. But couldn't find how to expand and collapse master view.
I tried in expand function as below.
self.splitViewController.preferredPrimaryColumnWidthFraction = 1.0
self.splitViewController.maximumPrimaryColumnWidth = self.splitViewController.view.bounds.size.width as! CGFloat
And collapse function as below.
self.splitViewController.preferredDisplayMode = .allVisible
self.splitViewController.preferredPrimaryColumnWidthFraction = 0.6
self.splitViewController.maximumPrimaryColumnWidth = self.splitViewController.view.bounds.size.width as! CGFloat
But they don't work. Any ideas on how to achieve this?
My setup uses a navigation bar, but the show/hide functionality would probably work without it. What I wanted to achieve is to have the user decide whether to show the "primary" view and have it shown no matter the orientation - something a UISplitViewController doesn't natively do.
I achieved this through activating/deactivating two arrays of constraints, pinning the secondary" view's leading anchor to the "primary's" trailing anchor. From there, all the constraint changes are with the "primary" view.
(I'm using quotation marks because these view's actually have much more logic that a UIView should have, so they are each UIViewControllers with one being a child of the other, but the constraints are view-related.)
Let's start with the static constraints:
primary.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
primary.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
primary.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
secondary.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
secondary.leadingAnchor.constraint(equalTo: toolBar.trailingAnchor).isActive = true
secondaary.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
secondary.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
Next, define/populate the two arrays that will show/hide the primary:
var isShowingPrimary = false
var showPrimary = [NSLayoutConstraint]()
var hidePrimary = [NSLayoutConstraint]()
showPrimary.append(primary.widthAnchor.constraint(equalToConstant: 300))
hidePrimary.append(primary.widthAnchor.constraint(equalToConstant: 0))
NSLayoutConstraint.activate(hidePrimary)
Notice that isActive is true for the static constraints, and I activated the hidePrimary constraints only.
Now all you need to do is wire up a UIBarButtonItem or UIButton to execute a toggle function that will show/hide the primary view, along with animating it:
func togglePrimary() {
if isShowingPrimary {
// hide primary view
NSLayoutConstraint.deactivate(showPrimary)
NSLayoutConstraint.activate(hidePrimary)
} else {
// show primary view
NSLayoutConstraint.deactivate(hidePrimary)
NSLayoutConstraint.activate(showPrimary)
}
// toggle flag and animate changes
isShowingPrimary.toggle()
UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() }
}

Gap between status bar and toplayout guide bottom anchor even though `edgesForExtendedLayout` set to `top`

I have a simple view controller which is being rendered modally simply by using
viewController.present(myVC, animated: true, completion: nil)
myVC view controller does not have any embedded navigation controller and in viewDidLoad of myVC I am setting view which is supposed to behave like navigation bar view (I can't use navigation bar / navigation controller unfortunately)
Here is how I add the view programmatically
self.view.addSubview(topView)
topView.translatesAutoresizingMaskIntoConstraints = false
topView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
topView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
topView.topAnchor.constraint(equalTo: self.topLayoutGuide.bottomAnchor).isActive = true
topView.heightAnchor.constraint(equalToConstant: 70).isActive = true
I have specified extended edges as top in my viewDidLoad of myVC
self.edgesForExtendedLayout = .top
And the UI looks like
There is a gap between status bar and view added highlighted by yellow border which I am not sure how to fix :(
Please help
You get the gap because you are adding your topView's top constraint to the view's topLayoutGuide.bottom (which sits a bit below the notch). So that is intended behavior.
The cleanest way to get rid of that gap is to embed you view controller in a UINavigationController and use a real navigation bar.
But if you cannot do that you have to get rid of the gap yourself.
I cannot think of an elegant way to get rid of this gap but you could add a negative constant to the constraint that is as high as the gap:
topView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: -14).isActive = true
But you would have to make sure that this is only done on devices that have a safeAreaLayoutGuide.topAnchor > 0. (iPhoneX etc.). On all other devices the constant has to be 0.
As I said this is not a very stable or elegant solution but it would work.
BTW If possible you should change self.topLayoutGuide.bottomAnchor (deprecated) to view.safeAreaLayoutGuide.topAnchor.

pageviewcontroller inside navigation controller causing layout problems

I have a UIViewController (using Swift)
It is just a simple list of Items. It is implemented as a UIViewController to which I have added a UITableView. This 'Items' view is invoked via this code in my slide menu implementation, and the initial view of the slide menu is embeded in a NavigationController - so all the views in the slide menu take on the navigation controller. Items is one of those slide menu views invoked like this
self.openViewControllerBasedOnIdentifier("Items")
This works fine and I get....
I then have an add button in the top right that you can push to add new Items. The add button brings up a PageViewController. I use page view because there are multiple pages of details associated with the Item being added. This also works so far in that I get the pageviewcontroller launched and I can flip between pages.
The problem is that the placement of elements on the page shifts under some circumstances (it is not consistent).
When I initially show the 1st page of the PageViewController I see the layout that I designed (on the left below). If I simply tab out of the Text field OR if I swipe to the 2nd page and then swipe back to the 1st page then I see the right image. The whole view jumps up!
I tried flipping all the different settings in the storyboard regarding . I even moved the majority of the drawing in to the code and out of storyboard in order to manually set constraints. These are the settings I played with. tried lots of combinations but they are mostly all off now.
Here is Before and After. How do I stop the view from jumping up and down. Also, interesting is that if i swipe from page-3 to page-2 then page-2 is shifted up. If I keep going back to page-1 and then swipe back to page-2 then page-2 is back down to correct position. So deepening on which way I enter page-2 it is different layout. From 1 to 2, page 2 is correct. From 3 to 2, page 2 is shifted up.
I can not get the layout to stay in one spot.
The other option, is to just shut off NavigationController when in the page view controller and deal without having it.
So...
1) how can I stop the jumping - best case
2) how can I shut off navigation controller when pageviewcontroller is up - second option
The pages of my pageviewcontroller are invoked via this code
instantiateViewController(withIdentifier: "itemDetail\(num)ViewController")
Where 'num' is 1,2, or 3. Using an indexed array and viewControllerBefore along with viewControllerAfter methods. The paging itself works fine.
Thanks
This does not answer the question of how to make storyboard work. That remains a mystery. HOWEVER, I was able to fix the strange behavior of shifting layout by doing the following.
I removed ALL of the drawing from Storyboard and put everything in the code. For example, I have defined a container view ...
// The container
let containerView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.white
view.translatesAutoresizingMaskIntoConstraints = false
view.layer.masksToBounds = true
return view
}()
Then later I place the container relative to the UIViewController's view
view.addSubview(containerView)
containerView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
containerView.topAnchor.constraint(equalTo: view.topAnchor, constant: 155).isActive = true
containerView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: 0).isActive = true
containerView.heightAnchor.constraint(equalTo: view.heightAnchor, constant: -155).isActive = true
Similarly, I have defined all of the other controls in the code and then added them to the containerView subView and made then all relative to the containerView.
This keeps everything in place as I swipe between pages of the PageViewController
I had the same problem, and found the solution. You just need to disable automaticallyAdjustsScrollViewInsets in your viewDidLoad. Here's my code:
override func viewDidLoad()
{
super.viewDidLoad()
self.dataSource = self
self.automaticallyAdjustsScrollViewInsets = false
if let detailsVC = viewControllersList.first as? DetailsViewController
{
detailsVC.delegate = self
self.setViewControllers([detailsVC], direction: .forward, animated: true, completion: nil)
}
}
Credits to this answer.

Collection view visible behind navigation bar in iMessage Extension in iOS 10

I'm developing iMessage Extension app.
My problem is that the collection view is visible behind navigation bar(I think navbar is automatically produced by iMessage Extension) in expand mode.
We can see collectionview (with transparency) behind navbar.
Is there anybody who has experience in this area?
Looking for any help.
After give Constraints(Top:0,Left:0,Right:0,Bottom:0),you should give Top Constraints of CollectionView to Top Layout Guide.Bottom ratherthan Superview.Top like this:
CollectionView.Top = Top Layout Guide.Bottom is set to 0.
In viewDidLoad():
CollectionView.frame = view.bounds
CollectionView.translatesAutoresizingMaskIntoConstraints = false
CollectionView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
CollectionView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
CollectionView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
CollectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
Referring to #seggy question comment:
have you set constraint perfectly?
You answered:
Yeah, of course, top:0, left:0, right:0, bottom: 0
The top constraint should be equals to 64 (it seems the top constraint is between the collectionView and the container view, not between the collectionView and the navigation bar).
Also, you might need to change the bottom constraint's constant value.
Hope this helped.

How to keep navigation bar from disappearing with MSMessagesViewController -> UIContainerView -> UINavigationController -> UITableViewController?

I am trying to put a navigation controller with a table view controller within an iMessage app (iOS 10). This seems to work when I put the UINavigationController within an UIContainerView within the MSMessagesViewController.
However, this breaks when in expanded view. The UINavigationBar that the controller has disappears.
How can I remedy this, or am I taking the wrong approach?
Let me start with the assumption that you used view.addSubview to add your UITableViewController to the MSMessagesAppViewController
In order to show the navigation bar correctly. Make sure you have set all your constraints correctly. Here's the example I have, and hopefully this would work for you:
// Embed the new controller. Recommended way of presenting VC shown in WWDC (icecream example). Ugly but does the work
addChildViewController(controller)
view.addSubview(controller.view)
let viewRect = view.bounds
controller.view.frame = viewRect
controller.view.translatesAutoresizingMaskIntoConstraints = false
controller.view.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
controller.view.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
controller.view.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true
if presentationStyle == .compact {
controller.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
} else {
controller.view.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor).isActive = true
}
controller.didMove(toParentViewController: self)
Here's the link to the post on the Apple Developer Forum which solved my problem: https://forums.developer.apple.com/thread/52049
In the screens you show there isn't any problem with the navigation bar!!
So in my case, I was using Storyboards, so I will add in the Storyboard solution to this.
Similar to DLee's answer, the top constraint needs to be set to the "Top Layout Guide" not "Top." Here's what it looks like in a Storyboard:
In my case, I used a Container View to hold everything, so with this having the top layout guide set as the top constraint, it made everything go in the right place.
In my post, I originally used the "Top" which was what caused parts of the iMessage app (specifically the navigation bar) to disappear.

Resources