Incorrect Popover size - when displaying UITableViews under Navigation Controller in Popover - ios

I am writing an iPad app which features UITableViews displayed under a Navigation Controller within a UIPopoverController.
The popover is displayed when I pick a button in the Main View Controller of my app. The popover opens displaying a first TableViewController, which has two rows (UITableViewCells) - "Search" in the first row and "Advanced Settings" in the second row. On initial display, the popover is sized just enough to display the two rows.
I have coded this first TableViewController's didSelectRowAtIndexPath such that when I pick "Search", it pushes a second TableViewController onto the NavigationViewController. This next View Controller allows the user to perform a search, and search results then get populated in its tableview.) This (search results) table view controller is sized long enough to accommodate all the rows returned by the search. The search popover therefore becomes longer when displaying the search tableview controller.
When I cancel the search (or hit the back button in the navigation bar) the popover returns to displaying the first table view controller (the one with just two rows). However this first table view controller now has the longer size. In other words, the popover, instead of resizing itself back to a two row table, remains the size of the second (search results) table view controller (so it now has the two rows "Search" and "Advanced Settings" plus a number of empty rows)
My question is: how can I get each tableview controller in the hierarchy in this implementation (i.e. where table view controllers are displayed in a popover under a navigation controller) to be sized individually and to return to its original size when the user navigates back and forward. There is probably a simple solution to this, but it escapes me! Appreciate if someone can point me to a solution.

As the above solution does not work anymore, here's a more current (Swift) alternative.
You can pass along the popovercontroller to your destinationViewControllers.
Then call preferredContentSizeDidChangeForChildContentContainer in viewWillAppear() and the popover will resize automatically.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if let ppc = popoverController {
ppc.preferredContentSizeDidChangeForChildContentContainer(self)
}
}
If it doesn't work make sure you properly implement the preferredContentSize. For example calculating the size of the your tableViewController with a single section as such:
override var preferredContentSize: CGSize {
get {
let sectionFrame = self.tableView.rectForSection(0)
let titleOnTop = self.navigationController!.navigationBar.frame.height
let height = sectionFrame.height + titleOnTop
return CGSize(width: super.preferredContentSize.width, height: height)
}
set { super.preferredContentSize = newValue }
}

I implemented the answer from the following StackOverflow post by user #krasnyk :
Popover with embedded navigation controller doesn't respect size on back nav
It worked great for me with one change ...
Basically added the same function detailed in the above post with one modification (I hardcoded the size for each VC in my view heirarchy in the PopupController)
I referenced this function to set the correct popover size in the ViewDidLoad and ViewDidAppear functions for each VC in the chain of VCs displayed in my PopoverController.
- (void) correctPopoverContentSize {
//
// removed the following line from the original code in above post as it did not
// work for me
// CGSize currentSetSizeForPopover = self.contentSizeForViewInPopover;
CGSize currentSetSizeForPopover = CGSizeMake(320.0f, 180.0f);
CGSize fakeMomentarySize = CGSizeMake(currentSetSizeForPopover.width - 1.0f,
currentSetSizeForPopover.height - 1.0f);
self.contentSizeForViewInPopover = fakeMomentarySize;
self.contentSizeForViewInPopover = currentSetSizeForPopover;
}

Related

TableView fail to reload after `tableView.reloadData()` is called

