How to close and open uiSearchBar on tap? - swift

I have been trying to open and close uiSearchBar but it slightly changes its y position, its width change is not consistent at first and it has a counterintuitive animation when triggered. Please try the code below you will see that the initial UI when closed is different than when you close it after opening the searchBar. here are what I see:
when component first lays out subviews:
When first opening the search
After closing it
Here is the code:
viewDidload:
override func viewDidLoad() {
super.viewDidLoad()
initSearchCont()
}
initSearchCont:
func initSearchCont(){
search.hidesNavigationBarDuringPresentation = false
search.loadViewIfNeeded()
search.searchResultsUpdater = self
search.obscuresBackgroundDuringPresentation = false
search.searchBar.enablesReturnKeyAutomatically = false
search.searchBar.translatesAutoresizingMaskIntoConstraints = false
search.searchBar.returnKeyType = UIReturnKeyType.done
definesPresentationContext = false
self.view.addSubview(search.searchBar)
navigationItem.hidesSearchBarWhenScrolling = false
search.searchBar.scopeButtonTitles = ["All", "Pair", "Odd"]
search.searchBar.delegate = self
search.searchBar.backgroundImage = UIImage()
}
viewDidLayoutSuviews:
override func viewDidLayoutSubviews() {
search.searchBar.frame = CGRect(x: 0, y: 50, width: search.isActive ? 400 : 50, height: search.isActive ? 100 : 50)
search.searchBar.placeholder = search.isActive ? "Search" : nil
}
searchBarDelegates:
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
search.searchBar.endEditing(true)
searchBar.resignFirstResponder()
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
}

