Here is my NavBar
Now I know how to add a customView on the leftBarButtonItem by using this code
navigationItem.leftBarButtonItem!.customView = someCustomView
As seen on my screenshot here
But the question is, Is there a way that I could detect if there are any navigationItems next to my leftBarButtonItem so I could set the width to be dynamic? instead of overlapping like the screenshots?
Here's more screenshots of my test case
Note: Yes I know how to set the frame by using the code someCustomView.frame = CGRect(.....) also it seems that the UINavigationBar/Item inherits from NSObject rather than UIView
Edit: So apparently after doing much coding, recoding, and research, the only part of the UINavigationBar/UINavigationItem that automatically resizes itself according to the left and right barButtons is the titleView which kind of sucks in a way.
So according to my knowledge, the only way to achieve this:
is by using the titleView property of the UINavigationItem which doesn't answer my question.
If -hopefully- these are the outputs that are looking for:
Then you should set the titleView of the navigationItem:
A custom view displayed in the center of the navigation bar when the
receiver is the top item.
It dynamically handles the width to not overlap.
For example, in the follwoing code snippet, I'll set the value of navigationItem.titleView as a UILabel (and of course you can customize it):
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 40))
label.text = "Some very long text that should be in the middle of the navigation bar"
label.textAlignment = .center
label.textColor = UIColor.blue
navigationItem.titleView = label
}
The output should be similar to the screenshots above.
Note that "Item 01", "Item 02" and "Item 03" that appear in the screenshots are added directly in the storyboard, there is no code related to adding them.
If you want to let the label to at next of "Item 01" button, simply let it:
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 40))
label.text = "my label"
label.textAlignment = .left
label.textColor = UIColor.blue
navigationItem.titleView = label
Output should be like:
Hope this helped.
Related
I've got a UIView within a UIScrollView, both of which I've created programmatically. The white rectangle is the UIView.
I want to put a UITableView within the UIView so that they're roughly the same size, except I'd like to leave room for the title at the top of the view. Here's what I've tried:
let tableView = UITableView(frame: view1.bounds)
view1.addSubview(tableView)
tableView.center(in: view1)
tableView.rowHeight = 30
tableView.backgroundColor = .blue
Except nothing shows up. How can I fix this?
If you're not instantiating your UIView with a frame: argument, you need to do so. Everything will seem normal if you omit this, and simply use the view1.size() method, except for some reason the tableView will not appear.
I tried below and I could see the table view.
let uiView = UIView(frame: CGRect(x: 100, y: 100, width: 200, height: 200))
self.view.addSubview(uiView)
let tableView = UITableView(frame: uiView.bounds)
uiView.addSubview(tableView)
tableView.rowHeight = 30
tableView.backgroundColor = .blue
tableView.separatorColor = .black
Below is my result :
You have to remove line tableView.center(in: view1)
UILabel does not wrap and shows more than one line.
According to the docs, my text should wrap on word boundary and be displayed in 2 lines. However, this does not occur. I'm using Swift 4 and the latest version of XCode.
let instruction = UILabel()
instruction.text = "Click And Touch Number To Make A Choice"
instruction.backgroundColor = .white
instruction.textColor = .black
instruction.font = UIFont.systemFont(ofSize: 20)
instruction.lineBreakMode = .byWordWrapping
instruction.textAlignment = .left
instruction.numberOfLines = 0
instruction.sizeToFit()
The label to wrap should have a frame whether with frame layout by setting width value or by auto layout by setting leading and trailing constraints that makes the label know it's boundaries and wrap when hitting the right most boundary to another line and so on according to it's content
let instruction = UILabel.init(frame: CGRect.init(x: 0, y: 0, width: 100, height: 100))
instruction.text = "Click And Touch Number To Make A Choice"
instruction.backgroundColor = .white
instruction.textColor = .black
instruction.font = UIFont.systemFont(ofSize: 20)
instruction.lineBreakMode = .byWordWrapping
instruction.textAlignment = .left
instruction.numberOfLines = 0
instruction.sizeToFit()
self.view.addSubview(instruction)
instruction.center = self.view.center
According to the docs:
In some cases, if a view does not have a superview, it may size itself to the screen bounds.
But in your case it doesn't. You just need to constrain label's width somehow and sizeToFit() wouldn't be even needed (Auto Layout will do its job) e.g.
instruction.translatesAutoresizingMaskIntoConstraints = false
instruction.widthAnchor.constraint(equalToConstant: 200.0).isActive = true
I have created a label via and a container view via:
let label = UILabel(frame: CGRectMake(0,0, 50, 50))
label.text = "omnomnom"
let labelView = UIView(frame: CGRectMake(50, 50, 100, 100))
labelView.addSubview(label)
labelView.bringSubviewToFront(label)
labelView.backgroundColor = UIColor.orangeColor()
label.textAlignment = .Center
But the label text might change so as a result for a longer word the whole thing won't be shown. I was wondering whether there is way to determine the intrinsic content size after the label is added, so that I can use those values to create a container view that is just a little bit bigger than that of the label.
You can use sizeToFit to accomplish that. So you only need to add label.sizeToFit() to the end of your label configuration.
This is how I customise my UIBarButtonItem:
if DBAppSettings.imageViewForCartBarButtonItem == nil {
DBAppSettings.imageViewForCartBarButtonItem = UIImageView(frame: CGRectMake(5, 5, 30, 30))
DBAppSettings.imageViewForCartBarButtonItem.backgroundColor = UIColor.greenColor()
}
let wrapperView = UIView(frame: CGRectMake(0, 0, 40, 40))
wrapperView.backgroundColor = UIColor.yellowColor()
wrapperView.addSubview(DBAppSettings.imageViewForCartBarButtonItem)
let cartBarButtonItem = UIBarButtonItem(customView: wrapperView)
Once I setup my UIBarButtonItem it looks ok:
but when I push and then pop view controller (this means that I use the same green subview to load it into another UIBarButtonItem) from the navigation stack, it is yellow. It looks like green subview is deallocated. Why?
This is how I keep references to my green subview:
class DBAppSettings: NSObject {
static var imageViewForCartBarButtonItem: UIImageView!
}
note:
Everything works fine, when I keep green subview locally:
let green = UIImageView(frame: CGRectMake(5, 5, 30, 30))
green.backgroundColor = UIColor.greenColor()
wrapperView.addSubview(green)
As I wrote in my comment. UIView can have just one superview. If you add it as a subview 2nd time, it's removed from the first superview and then added to the new one. Check addSubview... docs:
Views can have only one superview. If view already has a superview and
that view is not the receiver, this method removes the previous
superview before making the receiver its new superview.
It's not deallocated. It's just removed from your first wrapperView.
You can use custom factory method, like the following one:
extension UIBarButtonItem {
static func myBarButtonItem() -> UIBarButtonItem {
let imageView = UIImageView(frame: CGRectMake(5,5,30,30))
imageView.backgroundColor = UIColor.greenColor()
let wrapperView = UIView(frame: CGRectMake(0, 0, 40, 40))
wrapperView.backgroundColor = UIColor.yellowColor()
wrapperView.addSubview(imageView)
return UIBarButtonItem(customView: wrapperView)
}
}
Don't be afraid even if you use UIImages. UIImages are cached, shared, ... One image is not multiple times in memory, just once. Talking about UIImage & init?(named name: String).
In every UIViewController, just use UIBarButtonItem.myBarButtonItem(). Multiple instances of your wrapperView, imageView and one UIImage.
I want to add image to right side of navigation bar. But I can't drag image view to navigation bar. I can only drag a button.
How can I do this ? I want same thing with whatsapp profile image. You know they have circle profile picture on right side of navigation bar.
Drag a UIView and drop it on the navigation bar first. Then drag a UIImageView into the view.
That should give you what you are looking for.
Set the image view height and width using constraints. You have to set the width and height of the containing view in the size inspector.
**This will Display the image on right side on navigation bar**
func viewDidLoad(){
let containView = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
let imageview = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
imageview.image = UIImage(named: "photo.jpg")
imageview.contentMode = UIViewContentMode.scaleAspectFit
imageview.layer.cornerRadius = 20
imageview.layer.masksToBounds = true
containView.addSubview(imageview)
let rightBarButton = UIBarButtonItem(customView: containView)
self.navigationItem.rightBarButtonItem = rightBarButton
}
Don't know about storyboards. But from code you can create UIBarButtonItem with custom view.
let customView = UIImageView()
let customViewItem = UIBarButtonItem(customView: customView)
self.navigationItem.rightBarButtonItem = customViewItem
From storyboard:
Just drag UIBarButtonItem into your controller navigation bar. In element menu(Attribute inspector) select identifier: Custom and Image: the image you want.