swift tap gesture handler not invoked - swift

I have the following custom view implementation :
import UIKit
class ProfileTableHeaderView: UITableViewHeaderFooterView {
private var statusText : String = ""
private let fullNameLabel: UILabel = {
let view = UILabel()
view.text = "Hipster Pinguin"
view.font = UIFont.systemFont(ofSize: 18, weight: .bold)
view.textColor = .black
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private let avatarImage: UIImageView = {
let view = UIImageView()
view.clipsToBounds = true
view.layer.borderWidth = 3
view.layer.borderColor = UIColor.white.cgColor
view.image = UIImage(named: "avatar")
view.contentMode = .scaleAspectFill
view.layer.cornerRadius = 100/2
view.translatesAutoresizingMaskIntoConstraints = false
// let tapGesture = UITapGestureRecognizer(target : self, action : #selector(avatarImagePressHandler))
// view.isUserInteractionEnabled = true
// view.addGestureRecognizer(tapGesture)
return view
}()
let statusLabel: UILabel = {
let view = UILabel()
view.text = "Waiting for something"
view.font = UIFont.systemFont(ofSize: 14, weight: .regular)
view.textColor = .gray
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let statusTextField: UITextField = {
let view = TextFieldWithPadding()
view.placeholder = "add smth to show as status"
view.layer.cornerRadius = 12
view.layer.borderWidth = 1
view.layer.borderColor = UIColor.black.cgColor
view.backgroundColor = .white
view.font = UIFont.systemFont(ofSize: 15, weight: .regular)
view.textColor = .black
view.backgroundColor = .white.withAlphaComponent(0)
view.addTarget(self, action: #selector(statusTextChanged), for : .editingChanged)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let setStatusButton: UIButton = {
let view = UIButton()
view.setTitle("Show status", for: .normal)
view.setTitleColor(.white, for : .normal)
view.backgroundColor = UIColor(named: "myColor")
view.layer.cornerRadius = 14
view.layer.shadowRadius = 4
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOpacity = 0.7
view.layer.shadowOffset = CGSize(width: 4, height: 4)
view.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
setupViews()
}
#objc func avatarImagePressHandler()
{
print("avatar pressed")
}
#objc func buttonPressed()
{
statusLabel.text = statusText
}
#objc func statusTextChanged(_ textField: UITextField)
{
statusText = textField.text ?? ""
}
required init?(coder: NSCoder) {
fatalError("should not be called")
}
private func setupViews()
{
contentView.addSubview(avatarImage)
contentView.addSubview(fullNameLabel)
contentView.addSubview(statusLabel)
contentView.addSubview(statusTextField)
contentView.addSubview(setStatusButton)
let tapGesture = UITapGestureRecognizer(target : self, action : #selector(avatarImagePressHandler))
avatarImage.isUserInteractionEnabled = true
avatarImage.addGestureRecognizer(tapGesture)
let constraints = [
avatarImage.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16),
avatarImage.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
avatarImage.widthAnchor.constraint(equalToConstant: 100),
avatarImage.heightAnchor.constraint(equalToConstant: 100),
fullNameLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 27),
fullNameLabel.leadingAnchor.constraint(equalTo: avatarImage.trailingAnchor, constant: 16),
fullNameLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
statusLabel.topAnchor.constraint(equalTo: fullNameLabel.bottomAnchor, constant: 10),
statusLabel.leadingAnchor.constraint(equalTo: fullNameLabel.leadingAnchor),
statusLabel.trailingAnchor.constraint(equalTo: fullNameLabel.trailingAnchor),
statusTextField.topAnchor.constraint(equalTo: statusLabel.bottomAnchor, constant: 10),
statusTextField.heightAnchor.constraint(equalToConstant: 40),
statusTextField.leadingAnchor.constraint(equalTo: statusLabel.leadingAnchor),
statusTextField.trailingAnchor.constraint(equalTo: statusLabel.trailingAnchor),
setStatusButton.topAnchor.constraint(equalTo: avatarImage.bottomAnchor, constant: 16),
setStatusButton.leadingAnchor.constraint(equalTo: avatarImage.leadingAnchor),
setStatusButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
setStatusButton.heightAnchor.constraint(equalToConstant: 50)
]
NSLayoutConstraint.activate(constraints)
}
}
If I try to setup a tap gesture recognizer inside lambda I see no print inside the console, but if I configure it inside setupViews everything is fine. Why does it work this way? What am I missing?

Related

Delegate not working when I trying to redirect to another ViewController

I`ve made an birthday reminder project.I have BirthdayVC and in there there are plus buttons when I tap on it - popUpVC pops up.In popUpVC I ned to fill the form with data and this data(name and age) should be visible in BirthdayVC.But it's not visible in BirthdayVC
I have a protocol, set popUpVc.delegate = self.I don't know why it doesn't working
BirthdayVC.swift
protocol BirthdayVCDelegate: AnyObject {
func update(name: String, age: String, time: Date)
}
class BirthdayVC: UIViewController, BirthdayVCDelegate {
private let nameLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 10, weight: .regular)
label.textColor = UIColor.red
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let ageLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 10, weight: .regular)
label.textColor = UIColor.red
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
navigationItem.title = "Birthday"
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addProfile))
[nameLabel, ageLabel].forEach { subview in
view.addSubview(subview)
}
applyConstraints()
}
#objc func addProfile() {
let popUpVc = PopUpVC()
if let sheeet = popUpVc.sheetPresentationController {
sheeet.detents = [.large()]
}
present(popUpVc, animated: true, completion: nil)
popUpVc.delegate = self
}
func update(name: String, age: String, time: Date) {
nameLabel.text? = name.capitalized
ageLabel.text? += age + " years"
}
func applyConstraints() {
NSLayoutConstraint.activate([
nameLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 30),
nameLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
nameLabel.trailingAnchor.constraint(equalTo: view.leadingAnchor, constant: -10),
nameLabel.widthAnchor.constraint(equalToConstant: 200),
ageLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 10),
ageLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
ageLabel.trailingAnchor.constraint(equalTo: view.leadingAnchor, constant: -10),
ageLabel.widthAnchor.constraint(equalToConstant: 200)
])
}
}
popUpVC.swift
import UIKit
import Photos
import PhotosUI
class PopUpVC: UIViewController {
weak var delegate: BirthdayVCDelegate?
//MARK: DONEBUTTON
private let DoneButton: UIButton = {
let button = UIButton()
button.setTitle("Done", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(dismissSheet), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
private let profileImageView: UIImageView = {
let image = UIImageView()
image.image = UIImage(named: "person")
image.translatesAutoresizingMaskIntoConstraints = false
return image
}()
private let changeImageButton: UIButton = {
let button = UIButton()
button.setTitle("Change Image", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(changeImage), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
//MARK: Name label && textfield
private let nameLabel: UILabel = {
let label = UILabel()
label.text = "Name"
label.textColor = UIColor.blue
label.font = UIFont.systemFont(ofSize: 15, weight: .regular)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let nameTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "Name"
textField.font = UIFont.systemFont(ofSize: 15)
textField.borderStyle = .roundedRect
textField.autocorrectionType = .default
textField.keyboardType = .default
textField.returnKeyType = .done
textField.clearButtonMode = .whileEditing
textField.translatesAutoresizingMaskIntoConstraints = false
return textField
}()
//MARK: Date label && textfield
private let dateLabel: UILabel = {
let label = UILabel()
label.text = "Date of birth"
label.font = UIFont.systemFont(ofSize: 15, weight: .regular)
label.textColor = UIColor.blue
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let dateTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "Pick date"
textField.font = UIFont.systemFont(ofSize: 15)
textField.borderStyle = .roundedRect
textField.autocorrectionType = .default
textField.keyboardType = .default
textField.returnKeyType = .done
textField.clearButtonMode = .whileEditing
textField.translatesAutoresizingMaskIntoConstraints = false
return textField
}()
//MARK: Age label && textfield && picker
private let ageLabel: UILabel = {
let label = UILabel()
label.text = "Age"
label.font = UIFont.systemFont(ofSize: 15, weight: .regular)
label.textColor = UIColor.blue
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let agePicker: UIPickerView = {
let picker = UIPickerView()
picker.translatesAutoresizingMaskIntoConstraints = false
return picker
}()
let ageTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "Pick your age"
textField.font = UIFont.systemFont(ofSize: 15)
textField.borderStyle = .roundedRect
textField.autocorrectionType = .default
textField.keyboardType = .default
textField.returnKeyType = .done
textField.clearButtonMode = .whileEditing
textField.translatesAutoresizingMaskIntoConstraints = false
return textField
}()
//MARK: Sex label && textfield && picker
private let genreLabel: UILabel = {
let label = UILabel()
label.text = "Genre"
label.font = UIFont.systemFont(ofSize: 20, weight: .regular)
label.textColor = UIColor.blue
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let genreTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "Pick genre"
textField.font = UIFont.systemFont(ofSize: 15)
textField.borderStyle = .roundedRect
textField.autocorrectionType = .default
textField.keyboardType = .default
textField.returnKeyType = .done
textField.clearButtonMode = .whileEditing
textField.translatesAutoresizingMaskIntoConstraints = false
return textField
}()
private let genrePicker: UIPickerView = {
let picker = UIPickerView()
picker.translatesAutoresizingMaskIntoConstraints = false
return picker
}()
//MARK: Instagram label && textfield
private let instagramLabel: UILabel = {
let label = UILabel()
label.text = "Instagram"
label.font = UIFont.systemFont(ofSize: 15, weight: .regular)
label.textColor = UIColor.blue
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let instagramTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "Pick genre"
textField.font = UIFont.systemFont(ofSize: 15)
textField.borderStyle = .roundedRect
textField.autocorrectionType = .default
textField.keyboardType = .default
textField.returnKeyType = .done
textField.clearButtonMode = .whileEditing
textField.addTarget(self, action: #selector(didRecogniseTapGesture), for: UIControl.Event.editingDidBegin)
textField.translatesAutoresizingMaskIntoConstraints = false
return textField
}()
//MARK: Face ID label && switch
let genre = ["Male", "Female", "Unknown"]
let age = Array(1...100)
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .purple
agePicker.tag = 0
genrePicker.tag = 1
view.addSubview(profileImageView)
view.addSubview(changeImageButton)
view.addSubview(nameLabel)
view.addSubview(nameTextField)
view.addSubview(dateLabel)
view.addSubview(dateTextField)
dateTextField.datePicker(target: self, doneAction: #selector(doneAction), cancelAction: #selector(cancelAction))
view.addSubview(ageLabel)
view.addSubview(ageTextField)
ageTextField.inputView = agePicker
// picker
agePicker.dataSource = self
agePicker.delegate = self
// genre label && textfield
view.addSubview(genreLabel)
view.addSubview(genreTextField)
genreTextField.inputView = genrePicker
//
genrePicker.delegate = self
genrePicker.dataSource = self
// instagram label && textfield
view.addSubview(instagramLabel)
view.addSubview(instagramTextField)
view.addSubview(DoneButton)
applyConstraints()
view.backgroundColor = .white
}
//MARK: dateTextfield methods
#objc func cancelAction() {
self.dateTextField.resignFirstResponder()
}
#objc func doneAction() {
if let datePicker = self.dateTextField.inputView as? UIDatePicker {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let dateString = dateFormatter.string(from: datePicker.date)
self.dateTextField.text = dateString
}
self.dateTextField.resignFirstResponder()
}
//MARK: Instagram textfield
#objc func didRecogniseTapGesture() {
showInstagramAlert()
}
#objc func dismissSheet() {
dismiss(animated: true, completion: nil)
delegate?.update(name: nameTextField.text!, age: ageTextField.text!, time: Date())
}
#objc func changeImage() {
var config = PHPickerConfiguration(photoLibrary: .shared())
config.selectionLimit = 1
config.filter = PHPickerFilter.images
let vc = PHPickerViewController(configuration: config)
vc.delegate = self
present(vc, animated: true)
}
func showInstagramAlert() {
let alertController = UIAlertController(title: "Instagram nikname", message: "Enter your instagram nikname", preferredStyle: .alert)
alertController.addTextField { word in
word.placeholder = "Your nickName"
}
let ok = UIAlertAction(title: "Ok", style: .cancel) { action in
let text = alertController.textFields?.first
self.instagramTextField.text! = (text?.text!)!
}
alertController.addAction(ok)
self.present(alertController, animated: true, completion: nil)
}
private func applyConstraints() {
let DoneButtonConstrains = [
DoneButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 10),
DoneButton.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -10)
]
let personImageConstrains = [
profileImageView.topAnchor.constraint(equalTo: view.topAnchor, constant: 60),
profileImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
// profileImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 80),
// profileImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -80),
profileImageView.widthAnchor.constraint(equalToConstant: 80),
profileImageView.heightAnchor.constraint(equalToConstant: 90)
]
let changeImageButtonConstrains = [
changeImageButton.topAnchor.constraint(equalTo: profileImageView.bottomAnchor, constant: 10),
//changeImageButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -120)
changeImageButton.centerXAnchor.constraint(equalTo: view.centerXAnchor)
]
let nameLabelConstrains = [
nameLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30),
nameLabel.topAnchor.constraint(equalTo: changeImageButton.bottomAnchor, constant: 30),
]
let nameTextFieldConstrains = [
nameTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30),
nameTextField.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 15),
nameTextField.widthAnchor.constraint(equalToConstant: 300)
]
let dateLabelConstrains = [
dateLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30),
dateLabel.topAnchor.constraint(equalTo: nameTextField.bottomAnchor, constant: 15)
]
let dateTextfieldConstrains = [
dateTextField.topAnchor.constraint(equalTo: dateLabel.bottomAnchor, constant: 15),
dateTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30),
dateTextField.widthAnchor.constraint(equalToConstant: 300)
]
let ageLabelConstrains = [
ageLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30),
ageLabel.topAnchor.constraint(equalTo: dateTextField.bottomAnchor, constant: 15)
]
let agePickerConstrains = [
ageTextField.topAnchor.constraint(equalTo: ageLabel.bottomAnchor, constant: 15),
ageTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30),
ageTextField.widthAnchor.constraint(equalToConstant: 300)
]
let genreLabelConstrains = [
genreLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30),
genreLabel.topAnchor.constraint(equalTo: ageTextField.bottomAnchor, constant: 15)
]
let gennreTextFieldConstrains = [
genreTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30),
genreTextField.topAnchor.constraint(equalTo: genreLabel.bottomAnchor, constant: 15),
genreTextField.widthAnchor.constraint(equalToConstant: 300)
]
let instagramLabelConstrains = [
instagramLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30),
instagramLabel.topAnchor.constraint(equalTo: genreTextField.bottomAnchor, constant: 15)
]
let instagramTextFieldConstrains = [
instagramTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30),
instagramTextField.topAnchor.constraint(equalTo: instagramLabel.bottomAnchor, constant: 15),
instagramTextField.widthAnchor.constraint(equalToConstant: 300)
]
NSLayoutConstraint.activate(DoneButtonConstrains)
NSLayoutConstraint.activate(nameLabelConstrains)
NSLayoutConstraint.activate(nameTextFieldConstrains)
NSLayoutConstraint.activate(personImageConstrains)
NSLayoutConstraint.activate(changeImageButtonConstrains)
NSLayoutConstraint.activate(dateLabelConstrains)
NSLayoutConstraint.activate(dateTextfieldConstrains)
NSLayoutConstraint.activate(ageLabelConstrains)
NSLayoutConstraint.activate(agePickerConstrains)
NSLayoutConstraint.activate(genreLabelConstrains)
NSLayoutConstraint.activate(gennreTextFieldConstrains)
NSLayoutConstraint.activate(instagramLabelConstrains)
NSLayoutConstraint.activate(instagramTextFieldConstrains)
}
}
extension PopUpVC: PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
results.forEach { results in
results.itemProvider.loadObject(ofClass: UIImage.self) { [weak self] reading, error in
DispatchQueue.main.sync {
guard let image = reading as? UIImage, error == nil else { return }
self?.profileImageView.image = image
picker.dismiss(animated: true, completion: nil)
}
}
}
}
}
I tried to find what`s wrong, but I couldn't
The issue is not in the delegate, the issue is in assigning the value to the label text.
when you initialize the label here
private let nameLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 10, weight: .regular)
label.textColor = UIColor.red
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
there is no default value for the label text and its value is nil and when you try to assign the value here by adding a question mark to nameLabel.text?, this means if the value is nil, then don't assign the value to the label text so the value is always nil
func update(name: String, age: String, time: Date) {
nameLabel.text? = name.capitalized
ageLabel.text? += age + " years"
}
the solution is to remove the question mark and assign the value to the label.text even if the label.text is nil, like this
func update(name: String, age: String, time: Date) {
nameLabel.text = name.capitalized
ageLabel.text = age + " years"
}
and the result should be this
and also after that take care of the constraints of the labels
I hope this helps

Why don't my selector functions get called when I clicked on my radio buttons in my Xcode Swift project

I have a custom UIView class called SortView with two radio buttons and neither of their respective selector functions are being called when I click on them. I have added an instance of the SortView class to a parent class with view.addView(). Here is my custom class:
class SortView: UIView {
// MARK: - Properties
lazy var driverSortRadioButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(UIImage(named: "Radio Button - Unselected"), for: .normal)
button.setDimensions(height: 25, width: 25)
button.backgroundColor = .clear
button.contentMode = .scaleAspectFill
button.addTarget(self, action: #selector(handleDriverSortRadioButton), for: .touchUpInside)
return button
}()
lazy var pickupTimeSortRadioButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(UIImage(named: "Radio Button - Unselected"), for: .normal)
button.setDimensions(height: 25, width: 25)
button.backgroundColor = .clear
button.contentMode = .scaleAspectFill
button.addTarget(self, action: #selector(handlePickupTimeSortRadioButton), for: .touchUpInside)
return button
}()
private let driverSortTitleLabel: UILabel = {
let label = UILabel()
label.text = "Sort by driver:"
label.textAlignment = .left
label.textColor = .white
label.font = UIFont(name: "AvenirNext-DemiBold", size: 18)
label.backgroundColor = .clear
return label
}()
private let pickupTimeSortTitleLabel: UILabel = {
let label = UILabel()
label.text = "Sort by pickup time:"
label.textAlignment = .left
label.textColor = .white
label.font = UIFont(name: "AvenirNext-DemiBold", size: 18)
label.backgroundColor = .clear
return label
}()
private let driverSortTextField: UITextField = {
let tf = UITextField()
tf.textColor = .black
tf.textAlignment = .center
tf.placeholder = "Louise"
tf.font = UIFont(name: "AvenirNext-DemiBold", size: 15)
tf.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
tf.setWidth(width: 150)
tf.layer.cornerRadius = 5
return tf
}()
private let pickupTimeSortTextField: UITextField = {
let tf = UITextField()
tf.textColor = .black
tf.textAlignment = .center
tf.placeholder = "2:00pm"
tf.font = UIFont(name: "AvenirNext-DemiBold", size: 15)
tf.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
tf.setWidth(width: 150)
tf.layer.cornerRadius = 5
return tf
}()
private enum radioButtonStates {
case driver
case pickupTime
}
private var radioButtonState = radioButtonStates.driver
// MARK: - Lifecycle
override init(frame: CGRect) {
super.init(frame: frame)
configureUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Selectors
#objc func handleDriverSortRadioButton() {
print("DEBUG: driver sort radio button clicked")
if radioButtonState != .driver {
pickupTimeSortRadioButton.setImage(UIImage(named: "Radio Button - Unselected"), for: .normal)
pickupTimeSortTextField.isEnabled = false
pickupTimeSortTextField.text = ""
radioButtonState = .driver
driverSortRadioButton.setImage(UIImage(named: "Radio Button - Selected"), for: .normal)
driverSortTextField.isEnabled = true
driverSortTextField.text = ""
}
}
#objc func handlePickupTimeSortRadioButton() {
print("DEBUG: pickup time sort radio button clicked")
if radioButtonState != .pickupTime {
driverSortRadioButton.setImage(UIImage(named: "Radio Button - Unselected"), for: .normal)
driverSortTextField.isEnabled = false
driverSortTextField.text = ""
radioButtonState = .pickupTime
pickupTimeSortRadioButton.setImage(UIImage(named: "Radio Button - Selected"), for: .normal)
pickupTimeSortTextField.isEnabled = true
pickupTimeSortTextField.text = ""
}
}
// MARK: - Helper Functions
private func configureUI() {
let driverSortStackView = UIStackView(arrangedSubviews: [driverSortRadioButton,
driverSortTitleLabel,
driverSortTextField])
driverSortStackView.axis = .horizontal
driverSortStackView.distribution = .fill
driverSortStackView.spacing = 5
let pickupTimeSortStackView = UIStackView(arrangedSubviews: [pickupTimeSortRadioButton,
pickupTimeSortTitleLabel,
pickupTimeSortTextField])
pickupTimeSortStackView.axis = .horizontal
pickupTimeSortStackView.distribution = .fill
pickupTimeSortStackView.spacing = 5
let stackView = UIStackView(arrangedSubviews:[driverSortStackView,
pickupTimeSortStackView])
stackView.axis = .vertical
stackView.distribution = .fill
stackView.spacing = 10
self.addSubview(stackView)
stackView.centerX(inView: self)
stackView.centerY(inView: self)
}
}
The functions which are not being called are handleDriverSortRadioButton and handlePickupTimeSortRadioButton. Here is instantiation:
private lazy var sortView = SortView()
and here is where I use it in my parent UIViewController class:
view.addSubview(sortView)
sortView.anchor(top:tableView.bottomAnchor,
left: view.leftAnchor,
right: view.rightAnchor,
paddingTop: 40,
paddingLeft: 32,
paddingRight: 32)
I'm going to guess that the problem is that a containing view (perhaps one of the stack views) has zero size. The result would be that its subviews are visible but not tappable.
Here is a debugging utility method you can use to track down this sort of thing:
extension UIView {
#objc func reportSuperviews(filtering:Bool = true) {
var currentSuper : UIView? = self.superview
print("reporting on \(self)\n")
while let ancestor = currentSuper {
let ok = ancestor.bounds.contains(ancestor.convert(self.frame, from: self.superview))
let report = "it is \(ok ? "inside" : "OUTSIDE") \(ancestor)\n"
if !filtering || !ok { print(report) }
currentSuper = ancestor.superview
}
}
}
Wait until your interface is all set up and the buttons are untappable, and then call that on one of the untappable buttons to get a report in the console.
Fixed it by adding with and height to the bottom stack view
sortView.anchor(top:tableView.bottomAnchor,
left: view.leftAnchor,
right: view.rightAnchor,
paddingTop: 40,
paddingLeft: 32,
paddingRight: 32,
width: view.frame.width,
height: 100)

