TableView Content behind TabBar - swift

I've searched through S/OF and can't find a fix for the TableView being behind my TabBar.
I set up my TableView like this;
func setUpTableView() {
messagesTableView.frame = view.frame
messagesTableView.backgroundColor = .white
view.addSubview(messagesTableView)
messagesTableView.tableFooterView = UIView()
messagesTableView.delegate = self
messagesTableView.dataSource = self
messagesTableView.register(UITableViewCell.self, forCellReuseIdentifier: messagesCellIdentifier)
edgesForExtendedLayout = []
extendedLayoutIncludesOpaqueBars = false
messagesTableView.contentInsetAdjustmentBehavior = .never
}
Then I setUpTableView() in viewDidLoad().
According to all sources
edgesForExtendedLayout = []
extendedLayoutIncludesOpaqueBars = false
messagesTableView.contentInsetAdjustmentBehavior = .never
Should satisfy the content insets and not allow the TableView to scroll behind the TabBar.
Please note my TabBars' translucency is set to false inside TabBarController.
tabBar.isTranslucent = false
As always any help appreciated.
Adding Illustration
When scrolling to the bottom is not showing the complete TableView content as the TabBar covers the last few indexes.

Did you try this in viewDidAppear ?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
}
Edit
Remove
messagesTableView.frame = view.frame
and add autoLayout to your messagesTableView
messagesTableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
messagesTableView.topAnchor.constraint(equalTo: topAnchor),
messagesTableView.leftAnchor.constraint(equalTo: leftAnchor),
messagesTableView.bottomAnchor.constraint(equalTo: bottomAnchor),
messagesTableView.rightAnchor.constraint(equalTo: rightAnchor)
])

Related

Swift: Make the background image of UITableViewController embedded in Navigation Controller fill the entire screen

I managed to create translucent and rounded UITableViewCells in a UITableViewController that is embedded inside a Navigation Controller with this line of code in viewDidLoad():
tableView.backgroundView = UIImageView(image: UIImage(named: "nightTokyo"))
But I want the background image to fill the entire phone screen. I changed the code (and only this line of code) to:
navigationController?.view = UIImageView(image: UIImage(named: "nightTokyo"))
Now the background image fills up the entire phone screen, but my table and even the iPhone's time and battery indicator icons are missing.
What I want is for the background image to fill the entire screen, but the tableView, its cells, the iPhone time, battery level icon, etc. to remain displayed.
navigationController?.setNavigationBarHidden(true, animated: true)
Here is what I did which worked for me using Swift 5, XCode 12.
Step 1 (Optional) - Create a custom UINavigationController class
class CustomNavigationController: UINavigationController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationBar.isTranslucent = true
}
Replace your UINavigationController with this UINavigationController subclass. I mark this as optional as this is based on preference, if you do not set this, your navigation bar will be opaque and you cannot see what's beneath it.
Setting the navigationBar.isTranslucent = true allows you to see the background beneath it which is what I like. A subclass is also optional but you might need to make other updates to your nav bar so I always like to make this a subclass.
Step 2 - Set up your background view constraints
class CustomViewController: UIViewController {
// your background view
let bgImageView: UIImageView = {
let bgImageView = UIImageView()
bgImageView.image = UIImage(named: "gradient_background")
bgImageView.contentMode = .scaleAspectFill
return bgImageView
}()
// Get the height of the nav bar and the status bar so you
// know how far up your background needs to go
var topBarHeight: CGFloat {
var top = self.navigationController?.navigationBar.frame.height ?? 0.0
if #available(iOS 13.0, *) {
top += UIApplication.shared.windows.first?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
} else {
top += UIApplication.shared.statusBarFrame.height
}
return top
}
var isLayoutConfigured = false
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
title = "Site Visit"
// you only want to do this once
if !isLayoutConfigured() {
isLayoutConfigured = true
configBackground()
}
}
private func configBackground() {
view.addSubview(bgImageView)
configureBackgroundConstraints()
}
// Set up your constraints, main one here is the top constraint
private func configureBackgroundConstraints() {
bgImageView.translatesAutoresizingMaskIntoConstraints = false
bgImageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor,
constant: -topBarHeight).isActive = true
bgImageView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor,
constant: 0).isActive = true
bgImageView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor,
constant: 0).isActive = true
bgImageView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor,
constant: 0).isActive = true
view.layoutIfNeeded()
}
Before setting constraints:
After setting above constraints:

Cant change colour of searchBar and it jumps to top of view when selected

