Swift - toolbar follows tableview when scrolling - ios

In my UITableViewController, my toolbar follows my tableview when I scroll it. My code looks like this:
override func viewDidLoad() {
let toolbar: UIToolbar = UIToolbar()
let checkButton = [UIBarButtonItem(title: "Done", style: .Done, target: self, action: "checkedPress")]
toolbar.frame = CGRectMake(0, self.view.frame.size.height - 46, self.view.frame.size.width, 48)
toolbar.sizeToFit()
toolbar.setItems(checkButton, animated: true)
toolbar.backgroundColor = UIColor.redColor()
self.view.addSubview(toolbar)
}
and it looks like this when I run the app:
I want the toolbar to stick to the bottom of the view, how is this achieved?
Any suggestions would be appreciated.

The problem is that since you're using a UITableViewController, with self.view.addSubview(toolbar), you've added your toolbar as a subview of your UITableViewController's view, i.e. a UITableView. As a subview of the UITableView, the toolbar will scroll along with the table.
The solution: Use a UIView containing a UITableView instead of using a UITableViewController if you'd like to customize your view controller. That way you can add elements to the view that aren't subviews of your tableview.

You can also embed your TableViewController in a Navigation Controller and show from the storyboard or programmatically a Toolbar. This one also standard sticks to the bottom and stays on top of the Views content and you have some functionality to hide it automatically on some conditions.
You don't have to use your Navigation controller always for navigating, sometimes its a convenient way for doing stuff Xcode makes hard to use without changing your already made Views.

Related

Weired animation of tableview and searchbar of UISearchController embed in navigationbar

I've embed UISearchController into the navigationbar and my view controller has the table view. When I click on searchbar and keyboard appears tableview and searchbar does not animate smoothly. It seems like searchbar is overlaping navigationbar.
Here is the code,
Declare searchcontroller as a variable like,
var resultSearchController = UISearchController()
and defination in viewDidLoad is like,
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.obscuresBackgroundDuringPresentation = false;
self.navigationItem.searchController = controller
return controller
})()
Animation issue is like below gif(watch till end to see slow animation)
I want same duration for animation for tableview, navigation bar, and searchbar.
Any help will be appreciated.
Don't set UITableView top anchor to be pinned to safeArea but to superview.
Important extra note to the accepted answer:
If you use a UIViewController with subviews, including a UITableView, the UITableView has to be the first subview (i.e.: at index 0).

custom navigation bar with image and two titles in iOS

I am trying to achieve the following navigation bar with two titles and an image:
Large title variant:
Small title variant:
I tried subclassing UINavigationBar and adding subviews to it, but they did not render at all.
I tried setting a titleView in storyboard, however it seemed like the titleView is constrained in its height.
What is the proper way to achieve this custom navigation bar?
I also tried this (and setting the viewController in Storyboard to that class):
class NavViewController: UINavigationController {
var titleView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
self.navigationBar.topItem?.titleView?.backgroundColor = .gray
titleView.frame = CGRect(x: 0, y: 0, width: 100, height: 300)
self.navigationBar.topItem?.titleView = titleView
}
}
Inside the ViewControllerin viewDidLoad, add self.navigationController?.navigationBar.addSubview(imageView). (no need for subclassing)
There is even support for AutoLayout inside UINavigationbar, which is great for animation.
Design your custom view seprately in a xib file, then set that xib as the titleview for your navigationbar
self.navBar.topItem?.titleView = logoImage
Do this for large title, for the smaller one only populate an image in the titleView.

Swift - Toolbar done button not appearing in UIPickerView subview

