UIKit-Swift custom UIButton does not trigger action on tap - ios

I have defined a "RadioButton like" UIButton. To achieve this I have added a subview inside a UIButton that I change its color on .touchUpInside event.
The problem is that actions is not being triggered.
Here is the code of my RadioButton:
final class RadioButton: UIButton {
let stateView = UIView()
var isActive: Bool = false {
didSet {
if isActive == true {
stateView.backgroundColor = #colorLiteral(red: 0, green: 0.5839999914, blue: 0.5289999843, alpha: 1)
} else {
stateView.backgroundColor = .clear
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
convenience init() {
self.init(frame: .zero)
configure()
}
private func configureColorSubview() {
stateView.backgroundColor = .white
stateView.isUserInteractionEnabled = false
stateView.layer.cornerRadius = 10
stateView.translatesAutoresizingMaskIntoConstraints = false
stateView.widthAnchor.constraint(equalToConstant: 20).isActive = true
stateView.heightAnchor.constraint(equalToConstant: 20).isActive = true
stateView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
stateView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
}
private func configure() {
isUserInteractionEnabled = true
addSubview(stateView)
configureColorSubview()
stateView.layer.zPosition = -1
sendSubviewToBack(stateView)
layer.cornerRadius = 20
layer.borderWidth = 2
layer.borderColor = UIColor.darkGray.cgColor
translatesAutoresizingMaskIntoConstraints = false
widthAnchor.constraint(equalToConstant: 40).isActive = true
heightAnchor.constraint(equalToConstant: 40).isActive = true
}
}
This is the code of the view where a RadioButton is wrapped with a label:
final class RadioButtonLabelView: UILabel {
let radioBtn: RadioButton = {
let r = RadioButton()
return r
}()
let label: UILabel = {
let label = UILabel()
let text = "Text"
let attributedText = NSMutableAttributedString(string: text, attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 22, weight: .semibold), NSAttributedString.Key.foregroundColor: UIColor.black])
label.attributedText = attributedText
return label
}()
let stack: UIStackView = {
let stack = UIStackView()
stack.axis = .horizontal
stack.distribution = .fill
stack.spacing = 20
return stack
}()
override init(frame: CGRect) {
super.init(frame: frame)
}
convenience init(text: String) {
self.init(frame: .zero)
setupView(text: text)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupView(text: String) {
addSubview(stack)
stack.addArrangedSubview(radioBtn)
stack.addArrangedSubview(label)
label.text = text
stack.anchor(topAnchor: topAnchor, trailingAnchor: trailingAnchor, bottomAnchor: bottomAnchor, leadingAnchor: leadingAnchor)
}
}
And here is the final modal view code:
final class SelectHourModalView: UIViewController {
lazy var buttonToOptionsConstraint: NSLayoutConstraint = button.topAnchor.constraint(greaterThanOrEqualTo: radioButtons.bottomAnchor, constant: 50)
lazy var buttonToPicker: NSLayoutConstraint = button.topAnchor.constraint(greaterThanOrEqualTo: datePicker.bottomAnchor, constant: 50)
var isShowPicker: Bool = false {
didSet {
if isShowPicker {
showPicker()
} else {
hidePicker()
}
}
}
let radioButtons: RadioButtonOptionsView = {
let rb = RadioButtonOptionsView()
return rb
}()
let datePicker: UIDatePicker = {
let date = UIDatePicker()
date.datePickerMode = .dateAndTime
date.setValue(UIColor.black, forKeyPath: "textColor")
return date
}()
let button: IoTaxiBtn = {
let button = IoTaxiBtn(text: "Seleccionar", color: #colorLiteral(red: 0, green: 0.5839999914, blue: 0.5289999843, alpha: 1), insets: UIEdgeInsets(top: 10, left: 0, bottom: 8, right: 0), font: UIFont.systemFont(ofSize: 25, weight: .bold), corner: 3)
button.shadow(color: UIColor.gray.cgColor, opacity: 1, offset: CGSize(width: .zero, height: 2), radius: 5)
button.isUserInteractionEnabled = false
return button
}()
private lazy var rbCollection: [RadioButtonLabelView] = {
let rbc = [radioButtons.btnLabelNow, radioButtons.btnLabel1, radioButtons.btnLabel20, radioButtons.btnLabel30, radioButtons.btnLabelCustom]
return rbc
}()
override func viewDidLoad() {
super.viewDidLoad()
setupFront()
setActions()
initialViewConfiguration()
}
private func setupFront() {
view.backgroundColor = .white
[radioButtons, datePicker, button].forEach {
view.addSubview($0)
}
radioButtons.anchor(topAnchor: view.topAnchor, trailingAnchor: view.trailingAnchor, bottomAnchor: nil, leadingAnchor: view.leadingAnchor, padding: .init(top: 15, left: 20, bottom: .zero, right: 20))
datePicker.anchor(topAnchor: radioButtons.bottomAnchor, trailingAnchor: view.trailingAnchor, bottomAnchor: nil, leadingAnchor: view.leadingAnchor, padding: .init(top: 20, left: 20, bottom: .zero, right: 20), size: .init(width: .zero, height: 190))
button.anchor(topAnchor: nil, trailingAnchor: view.trailingAnchor, bottomAnchor: nil, leadingAnchor: view.leadingAnchor, padding: .init(top: .zero, left: 20, bottom: .zero, right: 20))
buttonToPicker.isActive = true
buttonToOptionsConstraint.isActive = false
}
private func setActions() {
rbCollection.forEach {
$0.radioBtn.addTarget(self, action: #selector(setHour), for: .touchUpInside)
}
print(radioButtons.btnLabelCustom.radioBtn.actions(forTarget: self, forControlEvent: .touchUpInside) ?? ["Nada"])
}
#objc private func setHour(_ sender: RadioButton) {
print("tap")
rbCollection.forEach {
$0.radioBtn.isActive = false
}
sender.isActive.toggle()
}
}
//MARK: - Constraints management
//TODO: Animations
private extension SelectHourModalView {
func initialViewConfiguration() {
radioButtons.btnLabelCustom.radioBtn.isActive = true
isShowPicker = true
}
func showPicker() {
buttonToOptionsConstraint.isActive = false
buttonToPicker.isActive = true
datePicker.isHidden = false
}
func hidePicker() {
buttonToPicker.isActive = true
buttonToOptionsConstraint.isActive = true
datePicker.isHidden = true
}
}
final class RadioButtonOptionsView: UIView {
let btnLabelNow: RadioButtonLabelView = {
let btnLabel = RadioButtonLabelView(text: "Recógeme ahora")
return btnLabel
}()
let btnLabel20: RadioButtonLabelView = {
let btnLabel = RadioButtonLabelView(text: "Recógeme en 20 minutos")
return btnLabel
}()
let btnLabel30: RadioButtonLabelView = {
let btnLabel = RadioButtonLabelView(text: "Recógeme en 30 minutos")
return btnLabel
}()
let btnLabel1: RadioButtonLabelView = {
let btnLabel = RadioButtonLabelView(text: "Recógeme en una hora")
return btnLabel
}()
let btnLabelCustom: RadioButtonLabelView = {
let btnLabel = RadioButtonLabelView(text: "Hora personalizada")
return btnLabel
}()
let stackContainer: UIStackView = {
let stack = UIStackView()
stack.axis = .vertical
stack.distribution = .fill
stack.spacing = 8
return stack
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupView() {
addSubview(stackContainer)
[btnLabelNow, btnLabel20, btnLabel30, btnLabel1, btnLabelCustom].forEach {
stackContainer.addArrangedSubview($0)
}
stackContainer.anchor(topAnchor: topAnchor, trailingAnchor: trailingAnchor, bottomAnchor: bottomAnchor, leadingAnchor: leadingAnchor)
}
}
This is the UI debugger:
What is the problem? The stack view has the same height as the RadioButton and the stateView inside RadioButton has -1 zIndex and is no user interactable.

Inherit RadioButtonLabelView with UIView instead of UILabel will resolve your issue
final class RadioButtonLabelView: UIView {
//....
}

Related

How do you install a tableHeaderView on a UITableView?

After a decade, I suspected no one has actually asked this question directly. There are many questions asking how to fix a tableHeaderView layout problem caused by rotation for example. But the real question is, how did Apple intend for this to work?
Auto-layout does not seem to play ball with tableHeaderView, as you can see in this almost 9 year old post
Is it possible to use AutoLayout with UITableView's tableHeaderView?
I have been doing iOS development daily, since 2011 and I have never come across API so poorly documented.
Given that auto-layout is such a pickle to work with when installing a tableHeaderView, I decided last week to use the old school method of autoresizing masks. It has been 4 full days and it still isn't working for me. This is quite humbling and I wanted to reach out to you guys, to ask this simple question.
How do you install a tableHeaderView, properly, using autoresizing masks (no auto-layout) ?
My failed attempt
final class EventDetailTableHeaderView: UIView {
private let titleContainer: TitleContainerView
private let subtitleContainer: SubtitleContainerView
init(_ width: CGFloat, event: CloudEvent) {
let size = CGSize(width: width, height: 0)
let frame = CGRect(origin: .zero, size: size)
titleContainer = TitleContainerView(frame: frame, text: event.title)
subtitleContainer = SubtitleContainerView(frame: frame, text: event.displayString)
super.init(frame: frame)
backgroundColor = StyleKit.wDOWhite
autoresizingMask = [.flexibleWidth]
setupSubviews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupSubviews() {
setupTitleContiner()
setupSubtitleContainer()
}
private func setupTitleContiner() {
addSubview(titleContainer)
titleContainer.autoresizingMask = [.flexibleWidth]
titleContainer.backgroundColor = StyleKit.wDOWhite
}
private func setupSubtitleContainer() {
addSubview(subtitleContainer)
subtitleContainer.autoresizingMask = [.flexibleWidth]
subtitleContainer.backgroundColor = StyleKit.wDOBlue
}
override func layoutSubviews() {
super.layoutSubviews()
positionSubtitleContainer()
frame = CGRect(
origin: .zero,
size: calculateSize()
)
}
private func positionSubtitleContainer() {
subtitleContainer.frame.origin.y = titleContainer.frame.height
}
private func calculateSize() -> CGSize {
CGSize(
width: frame.width,
height: calculateHeightOfSubviews()
)
}
private func calculateHeightOfSubviews() -> CGFloat {
let titleContainerHeight = titleContainer.frame.height
let subtitleContainerHeight = subtitleContainer.frame.height
return titleContainerHeight + subtitleContainerHeight
}
}
final class TitleContainerView: UIView {
private static let font = FontManagement.fontWithStyle(.heavy, withSize: 32.0)
private let label: UILabel = {
let label = UILabel()
label.autoresizingMask = [.flexibleWidth]
label.numberOfLines = 0
label.backgroundColor = StyleKit.wDOWhite
label.font = TitleContainerView.font
label.textColor = StyleKit.wDOBlue
return label
}()
convenience init(frame: CGRect, text: String) {
let font = TitleContainerView.font
let labelFrame = TitleContainerView.establishLabelFrame(frame, text, font)
var frame = frame
frame.size.height = TitleContainerView.establishHeight(labelFrame)
self.init(frame: frame)
label.text = text
label.frame = labelFrame
}
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(label)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private static let insets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
override func layoutSubviews() {
super.layoutSubviews()
let font = label.font!
let text = label.text ?? ""
label.frame = Self.establishLabelFrame(frame, text, font)
frame.size.height = Self.establishHeight(label.frame)
}
private static func establishLabelFrame(_ frame: CGRect, _ text: String, _ font: UIFont) -> CGRect {
let size = establishLabelSize(frame, text, font)
let origin = establishLabelOrigin(frame, size)
return CGRect(origin: origin, size: size)
}
private static func establishLabelSize(_ frame: CGRect, _ text: String, _ font: UIFont) -> CGSize {
let width = frame.width - TitleContainerView.insets.left - TitleContainerView.insets.right
let height = text.height(withConstrainedWidth: width, font: font)
return CGSize(
width: width,
height: height
)
}
private static func establishLabelOrigin(_ frame: CGRect, _ size: CGSize) -> CGPoint {
CGPoint(
x: (frame.width - size.width) / 2.0,
y: (frame.height - size.height) / 2.0
)
}
private static func establishHeight(_ labelFrame: CGRect) -> CGFloat {
labelFrame.size.height + TitleContainerView.insets.top + TitleContainerView.insets.bottom
}
}
extension String {
func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
return ceil(boundingBox.height)
}
}
override func viewDidLoad() {
super.viewDidLoad()
tableView = EventDetailTableView(frame: .zero, style: .plain)
tableView?.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView!)
let width = view.bounds.width
let tableHeaderView = EventDetailTableHeaderView(width, event: event)
tableHeaderView.layoutIfNeeded()
tableView?.tableHeaderView = tableHeaderView
NSLayoutConstraint.activate([
view.safeAreaLayoutGuide.topAnchor.constraint(equalTo: tableView!.topAnchor),
view.safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: tableView!.trailingAnchor),
view.safeAreaLayoutGuide.leadingAnchor.constraint(equalTo: tableView!.leadingAnchor),
view.bottomAnchor.constraint(equalTo: tableView!.bottomAnchor)
])
}
While I agree it seems like there would be a more straight-forward way of implementing an auto-height-sizing tableHeaderView, a common approach is to use auto-layout and an extension like this:
extension UITableView {
func sizeHeaderToFit() {
guard let headerView = tableHeaderView else { return }
let newHeight = headerView.systemLayoutSizeFitting(CGSize(width: frame.width, height: .greatestFiniteMagnitude), withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow)
var frame = headerView.frame
// avoids infinite loop!
if newHeight.height != frame.height {
frame.size.height = newHeight.height
headerView.frame = frame
tableHeaderView = headerView
}
}
}
We call that from within viewDidLayoutSubviews():
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.sizeHeaderToFit()
}
Here is a complete example, which should come pretty close to your layout:
class TestViewController: UIViewController {
let tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: g.topAnchor),
tableView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: g.bottomAnchor),
])
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.dataSource = self
tableView.delegate = self
let hView = EventDetailTableHeaderView(titleText: "Street Dance Championships", subTitleText: "4 June 2019 | 8:30 AM to 5:30 PM | Sports Wales National Centre | Cardiff")
tableView.tableHeaderView = hView
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.sizeHeaderToFit()
}
}
extension TestViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 30
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
c.textLabel?.text = "\(indexPath)"
return c
}
}
extension UITableView {
func sizeHeaderToFit() {
guard let headerView = tableHeaderView else { return }
let newHeight = headerView.systemLayoutSizeFitting(CGSize(width: frame.width, height: .greatestFiniteMagnitude), withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow)
var frame = headerView.frame
// avoids infinite loop!
if newHeight.height != frame.height {
frame.size.height = newHeight.height
headerView.frame = frame
tableHeaderView = headerView
}
}
}
class TitleContainerView: UIView {
private static let font: UIFont = .systemFont(ofSize: 32, weight: .heavy)
let label: UILabel = {
let v = UILabel()
v.numberOfLines = 0
v.textColor = UIColor(red: 0.044, green: 0.371, blue: 0.655, alpha: 1.0)
v.font = TitleContainerView.font
return v
}()
convenience init(text: String) {
self.init(frame: .zero)
label.text = text
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
backgroundColor = UIColor(red: 0.93, green: 0.94, blue: 0.95, alpha: 1.0)
label.translatesAutoresizingMaskIntoConstraints = false
addSubview(label)
NSLayoutConstraint.activate([
label.topAnchor.constraint(equalTo: topAnchor, constant: 8.0),
label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8.0),
label.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8.0),
label.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8.0),
])
}
}
class SubtitleContainerView: UIView {
private static let font: UIFont = .systemFont(ofSize: 20, weight: .bold)
let label: UILabel = {
let v = UILabel()
v.numberOfLines = 0
v.textColor = .white
v.font = SubtitleContainerView.font
return v
}()
convenience init(text: String) {
self.init(frame: .zero)
label.text = text
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
backgroundColor = UIColor(red: 0.044, green: 0.371, blue: 0.655, alpha: 1.0)
label.translatesAutoresizingMaskIntoConstraints = false
addSubview(label)
NSLayoutConstraint.activate([
label.topAnchor.constraint(equalTo: topAnchor, constant: 8.0),
label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8.0),
label.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8.0),
label.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8.0),
])
}
}
class EventDetailTableHeaderView: UIView {
var titleView: TitleContainerView!
var subTitleView: SubtitleContainerView!
convenience init(titleText: String, subTitleText: String) {
self.init(frame: .zero)
titleView = TitleContainerView(text: titleText)
subTitleView = SubtitleContainerView(text: subTitleText)
commonInit()
}
func commonInit() -> Void {
titleView.translatesAutoresizingMaskIntoConstraints = false
subTitleView.translatesAutoresizingMaskIntoConstraints = false
addSubview(titleView)
addSubview(subTitleView)
// this avoids auto-layout complaints
let titleViewTrailingConstraint = titleView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0)
titleViewTrailingConstraint.priority = UILayoutPriority(rawValue: 999)
let subTitleViewBottomConstraint = subTitleView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0.0)
subTitleViewBottomConstraint.priority = UILayoutPriority(rawValue: 999)
NSLayoutConstraint.activate([
titleView.topAnchor.constraint(equalTo: topAnchor, constant: 0.0),
titleView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
titleViewTrailingConstraint,
subTitleView.topAnchor.constraint(equalTo: titleView.bottomAnchor, constant: 0.0),
subTitleView.leadingAnchor.constraint(equalTo: titleView.leadingAnchor, constant: 0.0),
subTitleView.trailingAnchor.constraint(equalTo: titleView.trailingAnchor, constant: 0.0),
subTitleViewBottomConstraint,
])
}
}
and the output looks like this:
Edit -- same output, but using auto-layout only for adding the tableView to the main view.
Class names prefixed with RM_ (for Resizing Mask):
class RM_TestViewController: UIViewController {
let tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: g.topAnchor),
tableView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: g.bottomAnchor),
])
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.dataSource = self
tableView.delegate = self
let hView = RM_EventDetailTableHeaderView(titleText: "Street Dance Championships", subTitleText: "4 June 2019 | 8:30 AM to 5:30 PM | Sports Wales National Centre | Cardiff")
tableView.tableHeaderView = hView
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.rm_sizeHeaderToFit()
}
}
extension RM_TestViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 30
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
c.textLabel?.text = "\(indexPath)"
return c
}
}
extension UITableView {
func rm_sizeHeaderToFit() {
guard let headerView = tableHeaderView as? RM_EventDetailTableHeaderView else { return }
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
// avoids infinite loop!
if headerView.myHeight != headerView.frame.height {
headerView.frame.size.height = headerView.myHeight
tableHeaderView = headerView
}
}
}
class RM_TitleContainerView: UIView {
private static let font: UIFont = .systemFont(ofSize: 32, weight: .heavy)
let label: UILabel = {
let v = UILabel()
v.numberOfLines = 0
v.textColor = UIColor(red: 0.044, green: 0.371, blue: 0.655, alpha: 1.0)
v.font = RM_TitleContainerView.font
// during dev, so we can see the label frame
//v.backgroundColor = .green
return v
}()
convenience init(text: String) {
self.init(frame: .zero)
label.text = text
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
backgroundColor = UIColor(red: 0.93, green: 0.94, blue: 0.95, alpha: 1.0)
addSubview(label)
label.frame.origin = CGPoint(x: 8, y: 8)
}
override func layoutSubviews() {
super.layoutSubviews()
label.frame.size.width = bounds.width - 16
let sz = label.systemLayoutSizeFitting(CGSize(width: label.frame.width, height: .greatestFiniteMagnitude), withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow)
label.frame.size.height = sz.height
}
var myHeight: CGFloat {
get {
return label.frame.height + 16.0
}
}
}
class RM_SubtitleContainerView: UIView {
private static let font: UIFont = .systemFont(ofSize: 20, weight: .bold)
let label: UILabel = {
let v = UILabel()
v.numberOfLines = 0
v.textColor = .white
v.font = RM_SubtitleContainerView.font
// during dev, so we can see the label frame
//v.backgroundColor = .systemYellow
return v
}()
convenience init(text: String) {
self.init(frame: .zero)
label.text = text
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
backgroundColor = UIColor(red: 0.044, green: 0.371, blue: 0.655, alpha: 1.0)
addSubview(label)
label.frame.origin = CGPoint(x: 8, y: 8)
}
override func layoutSubviews() {
super.layoutSubviews()
label.frame.size.width = bounds.width - 16
let sz = label.systemLayoutSizeFitting(CGSize(width: label.frame.width, height: .greatestFiniteMagnitude), withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow)
label.frame.size.height = sz.height
}
var myHeight: CGFloat {
get {
return label.frame.height + 16.0
}
}
}
class RM_EventDetailTableHeaderView: UIView {
var titleView: RM_TitleContainerView!
var subTitleView: RM_SubtitleContainerView!
convenience init(titleText: String, subTitleText: String) {
self.init(frame: .zero)
titleView = RM_TitleContainerView(text: titleText)
subTitleView = RM_SubtitleContainerView(text: subTitleText)
commonInit()
}
func commonInit() -> Void {
addSubview(titleView)
addSubview(subTitleView)
// initial height doesn't matter
titleView.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: 8)
subTitleView.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: 8)
titleView.autoresizingMask = [.flexibleWidth]
subTitleView.autoresizingMask = [.flexibleWidth]
}
override func layoutSubviews() {
super.layoutSubviews()
// force subviews to update
titleView.setNeedsLayout()
subTitleView.setNeedsLayout()
titleView.layoutIfNeeded()
subTitleView.layoutIfNeeded()
// get subview heights
titleView.frame.size.height = titleView.myHeight
subTitleView.frame.origin.y = titleView.frame.maxY
subTitleView.frame.size.height = subTitleView.myHeight
}
var myHeight: CGFloat {
get {
return subTitleView.frame.maxY
}
}
}

