Issue reusing the same UI Image code across multiple view controllers | Swift 5 - swift

I have the following function I use to customize the navigation bar across almost all the apps view controllers and table view controllers - instead of replicating the code numerous times I am looking for way to easily call the function on those view controllers needing it.
I have tried wrapping in extension UIViewController { } but run into a selector issue saying the following:
Argument of '#selector' cannot refer to local function
'Tapped(tapGestureRecognizer:)'
Code:
func navBar(){
// Profile Image
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(url: URL(string: "test.com"))
imageView.contentMode = UIView.ContentMode.scaleAspectFit
imageView.layer.cornerRadius = 20
imageView.layer.masksToBounds = true
containView.addSubview(imageView)
let rightBarButton = UIBarButtonItem(customView: containView)
self.navigationItem.rightBarButtonItem = rightBarButton
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTapped(tapGestureRecognizer:)))
imageView.isUserInteractionEnabled = true
imageView.addGestureRecognizer(tapGestureRecognizer)
}
#objc func imageTapped(tapGestureRecognizer: UITapGestureRecognizer) {
print("Profile Tapped")
}
How can this UIImage be seen in the navigation bar across various view controller without needing to rewrite the same code across all.

Lot a way to do it. I'll usually istance and personalize an UIViewController and use it around the whole app.
class baseController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//call your navBar here
navbar()
}
func navBar(){
// Profile Image
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(url: URL(string: "test.com"))
imageView.contentMode = UIView.ContentMode.scaleAspectFit
imageView.layer.cornerRadius = 20
imageView.layer.masksToBounds = true
containView.addSubview(imageView)
let rightBarButton = UIBarButtonItem(customView: containView)
self.navigationItem.rightBarButtonItem = rightBarButton
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTapped(tapGestureRecognizer:)))
imageView.isUserInteractionEnabled = true
imageView.addGestureRecognizer(tapGestureRecognizer)
}
}
Now instance this class whenever you want and your controller will get your navBar() every time with this
class mineController:baseController {
//your code here...
}

Related

use tap gesture reconigzier on multiple image views

My swift code below places 2 different image views on a uiview controller. When the user hits a imageivew I want that specific imageview to change color. I dont know how to apply the method to multiple image views. I think you would use the sender method.
import UIKit
class ViewController: UIViewController {
var slider = UISlider()
var image1 = UIImageView()
var image2 = UIImageView()
var with = 80
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
[slider,image1,image2].forEach{
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
$0.backgroundColor = .systemOrange
}
slider.frame = CGRect(x: view.center.x-115, y: view.center.y+200, width: CGFloat(with), height: 30)
image1.frame = CGRect(x: view.center.x-115, y: view.center.y, width: CGFloat(with), height: 30)
image2.frame = CGRect(x: view.center.x-115, y: view.center.y-200, width: CGFloat(with), height: 30)
slider.minimumValue = 10
slider.maximumValue = 150
image1.isUserInteractionEnabled = true
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageViewTapped))
image1.addGestureRecognizer(gestureRecognizer)
image2.addGestureRecognizer(gestureRecognizer)
}
#objc func imageViewTapped(sender: UITapGestureRecognizer) {
if let imageView = sender.view as? UIImageView {
imageView.backgroundColor = .yellow
}
}
}
I tried your code:
add below two lines to enable the user interaction as mentioned by #matt:
image1.isUserInteractionEnabled = true
image2.isUserInteractionEnabled = true
and create two separate objects for gesture:
let gestureRecognizer1 = UITapGestureRecognizer(target: self, action: #selector(imageViewTapped))
image1.addGestureRecognizer(gestureRecognizer1)
let gestureRecognizer2 = UITapGestureRecognizer(target: self, action: #selector(imageViewTapped))
image2.addGestureRecognizer(gestureRecognizer2)
Output:
Happy coding...
A UIGestureRecognizer is to be used with a single view. So, you need to create two seperate UITapGestureRecognizer object for two different views.
For example:-
let image1GestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageViewTapped))
image1.addGestureRecognizer(image1GestureRecognizer)
let image2GestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageViewTapped))
image2.addGestureRecognizer(image2GestureRecognizer)

