Easiest way to setup custom NavigationBar in Swift? - swift

What is the easiest way to setup a custom NavigationBar in iOS/Swift?
I want to use a label above the navigation buttons (increased height). Is the only way to replace and hide the default NavigationBar and use an UIView?

(There's some missing things in your question, so this may not answer it.)
A UINavigationBar is simply a subclass of UIView, just as a UINavigationController is a subclass of a UIViewController. Where the former is just a "view" with a title and left/right bar buttons, the latter will have that and can also "control" a UIViewController stack.
Here's code for the former.
First, let's declare a label for the user name and a navigation bar with no title and a single left/right bar button. You can have an array of bar buttons on the left/right if you want. Also, I like to have SF Symbols with descriptive text, so I'll make UIButtons first.
let userName = UILabel()
let navBar = UINavigationBar()
let navItems = UINavigationItem(title: "")
var btnLeft:UIButton()
var btnRight:UIButton()
var barBtnLeft:UIBarButtonItem!
var barBtnRight:UIBarButtonItem!
Now let's populate things and set things up for auto layout. This all can be done during viewDidLoad, and change things (like user name) in other places.
// user name label
userName.translatesAutoresizingMaskIntoConstraints = false
userName.text = "User Name"
userName.textAlignment = .center
// create UIButtons with an SF Symbol and description
btnLeft.translatesAutoresizingMaskIntoConstraints = false
btnLeft.setImage(UIImage(systemName: "lessthan"), for: .normal)
btnLeft.setTitle(" left", for: .normal)
btnLeft.setTitleColor(.blue, for: .normal)
btnRight.translatesAutoresizingMaskIntoConstraints = false
btnRight.setImage(UIImage(systemName: "greaterthan"), for: .normal)
btnRight.setTitle(" right", for: .normal)
btnRight.setTitleColor(.blue, for: .normal)
// I like to "frame" these buttons, this is optional
btnLeft.layer.borderWidth = 1
btnLeft.layer.borderColor = UIColor.blue.cgColor
btnLeft.layer.cornerRadius = 5
btnRight.layer.borderWidth = 1
btnRight.layer.borderColor = UIColor.blue.cgColor
btnRight.layer.cornerRadius = 5
// now give the buttons a size
btnLeft.heightAnchor.constraint(equalToConstant: 44).isActive = true
btnLeft.widthAnchor.constraint(equalToConstant: 100).isActive = true
btnRight.heightAnchor.constraint(equalToConstant: 44).isActive = true
btnRight.widthAnchor.constraint(equalToConstant: 100).isActive = true
// don't forget to add targets!
btnLeft.addTarget(self, action: #selector(leftButtonSelector), for: .touchUpInside)
btnRight.addTarget(self, action: #selector(rightButtonSelector), for: .touchUpInside)
// make the result be a bar button
barBtnLeft = UIBarButtonItem(customView: btnLeft)
barBtnRight = UIBarButtonItem(customView: btnRight)
// add them to the navigation bar
navItems.leftBarButtonItems = [barBtnLeft]
navItems.rightBarButtonItems = [barBtnRight]
navBar.setItems([navItems], animated: false)
navBar.translatesAutoresizingMaskIntoConstraints = false
// add the label and navigation bar to your view
view.addSubview(userName)
view.addSubview(navBar)
// finally, lay the user name above the navigation bar
userName.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
userName.heightAnchor.constraint(equalToConstant: 44).isActive = true
userName.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
userName.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
navBar.topAnchor.constraint(equalTo: userName.bottomAnchor).isActive = true
navBar.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
navBar.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true

Related

How can I change my custom cell background when pressing a button?

I have a CollectionView and a Button inside of an UIView. The purpose of the button is change the theme of the app, and I can change all colors, except for the backgroundColor of a nameContainerView on my TatodexCell (a custom cell).
I can't figure out how to access that property, and then, change the color I want.
THE FOLLOWING CODE IS LOCATED ON MY TatodexController FILE
My button property is:
let buttonChangeTheme: UIButton? = {
let button = UIButton()
button.setTitle("Change to blue theme", for: .normal)
button.addTarget(self, action: #selector(themeButtonClicked), for: .touchUpInside)
button.titleLabel?.adjustsFontSizeToFitWidth = true
button.layer.borderWidth = 3
button.layer.borderColor = Colors.mainBlack?.cgColor
button.backgroundColor = Colors.darkBlue
button.tintColor = Colors.mainWhite
return button
}()
My button-func is this one:
#objc func themeButtonClicked() {
clickCheck = !clickCheck
if clickCheck {
navigationController?.navigationBar.barTintColor = Colors.lightBlue
collectionViewPokemon?.backgroundColor = Colors.darkBlue
buttonChangeTheme?.backgroundColor = Colors.darkRed
buttonChangeTheme?.setTitle("Return to red theme", for: .normal)
}
else {
navigationController?.navigationBar.barTintColor = Colors.lightRed
collectionViewPokemon?.backgroundColor = Colors.darkRed
buttonChangeTheme?.backgroundColor = Colors.darkBlue
buttonChangeTheme?.setTitle("Change to blue theme", for: .normal)
}
}
THE FOLLOWING CODE IS LOCATED ON MY TatodexCell FILE
The nameContainerView I wanna modify it's bg color is:
lazy var nameContainerView: UIView = {
let nameView = UIView()
nameView.backgroundColor = Colors.lightRed
nameView.addSubview(nameLabel)
nameLabel.center(inView: nameView)
return nameView
}()
My goal is to change nameView.backgroundColor = Colors.lightRed to nameView.backgroundColor = Colors.lightBlue, but I can't access that property.
I would really appreciate any help or advice. Perhaps the solution is hiding in plain sight, but I've tried many ways and none of them worked. Let me know if another chunk of code is needed to be shown.
If you implement the tintColorDidChange() method in your custom collection view cell, and in that method, you set the cell's background color to the tint color, then when you change the owning UICollectionView's tint color, the cells's tintColorDidChange() methods will fire and you'll see the change.

UIButton doesn't work in child view controller

UIButton gestures doesn't recognize in child view controller.
UIButton add targer work (button get target action)
User interaction is turned on everywhere, button size is okay. I try to set childVC as main, and then all work good, debug view hierarchy said that button view over other elements. IDK where is problem. I can provide more code, just tell me.
Add target code:
view.buttonOfLanguageFromTranslate.addTarget(self, action: #selector(self.openDetailView(_:)), for: .touchDown)
Add child VC code:
let childVC = ChildVC()
view.addSubview(childVC.view)
self.addChild(childVC)
childVC.didMove(toParent: self)
childVC.view.translatesAutoresizingMaskIntoConstraints = false
self.view.isUserInteractionEnabled = true
self.childVC = childVC
Button initialization:
var button: UIButton = {
let button = UIButton()
button.setTitle("Title", for: .normal)
button.setTitleColor(.black, for: .normal)
button.setTitleColor(.green, for: .selected)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
The problem was with
childVC.translatesAutoresizingMaskIntoConstraints = false
(in mainVC)
When i remove it, childVC recognise my tap
and i dont set additional size for childVC, view of childVC is display correct but not childVC. (as i think)

Toolbar doesn't center the button vertically

Can anyone explain to me why Xcode doesn't vertically center the button when you programmatically add the button to the toolbar? When I work with the toolbar added using the Storyboard, the button appears centered horizontally and vertically.
Toolbar added via a storyboard(looks perfect):
Toolbar added via a code:
My code:
self.navigationController?.isToolbarHidden = false
let testButton = UIButton(type: .custom)
testButton.setTitle("Test", for: .normal)
testButton.addTarget(self, action: #selector(test), for: .touchUpInside)
testButton.backgroundColor = .black
testButton.setTitleColor(.white, for: .normal)
testButton.titleLabel?.font = .boldSystemFont(ofSize: 17)
let tb = UIBarButtonItem(customView: testButton)
self.toolbarItems = [tb]

Swift NavBar Hide NavBarItem

Inside of my view controller I have a navBar with an item on the left side and right side. I also have 2 buttons inside of the view controller. I have it so each button does different functionality. I also have the swift file connected to the view.
My issue is that when I do either of the code below inside of my button actions the NavBar Items do not change. I don't know how to remove and add the item back, I also dont understand what Im doing wrong.
self.navigationItem.rightBarButtonItem?.isEnabled = false
self.navigationItem.rightBarButtonItem?.tintColor = UIColor.clear
self.navigationItem.rightBarButtonItem?.isEnabled = true
self.navigationItem.rightBarButtonItem?.tintColor = UIColor.red
Connect Navigation Bar from storyboard to View Controller Class
#IBOutlet var navBar: UINavigationBar!
This will hide Button
navBar.topItem?.rightBarButtonItem?.isEnabled = false
navBar.topItem?.rightBarButtonItem?.tintColor = UIColor.clear
This will show Button
navBar.topItem?.rightBarButtonItem?.isEnabled = true
navBar.topItem?.rightBarButtonItem?.tintColor = UIColor.red
Sorry, i can't make comment due to low reputetion. Which IDE are you using?
The answer of #dharmesh works, if you create the button programmatically:
let btn = UIButton(type: .system)
btn.setTitle("Indietro", for: btn.state)
btn.addTarget(self, action: #selector(annulla), for: .touchUpInside)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: btn)
If you use the one from the IDE it depends on the IDE itself to store the values to hide or change it

Swift Safe Area Layout not activating properly

I am learning some auto layout programatically and I want to add a button on the bottom part of the screen, just above the safe area.
I know the code is working, because I tested it in another project, I think it is a conflict because I get to this viewController from another one.
The code for my button
private let previousButton : UIButton = {
let button = UIButton(type: .system)
button.setTitle("Prev", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
The code for setting up my constraints
fileprivate func setupBottomControls() {
view.addSubview(previousButton)
previousButton.backgroundColor = .red
view.addSubview(previousButton)
NSLayoutConstraint.activate([
previousButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
previousButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
previousButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
previousButton.heightAnchor.constraint(equalToConstant: 50)
])
}
Like I said this code is working in another project but I think it is in conflict because of how I called this viewController.
This is a code from the first view controller that make the new viewController(GettingStartedController) to be shown, here it will be the button mentioned above.
func switchVC(){
//enter GettingStarted controller
let controller = GettingStartedController()
view.addSubview(controller.view)
present(UINavigationController(rootViewController: controller), animated: true,completion: nil) //used when no animation is present
}
I think the problem is here any ideas on how to change the call to the GettingStartedController so that will see the Safe Area the right way?
Check my code solution. I added your function to the button when it is tapped and it now presents the GettingStartedController():
private let previousButton : UIButton = {
let button = UIButton(type: .system)
button.setTitle("Prev", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(switchVC), for: .touchUpInside)
return button
}()
fileprivate func setupBottomControls() {
view.addSubview(previousButton)
previousButton.backgroundColor = .red
view.addSubview(previousButton)
NSLayoutConstraint.activate([
previousButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
previousButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
previousButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
previousButton.heightAnchor.constraint(equalToConstant: 50)
])
}
#objc func switchVC(){
//enter GettingStarted controller
let controller = GettingStartedController()
present(UINavigationController(rootViewController: controller), animated: true,completion: nil) //used when no animation is present
}