I've implemented an Edit button, which adds a button to my UICollectionViewCell. When I press Edit => Edit turns into "Done" and when i press Done it is supposed to delete the Button.
My Code does allow me to add the buttons, but doesn't hide them.
Any Ideas?
#IBAction func editButton(sender: AnyObject) {
if(editButton.titleLabel?.text as String! == "Edit")
{
editButton.setTitle("Done", forState: UIControlState.Normal)
for item in self.mainCollectionView.visibleCells() as! [SettingsCollectionViewCell] {
let indexpath : NSIndexPath = self.mainCollectionView!.indexPathForCell(item as SettingsCollectionViewCell)!
let cell : SettingsCollectionViewCell = mainCollectionView!.cellForItemAtIndexPath(indexpath) as! SettingsCollectionViewCell
//Close Button
let close : UIButton = cell.collectionViewButton
close.hidden = false
}
}
else
{
editButton.setTitle("Edit", forState: UIControlState.Normal)
self.mainCollectionView?.reloadData()
}}
And cellForRowAtIndexPath:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let collectionCell:SettingsCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("collectionCell", forIndexPath: indexPath) as! SettingsCollectionViewCell
if self.editButton.titleLabel?.text as String! == "Edit"
{
collectionCell.collectionViewButton.hidden = true
}
else if editButton.titleLabel?.text as String! == "Done"
{
collectionCell.collectionViewButton.hidden = false
}
collectionCell.collectionViewButton!.layer.setValue(indexPath.row, forKey: "index")
collectionCell.collectionViewButton!.addTarget(self, action: "deletePhoto:", forControlEvents: UIControlEvents.TouchUpInside)
return collectionCell
}
You need to change the logic in cellForItemAtIndexPath for retrieving the title from the button.
In order to retrieve title text from the button you need to use titleForState(UIControlState) instead of titleLabel?.text.
Here is code snippet for conditional statement in cellForItemAtIndexPath
if self.editButton.titleForState(.Normal) == "Edit" {
collectionCell.collectionViewButton.hidden = true
}
else if self.editButton.titleForState(.Normal) == "Done" {
collectionCell.collectionViewButton.hidden = false
}
I think the best strategy here would be to create a button on the main storyboard and check 'hidden' on the button's context menu.
To change a button's text: create an outlet (yes, an outlet) for the button.
#IBOutlet var editbutton: UIButton!
Then to unhide it:
editbutton.hidden = false
(you can put this inside an if statement if desired)
Inside an action of the button (you must assign the button to an action as well) do this to hide the button if it has already been pressed into "Done":
if editbutton.title == "Done" && editbutton.hidden == false {
editbutton.hidden = true
}
Then, to change the text of the button when it still says edit:
if editbutton.title == "Edit" {
editbutton.title = "Done"
}
The actions must be done in this order, or else the button will turn into "Done" and not be hidden, so the computer will go on and hide it before the user presses it a second time.
Related
So I have a list of songs, and when I click on the moreButton the SongDetailCell is presented and the setPlayPauseOnAppeaering() function is called on the selected song.
This function will either set a play or pause icon depending on the if statement. For some reason the pause-icon is not being, even though the correct console logs are being printed. For example, when "playing with spotify sample", the button is not being set to pause, even though it definitely should be. Does anyone know what I am doing wrong here.
class SongList: UICollectionViewCell {
#objc func moreButtonTap(gesture: UITapGestureRecognizer) {
var indexPath = getIndexPathFromRecognizer(gesture: gesture)
let songDetailView = SongDetailCell()
let song = self.songsArray[indexPath.item]
songDetailView.song = song
songDetailView.setPlayPauseOnAppearing()
}
}
class SongDetailCell: UICollectionViewCell {
var song: Song!
let playPauseButton: UIButton = {
let button = UIButton()
button.setImage(UIImage(named: "play-button-large"), for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.tintColor = UIColor.brandBlack()
button.contentMode = .scaleToFill
return button
}()
func setPlayPauseOnAppearing() {
func setPlayButton() {
playPauseButton.setImage(UIImage(named: "play-button-large")?.withRenderingMode(.alwaysTemplate), for: .normal)
}
func setPauseButton() {
playPauseButton.setImage(UIImage(named: "pause-button-large")?.withRenderingMode(.alwaysTemplate), for: .normal)
}
if SongPlayer.shared.doesPreviewExist(spotifyPreviewUrl: spotifyURL, iTunesPreviewUrl: itunesURL) {
if SongPlayer.shared.player.timeControlStatus == .paused {
if SongPlayer.shared.previewUrl == spotifyURL {
setPlayButton()
print("paused with Spotify sample")
} else if SongPlayer.shared.previewUrl == itunesURL {
setPlayButton()
print("paused with iTunes sample")
}
} else if SongPlayer.shared.player.timeControlStatus == .playing {
if SongPlayer.shared.previewUrl == spotifyURL {
setPauseButton()
print("playing with Spotify sample")
} else if SongPlayer.shared.previewUrl == itunesURL {
setPauseButton()
print("playing with iTunes sample")
}
} else {
print("no audio sample")
}
}
}
}
You are creating a button (playPauseButton). But none of your code ever actually puts that button into the interface. Thus you never see it. You are changing the icon just fine, but the button is off in memory somewhere.
The cell is parallel. You are saying
let songDetailView = SongDetailCell()
But that just makes a new cell, off in space somewhere. It is not the cell that actually is present in the interface. So what you do to it has no visible effect (and then it just vanishes in a puff of smoke).
I am trying to implement favorite and unfavorite functionality into my cell. Here, Initially I am showing in collection view cell button icon favorite (gray), its mean unfavorite but after clicked the button it will show green, its mean favorite. From JSON I am getting response which cells are already favorite and others unfavorite icon. Its working fine but when I click JSON favorite enabled green button it’s not changing gray by single click, its working fine double click but every click I am calling JSON so 3rd click it will be add again favorite.
Here, below my code -
var checked = false
#IBAction func favoritesTapped(_ sender: Any) {
if checked {
favbutton.setImage(UIImage(named: "item_grayfav.png"), for: .normal)
checked = false
delegate?.unfavoriteButtonPressed(cell: self)
} else {
favbutton.setImage(UIImage(named: "item_greenfav.png"), for: .normal)
checked = true
delegate?.favoriteButtonPressed(cell: self)
}
}
Above code I am using for button click to change image and also initially I fixed gray checked = false
Into collection view cell at item
if indexPath.row < cfavoritesIDData.count {
if let i = cfavoritesIDData[indexPath.item] {
cell.favbutton.setImage(UIImage(named: "item_greenfav.png"), for: .normal)
}
else {
print("id is nil")
cell.favbutton.setImage(UIImage(named: "item_grayfav.png"), for: .normal)
}
}
Issues: If I click JSON enabled green button (favorite), its calling to unfavorite well but button icon not changing green after 3’rd click only its changing gray.
You need to store your cell model in your View Controller, the cell should not have a checked boolean, when you are configuring your cell in cellForRow: you should tell it there wether it is checked or not.
When pressing the button you should have a delegate callback or closure to tell the View Controller that the cell at IndexPath 'x' has pressed its favourite button and check the model as to wether or not the reaction would be checked or not, then reconfigure the cell again.
You can use the UIButton's selected property instead of checked variable:
if indexPath.row < favoritesIDData.count {
if let i = cfavoritesIDData[indexPath.item] {
cell.favbutton.setImage(UIImage(named: "item_greenfav.png"), for: .normal)
cell.favbutton.setSelected = true
}
else {
print("id is nil")
cell.favbutton.setImage(UIImage(named: "item_grayfav.png"), for: .normal)
cell.favbutton.setSelected = false
}
}
#IBAction func favoritesTapped(_ UIButton: favButton) {
if favButton.isSelected {
favbutton.setImage(UIImage(named: "item_grayfav.png"), for: .normal)
checked = false
delegate?.unfavoriteButtonPressed(cell: self)
} else {
favbutton.setImage(UIImage(named: "item_greenfav.png"), for: .normal)
checked = true
delegate?.favoriteButtonPressed(cell: self)
}
}
Note: Don't copy paste change according to correct syntax.
I have created two custom cells in tableview and on the second cell, I have a list of check box items. When I click on check box the tableview scrolls automatically.
Here is my code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row == 0
{
let cell:SharedFirstTableViewCell = self.ListTableview.dequeueReusableCellWithIdentifier("CellF")! as! SharedFirstTableViewCell
tableView.estimatedRowHeight = 364.0
tableView.rowHeight = UITableViewAutomaticDimension
tableView .separatorColor = UIColor .clearColor()
tableView .tableFooterView = UIView (frame:CGRectZero)
cell .selectionStyle = UITableViewCellSelectionStyle .None
cell.contentView .addSubview(collectionView)
cell.btn_SelectAll.tag=indexPath.row
cell.btn_SelectAll.addTarget(self, action:#selector(ShareViewController.SelectAll(_:)), forControlEvents: .TouchUpInside)
cell.btn_ByCountry.tag=indexPath.row
cell.btn_ByCountry.addTarget(self, action:#selector(ShareViewController.Country(_:)), forControlEvents: .TouchUpInside)
groupname = cell.txt_GroupName
if country.isEmpty == true {
cell.lbl_ByCountry.text = "By Country"
}
else
{
cell.lbl_ByCountry.text = country
}
if load == true
{
cell.btn_SelectAll.setImage((UIImage (named: "box.png")), forState: .Normal)
}
else
{
cell.btn_SelectAll.setImage((UIImage (named: "Full Image-30.png")), forState: .Normal)
}
return cell
}
else
{
let cellFirst:SharedTwoTableViewCell = self.ListTableview.dequeueReusableCellWithIdentifier("CellT")! as! SharedTwoTableViewCell
ListTableview.bounces = false
tableView.estimatedRowHeight = 57.0
tableView.rowHeight = UITableViewAutomaticDimension
tableView .separatorColor = UIColor .clearColor()
tableView .tableFooterView = UIView (frame:CGRectZero)
cellFirst.lbl_Name.text = newDictionary .objectAtIndex(indexPath.row).valueForKey("name") as? String
cellFirst.lbl_Address.text = newDictionary .objectAtIndex(indexPath.row).valueForKey("address") as? String
let url_image = newDictionary .objectAtIndex(indexPath.row).valueForKey("profile_pic") as? String
if url_image != nil {
imageURL = ""
imageURL = imageURLs+(url_image )!
cellFirst.img_profile.hnk_setImageFromURL(NSURL(string: imageURL)!)
}
cellFirst.tickButton.tag=indexPath.row
cellFirst.tickButton.addTarget(self, action:#selector(ShareViewController.tickClicked(_:)), forControlEvents: .TouchUpInside)
if selectedArray .containsObject(newDictionary.objectAtIndex(indexPath.row))
{
cellFirst.tickButton.setImage((UIImage (named: "box.png")), forState: .Normal)
}
else
{
cellFirst.tickButton.setImage((UIImage (named: "Full Image-30.png")), forState: .Normal)
}
return cellFirst
}
}
It's probably auto scrolling because your tickClicked handler is making a textfield a first responder.
You can disable this by disabling the table view's scrolling during this transition:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// This is solving the issue where making the text field first responder
// automatically scrolls the scrollview down by the height of the search bar.
if (!scrollView.isDragging && !scrollView.isDecelerating &&
self.searchField.isFirstResponder &&
(scrollView.contentOffset.y < -scrollView.contentInset.top)) {
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, -scrollView.contentInset.top) animated:NO];
}
}
Other methods can be found here:
Disable UIScrollView scrolling when UITextField becomes first responder
Disabling automatic scrolling of UITableView when editing UITextField inside UITableViewCell
As you said when you click on check box table going to scroll so problem maybe in tickClicked.
I'm new to iOS dev and I've been trying to toggle a button/checkbox in my tableviewcell by tapping the cell. My code is pretty naive, switching to the 'check' image works fine, but the issue is switching back to un-checked image when double tapping the cell.
What's not working in my code? Also is there a better way to do this? (perhaps via delegate funcs from the cell itself?)
Code examples would be very helpful! Thanks.
var isChecked:Bool = true
This following func is called in didSelectRowAtIndexPath func in my Tableview class.
let btnCheckBox:UIButton = cell.contactCheckbox
setState(button: btnCheckBox)
func setState(button: UIButton){
let check = UIImage(named: "check_green")
let unCheck = UIImage(named: "un_check_green")
if isChecked {
button.setImage(check, for: .normal)
isChecked = false
print("First Tap - Bool is \(isChecked)")
} else {
button.setImage(unCheck, for: .normal)
isChecked = true
print("Double Tap - Bool is \(isChecked)")
}
}
You should use UIButton , and set image for state of button, sample
check_green for selected
check_red for none
And each UITableviewcell must have an isSelected
My tableview displays podcast data. All cells are reused fine and display podcasts in the right order. I add download and play buttons as subviews of each cell. When I scroll down the list, the play button which is meant to be hidden for non-downloaded episodes will be shown and will hold the data of the cell which previously initiated a download. For example if I tap download button in cell 1, it will download the right episode and the play button will play the episode perfectly. If I scroll down to cell 10, the same play button will appear and will play episode from button 1. What's wrong with my code?
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell? {
let cell: PFTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! PFTableViewCell
if let title = object?["title"] as? String {
cell.textLabel?.text = title
let downloadButton = UIButton(type: UIButtonType.Custom)
downloadButton.frame = CGRectMake(cell.contentView.bounds.width - 100, cell.contentView.bounds.height / 2, 100, 35)
downloadButton.setTitle("Download", forState: .Normal)
downloadButton.setTitleColor(UIColor.blueColor(), forState: .Normal)
downloadButton.tag = indexPath.row
downloadButton.addTarget(self, action: "downloadEpisode:", forControlEvents: .TouchUpInside)
cell.addSubview(downloadButton)
let playButton = UIButton(type: UIButtonType.Custom)
playButton.frame = CGRectMake(cell.contentView.bounds.width - 100, cell.contentView.bounds.height - 89, 100, 35)
playButton.setTitle("Play", forState: .Normal)
playButton.setTitleColor(UIColor.blueColor(), forState: .Normal)
playButton.tag = indexPath.row
playButton.addTarget(self, action: "playEpisode:", forControlEvents: .TouchUpInside)
if let isDownloaded = object?["isDownloaded"] as? String {
if isDownloaded == "yes" {
playButton.hidden = false
} else {
playButton.hidden = true
}
}
cell.addSubview(playButton)
}
return cell
}
Edit:
I've also tried this but it doesn't work and still creates 1 more button per cell:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell? {
let cell: PFTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! PFTableViewCell
if let title = object?["title"] as? String {
cell.textLabel?.text = title
}
if let button = cell.viewWithTag(indexPath.row) {
print(button)
} else {
if let isDownloaded = object?["isDownloaded"] as? String {
if isDownloaded == "yes" {
let downloadButton = UIButton(type: UIButtonType.Custom)
downloadButton.frame = CGRectMake(cell.contentView.bounds.width - 100, cell.contentView.bounds.height / 2, 100, 35)
downloadButton.setTitle("Play", forState: .Normal)
downloadButton.setTitleColor(UIColor.blueColor(), forState: .Normal)
downloadButton.tag = indexPath.row
downloadButton.addTarget(self, action: "playEpisode:", forControlEvents: .TouchUpInside)
cell.addSubview(downloadButton)
} else {
let downloadButton = UIButton(type: UIButtonType.Custom)
downloadButton.frame = CGRectMake(cell.contentView.bounds.width - 100, cell.contentView.bounds.height / 2, 100, 35)
downloadButton.setTitle("Download", forState: .Normal)
downloadButton.setTitleColor(UIColor.blueColor(), forState: .Normal)
downloadButton.tag = indexPath.row
downloadButton.addTarget(self, action: "downloadEpisode:", forControlEvents: .TouchUpInside)
cell.addSubview(downloadButton)
}
}
}
return cell
}
Edit2:
Added download method:
func downloadEpisode(sender: UIButton) {
print("Downloading..")
let indexPath = NSIndexPath(forRow: sender.tag, inSection: 0)
let object = self.objectAtIndexPath(indexPath)
if let result = object {
let urlstring = result["downloadURL"] as? String
if urlstring != nil {
let episodeURL = NSURL(string: urlstring!)
downloader.downloadPodcastEpisode(episodeURL!, podcast: result)
}
}
}
Answer:
Although all answers were right, I have decided to remove the button download/play on cell taps in the end. Many thanks!
The problem is that you're adding a new button every time. When your cell is reused, it will already have a button. You need to write code that guarantees a button will only be created once, and after that, modify the existing button instead of creating a new one.
There are two common approaches to this:
Use viewWithTag to check if a button exists (create a button and set its tag if it doesn't)
Subclass UITableViewCell, create the button in the initializer, and configure the button each time the cell is used.
Searching for "uitableviewcell viewwithtag" or "subclass uitableviewcell" should give you plenty of sample code, so I won't rehash that here.
This happens because placement of a button in a cell in your code is a one-way street: once a button is added to the cell, it is never removed or made invisible.
You need to add an else to your if statement to remove the button from the recycled cell, if that button exists.
Better yet, make the button a permanent part of the "Cell", and control its visibility in the same if statement: instead of adding the button, make it visible; instead of removing the button, make it invisible. As an added bonus, this would let you set up the visual appearance for your button in interface builder, and set some constraints, which would help avoiding "magic numbers" such as 100, 89, and 35 in the body of your code.
Looking at your code its clear you are already subclassing UITableViewCell and have custom UI, then its recommended add button in side UITableViewCell either in initialization or if you have xib for cell then add UIButton in xib (via interface builder) and create an IBOutlet for newly added button .
Now when a cell will be reused you will have that button as well, when cell is being reused its your responsibility to keep its state right (means updating its UI), for that override prepareForReuse method inside custom tableViewCell class and reset UI of cell, and in cellForRowAtIndexPath you can update UI as per new data object.