Updating navigation bar button item title outside of viewdidload - ios

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)

Related

UIButton titleLabel has padding

UIButton title label has top and bottom padding, I want to remove padding.
Set UIButton content mode did not work.
Here is my code
lazy var button: UIButton = {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitleColor(ThemeColor.red, for: .normal)
button.setTitle("Push", for: .normal)
button.addTarget(self, action: #selector(buttonDidTap), for: .touchUpInside)
button.backgroundColor = .blue
button.contentHorizontalAlignment = .fill
button.contentVerticalAlignment = .fill
button.contentMode = .scaleAspectFill
return button
}()
and it looks like
How can I remove the padding space!
As Matt pointed out, you can fix this by adjusting the button's contentEdgeInsets
However, one thing I noticed, if you set the contentEdgeInsets to 0 all around:
button.contentEdgeInsets = UIEdgeInsets(top: 0,
left: 0,
bottom: 0,
right: 0)
You still get the the vertical padding for some reason.
I remember seeing an answer which I cannot find now where it suggested to set an extremely small edge inset and this should work:
lazy var button: UIButton = {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitleColor(.red, for: .normal)
button.setTitle("Push", for: .normal)
button.backgroundColor = .blue
button.contentHorizontalAlignment = .fill
button.contentVerticalAlignment = .fill
button.contentMode = .scaleAspectFill
button.contentEdgeInsets = UIEdgeInsets(top: .leastNormalMagnitude,
left: .leastNormalMagnitude,
bottom: .leastNormalMagnitude,
right: .leastNormalMagnitude)
return button
}()
You can use UIButton.Configuration and then set its contentInsets to .zero
lazy var button: UIButton = {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
var configuration = UIButton.Configuration.plain()
configuration.background.backgroundColor = .blue
configuration.background.cornerRadius = 0
configuration.baseForegroundColor = .red
configuration.title = "Push"
configuration.contentInsets = .zero
button.configuration = configuration
return button
}()
You can use the button's configuration to get more precise control over its appearance like so:
lazy var myButton: UIButton = {
let newButton = UIButton()
newButton.translatesAutoresizingMaskIntoConstraints = false
newButton.contentMode = .scaleAspectFill
newButton.addTarget(self, action: #selector(buttonDidTap), for: .touchUpInside)
// 'configurationUpdateHandler' property can be used to set appearance depending on its state
newButton.configurationUpdateHandler = { button in
switch button.state { // here i'll just use default so it's the same over all states
default:
button.configuration?.title = "Push"
button.configuration?.baseBackgroundColor = .blue
// remove padding here
button.configuration?.contentInsets.top = 0
button.configuration?.contentInsets.bottom = 0
}
}
return newButton
}()
Of course you don't need to use updateHandler, you can just access the configuration directly and just set it there
button.configuration?.contentInsets.top = 0
button.configuration?.contentInsets.bottom = 0
See if this solves the problem...

Swift - UIView moves when changing tab controller tabs

Inside the viewDidAppear I have a function that contains this code, in order to make a UIView:
let contentView = UIView()
func addSleepingView() {
contentView.backgroundColor = .systemYellow.withAlphaComponent(0.25)
view.addSubview(contentView)
contentView.translatesAutoresizingMaskIntoConstraints = false
contentView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
contentView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
// Anchor your view right above the tabBar
contentView.bottomAnchor.constraint(equalTo: (tabBarController?.tabBar.topAnchor)!).isActive = true
contentView.heightAnchor.constraint(equalToConstant: 50).isActive = true
let label = UILabel()
label.text = "Test"
label.frame = CGRect(x: 0, y: 0, width: 25, height: 34.0)
label.lineBreakMode = .byWordWrapping
label.numberOfLines = 0
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(label)
label.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
let button = UIButton()
button.setImage(UIImage(systemName: "arrow.clockwise", withConfiguration: UIImage.SymbolConfiguration(scale: .large)), for: UIControl.State.normal)
button.tintColor = .systemGray
button.frame = CGRect(x: self.view.bounds.width-42, y: 8, width: 34, height: 34.0)
button.isUserInteractionEnabled = true
button.addTarget(self, action: #selector(wakeupFunction), for: .touchUpInside)
contentView.addSubview(button)
let button2 = UIButton()
button2.setImage(UIImage(systemName: "exclamationmark.triangle", withConfiguration: UIImage.SymbolConfiguration(scale: .large)), for: UIControl.State.normal)
button2.tintColor = .label
button2.frame = CGRect(x: 8, y: 8, width: 34, height: 34.0)
button2.isUserInteractionEnabled = false
contentView.addSubview(button2)
contentView.bringSubviewToFront(button)
}
This is what it looks like:
Now this is exactly how I want it. The problem comes when I change tab. For example go to the last tab, and back to the first tab again. Then it looks like this:
What am I doing wrong here?
You can just add your code of activating constraints inside the DispatchQueue.main block
Updated Line you can change and it will start working as you're expectation.
// Anchor your view right above the tabBar
DispatchQueue.main.async
{
self.contentView.bottomAnchor.constraint(equalTo: (self.tabBarController?.tabBar.topAnchor)!).isActive = true
self.contentView.heightAnchor.constraint(equalToConstant: 50).isActive = true
}

The size of the UIButton doesn't change - Swift

I'm trying to setup the custom navigation bar in my iOS app, but .frame = CGRect(...) doesn't change the size of buttons that I added to it. Buttons appeared inside the navigation bar, but they have wrong size, or maybe constraints, I don't know.
// Setting up the Navigation Bar
private func setupNavigationBar() {
// Remove the shadow under the Navigation Bar
self.navigationController?.navigationBar.shadowImage = UIImage()
let addButton = UIButton(type: .system)
addButton.setImage(UIImage(named: "ic_add")?.withRenderingMode(.alwaysOriginal), for: .normal)
addButton.frame = CGRect(x: 0, y: 0, width: 25, height: 25)
let settingsButton = UIButton(type: .system)
settingsButton.setImage(UIImage(named: "ic_settings")?.withRenderingMode(.alwaysOriginal), for: .normal)
settingsButton.frame = CGRect(x: 0, y: 0, width: 25, height: 25)
navigationItem.rightBarButtonItems = [UIBarButtonItem(customView: settingsButton), UIBarButtonItem(customView: addButton)]
}
(I call this function inside the viewDidLoad())
Here you can see the result on my iPhone:
This is work for me. Please try it. (iOS 11)
private func setupNavigationBar() {
// Remove the shadow under the Navigation Bar
self.navigationController?.navigationBar.shadowImage = UIImage()
let addButton = UIButton(type: .custom)
addButton.setImage(UIImage(named: "ic_add")?.withRenderingMode(.alwaysOriginal), for: .normal)
addButton.frame = CGRect(x: 0, y: 0, width: 25, height: 25)
let settingsButton = UIButton(type: .custom)
settingsButton.frame = CGRect(x: 0, y: 0, width: 10, height: 10)
settingsButton.setImage(UIImage(named: "ic_settings")?.withRenderingMode(.alwaysOriginal), for: .normal)
let menuBarItem = UIBarButtonItem(customView: settingsButton) // new line
let currWidth = menuBarItem.customView?.widthAnchor.constraint(equalToConstant: 10) // new line
currWidth?.isActive = true // new line
let currHeight = menuBarItem.customView?.heightAnchor.constraint(equalToConstant: 10) // new line
currHeight?.isActive = true // new line
navigationItem.rightBarButtonItems = [
menuBarItem, // modify
UIBarButtonItem(customView: addButton)]
}
Try to change the button type
Replace .system to .custom .
Also check the size of original image if it is very large.

Swift Gesture Recognizer by adding subview

I have added a button to my navigationBar in titleView position.
I have then added a subview to this button.
I am trying to add a gesture recognizer associated with this view (UIImageView) but the tap is not detected.
Is it because is in the NavigationBar?
Is there a way to make it work?
Here's my code:
func createHeaderButton() {
headerTitleBtn.setTitle(headerTitleBtnString, for: .normal)
headerTitleBtn.titleLabel?.minimumScaleFactor = 0.5
headerTitleBtn.titleLabel?.numberOfLines = 2
headerTitleBtn.semanticContentAttribute = .forceRightToLeft
let buttonImage = UIImageView()
buttonImage.frame = CGRect(x: headerTitleBtn.frame.size.width, y: headerTitleBtn.frame.size.height/2 - 6, width: 12, height: 12)
buttonImage.image = UIImage(named:"arrowDown")
buttonImage.contentMode = .scaleAspectFit
buttonImage.isUserInteractionEnabled = true
buttonImage.backgroundColor = UIColor.red
headerTitleBtn.contentMode = .scaleAspectFit
let tapHedaerBtn = UIGestureRecognizer(target: self, action: #selector(headerBtnTapped))
buttonImage.addGestureRecognizer(tapHedaerBtn)
headerTitleBtn.addSubview(buttonImage)
}
#objc func headerBtnTapped() {
print("here<")
}
and I call this function in viewDidLoad and viewWillAppear
Thank you
--------------------------- UPDATE
Here's my solution:
I change the code so i don't need to add a gesture recognizer and to everything on the button:
func createHeaderButton() {
headerTitleBtn.setTitle(headerTitleBtnString, for: .normal)
headerTitleBtn.titleLabel?.minimumScaleFactor = 0.5
headerTitleBtn.titleLabel?.numberOfLines = 2
headerTitleBtn.semanticContentAttribute = .forceRightToLeft
headerTitleBtn.contentMode = .scaleAspectFit
headerTitleBtn.contentEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 0)
headerTitleBtn.setImage(UIImage(named:"arrowDown"), for: .normal)
headerTitleBtn.imageEdgeInsets = UIEdgeInsets(top: 8, left: 15, bottom: 8, right: 8)
headerTitleBtn.imageView?.contentMode = .scaleAspectFit
headerTitleBtn.imageView?.tintColor = UIColor.white
headerTitleBtn.tintColor = UIColor.white
}

Put two (side by side) buttons as a right side overlay of an UITextField

(Swift3/iOS10)
I'm trying to put two buttons (save/cancel) inside of a UITextField using the rightView property. My basic setup code (called from viewDidLoad) is as follows:
private func accesorizeRenameField() {
let saveButton = UIButton(type: .system)
saveButton.setImage(#imageLiteral(resourceName: "buttonCheckmark"), for: .normal)
saveButton.addTarget(self, action: #selector(renameSave), for: .touchUpInside)
saveButton.tintColor = UIColor(white: 0.15, alpha: 1)
let cancelButton = UIButton(type: .system)
cancelButton.setImage(#imageLiteral(resourceName: "buttonX"), for: .normal)
cancelButton.addTarget(self, action: #selector(renameCancel), for: .touchUpInside)
cancelButton.tintColor = UIColor(227, 34, 60, 1)
let container = UIView()
container.addSubview(saveButton)
container.addSubview(cancelButton)
container.translatesAutoresizingMaskIntoConstraints = false
container.backgroundColor = UIColor.cyan
saveButton.centerYAnchor.constraint(equalTo: container.centerYAnchor).isActive = true
cancelButton.centerYAnchor.constraint(equalTo: container.centerYAnchor).isActive = true
saveButton.heightAnchor.constraint(equalTo: container.heightAnchor).isActive = true
cancelButton.heightAnchor.constraint(equalTo: container.heightAnchor).isActive = true
saveButton.leadingAnchor.constraint(equalTo: container.leadingAnchor).isActive = true
saveButton.trailingAnchor.constraint(equalTo: container.centerXAnchor).isActive = true
cancelButton.leadingAnchor.constraint(equalTo: container.centerXAnchor).isActive = true
cancelButton.trailingAnchor.constraint(equalTo: container.trailingAnchor).isActive = true
self.renameField.rightView = container
self.renameField.rightViewMode = .always
}
Later on, when I expose the rename field through some animation, I call
self.renameField.rightView?.bounds = CGRect(x: 0, y: 0, width: self.renameField.bounds.height * 2, height: self.renameField.bounds.height)
self.renameField.rightView?.setNeedsLayout()
"rightView.frame \(self.renameField.rightView?.frame)".print()
self.renameField.rightView?.subviews.enumerated().forEach() {index,child in
"child \(index) frame \(child.frame)".print()
}
However, I cannot get the buttons to show up. The cyan background (for debugging) shows up, but actually is square (it should be a 2:1 rectangle). The print shows that the subviews have frames of size 0. What is the right/idiomatic way to do this? I feel like I'm just throwing hammers here...
Create UIButton as type of .custom instead of .system.
And as you already know renameField's height in viewDidLoad, you don't need to bother with constraint.
The code below is enough.
override func
viewDidLoad() {
super.viewDidLoad()
let wSize = renameField.frame.size.height - 2
let saveButton = UIButton( type: .custom )
saveButton.setImage( #imageLiteral(resourceName: "buttonCheckmark"), for: .normal )
saveButton.addTarget( self, action: #selector(renameSave), for: .touchUpInside )
saveButton.frame = CGRect( x: 0, y: 0, width: wSize, height: wSize )
let cancelButton = UIButton( type: .custom )
cancelButton.setImage( #imageLiteral(resourceName: "buttonX"), for: .normal )
cancelButton.addTarget( self, action: #selector(renameCancel), for: .touchUpInside )
cancelButton.frame = CGRect( x: wSize, y: 0, width: wSize, height: wSize )
let wV = UIView()
wV.frame = CGRect( x:0, y:0, width: wSize * 2, height: wSize )
wV.addSubview( saveButton )
wV.addSubview( cancelButton )
renameField.rightView = wV;
renameField.rightViewMode = .always;
}

Resources