It may help you
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// this line is important
search.searchBar.layoutIfNeeded()
search.searchBar.frame = CGRect(x: 0, y: 50, width: search.isActive ? 400 : 50, height: search.isActive ? 100 : 50)
search.searchBar.placeholder = search.isActive ? "Search" : nil
}
BUT
I preferred custom implementation
private lazy var searchButton: UIButton = {
let button = UIButton()
button.frame = CGRect(x: 10, y: 50, width: 50, height: 50)
button.backgroundColor = .lightGray
button.layer.cornerRadius = 8
button.layer.masksToBounds = true
button.addTarget(self, action: #selector(toggleSearch), for: .touchDown)
return button
}()
#objc
func toggleSearch() {
search.isActive.toggle()
searchButton.isHidden = true
}
func initSearchCont() {
search.hidesNavigationBarDuringPresentation = false
search.searchResultsUpdater = self
search.obscuresBackgroundDuringPresentation = false
search.searchBar.enablesReturnKeyAutomatically = false
search.searchBar.translatesAutoresizingMaskIntoConstraints = false
search.searchBar.returnKeyType = UIReturnKeyType.done
definesPresentationContext = false
self.view.addSubview(search.searchBar)
navigationItem.hidesSearchBarWhenScrolling = false
search.searchBar.scopeButtonTitles = ["All", "Pair", "Odd"]
search.searchBar.delegate = self
search.searchBar.backgroundImage = UIImage()
search.searchBar.frame = CGRect(x: 0, y: 50, width: 0, height: 0)
view.addSubview(searchButton)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let isActive = search.isActive
search.searchBar.frame = CGRect(x: 0, y: 50, width: isActive ? 400 : 0, height: isActive ? 100 : 0)
search.searchBar.alpha = isActive ? 1 : 0
// custom animation
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
search.searchBar.endEditing(true)
searchBar.resignFirstResponder()
searchButton.isHidden = false
}

Dispatch on the main thread and it will work

Related

iOS UIkit custom segmented buttons

I'm looking to create a view with these buttons. There is a background animation when one of the button touched.
Not sure how to do this.
Is custom segmented buttons the way to go?
I went with custom control
import UIKit
protocol MSegmentedControlDelegate:AnyObject {
func segSelectedIndexChange(to index:Int)
}
class MSegmentedControl: UIControl {
private var buttonTitles:[String]!
private var buttons: [UIButton]!
private var selectorView: UIView!
var textColor:UIColor = .black
var selectorViewColor: UIColor = .white
var selectorTextColor: UIColor = .red
weak var delegate:MSegmentedControlDelegate?
public private(set) var selectedIndex : Int = 0
convenience init(frame:CGRect,buttonTitle:[String]) {
self.init(frame: frame)
self.buttonTitles = buttonTitle
}
override func draw(_ rect: CGRect) {
super.draw(rect)
self.backgroundColor = UIColor.white
updateView()
}
func setButtonTitles(buttonTitles:[String]) {
self.buttonTitles = buttonTitles
self.updateView()
}
func setIndex(index:Int) {
buttons.forEach({ $0.setTitleColor(textColor, for: .normal) })
let button = buttons[index]
selectedIndex = index
button.setTitleColor(selectorTextColor, for: .normal)
let selectorPosition = frame.width/CGFloat(buttonTitles.count) * CGFloat(index)
UIView.animate(withDuration: 0.2) {
self.selectorView.frame.origin.x = selectorPosition
}
}
#objc func buttonAction(sender:UIButton) {
for (buttonIndex, btn) in buttons.enumerated() {
btn.setTitleColor(textColor, for: .normal)
if btn == sender {
let selectorPosition = frame.width/CGFloat(buttonTitles.count) * CGFloat(buttonIndex)
selectedIndex = buttonIndex
delegate?.segSelectedIndexChange(to: selectedIndex)
UIView.animate(withDuration: 0.3) {
self.selectorView.frame.origin.x = selectorPosition
}
btn.setTitleColor(selectorTextColor, for: .normal)
}
}
}
}
//Configuration View
extension MSegmentedControl {
private func updateView() {
createButton()
configSelectorView()
configStackView()
}
private func configStackView() {
let stack = UIStackView(arrangedSubviews: buttons)
stack.axis = .horizontal
stack.alignment = .fill
stack.distribution = .fillEqually
addSubview(stack)
stack.translatesAutoresizingMaskIntoConstraints = false
stack.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
stack.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
stack.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
stack.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
}
private func configSelectorView() {
let selectorWidth = frame.width / CGFloat(self.buttonTitles.count)
selectorView = UIView(frame: CGRect(x: 0, y: 8, width: selectorWidth, height: 32))
selectorView.backgroundColor = selectorViewColor
selectorView.layer.cornerRadius = 16
selectorView.layer.opacity = 0.5
addSubview(selectorView)
}
private func createButton() {
buttons = [UIButton]()
buttons.removeAll()
subviews.forEach({$0.removeFromSuperview()})
for buttonTitle in buttonTitles {
let button = UIButton(type: .system)
button.setTitle(buttonTitle, for: .normal)
button.addTarget(self, action:#selector(MSegmentedControl.buttonAction(sender:)), for: .touchUpInside)
button.setTitleColor(textColor, for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
buttons.append(button)
}
buttons[0].setTitleColor(selectorTextColor, for: .normal)
}
}
Usage:
private let segControl: MSegmentedControl = {
let segControl = MSegmentedControl(
frame: CGRect(x: 0, y: 240, width: 280, height: 50),
buttonTitle: ["Average","Total","Pending"])
segControl.textColor = M.Colors.greyWhite
segControl.selectorTextColor = .white
return segControl
}()
To access index change event:
Implement the delegate on parent view:
addSubview(segControl)
segControl.delegate = self
Delegate:
func segSelectedIndexChange(to index: Int) {
switch index {
case 0: print("Average")
case 1: print("Total")
case 2: print("Pending")
default: break
}
}
Result:

Animate UILabel from a location to another location in the save UIViewController

I have 2 UILabels: usernameLabel located in left top corner and fadingWelcomeMessageLabel where I show a welcome message to user.
I want to implement a simple animation with a custom duration of few seconds which will minimise the size and fade (disolve) my fadingWelcomeMessageLabel from is original position to usernameLabel position.
How will be this possible ?
Below is a screenshot with what I want to do and also my current source code.
Thanks in advance.
import UIKit
class ViewController: UIViewController {
var fadingWelcomeMessageLabel: UILabel!
var usernameLabel : UILabel!
override func viewDidLoad() {
super.viewDidLoad()
setupUsernameLabel()
setupWelcomeMessageLabel()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UILabel.transition(with: usernameLabel, duration: 0.5, options: UIView.AnimationOptions.transitionCrossDissolve, animations: {
}, completion: { (fininshed: Bool) -> () in
self.fadeWelcomeMessage(message: "Hello, Mr User !", color: .orange, finalAlpha: 0.0)
})
}
func setupUsernameLabel() {
usernameLabel = UILabel()
usernameLabel.text = "Mr User"
usernameLabel.numberOfLines = 0
view.addSubview(usernameLabel)
usernameLabel.translatesAutoresizingMaskIntoConstraints = false
usernameLabel.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 10).isActive = true
usernameLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
usernameLabel.widthAnchor.constraint (equalToConstant: 200).isActive = true
usernameLabel.heightAnchor.constraint (equalToConstant: 50).isActive = true
}
func setupWelcomeMessageLabel() {
// Fading Welcome Message Label
fadingWelcomeMessageLabel = UILabel()
fadingWelcomeMessageLabel.text = String()
fadingWelcomeMessageLabel.numberOfLines = 0
view.addSubview(fadingWelcomeMessageLabel)
fadingWelcomeMessageLabel.isHidden = true
fadingWelcomeMessageLabel.translatesAutoresizingMaskIntoConstraints = false
fadingWelcomeMessageLabel.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
fadingWelcomeMessageLabel.topAnchor.constraint (equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
fadingWelcomeMessageLabel.widthAnchor.constraint (equalTo: view.safeAreaLayoutGuide.widthAnchor).isActive = true
fadingWelcomeMessageLabel.heightAnchor.constraint (equalToConstant: 200).isActive = true
}
func fadeWelcomeMessage(message: String, color: UIColor, finalAlpha: CGFloat) {
fadingWelcomeMessageLabel.text = message
fadingWelcomeMessageLabel.alpha = 1.0
fadingWelcomeMessageLabel.isHidden = false
fadingWelcomeMessageLabel.textAlignment = .center
fadingWelcomeMessageLabel.backgroundColor = color
fadingWelcomeMessageLabel.layer.cornerRadius = 5
fadingWelcomeMessageLabel.layer.masksToBounds = true
fadingWelcomeMessageLabel.font = UIFont.systemFont(ofSize: 24.0)
UIView.animate(withDuration: 6.0, animations: { () -> Void in
self.fadingWelcomeMessageLabel.alpha = finalAlpha
})
}
}
CGAffineTransform can help you. You can run my code and change value according to your needs. I have showed only for fadingWelcomeMessageLabel. Hope you will get the idea.
class ViewController: UIViewController {
let fadingWelcomeMessageLabel = UILabel()
let usernameLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .gray
viewSetup()
}
override func viewDidAppear(_ animated: Bool) {
DispatchQueue.main.asyncAfter(deadline: .now()+3, execute: {
UIView.animate(withDuration: 1.5) {
self.fadingWelcomeMessageLabel.transform = CGAffineTransform(translationX: -300, y: -2200).concatenating(CGAffineTransform(scaleX: 0.4, y: 0.2))
}
})
}
func viewSetup() {
fadingWelcomeMessageLabel.frame = CGRect(x: 10, y: view.frame.height/2 - 50, width: view.frame.width - 20, height: 300)
fadingWelcomeMessageLabel.text = "Welcome Mr. David"
fadingWelcomeMessageLabel.textColor = .green
fadingWelcomeMessageLabel.textAlignment = .center
fadingWelcomeMessageLabel.backgroundColor = .red
view.addSubview(fadingWelcomeMessageLabel)
}
}
Solution for my issue was resolved with 1 line of code inside animation closure:
func fadeWelcomeMessage(message: String, color: UIColor, finalAlpha: CGFloat) {
fadingWelcomeMessageLabel.text = message
fadingWelcomeMessageLabel.alpha = 1.0
fadingWelcomeMessageLabel.isHidden = false
fadingWelcomeMessageLabel.textAlignment = .center
fadingWelcomeMessageLabel.backgroundColor = color
fadingWelcomeMessageLabel.layer.cornerRadius = 5
fadingWelcomeMessageLabel.layer.masksToBounds = true
fadingWelcomeMessageLabel.font = UIFont.systemFont(ofSize: 24.0)
UIView.animate(withDuration: 6.0, delay: 4.0, animations: { () -> Void in
self.fadingWelcomeMessageLabel.transform = CGAffineTransform(translationX: -180, y: -80).scaledBy(x: 0.20, y: 0.20)
self.fadingWelcomeMessageLabel.alpha = finalAlpha
}) { finish in
self.setupWelcomeMessageLabel()
}
}
Output:

create a if statement where only one button at a time can have a border

I want my swift code to use a if statement or another sequence to only display a border on one of the buttons if click at a time. So a border can only be seen on one button at a time that button would be the last one pressed. I know I could say layer.border with 0 on each button that should be selected but I want to see if there is a more efficient way to do this.
import UIKit
class ViewController: UIViewController {
var ba = UIButton()
var bb = UIButton()
var bc = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
[ba,bb,bc].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
}
ba.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
bb.frame = CGRect(x: 100, y: 0, width: 100, height: 100)
bc.frame = CGRect(x: 200, y: 0, width: 100, height: 100)
ba.backgroundColor = .blue
bb.backgroundColor = .orange
bc.backgroundColor = .darkGray
ba.addTarget(self, action: #selector(pressa), for: .touchDown)
bb.addTarget(self, action: #selector(pressb), for: .touchDown)
bc.addTarget(self, action: #selector(pressc), for: .touchDown)
}
#objc func pressa(){
ba.layer.borderWidth = 2
}
#objc func pressb(){
bb.layer.borderWidth = 2
}
#objc func pressc(){
bc.layer.borderWidth = 2
}
}
you can add target to all buttons at the forEach and be only one method as #Sh_Khan mention
import UIKit
class ViewController: UIViewController {
var ba = UIButton()
var bb = UIButton()
var bc = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
[ba,bb,bc].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
$0.addTarget(self, action: #selector(pressAll(_:)), for: .touchDown)
}
ba.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
bb.frame = CGRect(x: 100, y: 0, width: 100, height: 100)
bc.frame = CGRect(x: 200, y: 0, width: 100, height: 100)
ba.backgroundColor = .blue
bb.backgroundColor = .orange
bc.backgroundColor = .darkGray
}
#objc func pressAll(_ sender:UIButton) {
[ba,bb,bc].forEach { $0.layer.borderWidth = 0 } // reset all
sender.layer.borderWidth = 2
}
}
See how you have used an array in [ba,bb,bc].forEach { ... } to reduce code duplication? Using arrays is the key. Rather than putting the three buttons in an array inline like that, create a property instead:
var buttons: [UIButton]!
override func viewDidLoad() {
super.viewDidLoad()
buttons = [ba,bb,bc]
buttons.forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
$0.addTarget(self, action: #selector(buttonPressed), for: .touchDown)
}
...
}
I have used the same selector buttonPressed for all three buttons. buttonPressed can accept a parameter of type UIButton, that tells us which button is pressed:
#objc func buttonPressed(_ sender: UIButton) {
buttons.forEach { ba.layer.borderWidth = 0 } // deselect all buttons first...
sender.layer.borderWidth = 2 // select the tapped button
}
If you have more than 3 buttons to manage, I suggest you don't use UIButtons at all. You should use a UICollectionView. (Learn how to use them) This view will handle the selection for you. It also allows scrolling when there's not enough space to show all the buttons. You just need to create a custom UICollectionViewCell and override its isSelected property:
override var isSelected: Bool {
didSet {
if isSelected {
self.layer.borderWidth = 2
} else {
self.layer.borderWidth = 0
}
}
}
It could be 1 method like this
[ba,bb,bc].forEach { $0.addTarget(self, action: #selector(pressAll(_:)), for: .touchDown) }
}
#objc func pressAll(_ sender:UIButton) {
[ba,bb,bc].forEach { $0.layer.borderWidth = 0 } // reset all
sender.layer.borderWidth = 2
}