Cell not displaying elements when being reused on a stack that adds a LinkPresentation view

I had this custom view who worked like a charm before i introduce a LinkView for a Metadata
After i introduce a LinkView, since it was inside a stackView i had to remove linkView from superview when preparing for reusable (not sure why tried to redraw layout, but seems this not work with LinkView) the problems shows up when scrolling down elements, seems the data get lost at certain point, curious thing is that it only happens with the reusable element that contains the linkView item, is there any reason for this ? How can i fix it ?
Here is the code i use for the cell
final class TimeLineTableViewCell: UITableViewCell {
var cornerRadius: CGFloat = 6
var shadowOffsetWidth = 0
var shadowOffsetHeight = 3
var shadowColor: UIColor = .gray
var shadowOpacity: Float = 0.3
lazy var containerView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
view.addSubview(stackViewContainer)
let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
view.layer.cornerRadius = cornerRadius
view.clipsToBounds = true
view.layer.masksToBounds = false
view.layer.shadowColor = shadowColor.cgColor
view.layer.shadowOffset = CGSize(width: shadowOffsetWidth, height: shadowOffsetHeight);
view.layer.shadowOpacity = shadowOpacity
view.layer.shadowPath = shadowPath.cgPath
return view
}()
lazy var stackViewContainer: UIStackView = {
let stack = UIStackView()
stack.axis = .horizontal
stack.alignment = .center
stack.translatesAutoresizingMaskIntoConstraints = false
stack.distribution = .fill
stack.spacing = 10.0
stack.addArrangedSubview(profileImage)
stack.addArrangedSubview(stackViewDataHolder)
return stack
}()
lazy var profileImage: UIImageView = {
let image = UIImage()
let imageView = UIImageView(image: image)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
return imageView
}()
lazy var userName: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
return label
}()
lazy var tweetInfo: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
return label
}()
lazy var tweetText: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
return label
}()
lazy var linkView: LPLinkView = {
let viewer = LPLinkView(frame: CGRect(origin: .zero, size: .init(width: 200, height: 20)))
viewer.translatesAutoresizingMaskIntoConstraints = false
return viewer
}()
lazy var stackViewDataHolder: UIStackView = {
let stack = UIStackView()
stack.axis = .vertical
stack.translatesAutoresizingMaskIntoConstraints = false
stack.distribution = .fillProportionally
stack.addArrangedSubview(userName)
stack.addArrangedSubview(tweetInfo)
stack.addArrangedSubview(tweetText)
return stack
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
override func prepareForReuse() {
linkView.removeFromSuperview()
}
func configure(viewModel: ProfileTweetViewModel) {
tweetInfo.configure(model: viewModel.tweetInfo)
userName.configure(model: viewModel.name)
tweetText.configure(model: viewModel.tweet)
if let metadata = viewModel.linkData {
linkView = LPLinkView(metadata: metadata)
stackViewDataHolder.addArrangedSubview(linkView)
//Tried almost all layoyt options but seems a previous view can't be updated since frame is wrong
}
if let url = viewModel.profilePic {
profileImage.downloadImage(from: url)
}
}
}
private extension TimeLineTableViewCell {
struct Metrics {
static let lateralPadding: CGFloat = 8
}
func constraints() {
NSLayoutConstraint.activate([
stackViewContainer.topAnchor.constraint(equalTo: containerView.topAnchor, constant: Metrics.lateralPadding),
stackViewContainer.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -Metrics.lateralPadding),
stackViewContainer.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: Metrics.lateralPadding),
stackViewContainer.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -Metrics.lateralPadding),
profileImage.heightAnchor.constraint(equalTo: profileImage.widthAnchor, multiplier: 1.0),
profileImage.widthAnchor.constraint(equalToConstant: 50.0),
])
}
func commonInit() {
addSubview(containerView)
backgroundColor = .clear
NSLayoutConstraint.activate([
containerView.topAnchor.constraint(equalTo: topAnchor),
containerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 4),
containerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -4),
containerView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -4),
])
constraints()
}
}
Thank you for your time.
The issue was related to .fillProportionally in stackView
since the linkView sometimes renders with 0 height, i just had to use .fill property in stackView in order to show it fully

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

