Button not enabled when added programmatically to collectionviewcell - swift

When I add a button programmatically to the custom UICollectionViewCell class, it is not clickable and will not run the function I attached to it.
I have tried these so far:
Button inside CollectionView not clickable
Swift - UIButton in UICollectionViewCell not clickable
but did not get any luck.
In my cell Class, I have:
let toggleButton: UIButton = {
let tb = UIButton(type: .custom) as UIButton
tb.setTitleColor(.white, for: .normal)
tb.setTitle("Example", for: .normal)
tb.titleLabel?.font = UIFont.customFont(name: "InterUI-Medium", size: 18)
tb.backgroundColor = UIColor(red:0.36, green:0.69, blue:0.55, alpha:1.0)
tb.layer.cornerRadius = 5
tb.titleLabel?.adjustsFontSizeToFitWidth = true
tb.titleLabel?.minimumScaleFactor = 0.4
tb.translatesAutoresizingMaskIntoConstraints = false
tb.addTarget(self, action: #selector(topButtonTouched), for: .touchUpInside)
return tb
}()
and
func setupView() {
self.addSubview(toggleButton)
let menuAndToggleConstraints = [
toggleButton.centerYAnchor.constraint(equalTo: self.centerYAnchor),
toggleButton.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -100,
toggleButton.widthAnchor.constraint(equalToConstant: 150)
]
NSLayoutConstraint.activate(menuAndToggleConstraints)
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
to setup the button in the cell. I have tried self.isUserInteractionEnabled to see if there are any conflicts but it does not resolve anything. I have one other gesture recognizer in the cell and have tried without it and the button is still not clickable.
Edit:
Here is my delegate and my function that calls the collectionView to change the size at indexPath:
weak var delegate: ExpandedCellDelegate?
public var indexPath: IndexPath!
#objc func topButtonTouched(_ sender: UIButton) {
print("hello")
if let delegate = self.delegate{
delegate.topButtonTouched(indexPath: indexPath)
}
}
I made sure to set the delegate and indexPath as self in cellForRow function in CollectionView class as demonstrated in this answer.

I was able to make the button work by putting this:
tb.addTarget(self, action: #selector(topButtonTouched), for: .touchUpInside)
into the setupView() method like so:
func setupView() {
self.addSubview(toggleButton)
toggleButton.addTarget(self, action: #selector(topButtonTouched), for: .touchUpInside)
let menuAndToggleConstraints = [
toggleButton.centerYAnchor.constraint(equalTo: self.centerYAnchor),
toggleButton.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -100,
toggleButton.widthAnchor.constraint(equalToConstant: 150)
]
NSLayoutConstraint.activate(menuAndToggleConstraints)
}
Not sure if it's correct but it works.

Related

Change Button image from view controller inside custom header cell

I have a VC that is UICollectionView that has header. This header is a custom cell, and inside that cell there is an button with image which I need to change based on info from API or user tap.
In func loadData i get info about icon and then I call function inside headerCell to change the icon. Do I need to do this in other way in order for this to work?
protocol HeaderCellDelegate: class {
func setDropdown()
func tapOnSelection()
}
class HeaderCell: UIGestureRecognizerDelegate {
// delegate
weak var hDelegate: HeaderCellDelegate?
#objc
lazy var selectionBtn: UIButton = {
let button = UIButton(type: .system)
button.setTitle("One", for: .normal)
button.addTarget(self, action: #selector(changeSelection), for: .touchUpInside)
return button
}()
#objc
lazy var oneOrTwo: UIButton = {
let button = UIButton()
button.setImage(UIImage(named: "oneOff")?.withRenderingMode(.alwaysTemplate), for: .normal)
button.addTarget(self, action: #selector(toggleOneOrTwo), for: .touchUpInside)
return button
}()
#objc
lazy var iconBtn: UIButton = {
let button = UIButton()
button.setImage(UIImage(named: "iconGreen")?.withRenderingMode(.alwaysTemplate), for: .normal)
button.addTarget(self, action: #selector(setIcon), for: .touchUpInside)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
// I setup view helper
// Just a button and icon on the right
// button opens DropDown list
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Delegate Functions
#objc
func toggleOneOrTwo(_ sender: Any) {
self.hDelegate?.setDropdown()
}
#objc
func changeSelection(sender: UIButton) {
self.hDelegate?.tapOnSelection()
}
#objc
func setIcon() {
isIconGreen
? iconColor.setImage(UIImage(named: "iconGreen")?.withRenderingMode(.alwaysTemplate), for: .normal)
: iconColor.setImage(UIImage(named: "iconRed")?.withRenderingMode(.alwaysTemplate), for: .normal)
}
}
ViewController
class ViewController: UICollectionViewController, UIGestureRecognizerDelegate {
fileprivate let headerCellId = "headerCellId"
lazy var headerCell = HeaderCell()
override func viewDidLoad() {
super.viewDidLoad()
// Register Cell Class
self.collectionView.register(HeaderCell.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: headerCellId)
loadData()
}
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionView.elementKindSectionHeader:
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerCellId, for: indexPath) as! HeaderCell
header.backgroundColor = .groupTableViewBackground
header.hDelegate = self
return header
default:
fatalError("This should never happen!!")
}
}
func loadData() {
// get data from API and isIconGreen value
let isIconGreen = true
headerCell.setIcon(isIconGreen: isIconGreen)
}
}

Type has no member during Apple tutorial

I just started learning Swift. I am following this Apple tutorial but I was met with this error. I copied the exact code from the tutorial. Not sure where did I go wrong.
Error message:
"Type 'RatingControl' has no member 'ratingButtonTapped(button:)'"
//Mark: Private Methods
private func setupButtons() {
for _ in 0..<5 {
//Create the button
let button = UIButton()
button.backgroundColor = UIColor.red
//Add constraints
button.translatesAutoresizingMaskIntoConstraints = false
button.heightAnchor.constraint(equalToConstant: 44.0).isActive = true
button.widthAnchor.constraint(equalToConstant: 44.0).isActive = true
//Setup the button action
button.addTarget(self, action: #selector(RatingControl.ratingButtonTapped(button:)), for: .touchUpInside)
//Add the button to the stack
addArrangedSubview(button)
//Add the new button to the rating button array
ratingButtons.append(button)
}
}
Initial to get the button action the RatingControl.swift class will be like
import UIKit
#IBDesignable class RatingControl: UIStackView {
//MARK: Initialization
override init(frame: CGRect) {
super.init(frame: frame)
}
required init(coder: NSCoder) {
super.init(coder: coder)
}
//MARK: Button Action
#objc func ratingButtonTapped(button: UIButton) {
print("Button pressed")
}
}
Create a file named RatingControl.swift with code and run your project. This issue will be resolved.
if you already have the full class the just add #objc before the method ratingButtonTapped.
More info: You can download the full project of the tutorial you currently following. The download link is present at the bottom.
We truly need to see the whole RatingControl class in order give you a complete answer. However your problem based on the error is that you there is no function with the name ratingButtonTapped inside your RatingControl class. There should be a class like the following in order to register it as a target to a button.
Example of .addTarget usage:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Setup the button action
let button = UIButton()
button.addTarget(self, action: #selector(ratingButtonTapped(_:)), for: .touchUpInside)
view.addSubview(button)
}
#objc func ratingButtonTapped(sender: UIButton) {
// Triggered when the button is pressed
}
}

addTarget() does not work on custom extensions

I made a custom UIView() and i'm trying to add a okButton to it.
However. I have added the button but when showing my UIView in one of my ViewControllers, But the button won't work. This is my code:
class Luna: UIView {
let luna = UIView()
let okButton = UIButton()
typealias completionHandler = (_ success:Bool) -> Void
// Not yet implemented
open var touchOutsideToHide: Bool = true;
private var screenWidth: CGFloat {
return UIScreen.main.bounds.width
}
override init(frame: CGRect) {
super.init(frame: frame)
okButton.setTitleColor(.blue, for: .normal)
okButton.setTitle("Okay", for: .normal)
okButton.titleLabel?.font = UIFont(name: "avenirnext-demibold", size: 16)
let gesture:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.pressButton(_:)))
okButton.addGestureRecognizer(gesture)
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
//The target function
#objc func pressButton(_ sender: UIButton){ //<- needs `#objc`
// .. //
print("btn hit")
}
I do not get btn hit in the console and the tap isn't registered
the button is visible, it's added as a subview:
Screenshot
Button have default touch gesture, you don't need to add it. just add target as follow to fix your issue. Create protocol of your customer class and declare delegate for it in same class as follow.
protocol LunaDelegate: class {
func okButtonTapped(sender: UIButton)
}
class Luna: UIView {
let luna = UIView()
let okButton = UIButton()
typealias completionHandler = (_ success:Bool) -> Void
weak var delegate: LunaDelegate?
// Not yet implemented
open var touchOutsideToHide: Bool = true;
private var screenWidth: CGFloat {
return UIScreen.main.bounds.width
}
override init(frame: CGRect) {
super.init(frame: frame)
okButton.setTitleColor(.blue, for: .normal)
okButton.setTitle("Okay", for: .normal)
okButton.titleLabel?.font = UIFont(name: "avenirnext-demibold", size: 16)
okButton.addTarget(self, action: #selector(pressButton(sender:)), for: .touchUpInside)
}
#IBAction func pressButton(sender: UIButton) {
if let selfDelegate = self.delegate {
selfDelegate.okButtonTapped(sender: sender)
}
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Then after implement your code as follow to use your custom view. When your button will pressed then delegate method will be call in your view controller.
Usage:
class LunaViewController: UIViewController, LunaDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let luna = Luna(frame: CGRect.init(x: 100, y: 100, width: 100, height: 40))
luna.delegate = self
self.view.addSubview(luna)
}
// MARK:- Luna Delegate
func okButtonTapped(sender: UIButton) {
print("OK Button Pressed From Luna View")
}
}
This is the proper solution which I have already using in my current project. I hope this will help you.

