How do I change/reset NavBarItem programatically without reloading VC? [Swift] - swift

This one is driving me crazy. I'm trying to change the RightBarButton from a function within the owning view controller (For more info, this is my structure: TabBarController > Navigation Controller > View Controller, with a unique Navigation Controller for each View Controller within the TabBarController)
func setProfileImage(url: String, action: Selector) {
print("Set profile image")
// Reset Profile Image
var barButton = UIBarButtonItem()
let button = UIButton(type: .custom)
let profileImage = UIImageView()
let storage = Storage.storage()
var reference: StorageReference!
reference = storage.reference(forURL: url)
reference.downloadURL { (url, error) in
let data = NSData(contentsOf: url!)
let image = UIImage(data: data! as Data)
profileImage.image = image ?? UIImage(named: "sullyPlaceholder")
button.addTarget(self, action: action, for: .touchUpInside)
button.frame = CGRect(x: 0, y: 0, width: 35, height: 35)
button.clipsToBounds = true
button.layer.cornerRadius = 0.5 * button.bounds.size.width
button.layer.borderWidth = 3
button.layer.borderColor = UIColor.lightGray.cgColor
button.setImage(profileImage.image, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.widthAnchor.constraint(equalToConstant: 35).isActive = true
button.heightAnchor.constraint(equalToConstant: 35).isActive = true
barButton = UIBarButtonItem(customView: button)
self.navigationItem.setRightBarButton(barButton, animated: true)
print("Profile Image Generated")
}
}
Setting the image the first time on viewDidAppear works perfectly fine, but when I try to run the function from an action within the same view controller, it will not change. When I switch tabs and come back, the button & image is updated. How do I update/refresh the navigationItem from within the VC?

Related

PHPicker can't load selected image to UIButton

I created a UIButton programmatically Which can pick profile photo.
So I made image picker with PHPicker, and it shows well.
But after I selected image, it doesn't show on the button.
I thought the original photos could be the problem. But it's the same with the newly downloaded photo except it sometimes shows white background or send real error phrase.
There is some warning on console like "Xxx(file directory)’ couldn’t be opened because there is no such file"
This PHPicker code worked well before, so I can't assume what is the problem.
How can I select photo well?
private let plusPhotoButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(UIImage(named: "plus_photo"), for: .normal)
button.tintColor = .white
button.addTarget(self, action: #selector(imagePickerTapped), for: .touchUpInside)
return button
}()
#objc func imagePickerTapped() {
print("imagePickerTapped")
var configuration = PHPickerConfiguration()
configuration.selectionLimit = 1
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self
self.present(picker, animated: true, completion: nil)
}
// MARK: - PHPicker extention
extension RegistrationController : PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
let itemProvider = results.first?.itemProvider
if let itemProvider = itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self) {
itemProvider.loadObject(ofClass: UIImage.self) { (image, error) in
DispatchQueue.main.async {
self.plusPhotoButton.layer.cornerRadius = self.plusPhotoButton.frame.width / 2
self.plusPhotoButton.layer.masksToBounds = true
self.plusPhotoButton.layer.borderColor = UIColor.black.cgColor
self.plusPhotoButton.layer.borderWidth = 2
self.plusPhotoButton.setImage(image as? UIImage, for: .normal)
}
}
} else {
print("Image wasn't loaded!!!!")
}
}
}
I added frame setting for this button below
view.addSubview(plusPhotoButton)
plusPhotoButton.centerX(inView: view)
plusPhotoButton.setDimensions(height: 140, width: 140)
plusPhotoButton.anchor(top: view.safeAreaLayoutGuide.topAnchor, paddingTop: 32)
let stack = UIStackView(arrangedSubviews: [emailTextField, passwordTextField, fullnameTextField, UsernameTextField, loginButton])
stack.axis = .vertical
stack.spacing = 20
view.addSubview(stack)
stack.anchor(top: plusPhotoButton.bottomAnchor, left: view.leftAnchor, right: view.rightAnchor, paddingTop: 32, paddingLeft: 32, paddingRight: 32)
'''
After reviewing your code you need make below changes in your code:
Replace
self.plusPhotoButton.setImage(image as? UIImage, for: .normal)
with
self.plusPhotoButton.setBackgroundImage(image as? UIImage, for: .normal)
And you also need to remove image from button with:
self.plusPhotoButton.setImage(nil, for: .normal)
And here is your code with minor improvement:
guard let img = image as? UIImage else { return }
self.profileImage = img
self.plusPhotoButton.layer.cornerRadius = self.plusPhotoButton.frame.width / 2
self.plusPhotoButton.layer.masksToBounds = true
self.plusPhotoButton.layer.borderColor = UIColor.black.cgColor
self.plusPhotoButton.layer.borderWidth = 2
self.plusPhotoButton.setBackgroundImage(img, for: .normal)
self.plusPhotoButton.setImage(nil, for: .normal)

UINavigation custom back button image not working in iOS 15

I want to apply UINavigation custom background image and back button image also for iOS 14 everything is working fine but as I try to run the app on iOS 15 back button image not working instead it's showing the default back button which I want to replace with custom back button image I am using below code
if #available(iOS 15.0, *) {
let appearance = UINavigationBarAppearance()
appearance.configureWithTransparentBackground()
appearance.backgroundImage = image
appearance.setBackIndicatorImage(backButtonImage, transitionMaskImage: backButtonImage)
if Locale.current.languageCode == "fr" {
appearance.titleTextAttributes = [ NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 14), NSAttributedString.Key.foregroundColor: UIColor(hexString: "#231F20")]
} else {
appearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor(hexString: "#231F20")]
}
navigationItem.standardAppearance = appearance
navigationItem.scrollEdgeAppearance = appearance
} else {
self.navigationController?.navigationBar.setBackgroundImage(image, for: .default)
self.navigationController?.navigationBar.backIndicatorImage = backButtonImage
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = backButtonImage
if Locale.current.languageCode == "fr" {
self.navigationController?.navigationBar.titleTextAttributes = [ NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 14), NSAttributedString.Key.foregroundColor: UIColor(hexString: "#231F20")]
} else {
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor(hexString: "#231F20")]
}
}
Sharing with you a common function I have made in a BaseViewController which is the superclass of all my View Controllers.
I Simply call the addBackButton function from viewDidLoad from any of the VC I want to add the back button.
It also handles the back navigation automatically. (takes care if you have presented or pushed a vc)
func addBackButton(tint : UIColor? = nil, backImage : UIImage? = Constants.Images.kImageForBackNavigation){
let buttonForBack = UIButton(type: .custom)
var tintColorForImage = UIColor.white
if tint != nil {
tintColorForImage = tint!
}
let image = backImage!.withRenderingMode(.alwaysTemplate)
buttonForBack.setImage(image, for: .normal)
buttonForBack.imageView?.tintColor = tintColorForImage
buttonForBack.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
buttonForBack.addTarget(self, action: #selector(BaseViewController.closeViewController), for: .touchUpInside)
let barButtonItemForClose = UIBarButtonItem(customView: buttonForBack)
self.navigationItem.setLeftBarButton(barButtonItemForClose, animated: false)
}
#objc func closeViewController(){
if self == self.navigationController?.children.first {
DispatchQueue.main.async {
self.navigationController?.dismiss(animated: true, completion: nil);
}
}
else{
DispatchQueue.main.async {
self.navigationController?.popViewController(animated: true)
}
}
}

add gesture recognizer uivew navigation bar swift not working

I have a navigation controller with a navigation bar, I have added a UIView with subview of imageView and UILabel for titleView.
I need to be able to click on that view to do something else with addGestureRecognizer on that view but nothing is printed on the console.
The UIImageView has to be next to the UILabel
Here is the code I tried so far
private func setupNavBarWithUser() {
let titleView = UIView()
let width = titleView.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)).width
titleView.frame = CGRect(origin:CGPoint.zero, size:CGSize(width: width, height: 500))
let profileImageView = UIImageView()
profileImageView.translatesAutoresizingMaskIntoConstraints = false
profileImageView.contentMode = .scaleAspectFill
profileImageView.layer.cornerRadius = 20
profileImageView.clipsToBounds = true
ImageService.getImage(withURL: NSURL(string: (user?.pictureURL)!)! as URL) { (image) in
profileImageView.image = image
}
titleView.addSubview(profileImageView)
profileImageView.leftAnchor.constraint(equalTo: titleView.leftAnchor).isActive = true
profileImageView.centerYAnchor.constraint(equalTo: titleView.centerYAnchor).isActive = true
profileImageView.widthAnchor.constraint(equalToConstant: 40).isActive = true
profileImageView.heightAnchor.constraint(equalToConstant: 40).isActive = true
let nameLabel = UILabel()
titleView.addSubview(nameLabel)
nameLabel.text = user?.first_name
nameLabel.textColor = .white
nameLabel.translatesAutoresizingMaskIntoConstraints = false
nameLabel.leftAnchor.constraint(equalTo: profileImageView.rightAnchor, constant: 8).isActive = true
nameLabel.centerYAnchor.constraint(equalTo: profileImageView.centerYAnchor).isActive = true
nameLabel.rightAnchor.constraint(equalTo: titleView.rightAnchor).isActive = true
nameLabel.heightAnchor.constraint(equalTo: profileImageView.heightAnchor).isActive = true
titleView.centerXAnchor.constraint(equalTo: titleView.centerXAnchor).isActive = true
titleView.centerYAnchor.constraint(equalTo: titleView.centerYAnchor).isActive = true
self.navigationItem.titleView = titleView
let recognizer = UITapGestureRecognizer(target: self, action: #selector(self.testing))
titleView.isUserInteractionEnabled = true
titleView.addGestureRecognizer(recognizer)
}
#objc func testing() {
print("hello")
}
Hope someone can help me with this problem, much thank you !!
UPDATE this is where I need to add a gesture recognizer
Add this method in viewDidLoad/viewWillAppear
if let navigationBar = self.navigationController?.navigationBar {
// create a button
let button = UIButton(type: .custom)
button.frame = CGRect(x: navigationBar.frame.width/2-20, y: 0, width: 100, height: 40)
button.setTitleColor(.red, for: .normal)
button.setTitle("Button", for: .normal)
button.addTarget(self, action: #selector(self.testing), for: .touchUpInside)
// create a imageview
let profileImageView = UIImageView()
profileImageView.contentMode = .scaleAspectFill
profileImageView.layer.cornerRadius = 20
profileImageView.clipsToBounds = true
profileImageView.frame = CGRect(x: navigationBar.frame.width/2-60, y: 0, width: 40, height: 40)
profileImageView.image = UIImage(named: "heart")
// Add two elements to navigationbar
navigationBar.addSubview(profileImageView)
navigationBar.addSubview(button)
}

swift, embed ui view controller with navigation controller to allow for back button

I am trying to get a back button on my MyServiceTypeSelector() controller so that after i present MyServiceTypeSelector() I can go back to the BRPServiceSelector() controller , how can i do this ? do i some how need to embed it with a nav controller and if so i am not using storyboards so it would need to be done programmatically?
import Foundation
import UIKit
class BRPServiceSelector: UIViewController, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
let businessAccountLabel: UILabel = {
let label = UILabel()
label.text = "Business Account"
label.backgroundColor = .white
label.translatesAutoresizingMaskIntoConstraints = false
label.textAlignment = .center
return label
}()
lazy var serviceSelectorButton: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = UIColor.black
button.setTitle("Select A Service Type?", for: .normal)
button.setTitleColor(UIColor.white, for: .normal)
button.addTarget(self, action: #selector(presentServiceSelector), for: .touchUpInside)
button.layer.cornerRadius = 3
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 14)
return button
}()
func presentServiceSelector(){
let msts = MyServiceTypeSelector()
let navController = UINavigationController(rootViewController: msts)
self.present(navController, animated: true, completion: nil)
let containerView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .white
return v
}()
let scrollView: UIScrollView = {
let v = UIScrollView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .white
return v
}()
func setupViews(){
containerView.addSubview(serviceSelectorButton)
serviceSelectorButton.anchor(top: containerView.topAnchor, left: nil, bottom: nil, right: nil, paddingTop: 50, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 220, height: 25)
serviceSelectorButton.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
}
}
}
If you want the VC in the navigation stack then push it onto the stack instead of presenting it. presenting is normally used for modal windows and they dont usually have navigation bars.
self.navigationController?.pushViewController(vc, animated: true)

AddGesture different in Swift 3?

I have no idea why my code isn't working :
let profileImageView: UIImageView = {
let image = UIImageView()
image.image = UIImage(named: "gameofthrones_splash")
image.translatesAutoresizingMaskIntoConstraints = false
image.contentMode = .scaleAspectFill
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleSelectProfileImageView))
image.addGestureRecognizer(tapGesture)
image.isUserInteractionEnabled = true
return image
}()
This imageView is loaded in the ViewDidLoad (I can see it)
func handleSelectProfileImageView() {
print("123")
}
But nothing get returned in the console when I'm tapping on it ! WTF?
It's in the target. This works when place in the viewDidLoad():
profileImageView.frame = CGRect(x: 60, y: 60, width: 70, height: 70)
view.addSubview(profileImageView)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleSelectProfileImageView))
profileImageView.addGestureRecognizer(tapGesture)
The target is "self", which is the UIViewController (not the UIImageView). In most cases (I'm sure there are outliers) you want to record a gesture in a view, but work with the gesture in a view controller.
Okay the solution was quite simple actually. You have to make it a lazy var instead of a let.
lazy var profileImageView: UIImageView = {
let image = UIImageView()
image.image = UIImage(named: "gameofthrones_splash")
image.translatesAutoresizingMaskIntoConstraints = false
image.contentMode = .scaleAspectFill
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleSelectProfileImageView))
image.addGestureRecognizer(tapGesture)
image.isUserInteractionEnabled = true
return image
}()
Swift 3.0:
There was some error I removed it -
Put this code in viewdidLoad
override func viewDidLoad() {
super.viewDidLoad()
var profileImageView:UIImageView {
var image = UIImageView()
image.backgroundColor = UIColor.red;
image.frame = CGRect(x: 0, y: 0, width: 44, height: 44)
image.translatesAutoresizingMaskIntoConstraints = false
image.contentMode = .scaleAspectFill
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleSelectProfileImageView))
image.addGestureRecognizer(tapGesture)
image.isUserInteractionEnabled = true
return image
}
view.addSubview(profileImageView);
// Do any additional setup after loading the view.
}
func handleSelectProfileImageView() {
print("123")
}
Just to test I removed the image, you can add it. But it's working fine.
I fixed this issue using an UIButton ->
let profileImageView: UIButton = {
let iv = UIButton()
let image = UIImage(named: "r2d2")
iv.setImage(image, for: .normal)
iv.translatesAutoresizingMaskIntoConstraints = false
iv.addTarget(self, action: #selector(selectProfileImage), for: .touchUpInside)
iv.isUserInteractionEnabled = true
return iv
}()
#objc func selectProfileImage(){
print("Hello")
}