Trying to make sense of UILabel behavior.

I am trying to replicate Apple's calculator UI layout.
Here is a gif of what I have so far.
The problems that I am encountering mostly have to do with the UILables.
As seen in the gif above, I am experiencing the following problems:
On device rotation, the labels "L1" and "L2" pop, instead of transitioning smoothly.
The labels on the brown colored buttons disappear when transitioning back to portrait.
For the labels "L1" and "L2" I have tried experimenting with the content mode and constraints, however, I still get clunky transitions.
As for the disappearing labels, instead of hiding/unhiding the stack view to make the layout appear and disappear via it's is hidden property, I instead tried using constraints on the stack view to handle the transition, however, the results remain the same.
I have also looked online and tried some suggestions, however, most answers were outdated or simply did not work.
The code is very straight forward, it primarily consists of setting up the views and its constraints.
extension UIStackView {
convenience init(axis: UILayoutConstraintAxis, distribution: UIStackViewDistribution = .fill) {
self.init()
self.axis = axis
self.distribution = distribution
self.translatesAutoresizingMaskIntoConstraints = false
}
}
class Example: UIView {
let mainStackView = UIStackView(axis: .vertical, distribution: .fill)
let subStackView = UIStackView(axis: .horizontal, distribution: .fillProportionally)
let portraitStackView = UIStackView(axis: .vertical, distribution: .fillEqually)
let landscapeStackView = UIStackView(axis: .vertical, distribution: .fillEqually)
var containerView: UIView = {
$0.backgroundColor = .darkGray
$0.translatesAutoresizingMaskIntoConstraints = false
return $0
}(UIView(frame: .zero))
let mainView: UIView = {
$0.backgroundColor = .blue
$0.translatesAutoresizingMaskIntoConstraints = false
return $0
}(UIView(frame: .zero))
let labelView: UIView = {
$0.backgroundColor = .red
$0.translatesAutoresizingMaskIntoConstraints = false
return $0
}(UIView(frame: .zero))
var labelOne: UILabel!
var labelTwo: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .red
autoresizingMask = [.flexibleWidth, .flexibleHeight]
labelOne = createLabel(text: "L1")
labelOne.translatesAutoresizingMaskIntoConstraints = false
labelOne.backgroundColor = .darkGray
labelTwo = createLabel(text: "L2")
labelTwo.translatesAutoresizingMaskIntoConstraints = false
labelTwo.backgroundColor = .black
landscapeStackView.isHidden = true
mainView.addSubview(labelView)
labelView.addSubview(labelOne)
labelView.addSubview(labelTwo)
addSubview(mainStackView)
mainStackView.addArrangedSubview(mainView)
setButtonStackView()
setStackViewConstriants()
setDisplayViewConstriants()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setStackViewConstriants() {
mainStackView.translatesAutoresizingMaskIntoConstraints = false
mainStackView.topAnchor.constraint(equalTo: topAnchor).isActive = true
mainStackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
mainStackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
mainStackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
}
func setDisplayViewConstriants() {
mainView.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 288/667).isActive = true
labelView.heightAnchor.constraint(equalTo: mainView.heightAnchor, multiplier: 128/288).isActive = true
labelView.centerYAnchor.constraint(equalTo: mainView.centerYAnchor).isActive = true
labelView.leadingAnchor.constraint(equalTo: mainView.leadingAnchor, constant: 24).isActive = true
labelView.trailingAnchor.constraint(equalTo: mainView.trailingAnchor, constant: -24).isActive = true
labelOne.heightAnchor.constraint(equalTo: labelTwo.heightAnchor, multiplier: 88/32).isActive = true
labelOne.trailingAnchor.constraint(equalTo: labelView.trailingAnchor).isActive = true
labelOne.leadingAnchor.constraint(equalTo: labelView.leadingAnchor).isActive = true
labelOne.topAnchor.constraint(equalTo: labelView.topAnchor).isActive = true
labelTwo.topAnchor.constraint(equalTo: labelOne.bottomAnchor).isActive = true
labelTwo.trailingAnchor.constraint(equalTo: labelView.trailingAnchor).isActive = true
labelTwo.leadingAnchor.constraint(equalTo: labelOne.leadingAnchor).isActive = true
labelTwo.bottomAnchor.constraint(equalTo: labelView.bottomAnchor).isActive = true
}
func createLabel(text: String) -> UILabel {
let label = UILabel(frame: .zero)
label.text = text
label.font = UIFont.init(name: "Arial-BoldMT", size: 60)
label.textColor = .white
label.textAlignment = .right
label.contentMode = .right
label.minimumScaleFactor = 0.1
label.adjustsFontSizeToFitWidth = true
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
return label
}
func createButton(text: String) -> UIButton {
let button = UIButton(type: .custom)
button.setTitle(text, for: .normal)
button.setTitleColor(.white, for: .normal)
button.layer.borderColor = UIColor.white.cgColor
button.layer.borderWidth = 1
button.titleLabel?.font = UIFont.init(name: "Arial-BoldMT", size: 60)
button.titleLabel?.minimumScaleFactor = 0.1
button.titleLabel?.adjustsFontSizeToFitWidth = true
button.titleLabel?.translatesAutoresizingMaskIntoConstraints = false
button.titleLabel?.leadingAnchor.constraint(equalTo: button.leadingAnchor).isActive = true
button.titleLabel?.trailingAnchor.constraint(equalTo: button.trailingAnchor).isActive = true
button.titleLabel?.topAnchor.constraint(equalTo: button.topAnchor).isActive = true
button.titleLabel?.bottomAnchor.constraint(equalTo: button.bottomAnchor).isActive = true
button.titleLabel?.textAlignment = .center
button.titleLabel?.contentMode = .scaleAspectFill
button.titleLabel?.numberOfLines = 0
return button
}
func setButtonStackView() {
for _ in 1...5 {
let stackView = UIStackView(axis: .horizontal, distribution: .fillEqually)
for _ in 1...4 {
let button = createButton(text: "0")
button.backgroundColor = .brown
stackView.addArrangedSubview(button)
}
landscapeStackView.addArrangedSubview(stackView)
}
for _ in 1...5 {
let stackView = UIStackView(axis: .horizontal, distribution: .fillEqually)
for _ in 1...4 {
let button = createButton(text: "0")
button.backgroundColor = .purple
stackView.addArrangedSubview(button)
}
portraitStackView.addArrangedSubview(stackView)
}
subStackView.addArrangedSubview(landscapeStackView)
subStackView.addArrangedSubview(portraitStackView)
mainStackView.addArrangedSubview(subStackView)
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
if UIDevice.current.orientation.isLandscape && landscapeStackView.isHidden == true {
self.landscapeStackView.isHidden = false
}
if UIDevice.current.orientation.isPortrait && landscapeStackView.isHidden == false {
self.landscapeStackView.isHidden = true
}
self.layoutIfNeeded()
}
}
Overview:
Do things incrementally with separate components / view controllers (easier to debug)
The below solution is only for labels L1 and L2.
For the calculator buttons, it would be best to use a UICollectionViewController. (I haven't implemented it, add as a child view controller)
Code:
private func setupLabels() {
view.backgroundColor = .red
let stackView = UIStackView()
stackView.axis = .vertical
stackView.alignment = .fill
stackView.distribution = .fill
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
stackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20).isActive = true
let label1 = UILabel()
label1.text = "L1"
label1.textColor = .white
label1.backgroundColor = .darkGray
label1.textAlignment = .right
label1.font = UIFont.preferredFont(forTextStyle: .title1)
let label2 = UILabel()
label2.text = "L2"
label2.textColor = .white
label2.backgroundColor = .black
label2.textAlignment = .right
label2.font = UIFont.preferredFont(forTextStyle: .caption1)
stackView.addArrangedSubview(label1)
stackView.addArrangedSubview(label2)
}