I have two tabs. The first tab contains a UITableViewController, with its tableView's cells containing some words. The second tab is for settings, containing a tableView, with a cell for setting font size. After the user taps the cell, the user will enter the interface for changing the font size. When the user presses the "save" button, (a button in the editing-font-size interface to save the adjusted font size.) the top view controller is popped back to the original tableView for settings.
I use observers to post notification when the save button is pressed, and the respective observers will update its view. I added an observer in the tableView for settings, and it works. When the top view controller for editing font size is popped, the original tableView is presented with adjusted font size.
However, when I added an observer in the UITableViewController in the first tab, the tableView is not reloaded. I have added a print message in the selector method of NotificationCenter.default.addObserver(_:selector:name:object:) and confirm that the selector method is called. The selector method looks like this:
#objc func reloadTableView() {
print("The method is called.")
tableView.reloadData()
}
The code that I use the font size in the first tab's tableView cell:
labelOne.font = UIFont(descriptor: fontDescriptor, size: fontSizeForLabelOne)
labelTwo.font = UIFont(descriptor: fontDescriptor, size: fontSizeForLabelTwo)
It seems very confusing to me, since the method to reload tableView is called, yet the table view is not reloaded? And since the font size is changed, when I scroll the tableView the new cells are randomly in the old font size or adjusted font size.
I have also tried it with labels, and it works on them even if it is in a different tab. In other words, I think the problem only occurs for tableView.
On the other hand, since it works for the table view in setting tab, am I right to say that tableView can only be reloaded when tableView.reloadData() is called when the tableView is about to be presented? If tableView is not presented and tableView.reloadData() is called, nothing will happen, and everything remains the same?
In short, is there anyway to ensure that tableView is reloaded when I change my font size?
Eventually I found a solution by, since my view controllers are managed by navigation controller, I pop the view controller and load it again as the alternative to reload the table view. Namely,
#objc func reloadTableView() {
navigationController.popViewController(animated: false)
navigationController.pushViewController(theViewController, animated: false)
}
The only downside of this is that the view controller will be reset to its initial state, i.e. if the table view in the view controller is scrolled down, when the user goes back to the view controller from setting, the table view is reset to its top position and does not show where it was scrolled to.
#objc func reloadTableView() {
print("The method is called.")
DispatchQueue.main.async {
tableView.reloadData()
}
}

How to stop TableViewController hitting status bar?

I have a new TableViewController that I dragged from the object library. It is now populated with data. When I run the app, the tableview is going all the way into the status bar. I don't think that is normal.
But I also don't see a way to resize the a tableview on the TableViewController scene. Any one have some suggestions how I should go about fixing this issue?
You can embed your TableViewController in a Navigation controller:
1. Select the TableViewController in the storyboard
2. Xcode top bar: Editor -> Embed in -> Navigation controller
If you don't want to embed your TableViewController in a nav. control, you can add padding to the table view in your viewDidLoad in your TableViewController class:
// ... in TableViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
// add top padding to table view
let myTopPadding: CGFloat = 50
self.tableView.contentInset = UIEdgeInsetsMake(myTopPadding, 0, 0, 0);
// ...
}
Note that with this solution, however, you data will still "hit" the status bar when scrolling, although not when entering your table view. This is apparently and issue that is hard to avoid (without navigation controller) when subclassing the UITableViewController:
iOS 7: UITableView shows under status bar
If you want padding also when scrolling then, based on the thread above, you're better off creating your own table via view controller -> insert table view -> ..., rather than using the finished UITableViewController "package".

UIPageViewController - view was not at the correct position initially