UITextfield isBecomeFirstResponder not working in #IBDesignable of UIView

I am trying to make OTP Pin view and created everything and works well except textfield not moving automatically.
class OTPTextField: UITextField {
var previousTextField: UITextField?
var nextTextFiled: UITextField?
override func deleteBackward() {
text = ""
previousTextField?.becomeFirstResponder()
}
}
#IBDesignable public class OTPView : UIView{
var textFieldArray = [OTPTextField]()
#IBInspectable public var numberOfDots : Int = 4{
didSet{
setUpView()
}
}
#IBInspectable public var bottomLineColor : UIColor = .white{
didSet{
setUpView()
}
}
#IBInspectable public var stackViewSpacing : CGFloat = 10{
didSet{
setUpView()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUpView()
}
public override func layoutSubviews() {
super.layoutSubviews()
}
fileprivate func setUpView(){
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.backgroundColor = .clear
stackView.isUserInteractionEnabled = true
stackView.translatesAutoresizingMaskIntoConstraints = false
// stackView.alignment = .fill
stackView.distribution = .fillEqually
stackView.spacing = CGFloat(stackViewSpacing)
self.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 0),
stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor, constant: 0),
stackView.heightAnchor.constraint(equalTo: self.heightAnchor, multiplier: 1),
stackView.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 1)
])
setTextFields(stackView: stackView)
}
private func setTextFields(stackView : UIStackView) {
for i in 0..<numberOfDots {
let field = OTPTextField()
textFieldArray.append(field)
stackView.addArrangedSubview(field)
field.delegate = self
field.backgroundColor = .clear
field.textAlignment = .center
field.keyboardType = .numberPad
field.addBottomBorderView(lineColor: bottomLineColor)
i != 0 ? (field.previousTextField = textFieldArray[i-1]) : ()
i != 0 ? (textFieldArray[i-1].nextTextFiled = textFieldArray[i]) : ()
}
}
}
extension OTPView: UITextFieldDelegate {
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let field = textField as? OTPTextField else {
return true
}
if !string.isEmpty {
field.text = string
field.resignFirstResponder()
field.nextTextFiled?.becomeFirstResponder()
return true
}
return true
}
}
extension UITextField {
func addBottomBorder(){
let bottomLine = CALayer()
bottomLine.frame = CGRect(x: 0, y: self.frame.size.height - 1, width: self.frame.size.width, height: 1)
bottomLine.backgroundColor = UIColor.white.cgColor
borderStyle = .none
layer.addSublayer(bottomLine)
}
func addBottomBorderView(lineColor : UIColor) {
var bottomBorder = UIView()
//MARK: Setup Bottom-Border
self.translatesAutoresizingMaskIntoConstraints = false
bottomBorder = UIView.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
bottomBorder.backgroundColor = lineColor
bottomBorder.translatesAutoresizingMaskIntoConstraints = false
addSubview(bottomBorder)
//Mark: Setup Anchors
bottomBorder.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
bottomBorder.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
bottomBorder.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
bottomBorder.heightAnchor.constraint(equalToConstant: 1).isActive = true // Set Border-Strength
}
}
Yes Need to remove all created previous fileds from array and from your UIStackView
private func setTextFields() {
self.removeFullyAllArrangedSubviews()
textFieldArray.removeAll()
for i in 0..<numberOfOTPdigit {
let field = OTPTextField()
field.keyboardType = .numberPad
field.textColor = textFieldColor
textFieldArray.append(field)
addArrangedSubview(field)
field.delegate = self
field.backgroundColor = .clear
field.textAlignment = .center
field.addBottomBorderView(lineColor: bottomLineColor)
i != 0 ? (field.previousTextField = textFieldArray[i-1]) : ()
i != 0 ? (textFieldArray[i-1].nextTextFiled = textFieldArray[i]) : ()
print("")
}
}