How to activate share extension (UIActivityViewController) when a button tapped (which is in UICollectionViewController)?

I'm trying to add some buttons or labels to one of UICollectionViewCell, but it seems I'm not be able to activate UIActivityViewController when the share button tapped.
Here are my codes:
class VerticalCollectionViewCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
containerView.addSubview(shareButton)
shareButton.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -16).isActive = true
shareButton.rightAnchor.constraint(equalTo: favoriteButton.leftAnchor, constant: -16).isActive = true
shareButton.widthAnchor.constraint(equalToConstant: 24).isActive = true
shareButton.heightAnchor.constraint(equalToConstant: 24).isActive = true
shareButton.addTarget(self, action: #selector(handleShareButton), for: .touchUpInside)
}
#objc func handleShareButton(sender: UIButton) {
let shareText = NSLocalizedString("I found something interesting you may want to take a look at.", comment: "share")
let shareImage = #imageLiteral(resourceName: "Bear")
let activityViewController : UIActivityViewController = UIActivityViewController(activityItems: [shareText, shareImage as Any], applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view
self.present(activityViewController, animated: true, completion: nil)
}
}
The errors are:
Value of type 'VerticalCollectionViewCell' has no member 'view'
and
Value of type 'VerticalCollectionViewCell' has no member 'present'.
I know presentViewController is a UIViewController method, UITableViewCell does not has a method called presentViewController, and UITableViewCell should never handle any business logic. It should be implemented in a view controller.
But I have no idea how to fix this.
you call do in this way
class Cell: UITableViewCell {
#IBOutlet let shareButton: UIButton!
}
class VC: UIViewController, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as? Cell {
cell?.shareButton.addTarget(self, action: #selector(VC.shareButtonPressed(_:)), for: .touchUpInside)
}
}
func shareButtonPressed(_ button: UIButton) {
//code here
}
}
What you can do, is define a protocol of the cell, which your tableview can conform to. When the share button on a collectionView is tapped, call this delegate method, passing in relevant information like the cell tag or the model for that collectionView cell. Using that information you can then present the ActivityController from your viewController.