Target Action for lazy Button does not work

Using Swift 5.1.3, iOS13.3, XCode11.3,
I try to create a simple Button Target Action.
However, the Button is inside its own StackView class and moreover, is a lazy button.
Why is the Target Action not working in my Code ? i.e. callButtonMethod is never called !
Could it be because of the late API responses or is it because a lazy button cannot do target action. I am clueless at the moment.
Thank you for any help on this.
Here is my code:
class CockpitHeaderStackView: UIStackView {
weak var profileBtnDelegate: CallButtonProfileImage?
var profileImageView = UIImageView()
var profileName = "Cockpit".localized
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
if let profile = MyAPI.profile, let person = profile.person {
profileName = "\(person.firstName ?? "") \(person.lastName ?? "")"
}
MyAPI.getPicture(PictureType.avatar) { [weak self] (error, image) in
guard let self = self else { return } // check if self still alive otherwise bail out
DispatchQueue.main.async {
if let image = image {
self.profileImageView.image = image
} else {
self.profileImageView.image = #imageLiteral(resourceName: "profile-placeholder-small")
}
self.profileImageView.contentMode = .scaleAspectFill
self.axis = .horizontal
self.alignment = .bottom
self.spacing = 10.0
self.addArrangedSubview(self.titleLabel)
self.addArrangedSubview(self.button)
}
}
}
lazy var titleLabel: UILabel = {
let labelWidth: CGFloat = UIScreen.main.bounds.width - 16.0 - 10.0 - 36.0 - 16.0 // FullScreenWidth minus (Leading + Spacing + ButtonWidth + Trailing)
let label = UILabel()
label.font = AppConstants.Font.NavBar_TitleFont
label.text = profileName
label.textColor = .white
label.tintColor = .white
label.widthAnchor.constraint(equalToConstant: labelWidth).isActive = true
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
lazy var button: UIButton = {
let buttonWidth: CGFloat = 36.0
let button = UIButton(frame: CGRect(origin: .zero, size: CGSize(width: buttonWidth, height: buttonWidth)))
button.setImage(self.profileImageView.image, for: .normal)
button.addTarget(self, action: #selector(callButtonMethod), for: .touchUpInside)
button.frame = CGRect(x: 0, y: 0, width: 36, height: 36)
button.layer.cornerRadius = button.frame.size.width / 2
button.layer.masksToBounds = false
button.clipsToBounds = true
button.translatesAutoresizingMaskIntoConstraints = false
button.widthAnchor.constraint(equalToConstant: buttonWidth).isActive = true
button.heightAnchor.constraint(equalToConstant: buttonWidth).isActive = true
return button
}()
#objc func callButtonMethod() {
profileBtnDelegate?.callProfileBtnMethod()
}
}
The CockpitHeaderStackView is used to create a custom NavigationBar of my ViewController.
Here the code for the custom NavigationBar with usage of the CockpitHeaderStackView :
protocol CallButtonProfileImage: AnyObject {
func callProfileBtnMethod()
}
class MyViewController: UIViewController {
// ...
lazy var titleStackView: CockpitHeaderStackView = {
let titleStackView = CockpitHeaderStackView(frame: CGRect(origin: .zero, size: CGSize(width: view.bounds.width, height: 88.0)))
titleStackView.translatesAutoresizingMaskIntoConstraints = false
return titleStackView
}()
lazy var cockpitHeaderView: UIView = {
let cockpitHeaderView = UIView(frame: CGRect(origin: .zero, size: CGSize(width: view.bounds.width, height: 88.0)))
cockpitHeaderView.addSubview(titleStackView)
titleStackView.leadingAnchor.constraint(equalTo: cockpitHeaderView.leadingAnchor, constant: 16.0).isActive = true
titleStackView.topAnchor.constraint(equalTo: cockpitHeaderView.topAnchor).isActive = true
titleStackView.trailingAnchor.constraint(equalTo: cockpitHeaderView.trailingAnchor, constant: -16.0).isActive = true
titleStackView.bottomAnchor.constraint(equalTo: cockpitHeaderView.bottomAnchor).isActive = true
return cockpitHeaderView
}()
override func viewDidLoad() {
super.viewDidLoad()
// ...
view.clipsToBounds = true
navigationController?.set_iOS12_lookAndFeel()
navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
navigationItem.largeTitleDisplayMode = .always
}
override func viewWillLayoutSubviews() {
// replace NavBar title by custom cockpitHeaderView
self.title = ""
self.navigationItem.titleView = self.cockpitHeaderView
// position the cockpitHeaderView inside the largeTitleDisplayMode NavBar
self.cockpitHeaderView.translatesAutoresizingMaskIntoConstraints = false
self.cockpitHeaderView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
if let navBarBottomAnchor = self.navigationController?.navigationBar.bottomAnchor {
if UIScreen.main.bounds.height > 568.0 {
self.cockpitHeaderView.topAnchor.constraint(equalTo: navBarBottomAnchor, constant: -48.0).isActive = true
} else {
self.cockpitHeaderView.topAnchor.constraint(equalTo: navBarBottomAnchor, constant: -46.0).isActive = true // iPhone SE space limitation
}
} else {
self.cockpitHeaderView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 89.0).isActive = true
}
}
func callProfileBtnMethod() {
print("right BarButton called here")
}
}
I finally found "a solution": The lazy initialisations seem to be the cause of the error-behaviour.
In fact, when I replace all lazy initialisation and also eliminate the StackView (called CockpitHeaderStackView) and put everything in non-lazy let-constants, then it works !!
Here is the final and relevant code (i.e. I placed everything inside the viewWillAppearmethod):
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let cardsHorizontalController = CardsHorizontalController()
self.view.addSubview(cardsHorizontalController.view)
cardsHorizontalController.view.translatesAutoresizingMaskIntoConstraints = false
cardsHorizontalController.view.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100.0).isActive = true
cardsHorizontalController.view.heightAnchor.constraint(equalToConstant: 279).isActive = true
cardsHorizontalController.view.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width).isActive = true
navigationController?.set_iOS12_lookAndFeel()
navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
navigationItem.largeTitleDisplayMode = .always
PeaxAPI.getPicture(PictureType.avatar) { [weak self] (error, image) in
guard let self = self else { return } // check if self still alive otherwise bail out
DispatchQueue.main.async {
if let image = image {
self.profileImageView.image = image
} else {
self.profileImageView.image = #imageLiteral(resourceName: "profile-placeholder-small")
}
self.profileImageView.contentMode = .scaleAspectFill
if let profile = PeaxAPI.profile, let person = profile.person {
self.profileName = "\(person.firstName ?? "") \(person.lastName ?? "")"
}
let titleStackView = UIStackView(frame: CGRect(origin: .zero, size: CGSize(width: self.view.bounds.width, height: 88.0)))
titleStackView.isUserInteractionEnabled = true
titleStackView.translatesAutoresizingMaskIntoConstraints = false
titleStackView.axis = .horizontal
titleStackView.alignment = .bottom
titleStackView.spacing = 10.0
let labelWidth: CGFloat = UIScreen.main.bounds.width - 16.0 - 10.0 - 36.0 - 16.0 // FullScreenWidth minus (Leading + Spacing + ButtonWidth + Trailing)
let label = UILabel()
label.font = AppConstants.Font.NavBar_TitleFont
label.text = self.profileName
label.textColor = .white
label.tintColor = .white
label.widthAnchor.constraint(equalToConstant: labelWidth).isActive = true
label.translatesAutoresizingMaskIntoConstraints = false
let buttonWidth: CGFloat = 36.0
let button = UIButton(frame: CGRect(origin: .zero, size: CGSize(width: buttonWidth, height: buttonWidth)))
button.setImage(self.profileImageView.image, for: .normal)
button.isUserInteractionEnabled = true
button.addTarget(self, action: #selector(self.callProfileBtnMethod), for: .touchUpInside)
button.frame = CGRect(x: 0, y: 0, width: 36, height: 36)
button.layer.cornerRadius = button.frame.size.width / 2
button.layer.masksToBounds = false
button.clipsToBounds = true
button.translatesAutoresizingMaskIntoConstraints = false
button.widthAnchor.constraint(equalToConstant: buttonWidth).isActive = true
button.heightAnchor.constraint(equalToConstant: buttonWidth).isActive = true
titleStackView.addArrangedSubview(label)
titleStackView.addArrangedSubview(button)
let cockpitHeaderView = UIView(frame: CGRect(origin: .zero, size: CGSize(width: self.view.bounds.width, height: 88.0)))
cockpitHeaderView.isUserInteractionEnabled = true
cockpitHeaderView.addSubview(titleStackView)
titleStackView.leadingAnchor.constraint(equalTo: cockpitHeaderView.leadingAnchor, constant: 16.0).isActive = true
titleStackView.topAnchor.constraint(equalTo: cockpitHeaderView.topAnchor).isActive = true
titleStackView.trailingAnchor.constraint(equalTo: cockpitHeaderView.trailingAnchor, constant: -16.0).isActive = true
titleStackView.bottomAnchor.constraint(equalTo: cockpitHeaderView.bottomAnchor).isActive = true
// replace NavBar title by custom cockpitHeaderView
self.title = ""
self.navigationItem.titleView = cockpitHeaderView
cockpitHeaderView.sizeToFit()
}
}
}
I had to add addTarget after adding the button as a subview and that worked