How to show all TextField in a view that stored in a array / Swift 5

I have a tapGesture, that every time when you click on it a new TextField displays and will saved in a array.
I can create multiple TextField and can store them in a Array, but I have no idea, how I can display them on a view.
At the Moment only the first one will be shown in the view.
I want to build something similar like snapchat with the text.
var myTextField: UITextField = UITextField(frame: CGRect(x: 0,y: 0, width: 300.0, height:30.0))
func addTapGestureToTextImageView() {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTextImage))
textImageView.isUserInteractionEnabled = true
textImageView.addGestureRecognizer(tapGesture)
}
#objc func handleTextImage() {
textFields.append(myTextField)
let myTextField = UITextField(frame: CGRect(x: 0,y: 0, width: 300.0, height:30.0))
for i in 0..<textFields.count {
myTextField.tag = i
view.addSubview(textFields[i])
print(textFields[i])
}
print(textFields.count)
}
If you want to pile them vertically you have to increment the y with the height for each textField and hence change it's frame.
for i in 0..<textFields.count {
var field = textFields[i]
field.frame = CGRect(x: 0, y: 30*i, width: 300, height: 30)
view.addSubview(field)
print(textFields[i])
}
It will look something like this. But you will need to tweak it to properly align it.
My suggestion would be to put a UIStackView in the baseView and then use it's addArrangedSubview() and it will do the stacking for you.
Use stackView for this:
lazy var textFieldsView: UIStackView = {
let stackView = UIStackView()
stackView.spacing = 4
stackView.distribution = .fillEqually
stackView.alignment = .fill
stackView.frame.size.width = 300
view.addSubview(stackView)
return stackView
}()
#objc func handleTextImage() {
let myTextField = UITextField(frame: CGRect(x: 0, y: 0, width: 300.0, height: 30.0))
for pair in textFields.enumerated() {
pair.element.tag = pair.offset
textFieldsView.addArrangedSubview(pair.element)
}
// Update stackView frame if needed
}

How to add UIImageView to navigation bar in swift?

