I have just implemented a UISearchController in a regular UIViewController, and I have a kind of weird issue.
I have a UIView that is adapted exactly to the size I want my UISearchBar to take. On first launch of the view, I add my UISearchBar as a SubView to this UIView.
But when I launch it, the UISearchBar doesn't take the size of its parent UIView -- it just takes the width of the whole screen.
As you can see, it overlaps with the button on the right.
But once I click on the search bar and cancel it, it resizes to the exact size I want.
What could be the issue here? I've tried adding AutoLayout Constraints from the SearchBar to its parent view but it doesn't seem to change anything. Doesn't [UIView addSubview:] handle this?
UISearchController's view behaviour is weird indeed, but there is a simple workaround.
Use an additional container view
put UISearchController's view inside
set UISearchController's view autoresizing mask to UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
set UISearchController's frame to container's view bounds
At this point UISearchController.view won't resize beyond container.bounds.
For me this didn't work, but I found a solution that I would like to share:
Instead of putting the searchBar in a containerView, put a navigationBar in the containerView, and put the searchBar in this navigationBar. For me, the problem still exists at this point.
But then I put a 1-pixel wide view as a navigationItem to the right of the navigationBar. From then it works all fine, the textfield of the searchBar didn't stretch anymore after the first selection.
It is more of a hack instead of a good solution to an annoying bug(?), but for me this hack is acceptable as I already needed some margins on both side of the searchBar. Here is some code:
//on init or viewDidLoad
navigationBar = UINavigationBar(frame: .zero)
let navigationItem = UINavigationItem()
navigationItem.titleView = searchController.searchBar
let leftView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: Design.margin, height: 1))
leftView.backgroundColor = .clear
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: leftView)
let rightView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: Design.margin, height: 1))
rightView.backgroundColor = .clear
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: rightView)
navigationBar.items = [navigationItem]
containerView.addSubview(navigationBar)
// setup frame sizes in your layout
Related
I have this view hierarchy on a view embedded in a UINavigationController:
When I scroll the UITableView the navigation bar is not moving up (the title is not becoming smaller) it stays like this:
If I remove the image view as background view everything works well.
My navigation is configured like this:
navigationItem.title = "Title here"
navigationItem.largeTitleDisplayMode = .always
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationBar.shadowImage = UIImage()
navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
navigationController?.navigationBar.isTranslucent = true
navigationController?.navigationBar.tintColor = .white
navigationController?.navigationBar.barStyle = UIBarStyle.blackTranslucent
navigationController?.navigationBar.backgroundColor = .clear
A project demonstrating the problem is available here:
https://drive.google.com/file/d/181Aggala2ZfGN0lDjEtHWg0vobkM0iJc/view?usp=sharing
I already tried to change the insets of the tableview but it didn't work.
tableView.contentInset = UIEdgeInsets(top: navigationController?.navigationBar.height, left: 0, bottom: 0, right: 0)
Thanks!
As you discovered, to make large title fonts work as you want them to, the UIScrollView or any of its subclass needs to be the first element in the view hierarchy.
To fix your problem, you can try setting the background image to be the background of the UITableView directly.
Edit: Okay soo according to your comment you want a background behind everything including navigation bar. There is one way of achieving this and that is to subclass your UINavigationController and inside viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
let image = UIImage(named: "wallpaper")
let imageView = UIImageView(image: image)
imageView.contentMode = .scaleAspectFill
imageView.frame = UIScreen.main.bounds
//this below part is important. Make sure to set it to 0 otherwise it will cover everything else
view.insertSubview(imageView, at: 0)
}
And then make sure your UIViewController containing the UITableView has a clear color for the UIView and remove the background image from that UIViewController
This question asked to be implemented in Swift 4, iOS 11
Is there any way to make every subview of ViewController's view to be pushed down when it is under UINavigationBar?
If navigation bar is NOT TRANSLUCENT the subview is under it. This is what I want.
Desired Result
But when navigation bar is TRANSLUCENT the subview is lying under it. I dont want it. I want the subview is pushed down just be like if navigation bar is not translucent.
Undesired Result
I create the view programmatically :
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.red
let navBar = (self.parent as? UINavigationController)?.navigationBar
navBar?.isTranslucent = true
}
func makeChildView() {
let myframe = CGRect(x: 0, y: 10, width: self.view.frame.width, height:
self.view.frame.height * 0.4)
let view = UIView(frame: myframe)
view.backgroundColor = UIColor.green
self.view.addSubview(view)
}
Using Autolayout
I am able to solve this problem using autolayout. But I just want to know how to achieve this result without autolayout if possible. Is there any other approach?
Swift 3.x
navBar?.isTranslucent = true
self.automaticallyAdjustsScrollViewInsets = false
Add this line & you are good to go.
Hey I am trying to add a custom view to a navigation bar. The current result is like so:
I initialise this navigation bar like so:
func setupNavBar() {
navigationController?.navigationBar.isTranslucent = false
let navBarView = ExamCoachNavBarView(frame: CGRect(x: 0, y: 0, width: 236, height: 45))
navigationItem.titleView = navBarView
}
I want to move the custom view down to the bottom of the nav bar. I have tried changing the y value for the navBarView frame. This make absolutely no difference those. Regardless to what that value is the frame always stays in the same position at the top of the bar. What am I doing wrong here? How can I move the bar to the bottom of the screen?
I have a UIView with a UITableView right below it. The tableview has a custom header which hold a UISearchbar and a UISegmententedController. The top view and tableview are constrained to each other and to the superview. If I hide the top view, the header of the tableview gets stretched.
Top view visible:
Top view hidden (height constraint set to 0):
My expectation would be that when I hide the top view my tableview content should scroll up, but instead of repositioning the cells, the tableview stretches out the header view.
Thanks for any help!
I'm not sure why but if the custom tableView header is created from a xib then it gets messed up. But if the view is created programmatically it works. I don't know why but to fix the problem just needed to create the tableview header programmatically
Bad:
if let headerView = Bundle.main.loadNibNamed("HeaderView", owner: self, options: nil)?[0] as? HeaderView {
tableview.tableHeaderView = headerView
}
Good:
tableview.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 80))
If you want to make the tableView scroll up, I would recommend getting the height constraint of the top view and reducing it to 0. You can update the height constraint in a UIView.animate block to have a smoother transition when showing and hiding the top view.
Refer here for more details: AutoLayout with hidden UIViews?
I have created layout with navigation bar and I have turned navigation bar translucent to no. I have added this code:
var overlay : UIView? // This should be a class variable
overlay = UIView(frame: view.frame)
overlay!.backgroundColor = UIColor.blackColor()
overlay!.alpha = 0.8
view.addSubview(overlay!)
if I understand it correctly this should create overlay over my view. But it gives me result of this:
So I think that this missalignes my view. Any idea how to fix this?
Its is happening because view origin changes if you set translucent
off. So Instead of using view.frame use view.bounds.
var overlay : UIView?
overlay = UIView(frame: view.bounds)
overlay!.backgroundColor = UIColor.blackColor()
overlay!.alpha = 0.8
view.addSubview(overlay!)
Replace your code as below.
overlay = UIView(frame: view.bounds)
overlay!.backgroundColor = UIColor.blackColor()
overlay!.alpha = 0.8
view.addSubview(overlay!)
Reason to use bounds instead of frame is,
you have turned of translucency. So your view will start from (0,64) instead of (0,0);
thats why its getting y=64 in frame value,
you can set y=0 or directly use view.bounds, in bounds it will give(x,y) = (0,0) and height and width as same as view.