I'm trying to attach a search bar to the top of my tableView and change its attributes (eg. colour, placeholder). However, I can't figure out how. I've tried embedding the tableView in another view but that didn't help. Any ideas?
func setupSearch(){
search.delegate = self
search.automaticallyShowsCancelButton = false
search.searchBar.tintColor = UIColor.red
search.searchBar.barTintColor = UIColor.red
search.obscuresBackgroundDuringPresentation = false
search.hidesNavigationBarDuringPresentation = false
search.searchBar.placeholder = "Type something here to search"
navigationItem.searchController = search
tableView.tableHeaderView = search.searchBar
}
This function is called in the viewDidLoad() and the tableView is added but not with the right colour or placeholder and jumps to the top of the screen when selected.
Any help would be appreciated.
This is the updated code for setupSearch (everything is working fine except the bar jumps to the top when selected):
func setupSearch(){
search.delegate = self
search.automaticallyShowsCancelButton = false
search.searchBar.barTintColor = UIColor.red
search.obscuresBackgroundDuringPresentation = false
search.hidesNavigationBarDuringPresentation = false
tableView.tableHeaderView = search.searchBar
}
I declare the search bar at the start using:
let search = UISearchController(searchResultsController: nil)
Any ideas on how to stop the bar jumping to the top?
Just add search bar as tableview's headerview not with navigation's item searchcontroller(not add search bar with both(tableview and navigation) as in your code). You can try with updated code below:
func setupSearch(){
search.delegate = self
search.automaticallyShowsCancelButton = false
search.searchBar.barTintColor = UIColor.red
search.obscuresBackgroundDuringPresentation = false
search.hidesNavigationBarDuringPresentation = false
search.searchBar.placeholder = "Type something here to search"
tableView.tableHeaderView = search.searchBar
self.definesPresentationContext = true
}
override func viewDidLoad() {
super.viewDidLoad()
setupSearch()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil) // be notified when the keyboard changes your table View frame
}
#objc func keyboardWillShow(notification: NSNotification) {
tableView.frame.origin.y = 0 // reset the table view to its original coordinates
}
func setupSearch(){
// for iOS 12 and lower, you can change the placeholder like this :
let textFieldSearchBar = searchBar.value(forKey: "searchField") as? UITextField
textFieldSearchBar?.textColor = .red
let searchBarLabel = textFieldSearchBar!.value(forKey: "placeholderLabel") as? UILabel
textFieldSearchBarLabel?.textColor = .red
}

Table View layout not updating on device orientation change

So I have created a tableView and have made it's frame identical to the view's frame, so therefore it should be the same size of the phone screen. However when I change the device orientation to landscape in the simulator the table view keeps the same dimensions from portrait mode.
Here is my tableView code:
func setTableView() {
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.frame = view.frame
tableView.backgroundColor = UIColor.lightGray
tableView.delegate = self
tableView.dataSource = self
tableView.separatorColor = UIColor.lightGray
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
Here is the viewDidLoad method:
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)
setTableView()
}
Here is the method where I detect orientation change:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
if UIDevice.current.orientation.isLandscape {
print("Landscape")
} else {
print("Portrait")
}
}
Here is what I get in the simulator. In landscape mode the table view is only half of the width of the view while it should always fill the whole screen.
Usually if you want the frame of a view to determine its anchors, you don't set tableView.translatesAutoresizingMaskIntoConstraints = false. Setting that flag to false will force it to rely on anchors as opposed to its own view frame to set its constraints.
You could try setting it to true, or you could try just constraining the table to the view like so:
self.view.addSubview(tableView)
tableView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
tableView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
This will constrain it to the view frame. If you do it this way, you don't need to worry about setting tableView.frame = view.frame. Personally, I prefer this method over relying on view's frames.

UITableViewController overlapping bottom safe area

I added a Tableview Controller in storyboard, The problem is that it's overlapping the bottom safe area on iPhone X. I also have a view at the bottom which is above the safeAreaLayoutGuide. I tried to use auto layout and anchor it to view.safeAreaLayoutGuide.bottomAnchor, but this doesn't work. Any help would be appreciated. This is what I've tried, with other options but no help.
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor).isActive = true
tableView.leadingAnchor.constraint(equalTo:view.safeAreaLayoutGuide.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo:view.safeAreaLayoutGuide.trailingAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor).isActive = true }

Swift - "navigationController?.pushViewController" doesn't work

I have 3 ViewControllers like below.
A: HomeController,
B: NavController1,
C: NavController2
I set a custom navigation bar in B and C and I use "navigationController?.pushViewController" for the segues From A to B to C.
These are the codes.
Custom navigation bar:
class CustomNavBar: UINavigationBar {
let navBarView: UIView = {
let view = UIView()
view.backgroundColor = .black
// Logo
let logo = UIImageView(image: <image-file>)
view.addSubview(logo)
logo.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
logo.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
return view
}()
public func setupNavBar() {
if let window = UIApplication.shared.keyWindow {
window.addSubview(navBarView)
navBarView.topAnchor.constraint(equalTo: window.topAnchor, constant: 0).isActive = true
navBarView.leftAnchor.constraint(equalTo: window.leftAnchor, constant: 0).isActive = true
navBarView.rightAnchor.constraint(equalTo: window.rightAnchor, constant: 0).isActive = true
}
}
}
And in the ViewControllers B and C I put these codes.
class NavController1: UIViewController {
let navBar = CustomNavBar()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
navBar.setupNavBar()
}
override func viewWillDisappear(_ animated: Bool) {
loginHeader.headerBg.isHidden = true
loginHeader.backButton.isHidden = true
}
}
And for the segues, I used the code below with a UIButton action.
self.navigationController?.pushViewController(NavController1, animated: true)
It works with the segue from A to B but it doesn't work with B to C.
Can someone point out what the problem is? Thank you!
UINavigationController is derived from UIViewController, which means it has a navigationController property. Thing is, this will be nil on the actual UINavigationController because it doesn't have a navigation controller, it is the navigation controller. Because you are using optional chaining, the message is never getting sent because navigationController is nil. Try sending the message to self instead (given that self is of type UINavigationController, and the current one).
I.E. self.pushViewController(NavController1, animated: true)