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

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()
}
}

Related

How can I get a Table View to register taps when each cell has an Image View covering it?

I've been trying to create table view cells each with a UIImageView serving as a background for them. However, when I tap on each cell the table view will not register the click and will not transition to the view controller I have hooked up, even while I'm using the didSelectRowAtIndexPath function.
I realize it's probably an issue with the ImageView obstructing the table views ability to register the cell tap. The cells will transition however when I drag my finger/mouse on it from left to right, just not on taps. I tried to use a Tap Gesture Recognizer on the Image View however it only worked for the cell at the very top and not all of them. How do I tackle this issue?
Here is an image of the table view cells so you have an idea of what I'm working with: http://imgur.com/a/Ku4uD. Thank you!
If you uncheck User Interaction Enabled on your Image View, the problem should be solved. When running into a problem always check the user interaction of the most child view and work your way up.
One trick I have also learned is to create a subclass of a child and override touchesShouldCancel like so:
override func touchesShouldCancel(in view: UIView) -> Bool {
print("touchesShouldCancel")
//Run extra code that you want when the user selects this view
//then still retrieve the tap by its parent.
return false
}
I am unsure of exactly what your problem is, but I would delete whatever segue that you have, add a new one by dragging from the yellow circle on the left side of the center portion of the top of your tableView ViewController inside the storyboard, to the viewController that you desire it to segue to. Give the segue an appropriate identifier, and then inside your tableView class under tableView didSelectRow add performSegue(withIdentifier: "ChosenIdentifier", sender: indexPath)
Then in prepare forSegue add in:
if let vc = sender.destination as? TheViewControllerYouAreSegueingTo {
if let indexPath = sender as? IndexPath {
vc.variableIdentifyingWhatCellWasClicked = indexPath.row
}
}
with whatever adjustment is needed to meet your specific needs.

Calling endEditing on a UIView

I have a tableView with two UITextViews. The user can add as many cells as they want by tapping on a "+" button. The tableView starts out with one row (cell). Lets say the user adds some information to the two textViews and then taps the plus button and scrolls to get to it. I call this function to dismiss the keyboard when a user scrolls so it isn't in the way:
override func scrollViewDidScroll(scrollView: UIScrollView) {
self.view.endEditing(true)
}
Now, the keyboard disappears and the user goes to tap on the second row's text field. The tableView jumps up (I think to accommodate the keyboard), but the textField can't be edited (no cursor and keyboard doesn't appear). If I go back up to the first row (row 0), the keyboard appears and I can edit the textView. Why is this happening?
There is a property on scrollView subclasses (e.g TableViews)
tableView.keyboardDismissMode = .OnDrag
Once the user begins a descending drag it dismisses the keyboard
scrollViewDidScroll calls everytime the tableview moves, so when "The tableView jumps up (I think to accommodate the keyboard)" it also calls and close the keyboard. You should add there some confines or put endEditing code in the other place.

Swift: The best way to show and hide different views based off of my UISegmentedControl?

I want to show one view when the segmented control highlights the first option. When the user highlights the other option, I want the first view to disappear (or hide) and the other view to become visible. Then, if the user presses the first option again, the second view hides and the first becomes visible.
What is the best way to do this?
I do not want to switch ViewControllers, but simply Views who are both using the same ViewController.
A way to do that would be to give a tag to each view (as in your segment's indices) and call a method when the its value is changed that would hide all view's accept the one with the right tag number.
You could hook the UISegmentedControl from the view controller to the code using an #IBAction and use the following method:
#IBAction func switchView(sender: UISegmentedControl) {
// Change your view controller's view property
// to reference whatever custom views you have.
if(sender.selectedSegmentIndex == 0) {
self.view = viewOne
} else {
self.view = viewTwo
}
}
viewOne and viewTwo being UIViews

Present a UIScrollView when opening an App for the first time

I have an iPad App that I want to be compatible from iOS 5.0 to 6.0. My main view contains a scroll view z-indexed on the front, which is initially set to hidden. I also have a toolbar containing a button that cycle the scroll view hidden or not.
I would like to add a feature to present the scroll view as initially visible when the user opens the App for the first time to make the help visible by default to new users.
My code to cycle between visible and hidden is the following:
- (void)showHelpView:(id)sender {
BOOL hidden = [blackTranslucent isHidden];
[self.view bringSubviewToFront:scrollViewOutlet];
if (hidden) {
[scrollViewOutlet setHidden:FALSE animationStyle:KGAnimationFade duration:0.7];
[blackTranslucent setHidden:FALSE animationStyle:KGAnimationFade duration:0.5];
}
else {
[scrollViewOutlet setHidden:TRUE animationStyle:KGAnimationFade duration:0.5];
[blackTranslucent setHidden:TRUE animationStyle:KGAnimationFade duration:0.7];
}
}
where the sender is my toolbar button, blackTranslucent is a view on top of the main view and scrollViewOutlet is my scroll view IBOutlet.
Add a property "isNewUser" on NSUserDefaults in the application:willFinishLaunchingWithOptions: method which will only be written once by checking if the key exists.
In your main view in the viewDidLoad check this property if true make the view visible and update the key to false. if not just continue regularly.
Further information on NSUserDefaults
Hope that helps

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

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;
}

Resources