Cannot see button when placing UIView in front of Nav Bar - ios

I have a UIView which has a button, I have added a round border around the button using UIView, the button works and is positioned perfectly, and I want the UIView in front of the navigation bar, but once I add the UIView in front of the navigation bar, the button disappears but the UIVew come in front of the navigation bar.
let viewBar = UIView()
let userProfileView = UIView()
let userProfileButton = UIButton(type: .custom)
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
setupNextButton()
setupViewBar()
setupUserProfileButton()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// To Bring the viewBar in front of the navigation bar
self.navigationController?.view.addSubview(viewBar)
}
func setupViewBar() {
viewBar.backgroundColor = UIColor.init(red: 0, green: 0, blue: 0, alpha: 0.5)
view.addSubview(viewBar)
addNavigationBarConstraints()
}
func setupUserProfileButton() {
userProfileButton.setImage(#imageLiteral(resourceName: "profilePictureSmall.png"), for: .normal)
userProfileButton.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
userProfileButton.addTarget(self, action: #selector(profilePictureTapped), for: .touchUpInside)
userProfileView.layer.cornerRadius = 20
userProfileView.layer.borderWidth = 1.5
userProfileView.clipsToBounds = true
userProfileView.layer.borderColor = UIColor.black.cgColor
userProfileView.addSubview(userProfileButton)
//viewBar.addSubview(userProfileView)
view.addSubview(userProfileView)
addUserProfileButtonConstraints()
}
Here are the constraints to the user profile button and the viewBar
func addViewBarConstraints() {
viewBar.translatesAutoresizingMaskIntoConstraints = false
viewBar.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
viewBar.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
viewBar.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
viewBar.heightAnchor.constraint(equalToConstant: 100).isActive = true
}
func addUserProfileButtonConstraints() {
userProfileView.translatesAutoresizingMaskIntoConstraints = false
userProfileView.topAnchor.constraint(equalTo: viewBar.safeAreaLayoutGuide.topAnchor, constant: 5).isActive = true
userProfileView.trailingAnchor.constraint(equalTo: viewBar.safeAreaLayoutGuide.trailingAnchor, constant: -15).isActive = true
userProfileView.widthAnchor.constraint(equalToConstant: 40).isActive = true
userProfileView.heightAnchor.constraint(equalToConstant: 40).isActive = true
}
How can I make the button appear? I have added some images too.

The navigation bar goes on top of the stack of views and so it's hiding your red UIView. So, essentially, you have to add the button to the navigation bar instead of adding it to the UIView:
let barButton = UIBarButtonItem(customView: userProfileButton)
self.navigationItem.rightBarButtonItem = barButton

Related

Not able to centre elements in UIstackview?

I was working on a live playground project and I created everything programmatically but I am not able to align my buttons in the stackView to centre please help me
here is code for the main playground
import PlaygroundSupport
import UIKit
let vc = HomeViewController()
let navController = UINavigationController(rootViewController: vc)
navController.view.frame = CGRect(x: 0, y: 0, width: 600, height: 600)
PlaygroundPage.current.liveView = navController
here is my code for HomeVC
import UIKit
public class HomeViewController:UIViewController{
let stackView:UIStackView = {
let st = UIStackView()
st.axis = .horizontal
st.alignment = .center
st.distribution = .fillEqually
st.backgroundColor = .cyan
st.spacing = 10
st.translatesAutoresizingMaskIntoConstraints = false
return st
}()
let generateButton:UIButton = {
let btn = UIButton()
btn.setTitle("Generate Array", for: .normal)
btn.backgroundColor = .yellow
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let generateButton2:UIButton = {
let btn = UIButton()
btn.setTitle("Generate 2", for: .normal)
btn.backgroundColor = .brown
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
public override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemPink
view.addSubview(stackView)
stackView.addArrangedSubview(generateButton)
stackView.addArrangedSubview(generateButton2)
stackView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
stackView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
stackView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 120).isActive = true
}
}
All I want is to align the buttons in that stack view to centre ..... please help me
Your buttons are centred correctly, UINavigationBar give you an illusion that they are not. To fix the issue, you have few options:
Hide navigation bar:
navigationController?.setNavigationBarHidden(true, animated: false)
Remove navigation bar translucency:
navigationController?.navigationBar.isTranslucent = false
Set edgesForExtendedLayout to an empty array (Source):
edgesForExtendedLayout = []
All these actions can be performed in viewDidLoad() function.

Updating navigation bar button item title outside of viewdidload

I am creating a navigation bar in viewDidLoad method with following code:
let backButtonImage = UIImage(named: "backButton")
normalButton = UIButton(frame: CGRect(x: 0, y: 0, width: backButtonImage!.size.width, height: backButtonImage!.size.height))
normalButton.setImage(backButtonImage, for: .normal)
normalButton.setTitleColor(.systemBlue, for: .normal)
normalButton.contentHorizontalAlignment = .leading
normalButton.contentVerticalAlignment = .center
normalButton.titleEdgeInsets = UIEdgeInsets(top: 0, left: 0.0, bottom: 0.0, right: 0.0)
let backBarButton = UIBarButtonItem(customView: normalButton)
backBarButton.tintColor = .systemBlue
item.leftBarButtonItems = [backBarButton]
navigationBar.isTranslucent = false
navigationBar.barTintColor = .white
self.view.addSubview(navigationBar)
navigationBar.translatesAutoresizingMaskIntoConstraints = false
navigationBar.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
navigationBar.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
navigationBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
navigationBar.items = [item]
navigationBar.delegate = self
But i want to update normalButton title when user touches an another button inside the view. I tried following code:
if let letnavItem = navigationBar.items?[0],let bb = letnavItem.leftBarButtonItems?[0] {
if let but = bb.customView, let but2 = but as? UIButton {
print("counter: \(counter)")
but2.setTitle("asd", for: .normal)
}
}
But it is not working. But if i run the second code in viewDidLoad method right after the navigationBar.delegate line, it is working well.
Why is it not working outside of the viewDidLoad?
All above code is correct just you need to remove image from the button before setting off the button text like this:
but2.setImage(nil, for: .normal)
but2.setTitle("asd", for: .normal)

How to add a view as subview for certain controllers

I have multiple storyboards in my app. I want to add a view on always on the top just below the navigation bar for some of the controllers. How Can I achieve this?
I already used navigation delegate and add a view in the window but no luck. Steps to show the gray view in the attached image is.
1. On click of a button on that view controller; a gray view should show and remain on the top of the controllers until all the scanning of the device is not done whether the user should go any of the viewControllers.
You can create a UINavigationController subclass and add the view in it.
class NavigationController: UINavigationController {
let customView = UIView()
let iconImgView = UIImageView()
let msgLbl = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
customView.isHidden = true
customView.translatesAutoresizingMaskIntoConstraints = false
customView.backgroundColor = .gray
view.addSubview(customView)
iconImgView.contentMode = .scaleAspectFit
iconImgView.translatesAutoresizingMaskIntoConstraints = false
customView.addSubview(iconImgView)
msgLbl.numberOfLines = 0
msgLbl.lineBreakMode = .byWordWrapping
msgLbl.textColor = .white
msgLbl.translatesAutoresizingMaskIntoConstraints = false
customView.addSubview(msgLbl)
customView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
customView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
customView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
iconImgView.widthAnchor.constraint(equalToConstant: 40).isActive = true
iconImgView.heightAnchor.constraint(equalToConstant: 40).isActive = true
iconImgView.centerYAnchor.constraint(equalTo: customView.centerYAnchor).isActive = true
iconImgView.leadingAnchor.constraint(equalTo: customView.leadingAnchor, constant: 15).isActive = true
iconImgView.trailingAnchor.constraint(equalTo: msgLbl.leadingAnchor, constant: 15).isActive = true
msgLbl.topAnchor.constraint(equalTo: customView.topAnchor, constant: 10).isActive = true
msgLbl.bottomAnchor.constraint(equalTo: customView.bottomAnchor, constant: 10).isActive = true
msgLbl.trailingAnchor.constraint(equalTo: customView.trailingAnchor, constant: -15).isActive = true
msgLbl.heightAnchor.constraint(greaterThanOrEqualToConstant: 30).isActive = true
}
func showCustomView(message: String, icon: UIImage) {
msgLbl.text = message
iconImgView.image = icon
customView.isHidden = false
}
func hideCustomView() {
customView.isHidden = true
}
}
Embed all your view controllers in this navigation controller. When you want to show/hide the gray view in a view controller use
Show
(self.navigationController as? NavigationController)?.showCustomView(message: "Any Message", icon: UIImage(named: "anyImage")!)
Hide
(self.navigationController as? NavigationController)?.hideCustomView()
When you push another view controller from the same navigation controller the view won't be hidden until you call the hide method
You can simply create a custom UIView with the relevant frame and call addSubview() on the view you want to add it to.
lazy var customView: UIView = {
let customView = UIView(frame: CGRect.init(x: 0, y: self.view.safeAreaInsets.top, width: UIScreen.main.bounds.width, height: 100))
customView.backgroundColor = .gray
return customView
}()
#IBAction func onTapButton(_ sender: UIButton) {
self.view.addSubview(customView)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.customView.removeFromSuperview()
}
To add it below the navigationBar, use y position of frame as self.view.safeAreaInsets.top. With this your customView will always be aligned below the navigationBar.
You can create the view with the height as per your requirement. I've used height = 100.
Give the correct frame and you can add any view as a subView to another view.