How to toggle hide imageView from subclassing UIButton

I have a subclass of UIButton and it have an initialiser that accept a name and boolean. I have a function to toggle the hide and show imageView, and my auto layout set to when imageView hidden the anchor move into another imageView. I use the content hugging priority programmatically in this. so here is my code, can you show me why my uiimageView not hiding.
// This is in my subclass of UIButton
let profileLbl = UILabel()
let badgeImageView = UIImageView()
let rightArrowImageView = UIImageView()
var isHiddenBadge = false
var visibleProfileTrailingConstraint: NSLayoutConstraint!
var hiddenProfileTrailingConstraint: NSLayoutConstraint!
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
configure()
}
init(name: String, isBadgeHidden: Bool = false) {
super.init(frame: .zero)
profileLbl.text = name
profileLbl.font = UIFont(name: "NunitoSans-SemiBold", size: 16)
profileLbl.textColor = #colorLiteral(red: 0.2, green: 0.2, blue: 0.2, alpha: 1)
isHiddenBadge = isBadgeHidden
toggleHide(badge: isHiddenBadge)
configure()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func configure() {
translatesAutoresizingMaskIntoConstraints = false
[profileLbl, badgeImageView, rightArrowImageView].forEach({ v in
v.translatesAutoresizingMaskIntoConstraints = false
addSubview(v)
})
visibleProfileTrailingConstraint = profileLbl.trailingAnchor.constraint(equalTo: badgeImageView.leadingAnchor, constant: -5)
hiddenProfileTrailingConstraint = profileLbl.trailingAnchor.constraint(equalTo: rightArrowImageView.leadingAnchor, constant: -5)
visibleProfileTrailingConstraint.priority = .defaultHigh
hiddenProfileTrailingConstraint.priority = .defaultLow
badgeImageView.image = #imageLiteral(resourceName: "warning_error 1")
rightArrowImageView.image = #imageLiteral(resourceName: "ic-arrow-right")
NSLayoutConstraint.activate([
profileLbl.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 24),
profileLbl.centerYAnchor.constraint(equalTo: centerYAnchor),
visibleProfileTrailingConstraint,
hiddenProfileTrailingConstraint,
badgeImageView.centerYAnchor.constraint(equalTo: centerYAnchor),
badgeImageView.widthAnchor.constraint(equalToConstant: 24),
badgeImageView.heightAnchor.constraint(equalToConstant: 24),
rightArrowImageView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -24),
rightArrowImageView.centerYAnchor.constraint(equalTo: centerYAnchor),
rightArrowImageView.widthAnchor.constraint(equalToConstant: 16),
rightArrowImageView.heightAnchor.constraint(equalToConstant: 16)
])
}
private func toggleHide(badge: Bool) {
if badge == false {
if badgeImageView.isHidden {
badgeImageView.isHidden = false
visibleProfileTrailingConstraint.priority = .defaultHigh
hiddenProfileTrailingConstraint.priority = .defaultLow
}
} else {
visibleProfileTrailingConstraint.priority = .defaultLow
hiddenProfileTrailingConstraint.priority = .defaultHigh
badgeImageView.isHidden = true
}
}
// I initialise it in my viewController
let infoBtn = GTProfileBtn(name: "Basic Info", isBadgeHidden: false)
// this is when I try to test it in my viewDidLoad
infoBtn.isHiddenBadge = true
Use following
var isHiddenBadge = false {
didSet {
toggleHide(badge: isHiddenBadge)
}
}
The problem is you not calling toggleHide after setting isHiddenBadge. The above code will solve the issue.