I have this code that adds a rounded border around a UIImage using UIImageView and I've used UITapGestureRecognizer to let the user tap on the button:
var profilePicture = UIImageView()
func setupUserProfileButton() {
let defaultPicture = UIImage(named: "profilePictureSmall")
profilePicture = UIImageView(image: defaultPicture)
profilePicture.layer.cornerRadius = profilePicture.frame.width / 2
profilePicture.clipsToBounds = true
profilePicture.layer.borderColor = UIColor.black.cgColor
profilePicture.layer.borderWidth = 1
// Letting users click on the image
profilePicture.isUserInteractionEnabled = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(profilePictureTapped))
profilePicture.addGestureRecognizer(tapGesture)
}
How can I add this to the left side of a navigation bar? Is it possible? And I don't think the tap gesture is needed if I can add the ImageView to the navigation bar as a barButtonItem, so you can ignore that. I kinda found some similar questions but they were in objective C and none of what I tried worked.
Here is what I came up with based on an answer:
import UIKit
import Firebase
class CreateStoryPage: BaseAndExtensions {
let userProfileButton = UIButton(type: .custom)
override func viewDidLoad() {
super.viewDidLoad()
// Call all the elements
setupUserProfileButton()
}
// MARK:- Setups
// Setup the user profile button
func setupUserProfileButton() {
userProfileButton.setImage(#imageLiteral(resourceName: "profilePictureSmall.png"), for: .normal)
userProfileButton.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
userProfileButton.addTarget(self, action: #selector(profilePictureTapped), for: .touchUpInside)
let userProfileView = UIView(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
userProfileView.layer.cornerRadius = 14
userProfileView.backgroundColor = .red
userProfileView.addSubview(userProfileButton)
let leftNavBarItem = UIBarButtonItem(customView: userProfileView)
self.navigationItem.setLeftBarButton(leftNavBarItem, animated: true)
}
// if user taps on profile picture
#objc func profilePictureTapped() {
let userProfilePage = UserProfilePage()
present(userProfilePage, animated: true, completion: nil)
}
}
Try this;
private func setupRightItem() {
let userProfileButton = UIButton(type: .custom)
userProfileButton.imageView?.contentMode = .scaleAspectFill
userProfileButton.clipsToBounds = true
userProfileButton.addTarget(self, action: #selector(profilePictureTapped), for: .touchUpInside)
userProfileButton.setImage(#imageLiteral(resourceName: "profilePictureSmall.png"), for: .normal)
userProfileButton.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: userProfileButton)
userProfileButton.widthAnchor.constraint(equalToConstant: 30).isActive = true
userProfileButton.heightAnchor.constraint(equalToConstant: 30).isActive = true
}
#objc private func goProfile() {
/// -> Action
}
let navBtn = UIButton(type: .custom)
navBtn.setImage("yourImage", for: .normal)
navBtn.frame = CGRect(x: 0, y: 0, width: 28, height: 28)
navBtn.addTarget(self, action: #selector(self.openProfile(_:)), for: .touchUpInside)
let view = UIView(frame: CGRect(x: 0, y: 0, width: 28, height: 28))
view.cornerRadius = 14
view.backgroundColor = Global.colorBlue
view.addSubview(navBtn)
let leftNavBarItem = UIBarButtonItem(customView: view)
self.navigationItem.setLeftBarButton(leftNavBarItem, animated: true)
#objc
func openProfile(_ sender: UIButton) {
}

Set image just one time on NavigationBar Swift

I have an app with several ViewControllers, and I have to display an image in the title of the navigation bar, I already have this code to do it.
public func carregarLogoNav() -> Void {
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 86, height: 38))
imageView.contentMode = .scaleAspectFit
let image = UIImage(named: "logo-simples.png")
imageView.image = image
navigationItem.titleView = imageView
}
But I would like to know if it is possible to replicate this image to all other views, without having to paste this code in each one.
Another way would be to just call this function in another ViewController, but it is not working either
anotherViewController().carregarLogoNav()
If you wish to have the same titleView in all of your view controllers then you can put your carregarLogoNav function in an extension and then call it from viewDidLoad of each of your view controllers:
extension UIViewController {
public func setupCarregarLogoNav() {
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 86, height: 38))
imageView.contentMode = .scaleAspectFit
let image = UIImage(named: "logo-simples.png")
imageView.image = image
navigationItem.titleView = imageView
}
}
Then in each view controller:
override func viewDidLoad() {
super.viewDidLoad()
setupCarregarLogoNav()
// any other code
}
This way your setup code is only in once place. It's just called from other places.

Swift: SegmentedControl in NavBar with Small TitleView

I am attempting to include a segmentedControl on my navBar that looks like this:
The idea here is that the text "fetching..." is a small titleView. However, my current implementation would result in the text "fetching..." on the lower side like so:
I implement large titles so that I can get two "rows" on the navBar, else the word "fetching..." will be hidden behind the segmentedControl.
Code:
let segmentedControl: UISegmentedControl = {
let items = ["Now","In 15 mins", "In 1 hour"]
let sc = UISegmentedControl(items: items)
sc.selectedSegmentIndex = 0
return sc
}()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.backBarButtonItem?.title = "Back"
navigationItem.largeTitleDisplayMode = .automatic
navigationItem.titleView = segmentedControl
}
Does anyone have any advice?
You can create a customView that holds all the views you want to show in the navigation bar and set that view as titleView like below,
let segmentedControl: UISegmentedControl = {
let items = ["Now","In 15 mins", "In 1 hour"]
let sc = UISegmentedControl(items: items)
sc.selectedSegmentIndex = 0
return sc
}()
let fetchingLabel: UILabel = {
let label = UILabel(frame: .zero)
label.text = "Fetching..."
return label
}()
In viewDidLoad
let customView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 250))
customView.addSubview(segmentedControl)
customView.addSubview(fetchingLabel)
fetchingLabel.frame = CGRect(x: 150, y: 0, width: self.view.frame.width, height: 60)
segmentedControl.frame = CGRect(x: 60, y: 50, width: self.view.frame.width * 0.75, height: 30)
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.largeTitleDisplayMode = .automatic
navigationItem.titleView = customView
This should give you below result. You can play with the values to do what you want.