I'm a beginner in swift and trying to learn UIPickerView which has done button to close after the selection from the picker view. I have following code to add the tool bar and the done button to the picker as a toolbar in subview. It shows up as a blank black toolbar (attached screenshot)
let toolBar = UIToolbar()
toolBar.barStyle = .black
toolBar.sizeToFit()
let doneBtn = UIBarButtonItem.init(title: "Done", style: .plain, target: self, action: #selector(self.closePicker))
toolBar.items = [doneBtn]
toolBar.isUserInteractionEnabled = true
picker.addSubview(toolBar)
Picker is the outlet for UIPickerView in my controller. What am I doing wrong? I referred to other questions but they dont seem to solve my problem. Any suggestions?
You are wrong at this line:
picker.addSubview(toolBar)
Picker is not supposed to have any subviews - it's a comprehensive view by itself and there is no area in it to accommodate anything in addition.
Instead you need to add both the picker and the toolbar on the same view and align them next to each other
let toolBar = UIToolbar()
...configure your toolbar here...
guard let superview = picker.superview else { return }
superview.addSubview(toolBar)
toolBar.translateAutoresizingMaskIntoConstraints = false
NSLayoutConstraints.activate([
toolBar.topAnchor.constraint(equalTo: superview.topAnchor),
toolBar.leftAnchor.constraint(equalTo: superview.leftAnchor),
toolBar.rightAnchor.constraint(equalTo: superview.rightAnchor),
toolBar.bottomAnchor.constraint(equalTo: picker.topAnchor)
])
You should not add toolBar as subView of picker.
You should set the toolBar as inputAccessoryView of textField.
As I searched for this problem I got that the common way (also easiest way) to achieve what you want is Using dummy textField.
It means create a textField at the exact frame of button and hide it, when user touches the button make the textField firstResponder.
#IBAction func pickerButtonClicked(_ sender: Any) {
self.pickerViewTextField.becomeFirstResponder
}

Cancel button is not shown in UISearchBar

I have UICollectionView. On clicking search button in UINavigationBar, I am adding the UISearchController's searchbar as titleview for UINavigationItem. For iPhone it is working properly. For iPad the cancel button is not shown. The Searchbar alone takes the entire width.
Can anyone help me out on this?. Thanks in advance.
iOS7 does not show the cancel button when added to a navigation bar.You can put searchbar in another view like this.
UISearchBar *searchBar = [UISearchBar new];
searchBar.showsCancelButton = YES;
[searchBar sizeToFit];
UIView *viewForSearchBar = [[UIView alloc]initWithFrame:searchBar.bounds];
[viewForSearchBar addSubview:searchBar];
self.navigationItem.titleView = viewForSearchBar;
I had the same problem, on iPhone the search cancel was shown well, but on iPad it didn't.
The workaround of wrapping the UISearchBar in another UIView didn't work well for me since it had different appearance and wrong width on rotation.
My solution is a simple one - use search WITHOUT cancel, and add cancel as a UIBarButtonItem.
Added rightBarButtonItem with selector will work fine for me. And adding searchBar inside view before setting to navigation title view was not showing properly.
Code:-
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.plain, target: self, action: #selector(self.dismissView))
func dismissView() {
if self.controller?.navigationController?.popViewController(animated: true) == nil {
self.controller?.dismiss(animated: true, completion: nil)
}
}
As per apple documentation setShowsCancelButton
Cancel buttons are not displayed for apps running on iPad, even when
you specify YES for the showsCancelButton parameter.
I am not sure about the alternate but this is what apple provides us.
Try this. Add a checkmark for shows cancel button.
Swift version :-
I tried the #Nikita Khandelwal method, but still it doesn't fit for ipad view. Here is the swift code, which was given as corrected answer :-
let searchBar: UISearchBar = UISearchBar()
searchBar.showCancelButton = true
searchBar.placeholder = "Search Your Job Title"
searchBar.fitToSize()
searchBar.delegate = self //do not need if you delegate searchBar
let viewForSearchBar: UIView = UIView(frame: searchBar.bounds)
viewForSearchBar.addSubview(searchBar)
self.navigationItem.titleView = viewForSearchBar
********* But There is another way to set cancel button correctly and fit for the view :-
Set search bar as the Navigation bar title view :-
let searchBar: UISearchBar = UISearchBar()
searchBar.showCancelButton = true
searchBar.placeholder = "Search Your Job Title"
searchBar.delegate = self //do not need if you delegate searchBar
self.navigationItem.titleView = searchBar
Drag and drop Bar button to the right side of the view controller & name it as Cancel.
Then connect that button to this function :-
#IBAction func iPadCancelButton(sender: AnyObject) {
UIApplication.sharedApplication().sendAction("resignFirstResponder", to:nil, from:nil, forEvent:nil)
self.dismissViewControllerAnimated(true, completion: nil)
}
For iOS 13 built with Xcode 11, I'm needing to set manually set the display value on the cancel button, depending on whether the search controller is visible