Arranging buttons programmatically with constraints

I have an array of buttons that I am iterating through and adding the buttons onto the view. Each button should be adjacent to the previous button, so I'm setting the leading constraint to the previous button's trailing. But the buttons end up layered on top of each other with only the top one displayed.
for k in 0 ..< buttons.count {
view.addSubview(buttons[k])
if k > 0 {
buttons[k].leadingAnchor.constraint(equalTo: buttons[k-1].trailingAnchor).isActive = true
}
}
Edit:
I don't know if this is part of the problem, but here's how I'm creating the buttons. I set each to (0,0) because I don't know where they'll end up. I assume the constraint would reposition them as needed (first time use programmatic constraints).
let size = CGRect(x: 0, y: 0, width: buttonWidth, height: buttonHeight)
let button: UIButton = UIButton(frame: size)
Here a simple playground that works with a UIStackView. You can play a bit and accommodate for your goal.
UIStackViews are very flexible components if you want avoid creating constraints manually.
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
class MyViewController: UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let buttons = createButtons()
let stackView = createStackView(with: UILayoutConstraintAxis.vertical)
buttons.forEach { button in
stackView.addArrangedSubview(button)
}
view.addSubview(stackView)
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
self.view = view
}
func createStackView(with layout: UILayoutConstraintAxis) -> UIStackView {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = layout
stackView.distribution = .equalSpacing
stackView.spacing = 0
return stackView
}
func createButtons() -> [UIButton] {
var buttons = [UIButton]()
for x in 0..<5 {
let button = UIButton(type: .custom)
button.backgroundColor = .red
button.translatesAutoresizingMaskIntoConstraints = false
button.widthAnchor.constraint(equalToConstant: 50).isActive = true
button.heightAnchor.constraint(equalToConstant: 100).isActive = true
button.setTitle("Title \(x)", for: .normal)
buttons.append(button)
}
return buttons
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
The key problem is you should use isActive to active constraint.
The following is example
var buttons: [UIButton] = []
for index in 0...5 {
let button = UIButton(frame: .zero)
button.setTitleColor(.black, for: .normal)
button.setTitle("button \(index)", for: .normal)
button.layer.borderColor = UIColor.gray.cgColor
button.layer.borderWidth = 1.0
button.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(button)
buttons.append(button)
}
for index in 0...5 {
let button = buttons[index]
if index == 0 {
button.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 8.0).isActive = true
button.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 20.0).isActive = true
} else {
let preButton = buttons[index - 1]
button.leadingAnchor.constraint(equalTo: preButton.trailingAnchor, constant: 8.0).isActive = true
button.topAnchor.constraint(equalTo: preButton.topAnchor, constant: 0.0).isActive = true
}
}