I am using UIPageViewController to create the introduction pages of my app.
As the gif(I swiped to the 2nd view and swiped back to the 1st view):
My second page's view was not at the correct position initially. It's upper than it should be and dropped down later. However, if I swiped back, the first page's view was correct.
I guess it's because the UIPageViewController didn't load my 2nd view early enough, so the auto layout system was still calculating the position when the 2nd view already appeared. When I swiped back to the 1st view, since the view was already loaded, there was no such issue.(it's just my guess, I am not sure.)
I found I can use 2ndViewController.loadView() as a workaround, but Apple's document discourages programmers to call this method directly. I also found calling this method directly is buggy.
How do I prevent this correctly?
If you are setting constraints, fix the view's top space from Superview.
In func create view at index, let try my code
func pageTutorialAtIndex(index: Int) ->TestNodeController
{
let pageContentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("TestNodeController") as! TestNodeController
pageContentViewController.automaticallyAdjustsScrollViewInsets = false
pageContentViewController.pageIndex = index
return pageContentViewController
}

How to name a back button in UISplitViewController

I have UITableViewController (its name is News) and UIViewController (its name is DetailViewController) and UISplitViewController. I want it to show a back button when I use an iPad in portrait orientation. I made the button but I cannot name it. I wrote following code
detailController.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
detailController.navigationItem.leftItemsSupplementBackButton = true
detailController.navigationItem.leftBarButtonItem?.title = navigationController?.topViewController.title
But it doesn't show the name of the button. I see only the arrow (the arrow works).
I also tried the following in my UITableViewController(News) but it didn't help me
I use two segues for different devices with this code.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
var screen = UIScreen.mainScreen().currentMode?.size.height
if (UIDevice.currentDevice().userInterfaceIdiom == UIUserInterfaceIdiom.Pad) || screen >= 2000 && UIDevice.currentDevice().orientation.isLandscape == true && (UIDevice.currentDevice().userInterfaceIdiom == .Phone){
performSegueWithIdentifier("showDetailParse", sender: nil)
self.dismissViewControllerAnimated(true, completion: nil)
} else if (UIDevice.currentDevice().userInterfaceIdiom == .Phone) {
performSegueWithIdentifier("showParse", sender: nil)
}
}
My result on an iPad
My result on an iPhone
Thanks to Paul Hegarty and his invaluable lectures at Stanford University and available on iTunes U... in this case his 2013 lectures under the title "Developing iOS 7 Apps for iPhone and iPad" and specifically "Lecture 11 Table View and the iPad".
If you're using storyboards, then:
Open your main storyboard and select the Navigation Controller that links to the Master View Controller in your Split View Controller group;
Open the Inspector;
Under the heading View Controller, against the property Title, enter the words that you would like to appear alongside the "Back" button chevron.
See screenshot of Master Detail Xcode template set up with a Split View Controller...
If you're instantiating views in code, then:
obtain a reference to the Navigation Controller for the Master View controller;
set the title property of that Navigation Controller with the NSString of words that you would like to appear alongside the "Back" button chevron.
As an aside, I would highly recommend implementation of Auto Layout and Size Classes, that you remove the text for the Back Button property and let size classes determine the appropriate words for your Back Button.
For example, as per the question...
The Solution:
Here is the way to fix the issue with the detail view controller's back button:
For any view controller that gets pushed onto the primary navigation controller's stack, set that view controller's title. (Either in its viewDidLoad: method or in the pushing view controller's prepareForSegue:sender: method.)
Set the primary navigation controller's title in the child view controller's viewDidLoad: method.
For example, in MasterViewController.m:
- (void)viewDidLoad {
[super viewDidLoad];
[self setTitle:#"Foo"];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[self navigationController] setTitle:[self title]];
}
This will keep the detail view controller's back button title in sync with the top primary view controller's title.
What Is Going On:
UINavigationController, its rootViewController, and UINavigationItem each have a title property.
Note that the back button shown for a current view controller is actually the previous view controller's backButtonItem. (See Figure 1-7 Navigation bar structure)
A UINavigationController will automatically inherit the value of the title of its root view controller, but will not automatically inherit the title of any other controller that gets pushed onto its stack. This is why, by default, the back button of the detail view controller will always show the title of the primary navigation controller's root view controller. You might allocate, initialize, and push multiple child view controllers, but only one navigation controller is allocated and initialized for each side of a standard split view controller.
Additionally, a view controller's navigationItem's title property (whose value will appear in the label in the center of the navigation bar) does not inherit its value from the navigation controller, but from the view controller itself. If you set the view controller's title property to "Bar", and the containing navigation controller's title to "Foo", the label displayed in the center of the navigation bar will say "Bar".

popover content view doesn't display while viewcontroller has a child VC present

I have a container view controller that consists of a navigation view at top, and a content view for the remainder of the screen. The navigation menu consists of several buttons, some of which present a popover with UITableView for secondary navigation. This all worked until I assigned a child view controller and set it's view as subview of the content view. Now, the popover appears, but has nothing inside it (no tableview, just black).
Why is this?
Here's the code I added for the child vc in container view:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
ContentWebViewController *initialVC = [[ContentWebViewController alloc] init];
[self addChildViewController:initialVC];
initialVC.view.frame = self.view.bounds;
[self.containerView addSubview:initialVC.view];
self.currentController = initial;
}
See the screenshot below. I added a vc with a simple webview showing google (just as a placeholder for now). The popover was working fine before I assigned the child VC.
Maybe it will help other in other cases -
If you are using size classes (probably you are since you are developing this to iPad) -
Design your popover view controller in Any-Any size and it should be OK - after that you can return to your wanted size.
(You can also uninstall the size classes of any object in that view controller instead of redesign the VC)
I somehow (don't ask me how) changed the class that my table view controller was inheriting from. It should have been (obviously) UITableViewController, but was UITableViewController, so initWithStyle was not being called....

Resources