largeTitleDisplayMode doesn't work properly? - ios

I added the UINavigationBar with a large title in it. After that, I added a UITableView to this UIViewController. I gave the large title content mode to .always. But when I scroll the UITableView it hides and showed in the top of theUINavigationBar`
navigationController?.navigationBar.prefersLargeTitles = true
self.navigationItem.largeTitleDisplayMode = .always

If you want to keep your large title while scrolling your UITableView, then add a UIView on top of your UITableView, like:

Related

Hiding Navigation Bar Causes Space Under It and Above View

Problem
A navigation bar animates as it hides on scroll (hidesBarsOnSwipe). If there is a table on the page with a section header, the section header will stick to the top of the view and follow the user as they scroll. When using these two together, there is a gap between the section header and the navigation bar when the navigation bar is hiding, showing the background table cells. I would expect the section header to follow the navigation bar as it hides without a gap.
Using a section header is not strictly necessary for my issue. I'd simply like a bar that sticks to the top of the screen and follows the user as they scroll.
I've tried alternative approaches to the example posted below, which uses a UITableViewController. Adjusting the insets causes the section header to slip under the status bar. Using a UIViewController containing a static UIView (for the section header) and UITableView causes the same issue.
What would be an appropriate solution for this issue?
Example
The status bar's background is gray and the section header's background is red to better highlight the gap between the navigation bar and the section header. As the table is slowly scrolled down, the yellow table cells peeking through the gap become apparent. After the navigation bar is hidden, the section header resets on the status bar as expected.
View controller used for the above animation:
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Color status bar background.
let statusBar: UIView = UIApplication.shared.value(forKey: "statusBar") as! UIView
if statusBar.responds(to: #selector(setter: UIView.backgroundColor)) {
statusBar.backgroundColor = .lightGray
}
UIApplication.shared.statusBarStyle = .default
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView()
view.backgroundColor = .red
return view
}
}
Update 1
Removing the background color from the status bar does not resolve the issue I am seeing. The GIF below shows the behavior with the status bar in its original, transparent state.
TLYShyNavBar has a GIF of the behavior I’d like to implement (added below). This project is not maintained and does not work without additional manipulation but has a good example of what I would expect the behavior to look like.
Update 2
I’m understanding that this behavior is not an error/issue but as Apple prefers it. Coloring the background of the View is an option (see answer below). There are two ways to go about that for this situation:
Color the background to match the navigation bar and remove the navigation bar’s shadow to make the view seamless.
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.isTranslucent = false
Color the background to match the red bar.
Unfortunately, this method masks the gap instead of removing it. It makes one of the bars appear to distort in shape during their animation until they've completed their transition in both cases.
The problem is that a table view does not work well fullscreen when there is a status bar, because the cells come up behind the status bar but the section headers do not. So don't do that.
To use a table view controller to work without a navigation bar but with the status bar showing, configure the table view controller as an embedded (custom child) view controller so that you are in charge of the table view's position. Pin the table view top to the safe area top.
That gives the configuration you are after:
The status bar is clear, and the parent view controller's view is visible behind it. The table view itself, along with its section headers and cells, stops at the bottom of the status bar; the cells do not come up behind the status bar. Thus we don't get the mismatch seen in your illustrations.
Here's a gif showing that the result is coherent:
I've colored the nav bar and the main view differently so you can distinguish them, but imagine they were exactly the same color — then this would be totally coherent.

Unwanted UITableView reload animation when navigating from iOS 11 search controller

I have a view controller with a tableview containing a list of chats, a search controller enbeded in the navigation item (iOS 11 feature)
let searchController = UISearchController(searchResultsController: nil)
searchController.dimsBackgroundDuringPresentation = false
navigationItem.searchController = searchController
definesPresentationContext = true
When the user taps a chat in the table view the app pushes a new view controller with another table view containing messages for that chat. That works like it is supposed to:
The problem is that when the user activates the search controller, find some chat and taps it, the pushed view controller containing table view with the chat messages does some really strange animation with the table view that should not happen:
I load the data before the actual navigation and bind it to the table view in viewDidLoad using just reload() on the table view. The problematic table view uses auto layout and custom cells.
The problem is very similar to UITableView has unwanted animation when reloadData is called but for me it only happens when the iOS 11 search controller is active.
Edit: If I remove tableView.rowHeight = UITableViewAutomaticDimension and use a fixed height using func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat the problem is still there
If you just hide the searchBar before pushing a new viewController then it may fix your problem.
You need to create a global variable for searchBarCancelButton and find the cancel button from its subviews when you search something
let buttons = searchController.searchBar.subviews.first?.subviews.filter { (view) -> Bool in
return NSStringFromClass(view.classForCoder) == "UINavigationButton"
} as? [UIButton]
searchBarCancelButton = buttons?.first
then you can manually cancel it.
self.searchBarCancelButton?.sendActions(for: .touchUpInside)
Personally, I would simply hide the searchView controller before presenting the new view controller.
( Using UIView.animates with a completion handler for example )
I would not try to investigate further because since iOS11, there is an esoteric problem in the safe area management. A bug ? :)
Even the launch screens layouts are not correctly handled.
So many majors logos miss their middle part at launch !
You can try to call cell.layoutIfNeeded() right after dequeuing and setting a content of the cell
iOS 11 completely revamped the safe area API, including scroll view inset adjustment behaviors, which can cause unwanted animation when ignored. Therefore, disable automatic content inset adjustment for the scroll view with the unwanted animation:
if #available(iOS 11.0, *) {
tableView.contentInsetAdjustmentBehavior = .never
} else {
// < iOS 11 logic
}
Do not invoke reloadData() method in your viewDidLoad or viewWillAppear. Instead of this reload your tableView with empty data in viewDidLoad so your tableView will show nothing and then in your viewDidAppear invoke reloadData() method to load all of your chats. This will restrict your tableView from loading with unwanted animation.
var shouldShowEmpty = true
func viewDidLoad() {
super.viewDidLoad()
tableView.reloadData()
}
func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
shouldShowEmpty = false
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if shouldShowEmpty {
return 0
}
return yourArray.count
}
As per attachment, It looks like due to auto layout, cell's element's height was zero (UILabel or UIImageView) and then suddenly when table view reloads it gets the data inside the cell which increase its height(automatic dimension or whatever) which cause this animation.
Try to push it without animation or try to set fix cell's element's height and width and check is it still showing this animation.
Did you check without invoking UISearchBar, If you select on any cell same animation is happening ?? or did you try to remove UISearchbar and select on the cell and check the animation part ?
Please share your code so we can see more clearly.
I have had a similar problem, and I believe that solution is the same. The keyboard is causing the problem, to be more correct, keyboardWillHideNotification trigger. You have text field at the bottom, that probably listens to notifications for keyboard show/hide, where layoutIfNeeded() is triggered if you animate bottom constraint so that your keyboard doesn't overlap your text field. So when you finish your search in the search text field, keyboardWillHideNotification gets triggered at an unwanted time. I solved my issue by calling:
resignFirstResponder()
for the text field that causes this event. In my case, that was after pressing the button, in your, I believe it's in didSelect tableView cell in the search table view.
If you are still active at this issue, please let me know if you manage to solve it. I broke my head trying to solve this issue, which apparently is so simple and straightforward.
In VC2 Try a delay function before reloading table
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
tableView.reloadData
}
or else
In VC1 in didSelect resign first responder of search controller before pushing to VC2
or
In VC1 in didSelect resign first responder of search controller and set a delay before pushing to VC2.

iOS 11 issues with navigationBar prefersLargeTitles

Xcode 9 beta 6 iOS 11
I have a storyboard for a viewController, the background ImageView, tableView and the searchFooter view are the subViews of the view.The structure is as follows:
In the AppDelegate class,I set
UINavigationBar.appearance().prefersLargeTitles = true
and
UINavigationBar.appearance().setBackgroundImage(UIImage(),for: .default)
UINavigationBar.appearance().shadowImage = UIImage()
In the viewControllerclass,I set navigationItem.searchController = searchController and navigationItem.hidesSearchBarWhenScrolling = true
When I run the application, I found that the shadowImage of the
navigationBar was still exist.
The navigationBar wouldn't become normal(not the prefersLargeTitles
mode ) automatically(the left gif) when I scrolled the tableView.I hope the navigationBar can work just like the system
app(the right gif).
A weird behavior: When I remove the background imageView, it can work as well as
the system app on the simulator.
I don't know why it happened,I wonder how to solve it? Thanks.
I've made a small application for your issue. You can reach the project on Github.
Transparent NavigationBar for iOS11
Note: You must use a color for UINavigationBar when scrolling up.
The account has been deleted by Stack overflow team. So I use my another ID to comment here.
I have reported the issue to Apple,they asked me to provide a project and I did, but no more further comment from Apple.
Make your TableView Top constraint right under NavigationController's NavigationsBar, not ViewControllers Top. It should work
You should add below line in viewDidLoad()
self.navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.hidesSearchBarWhenScrolling = false
OR YOU CAN implement viewForHeaderInSection method of UITableViewDataSource
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
{
let search = UISearchController(searchResultsController: nil)
search.searchResultsUpdater = self
return search.searchBar
}
Link to see the screenshot 1 Link to see the screenshot 2

Table View Footer in Swift

I'm trying to create a footer in a table view using Swift. A single footer that's always at the bottom of the screen. I set up a .swift file called TableViewFooter.swift that creates a class called TableViewFooter, a subclass of UITableViewHeaderFooterView. TableViewFooter has 2 labels, a button, and a progress view. In my TableViewController I put the code:
let nib = UINib(nibName: "TableViewFooter", bundle: nil)
tableView.registerNib(nib, forHeaderFooterViewReuseIdentifier: "TableViewFooter")
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let currSection = fetchedResultsController.sections?[section]
let title = currSection!.name
let cell = self.tableView.dequeueReusableHeaderFooterViewWithIdentifier("TableViewFooter")
let header = cell as! TableViewFooter
header.titleLabel.text = title
return cell
}
I'm getting an "Expected declaration" error after the tableView.registerNib line, a "Use of unresolved identifier 'fetchedResultsController'" error after the let currSection line, and a "Use of unresolved identifier 'header'" error after the header.titleLabel.text line. I'm new at this, but I'm using a Swift file, not a nib, so shouldn't I be putting something else instead? The only tutorials I found are using nibs or the cryptic Apple reference. Any help would be greatly appreciated!
It sounds like you want one of two things:
A UITableView footer. This is a view at the bottom of your UIScrollView, meaning that it only shows when you scroll to the bottom of the table. You can modify this, by accessing the tableViewFooter property of your UITableView, in your view controller, which I assume is your delegate and dataSource.
Or, a view that always remains at the bottom of your screen. This is not a table view footer, but a view that you will add to the main view of your view controller. To do this, add a custom UIView at the bottom of your view, making sure that it does not overlap with your table view (otherwise, the user won't be able to get to the bottom cell), and make sure that the scrollView property of your table view is large enough to contain all of its cells.

Eliminating a distinctive blur edge between UINavigationBar and UIVisualEffectView

Currently, my view hierarchy consists of a UIViewController (NOT UITableViewController), a UITableView nested in the view controller and a UIVisualEffectView (set to Extra Light) in front of the UITableView, aligned to the bottom of a UINavigationBar. The effect I want to achieve is somewhat similar to that of the App Store's segmented view.
However, I noticed a weird blur edge occurring at the boundary between the navigation bar and the UIVisualEffectView that makes the view look inconsistent, as pictured below (highlighted by the red circle):
Optimally, I would prefer that the UIVisualEffectView blends perfectly with the UINavigationBar's blur.
Thanks.
Try to use a UIToolBar instead of a UIVisualEffectView as the background of the segment. The navigation bar has translucent background rather than blur effect. UIToolBar has the same translucent background as navigation bar, so it would look seamless at the edge.
Looking to your picture it seems your issue is not attributable to UINavigationBar but to a view where you have added UISegmentedControl.
I don't know your structure but it could be the tableHeaderView (self.tableView.tableHeaderView) so a reasonable way to solve this problem is to change the header color:
Code example:
override func tableView(tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
var headerView: UITableViewHeaderFooterView = view as! UITableViewHeaderFooterView
header.contentView.backgroundColor = UIColor.clearColor()
return header
}

Resources