Is there any easier way to view UIImageView programmatically?

I have 8 buttons and I want to display a picture every time I press the buttons.
What I wonder is, do I need to have 8 functions to display these images?
Or is there any easier ways?
Here is how I've done it, it works as it should, but I do not want to repeat the same things over and over again?
var imageView1:UIImageView!
var imageView2:UIImageView!
var imageView3:UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
showImage1()
showImage2()
showImage3()
tapGestureRecognizerFunc()
}
#objc func button1Tap() {
if self.imageView1.isHidden {
self.imageView1.isHidden = false
}else{
self.imageView1.isHidden = true
}
}
#objc func button2Tap() {
if self.imageView2.isHidden {
self.imageView2.isHidden = false
}else{
self.imageView2.isHidden = true
}
}
#objc func button3Tap() {
if self.imageView3.isHidden {
self.imageView3.isHidden = false
}else{
self.imageView3.isHidden = true
}
}
func showImage1() {
imageView1 = UIImageView(frame: CGRect(x: 271, y: 8, width: 29, height: 29))
imageView1.image = UIImage(named: "Done.png")
imageView1.contentMode = .scaleAspectFit
View1.addSubview(imageView1)
imageView1.isHidden = true
}
func showImage2() {
imageView2 = UIImageView(frame: CGRect(x: 271, y: 8, width: 29, height: 29))
imageView2.image = UIImage(named: "Done.png")
imageView2.contentMode = .scaleAspectFit
View2.addSubview(imageView2)
imageView2.isHidden = true
}
func showImage3() {
imageView2 = UIImageView(frame: CGRect(x: 271, y: 8, width: 29, height: 29))
imageView2.image = UIImage(named: "Done.png")
imageView2.contentMode = .scaleAspectFit
View3.addSubview(imageView2)
imageView2.isHidden = true
}
func tapGestureRecognizerFunc () {
let exercise1Tap = UITapGestureRecognizer(target: self, action: #selector(button1Tap))
exercise1Tap.numberOfTapsRequired = 2
View1.addGestureRecognizer(exercise1Tap)
let exercise2Tap = UITapGestureRecognizer(target: self, action: #selector(button2Tap))
exercise2Tap.numberOfTapsRequired = 2
View2.addGestureRecognizer(exercise2Tap)
let exercise3Tap = UITapGestureRecognizer(target: self, action: #selector(button3Tap))
exercise3Tap.numberOfTapsRequired = 2
View3.addGestureRecognizer(exercise3Tap)
}
yes i'm newbie
Since I don't know, where and how you create the Buttons it is difficult to answer.
The following is just a tip.
You should use an array of UIImageView
Your callback should use the Form buttonAction(sender : UIButton)
You could use a tag for the button to get the number of the corresponding button
For example:
class ViewController: UIViewController {
var imageviews : [UIImageView] = []
override func viewDidLoad() {
super.viewDidLoad()
for i in 1...8 {
let imageview = UIImageView()
view.addSubview(imageview)
imageview.tag = i
imageviews.append(imageview)
let button = UIButton(frame: CGRect(x: 0, y: CGFloat(i)*50.0, width: 100, height: 30))
view.addSubview(button)
button.setTitle("Button \(i)", for: .normal)
button.setTitleColor(.black, for: .normal)
button.tag = i
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
}
}
#objc func buttonAction(sender : UIButton) {
let index = sender.tag
print("Button \(index) pressed")
imageviews[index].isHidden = !imageviews[index].isHidden
}
}