I am learning iOS programming for last few days, so I am new to this tech. I am building simple app where I'm using collection view which has EventCell inside it. Each EventCell has one UIButton and EventCell has uniq value(ID coming from JOSN API response). I need to pass that value to delegate which call new ViewController. I have setup the delegate method which is working correctly, just finding the solution for how to pass value on button click
PS: I am not using storyboard
**EventCell.swift**
lazy var leaderboardButton: UIButton = {
let leaderboardBtn = UIButton(type: .system)
leaderboardBtn.setTitle("LeaderBoard", for: .normal)
leaderboardBtn.addTarget(self, action: #selector(handleLeaderBoardClick), for: .touchUpInside)
leaderboardBtn.tintColor = .white
return leaderboardBtn
}()
weak var delegate: HomeControllerDelegate?
func handleLeaderBoardClick() {
// need to get uniq value and pass here....
delegate?.clickOnLeaderBoard()
}
If you need to pass that ID value to delegate object which is in your case is your ViewController - then you need to modify clickOnLeaderboard function from the HomeControllerDelegate protocol.
Modify it and pass your EventCell's ID as an additional argument to it.
delegate?.clickOnLeaderBoard(cellID)
Also, don't forget to update function signature in view controller class.
In func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) overload
add this:
cell.yourButton.tag = indexPath.row
cell.yourButton.addTarget(self, action: #selector(buttonClicked(sender:)), for: .touchUpInside)
you should have buttonClicked(sender:) in your UIViewController.
buttonClicked(sender:){
tag = sender.tag
// you can use this tag to change your view controller
}
Change your function to include a parameter:
func handleLeaderBoardClick(_ sender: UIButton) {
// need to get uniq value and pass here....
delegate?.clickOnLeaderBoard()
}
Update the selector:
leaderboardBtn.addTarget(self, action: #selector(handleLeaderBoardClick(_:)), for: .touchUpInside)
Now you can perform a comparison between sender and leaderboardButton to see if they're the same.
Related
I'm making an app that allows a user to "pin" certain elements in a collection view I have implemented in my home ViewController class. To pin an element, the user must access a button that is part of my WordCell (UICollectionViewCell) class. However, when I try to press the button from my home view controller, nothing happens.
Here is all the relevant code and screenshots:
The star on the right hand side is the button inside the CollectionViewCell that I want the user to be able to push through the home view.
Below is all the relevant code in my ViewController class. I am using a delegate to pass the cell that was pressed into my home class ViewController. I also plan on passing more data back and forth between the cell (UICollectionCellView) class and ViewController in the future.
extension ViewController: UICollectionViewDataSource{
//......
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let wordCell = collectionView.dequeueReusableCell(withReuseIdentifier: wordCellReuseID, for: indexPath) as! WordCell
wordCell.delegate = self
wordCell.configure(word: tempWords[indexPath.item])
return wordCell
}
//......
}
extension ViewController: WordCellDelegate{
func star(wasPressedOnCell: WordCell){
print("touched")
if(wasPressedOnCell.isStarred){ //if the button has already been starred, unstar it
wasPressedOnCell.starButton.setImage(UIImage(named: "unfilled_star.png"), for: .normal)
wasPressedOnCell.isStarred = false
}
else{ //else, star the button
wasPressedOnCell.starButton.setImage(UIImage(named: "filled_star.png"), for: .normal)
wasPressedOnCell.isStarred = true
}
}
}
Here is relevant code in my class that conforms to UICollectionCellView:
//delegate setup for home class
protocol WordCellDelegate: class{
func star(wasPressedOnCell cell: WordCell) //parameter: cell that was pressed
}
//........
//button setup
starButton = UIButton()
starButton.setImage(UIImage(named: "unfilled_star.png"), for: .normal)
starButton.translatesAutoresizingMaskIntoConstraints = false
starButton.addTarget(self, action: #selector(starred), for: .touchUpInside)
contentView.addSubview(starButton)
//......
//button objective function
#objc func starred(){
print("touched")
delegate?.star(wasPressedOnCell: self)
//starredTapAction?() //chained back to main view controller
}
However, when I try to press the star on my home view controller screen, the objc function inside my UICollectionCellView class is not called. I've read from previous posts that this is most likely due to a hierarchy of classes and which view controls which objects, but I haven't been able to find a solution to this issue yet. I'm not sure what needs to be changed so the button inside the collection cell can be pressed through the view of the collection.
Please let me know if you need any more information, and thank you for reading this post!
Please disable cell selection of UIcollection view and your button touch event will fire, basically at a time you can either use collection view did select method or button action method
I had the exact same issue less than 2 weeks ago; I am still not sure about the root cause but I know the fix.
Move this line --
starButton.addTarget(self, action: #selector(starred), for: .touchUpInside)
inside your cell configuration method --
WordCell.configure (word: )
Add --
starButton.isEnabled = true
starButton.isUserInteractionEnabled = true
under your --
//button setup
only if the tap is not registering (ie if you can't see the button getting tapped)
It's really about setting the button target under cellForItemAt(); that'll do the trick.
I am new in swift and I want to get the value of label from tableview on button click
I am using code like this but it is getting crash
in cellforrowatindexpath
cell.btnsubmit.tag = indexPath.row
cell.btnsubmit.addTarget(self, action: #selector(buttonSelected), for: .touchUpInside)
#objc func buttonSelected(sender: UIButton){
print(sender.tag)
let cell = sender.superview?.superview as! PatientUpdateVCCell
surgery_date = cell.surgeryDateTextField.text!
discharge_date = cell.dischargeDateTextField.text!
follow_up_duration = cell.lblfolowup.text!
follow_up_date = cell.firstFollowUpTextField.text!
patient_status = cell.patientStatusTextView.text!
}
but it is getting crash. How can I achieve this
crash
Could not cast value of type 'UITableViewCellContentView' (0x11a794af0) to 'appname.PatientUpdateVCCell' (0x10ae74ae0).
According to your crash last superView is contentView then it's superView is the needed cell , so You need
let cell = sender.superview!.superview!.superview as! PatientUpdateVCCell
Target/action is pretty objective-c-ish. And view hierarchy math is pretty cumbersome.
A swiftier way is a callback closure which is called in the cell and passes the cell.
In the cell add a callback property and an IBAction. Connect the action to the button
var callback : ((UITableViewCell) -> Void)?
#IBAction func buttonSelected(_ sender: UIButton) {
callback?(self)
}
In cellForRow rather than the tag assign the closure
cell.callback = { currentCell in
self.surgery_date = currentCell.surgeryDateTextField.text!
self.discharge_date = currentCell.dischargeDateTextField.text!
self.follow_up_duration = currentCell.lblfolowup.text!
self.follow_up_date = currentCell.firstFollowUpTextField.text!
self.patient_status = currentCell.patientStatusTextView.text!
}
And delete the action method in the controller
I want to show/hide collection view inside the table view's check button selection
I am trying to use delegate and protocol for that.
1.I create protocol in table view cell class
protocol CustomCellDelegate{
func selectCollectionView(cell: InsideTableViewCell)
}
Note:I create selectCollectionView function inside the main view controller
2.declare delegate variable inside the main view class
var delegate: CustomCellDelegate?
3.Confirm to the CustomCellDelegate in the main class
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,UICollectionViewDelegate,UICollectionViewDataSource,CustomCellDelegate
4.Use selector function to provide table view button inside the cellForRowAtIndexPath function
cell.checkButton.addTarget(self, action: #selector(self.selectCheck(_:)), for: .touchUpInside)
cell.delegate = self
5.Selector function
#objc func selectCheck(_ sender: UIButton) {
if sender.isSelected {
sender.isSelected = false
print("Check 1")
delegate?.selectCollectionView(cell: InsideTableViewCell)
} else{
print("Check 2")
sender.isSelected = true
}
}
6.selectCollectionView function
func selectCollectionView(cell: InsideTableViewCell) {
cell.clCollectionView.isHidden = true
}
I am trying to call selectCollectionView function inside the selectCheck button
but I get error like "Cannot convert value of type 'InsideTableViewCell.Type' to expected argument type 'InsideTableViewCell'"
If I do mistake please let me know.
I referred this link:How to access the content of a custom cell in swift using button tag?
And Screenshot of output:
This is a follow on from a previous question I have asked but I feel I am missing something very simple and its driving me up the wall!
I have a custom tableview cell which contains a switch and I need to trigger a function each time it's value is changed. I've tried using .addTarget but it never seems to trigger the function so maybe my selector syntax is incorrect.
I create the switch programatically within the tableview cell like this:
let thisSwitch: UISwitch = {
let thisSwitch = UISwitch()
thisSwitch.isOn = false
thisSwitch.translatesAutoresizingMaskIntoConstraints = false
thisSwitch.addTarget(self, action: Selector("switchTriggered:"), for: .valueChanged)
return thisSwitch
}()
Then directly below that I have my function:
func switchTriggered(sender: AnyObject) {
print("SWITCH TRIGGERED")
let sentSwitch = sender as! UISwitch
privateExercise.switchState = sentSwitch.isOn
}
It shows an error message stating " No method declared with Objective-C selector 'switchTriggered:' ". What am I missing here? Any help would be much appreciated!
The selector syntax should be
thisSwitch.addTarget(self, action: #selector(switchTriggered), for: .valueChanged)
Also keep the parameter as UISwitch type itself in order to avoid casting in function
func switchTriggered(sentSwitch: UISwitch) {
print("SWITCH TRIGGERED")
privateExercise.switchState = sentSwitch.isOn
}
I’m reopening this question because my last one wast flagged as duplicate even if it’s actually not ! It’s the same problem but the solutions are not working with my code. I’m using swift 2.
So my problem is, as the title says : I have a UIButton in a tableViewCell and when I use the method « setTitle », it takes from 10 to 60 seconds to update the title. At the same time I’m using « addTarget » and it works instantly. So the title should also update. My button is set as « custom » in my storyboard.
When the view is loading I’m running the following code :
/* viewDidLoad */
override func viewDidLoad() {
super.viewDidLoad()
boolAlready = false
findParticipation()
}
/* findParticipation */
func findParticipation() {
// After server request response :
boolAlready = true
self.tableView.reloadData()
}
/* cellForRowAtIndexPath */
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellActions = tableView.dequeueReusableCellWithIdentifier(informationsCellArray[indexPath.row], forIndexPath: indexPath) as ! eventDetailsAction
if boolAlready {
cellActions.foundParticipation
} else {
cellActions.btnParticipate.setTitle("...", forState: UIControlState.Normal)
cellActions.btnParticipate.addTarget(self, action: « deleteParticion", forControlEvents: .TouchUpInside)
}
/* In my custom cell */
func foundParticipation () {
self.btnParticipate.setTitle("Annuler", forState: UIControlState.Normal)
self.btnParticipate.addTarget(self, action: "deleteParticipation", forControlEvents: .TouchUpInside)
}
Different things I found on forums that didn’t worked :
Putting my settitle action around
dispatch_async(dispatch_get_main_queue()) {}
Setting title for all differents UIControlStates
Using setAttributedTitle()
Using self.btnParticipate.setNeedsLayout() and self.btnParticipate.layoutIfNeeded() after the setTitle
Disabling the button before and enable it after the setTitle
self.addSubview(self.btnParticipate)
Changing the title in titleLabel.text
Doing everything said previously in the parent viewController using cellActions.btnParticipate
UIView.performWithoutAnimation {
self.btnParticipate.setTitle("Annuler", forState: .Normal)
}
I’m now stuck and can’t find a solution for that.
You can try to refer to this answer https://stackoverflow.com/a/29633647/4478037
Make sure your button is a "custom" button and not a "system" button.
Try to wrap your setTitle call into performWithoutAnimation:
UIView.performWithoutAnimation {
self.btnParticipate.setTitle("Annuler", forState: .Normal)
}
Your issue is when you are creating the cell the btnParticipate function won't run properly you have to code all those conditions in func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) method
Try this
let cellActions = tableView.dequeueReusableCellWithIdentifier(informationsCellArray[indexPath.row], forIndexPath: indexPath) as ! eventDetailsAction
cellActions.removeTarget(nil, action: nil, forControlEvents: .AllEvents)
if boolAlready {
cellActions.btnParticipate.setTitle("Annuler", forState: UIControlState.Normal)
cellActions.btnParticipate.addTarget(self, action: "deleteParticipation", forControlEvents: .TouchUpInside)
} else {
cellActions.btnParticipate.setTitle("...", forState: UIControlState.Normal)
cellActions.btnParticipate.addTarget(self, action: « deleteParticion", forControlEvents: .TouchUpInside)
}
You can use
reloadRowsAtIndexPaths(_:withRowAnimation:)
to force a reload of the cell.
When the title changes that means that your code seems to be correct and its about rendering cycles. This would be one way to to reload the cell.
For reference: Apple Docs - UITableView reloadRowsAtIndexPaths
EDIT:
You have to call this method when some event gets fired, an requests finished etc. to update the cell. I had a second look at your code and it seems that your if-else in cell for row at index path is not doing what it should do. You should NOT call there any "action-performing-methods". Its just for initialization.
Your problem seems to be that your boolean boolAlready is the same for all cells. That will result in a never executing else-block. An therefore the title is not set, or its just set when the bool is set to false. You should probably print the bool there. And/or update your post with more information.
And its not good-readable code when you use nearly identical names like findParticipation and foundParticipation. You should probably refactor that.
So after 1 week of tries, I decided to stop searching...
I'm just calling findParticipation() synchronously and I'm not instantiating my tableView until I get foundParticipation() response.
I know it's way worse for the UX but it's still less time to wait.