UINavigationBar contentMode not being set

I am trying to make the background of the UINavigationBar an image, and the image isn't the exact size of the bar. I can't seem to set the contentMode of the bar correctly for some reason. This is the code I have:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// ...
let image = UIImage(named: object?.imageName ?? "")
navigationController?.navigationBar.alpha = 0
navigationController?.navigationBar.setBackgroundImage(image, for: .default)
self.navigationController?.navigationBar.contentMode = .scaleAspectFill
UIView.animate(withDuration: Double(UINavigationControllerHideShowBarDuration), animations: {
self.navigationController?.navigationBar.alpha = 1
})
}
I don't know what I'm doing wrong, or maybe contentMode just doesn't do anything for UINavigationBar.
What's currently happening is the background image is just presented again like mosaic
Thanks
You can create UIView and add Image and Text to it, but you must set constraints
let titleView = UIView()
titleView.frame = CGRect(x: 0, y: 0, width: 100, height: 40)
let containerView = UIView()
containerView.translatesAutoresizingMaskIntoConstraints = false
titleView.addSubview(containerView)
let profileImageView = UIImageView()
profileImageView.translatesAutoresizingMaskIntoConstraints = false
profileImageView.contentMode = .scaleAspectFill
profileImageView.layer.cornerRadius = 20
profileImageView.clipsToBounds = true
containerView.addSubview(profileImageView)
profileImageView.leftAnchor.constraint(equalTo: containerView.leftAnchor).isActive = true
profileImageView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
profileImageView.widthAnchor.constraint(equalToConstant: 40).isActive = true
profileImageView.heightAnchor.constraint(equalToConstant: 40).isActive = true
self.navigationItem.titleView = titleView
titleView.addGestureRecognizer(UITapGestureRecognizer(target:
self, action: #selector(func)))
I hope that I helped you

Resources