UINavigationBar overlaps UITableView when programmatically setting prompt

I have a UINavigationController which contains a UITableViewController. This navigation controller pushes other UITableViewControllers around and eventually these table view controllers will have a prompt.
The problem is when I set this prompt programmatically it overlaps the content of the table view underneath it.
(A search bar is being hidden by the navigation bar)
I was looking around in SO and found this answer. I tried the suggestion there in two different ways in the affected view controller but nothing changed:
override func viewDidLoad() {
super.viewDidLoad()
self.edgesForExtendedLayout = .None;
self.extendedLayoutIncludesOpaqueBars = false;
self.navigationItem.title = NSLocalizedString("Add Anime or Manga", comment: "")
self.navigationItem.prompt = NSLocalizedString("Search media belonging to this series.", comment: "")
}
-
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = NSLocalizedString("Add Anime or Manga", comment: "")
self.navigationItem.prompt = NSLocalizedString("Search media belonging to this series.", comment: "")
self.edgesForExtendedLayout = .None;
self.extendedLayoutIncludesOpaqueBars = false;
}
A comment in that same answer linked to this Apple guide on preventing views from overlapping each other. The problem is UITableViewController doesn't appear to have top/bottom layout guides so I can't create a constraint (another SO answer says having said layouts in table view controllers is irrelevant).
As such I have exhausted all my options.
I have tried to reproduce your problem and it seems that when not all the viewControllers have a prompt the navigationBar is somehow not resizing properly.
It seems you need to somehow trigger the layouting for the UINavigationController. The only way I could make it work properly was by adding this in viewWillAppear:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES];
[self.navigationController setNavigationBarHidden:NO];
}
Maybe this prompt is meant to be used consistently across the entire application (meaning having one for all viewControllers or none of them), that's why the UINavigationController does not layout it's subviews when it changes.
Hope this works for you too.
Select your TableViewController from document outline and change the value to translucent navigation bar of top bar in attributes inspector. Be sure that you will not select uitableview you should select your your table view controller(aka File's Owner) from document outline.
You have to set prompt only if view did appear, then it works:
override func viewDidAppear(_ animated: Bool) {
navigationItem.prompt = "your prompt here"
}
It's 2019 and this is still not fixed. Slow Clap. I refuse to be cowed by such things so I hammered iOS into submission with the dirtiest trick in the book. I fixed this by doing a disgusting -44 "y trick" on the UINavigation while placing the UITableView in top 44, I know it's stupid, but it works.. I am sure new fangled phones will ruin my genius.. but hey ho.. I have lazily left irrelevant code (because I am idle) but hopefully you can see what I did.
WITHOUT THE y: -44 Hack
WITH THE y: -44 Hack
let screenSize: CGRect = UIScreen.main.bounds
let navBar = UINavigationBar(frame: CGRect(x: 0, y: -44, width: screenSize.width, height: 44)) //<<--note minus 44
navBar.barTintColor = Globals.Color_BackgroundGrey()
navBar.isTranslucent = false
tableView.contentInset = UIEdgeInsets(top: 44, left: 0, bottom: 0, right: 0); //<--note plus 44
self.edgesForExtendedLayout = []
let navItem = UINavigationItem(title: "Boaty Mc Boatface")
let doneItem = UIBarButtonItem(title: "Back", style: .plain, target: self, action: #selector(done))
navItem.rightBarButtonItem = doneItem
navBar.setItems([navItem], animated: false)
view.addSubview(navBar)

Resources