Creating a checkbox programmatically using images as the UIButtons background

I have seen multiple questions on how to implement a checkbox by just changing the background image when clicked, but I don't understand why the checkbox only shows when I add it to my ViewController setupViews.
It simply will not show up or change when I have all the functionality in my actionButton function. Should i be using a protocol and delegate set up to get my button showing changing when clicked? Below is my code, I am hoping someone can shed some light as to what I am missing here?
class MainMenuViewController: UIViewController {
let clickingCheckbox = ClickingCheckbox()
var checkbox = UIImage(named: "Checked_Checkbox")
var empty_checkbox = UIImage(named:"Empty_Checkbox")
var isBoxClicked: Bool!
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
func setupViews() {
self.backgroundImage.addSubview(contentView)
self.contentView.addSubview(clickingCheckbox)
clickingCheckbox.snp.makeConstraints { (make) in
make.top.equalTo(signInButton.snp.bottom).offset(-MainMenuViewController.padding)
make.leading.equalTo(buttonView)
make.width.equalTo(signInButton).multipliedBy(0.2)
make.height.equalTo(clickingCheckbox.snp.width)
}
clickingCheckbox.addTarget(self, action: #selector(buttonAction(_:)), for: .touchUpInside)
clickingCheckbox.setImage(empty_checkbox, for: UIControlState.normal) #The checkbox only shows on screen if I put it here, however it does nothing when clicked!
}
#objc func buttonAction(_ sender: ClickingCheckbox) {
if isBoxClicked == true {
isBoxClicked = false
}else{
isBoxClicked = true
}
if isBoxClicked == true {
sender.setImage(checkbox, for: UIControlState.selected)
}else{
sender.setImage(empty_checkbox, for: UIControlState.normal)
}
print("test")
}
In my Class I have.....
class ClickingCheckbox: UIButton {
override func draw(_ rect: CGRect) {
super.draw(rect)
}
}
I have tried keeping the buttonAction functionality in the class, but that didn't work, I have tried a multiple different ways but can't get my head around how to show it working. All other advice is given to implement using IBOutlets so this would be really helpful for me to understand. Thanks
Try something like this:
class ClickingCheckbox: UIButton {
convenience init() {
self.init(frame: .zero)
self.setImage(UIImage(named: "Empty_Checkbox"), for: .normal)
self.setImage(UIImage(named: "Checked_Checkbox"), for: .selected)
self.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
}
#objc private func buttonTapped() {
self.isSelected = !self.isSelected
}
}