Transparent gradient not working in UIView Class

I'm trying to add a transparent gradient to UIView in UIView Class but it doesn't work.
class RecipesDetailsView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
layoutUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var containerView: UIView = {
let containerView = UIView()
containerView.backgroundColor = .white
let gradientMaskLayer = CAGradientLayer()
gradientMaskLayer.frame = containerView.bounds
gradientMaskLayer.colors = [UIColor.clear.cgColor, UIColor.white.cgColor]
gradientMaskLayer.locations = [0, 1]
containerView.layer.mask = gradientMaskLayer
containerView.fadeView(style: .bottom, percentage: 0.5)
containerView.translatesAutoresizingMaskIntoConstraints = false
return containerView
}()
lazy var startCookingButton: UIButton = {
let startCookingButton = UIButton(type: .system)
startCookingButton.setTitle("Start cooking", for: .normal)
startCookingButton.setTitleColor(.white, for: .normal)
startCookingButton.backgroundColor = .CustomGreen()
startCookingButton.layer.cornerRadius = 8.0
startCookingButton.translatesAutoresizingMaskIntoConstraints = false
startCookingButton.titleLabel?.font = UIFont(name: "AvenirNext-Bold", size: 14)
return startCookingButton
}()
lazy var saveButton: UIButton = {
let saveButton = UIButton(type: .system)
saveButton.setTitleColor(.customDarkGray(), for: .normal)
saveButton.setTitle("Save", for: .normal)
saveButton.setImage(UIImage(systemName: "heart"), for: .normal)
saveButton.imageEdgeInsets = UIEdgeInsets(top: 0,left: -5,bottom: 0,right: 0)
saveButton.titleEdgeInsets = UIEdgeInsets(top: 0,left: 0,bottom: 0,right: -5)
saveButton.titleLabel?.font = UIFont(name: "AvenirNext-Bold", size: 14)
saveButton.tintColor = .customDarkGray()
saveButton.backgroundColor = .clear
saveButton.translatesAutoresizingMaskIntoConstraints = false
return saveButton
}()
func setupContainerViewConstraints() {
NSLayoutConstraint.activate([
containerView.bottomAnchor.constraint(equalTo: bottomAnchor),
containerView.leadingAnchor.constraint(equalTo: leadingAnchor),
containerView.trailingAnchor.constraint(equalTo: trailingAnchor),
containerView.heightAnchor.constraint(equalToConstant: frame.width / 5)
])
}
func setupStartCookingButton() {
NSLayoutConstraint.activate([
startCookingButton.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16),
startCookingButton.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -32),
startCookingButton.heightAnchor.constraint(equalToConstant: 55),
startCookingButton.widthAnchor.constraint(equalToConstant: frame.width * (2.5/4))
])
}
func setupSaveButtonConstraints() {
NSLayoutConstraint.activate([
saveButton.centerYAnchor.constraint(equalTo: startCookingButton.centerYAnchor),
saveButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
saveButton.heightAnchor.constraint(equalTo: startCookingButton.heightAnchor),
saveButton.widthAnchor.constraint(equalToConstant: frame.width * (1.2/4))
])
}
func addSubviews() {
addSubview(containerView)
containerView.addSubview(startCookingButton)
containerView.addSubview(saveButton)
}
func layoutUI() {
addSubviews()
setupContainerViewConstraints()
setupStartCookingButton()
setupSaveButtonConstraints()
}
}
What I want to get:
What I get from my code:
Layers do not "auto-size" with their views, so you need to keep that gradient layer as a property and update its frame when the view layout changes.
Add this property:
private var gradientMaskLayer: CAGradientLayer!
then, in lazy var containerView: UIView = change:
let gradientMaskLayer = CAGradientLayer()
to:
gradientMaskLayer = CAGradientLayer()
then, add this func:
override func layoutSubviews() {
super.layoutSubviews()
gradientMaskLayer.frame = bounds
}
Edit
However, that will apply the gradient mask to containerView AND its subviews (the buttons), which is probably not what you want.
So, change your addSubviews() func to:
func addSubviews() {
addSubview(containerView)
// add buttons to self, not to containerView
//containerView.addSubview(startCookingButton)
//containerView.addSubview(saveButton)
addSubview(startCookingButton)
addSubview(saveButton)
}
Edit 2
Here is a complete implementation, with the view controller's background set to red:
class TestViewController: UIViewController {
var rv: RecipesDetailsView!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// with the way you are setting up the layout,
// we need to add the view here where we know the
// frame has been setup
if rv == nil {
let w = view.frame.width
let h = w / 5.0 * 2.0
let t = view.frame.height - h
rv = RecipesDetailsView(frame: CGRect(x: 0.0, y: t, width: w, height: h))
view.addSubview(rv)
}
}
}
class RecipesDetailsView: UIView {
// add this var / property
private var gradientMaskLayer: CAGradientLayer!
override init(frame: CGRect) {
super.init(frame: frame)
layoutUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
// layers do not follow frame changes, so update here
gradientMaskLayer.frame = bounds
}
lazy var containerView: UIView = {
let containerView = UIView()
containerView.backgroundColor = .white
gradientMaskLayer = CAGradientLayer()
gradientMaskLayer.frame = containerView.bounds
gradientMaskLayer.colors = [UIColor.clear.cgColor, UIColor.white.cgColor]
gradientMaskLayer.locations = [0, 1]
containerView.layer.mask = gradientMaskLayer
//containerView.fadeView(style: .bottom, percentage: 0.5)
containerView.translatesAutoresizingMaskIntoConstraints = false
return containerView
}()
lazy var startCookingButton: UIButton = {
let startCookingButton = UIButton(type: .system)
startCookingButton.setTitle("Start cooking", for: .normal)
startCookingButton.setTitleColor(.white, for: .normal)
//startCookingButton.backgroundColor = .CustomGreen()
startCookingButton.backgroundColor = .systemGreen
startCookingButton.layer.cornerRadius = 8.0
startCookingButton.translatesAutoresizingMaskIntoConstraints = false
startCookingButton.titleLabel?.font = UIFont(name: "AvenirNext-Bold", size: 14)
return startCookingButton
}()
lazy var saveButton: UIButton = {
let saveButton = UIButton(type: .system)
//saveButton.setTitleColor(.customDarkGray(), for: .normal)
saveButton.setTitleColor(.darkGray, for: .normal)
saveButton.setTitle("Save", for: .normal)
saveButton.setImage(UIImage(systemName: "heart"), for: .normal)
saveButton.imageEdgeInsets = UIEdgeInsets(top: 0,left: -5,bottom: 0,right: 0)
saveButton.titleEdgeInsets = UIEdgeInsets(top: 0,left: 0,bottom: 0,right: -5)
saveButton.titleLabel?.font = UIFont(name: "AvenirNext-Bold", size: 14)
//saveButton.tintColor = .customDarkGray()
saveButton.tintColor = .darkGray
saveButton.backgroundColor = .clear
saveButton.translatesAutoresizingMaskIntoConstraints = false
return saveButton
}()
func setupContainerViewConstraints() {
NSLayoutConstraint.activate([
containerView.bottomAnchor.constraint(equalTo: bottomAnchor),
containerView.leadingAnchor.constraint(equalTo: leadingAnchor),
containerView.trailingAnchor.constraint(equalTo: trailingAnchor),
containerView.heightAnchor.constraint(equalToConstant: frame.width / 5)
])
}
func setupStartCookingButton() {
NSLayoutConstraint.activate([
startCookingButton.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16),
startCookingButton.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -32),
startCookingButton.heightAnchor.constraint(equalToConstant: 55),
startCookingButton.widthAnchor.constraint(equalToConstant: frame.width * (2.5/4))
])
}
func setupSaveButtonConstraints() {
NSLayoutConstraint.activate([
saveButton.centerYAnchor.constraint(equalTo: startCookingButton.centerYAnchor),
saveButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
saveButton.heightAnchor.constraint(equalTo: startCookingButton.heightAnchor),
saveButton.widthAnchor.constraint(equalToConstant: frame.width * (1.2/4))
])
}
func addSubviews() {
addSubview(containerView)
// add buttons to self, not to containerView
//containerView.addSubview(startCookingButton)
//containerView.addSubview(saveButton)
addSubview(startCookingButton)
addSubview(saveButton)
}
func layoutUI() {
addSubviews()
setupContainerViewConstraints()
setupStartCookingButton()
setupSaveButtonConstraints()
}
}
Result:

How can I get a button in my subview to enable the rightbarbuttonitem in my view controller

I have a UIView with a textfield and a button, this UIView is a subview of my view controller. How can I have the button in my subview enable the view controller's navigation rightbarbuttonitem when it is pressed?
class CreateActivityView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
lazy var addButton: UIButton = {
let button = UIButton(type: UIButton.ButtonType.system)
button.setImage(UIImage(named: "add_dark.png"), for: .normal)
button.frame = CGRect(x: 0, y: 0, width: 24.0, height: 24.0)
button.addTarget(self, action: #selector(addButtonTapped), for: .touchUpInside)
button.isEnabled = false
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
lazy var activityNameTextField: DataEntryTextField = {
let textField = DataEntryTextField()
textField.frame = CGRect(x: 0, y: 0, width: 250.0, height: 30.0)
textField.placeholder = "Add an activity"
textField.font = UIFont(name: "Avenir", size: 17)
textField.keyboardType = UIKeyboardType.default
textField.returnKeyType = UIReturnKeyType.done
textField.translatesAutoresizingMaskIntoConstraints = false
return textField
}()
lazy var activityNameLabel: UILabel = {
let label = UILabel()
label.frame = CGRect(x: 0, y: 0, width: 0, height: 30.0)
label.font = UIFont(name: "Avenir", size: 17)
label.translatesAutoresizingMaskIntoConstraints = false
label.isHidden = true
return label
}()
func setupView() {
backgroundColor = UIColor.lightGray
addSubview(addButton)
addButton.rightAnchor.constraint(equalTo: self.safeAreaLayoutGuide.rightAnchor, constant: -16.0).isActive = true
addButton.centerYAnchor.constraint(equalTo: self.safeAreaLayoutGuide.centerYAnchor).isActive = true
addButton.heightAnchor.constraint(equalToConstant: 24.0).isActive = true
addButton.widthAnchor.constraint(equalToConstant: 24.0).isActive = true
addSubview(activityNameTextField)
activityNameTextField.leftAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leftAnchor, constant: 16.0).isActive = true
activityNameTextField.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor, constant: 10.0).isActive = true
activityNameTextField.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor, constant: -10.0).isActive = true
activityNameTextField.rightAnchor.constraint(equalTo: addButton.safeAreaLayoutGuide.leftAnchor, constant: -20.0).isActive = true
addSubview(activityNameLabel)
activityNameLabel.leftAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leftAnchor, constant: 16.0).isActive = true
activityNameLabel.rightAnchor.constraint(equalTo: self.safeAreaLayoutGuide.rightAnchor, constant: -16.0).isActive = true
activityNameLabel.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor, constant: 10.0).isActive = true
activityNameLabel.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor, constant: -10.0).isActive = true
}
#objc func addButtonTapped() {
guard let validatedText = activityNameTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines), !validatedText.isEmpty else {
return
}
displayActivityNameLabel(with: validatedText)
}
func displayActivityNameLabel(with text: String) {
activityNameLabel.text = text
activityNameLabel.isHidden = false
activityNameTextField.isHidden = true
print(activityNameLabel.text)
}
}
class QuickLogViewController: UIViewController {
let screenSize = UIScreen.main.bounds.size
var createActivityView: CreateActivityView!
override func viewDidLoad() {
super.viewDidLoad()
title = "Quick Log"
setupView()
}
func setupView() {
initializeCreateActivityView()
self.view.addSubview(createActivityView)
createActivityView.leftAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leftAnchor).isActive = true
createActivityView.rightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.rightAnchor).isActive = true
createActivityView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
createActivityView.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
}
func initializeCreateActivityView() {
createActivityView = CreateActivityView()
createActivityView.frame = CGRect(x: 0, y: 0, width: screenSize.width, height: 50.0)
createActivityView.translatesAutoresizingMaskIntoConstraints = false
createActivityView.activityNameTextField.delegate = self
}
func enableCancelButton() {
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancelButtonPressed))
}
}
I added my CreateActivityView to my view controller. The button target is handled by my createActvitiyView. So how can I access the rightbarbuttonitem from the subview?
You can try to use delegate
class CreateActivityView: UIView {
weak var delegate:VCName?
and
createActivityView = CreateActivityView()
createActivityView.delegate = self
after that inside any action do
delegate?.methodInsideTheVC()
Or write button action inside the vc
createActivityView = CreateActivityView()
createActivityView.button.addTarget(self, action: #selector(addButtonTapped), for: .touchUpInside)
and place
#objc func addButtonTapped() {

Resources