Can't get Cell Swipe Actions to work - swift3

It's been awhile since I've made a fool of myself here, so here goes again as I'm still trying to learn and feel as though I'm chasing my tail on this project.
With that said, ultimately I would like to get Jerkoch's SwipeCellKit to work, but not necessary at this point as I just need to figure out how to add more options other than just the 'delete' cell action.
This is what I've got: My Project
This is what I'd like: Jerkoch's Project
I loose my data if I set the delegate property on tableView from
let cell = tableView.dequeueReusableCell(withIdentifier: "CommentCell", for: indexPath) as! CommentTableViewCell
to
let cell = tableView.dequeueReusableCell(withIdentifier: "CommentCell", for: indexPath) as! SwipeTableViewCell
Here is my CommentViewController Code:
import UIKit
import SwipeCellKit
class CommentViewController: UIViewController {
var postId: String!
var comments = [Comment]()
var users = [User]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView(frame: CGRect.zero)
tableView.separatorInset = UIEdgeInsets.zero
tableView.dataSource = self
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
loadComments()
}
func loadComments() {
Api.Post_Comment.REF_POST_COMMENTS.child(self.postId).observe(.childAdded, with: {
snapshot in
Api.Comment.observeComments(withPostId: snapshot.key, completion: {
comment in
self.fetchUser(uid: comment.uid!, completed: {
self.comments.append(comment)
self.tableView.reloadData()
})
})
})
}
func fetchUser(uid: String, completed: #escaping () -> Void ) {
Api.User.observeUser(withId: uid, completion: {
user in
self.users.append(user)
completed()
})
}
}
extension CommentViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return comments.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CommentCell", for: indexPath) as! CommentTableViewCell// Can't update this to SwipeTableViewCell - Will loose data
cell.selectedBackgroundView = createSelectedBackgroundView()
let comment = comments[indexPath.row]
let user = users[indexPath.row]
cell.comment = comment
cell.user = user
cell.delegate = self
return cell
}
}
extension CommentViewController: SwipeTableViewCellDelegate{
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? {
guard orientation == .right else { return nil }
let deleteAction = SwipeAction(style: .destructive, title: "Delete") { action, indexPath in
// handle action by updating model with deletion
}
// customize the action appearance
deleteAction.image = UIImage(named: "delete")
let flagAction = SwipeAction(style: .destructive, title: "Flag") { action, indexPath in
// handle action
}
deleteAction.image = UIImage(named: "flag")
let blockAction = SwipeAction(style: .destructive, title: "Block") { action, indexPath in
// handle action
}
deleteAction.image = UIImage(named: "block")
return [deleteAction, flagAction, blockAction]
}
}
I've even tried something like the following:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
// THIS GIVES THE DEFAULT 'DELETE' ACTION
print("Commit editingStyle")
}
func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
print("editActionsForRowAtIndexPath")
let blockAction = UITableViewRowAction(style: UITableViewRowActionStyle.normal, title: "Block", handler: { (action:UITableViewRowAction, IndexPath:IndexPath) in
//self.isEditing = false
print("Block action Pressed")
})
let flagAction = UITableViewRowAction(style: UITableViewRowActionStyle.normal, title: "Flag", handler: { (action:UITableViewRowAction, IndexPath:IndexPath) in
print("Flag action Pressed")
})
let deleteAction = UITableViewRowAction(style: UITableViewRowActionStyle.destructive, title: "Delete", handler: { (action:UITableViewRowAction, IndexPath:IndexPath) in
print("Delete action Pressed")
})
return [blockAction, flagAction, deleteAction]
}
No matter what I do, I can't get more than just the generic 'Delete' action to show on a swipe action.
I don't even think the following function is being called/referenced:
func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
What am I doing wrong, or what do I need to CUD to get 3 swipe actions; Delete, Flag and Block, to make Apple happy for my next submission.
Thank you all in advance!

This question is outdated but i'll write the answer which i think would have done the trick :)
You should be using UITableViewController not UIViewController for your view. SwipeCellKit only works with TableViewControllers.

Related

How to get selected index of items in more navigation controller

I use below code for get selected items of tab bar controller. My UITabbar has 7 view controllers(there are 3 items in More tab).
this code work only for 5 tabs but it don`t return selected index of items on More!
import UIKit
class CustomTabbarController: UITabBarController{
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
print(self.selectedIndex)
}
}
Following code worked for me. I had to redesign moreTableView to follow my app design. Function 'didSelectRowAt' returns what index is selected.
This code is added to 'UITabBarController' class.
var moreTableView: UITableView?
weak var currentTableViewDelegate: UITableViewDelegate?
func customizeMoreTableView() {
moreTableView = moreNavigationController.topViewController?.view as? UITableView
currentTableViewDelegate = moreTableView?.delegate
moreTableView?.delegate = self
moreTableView?.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (viewControllers?.count ?? 4) - 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let moreCell = UITableViewCell()
let item = viewControllers?[indexPath.row + 4].tabBarItem
moreCell.textLabel?.text = item?.title
moreCell.textLabel?.textColor = .white
moreCell.imageView?.image = item?.image
moreCell.imageView?.tintColor = .white
moreCell.backgroundColor = .black
return moreCell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
currentTableViewDelegate?.tableView!(tableView, didSelectRowAt: indexPath)
}
Get selected item like this :
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
print(tabBar.items?.index(of: item))
}

Presenting a UIViewController with a button or UIImageview in a UITableViewCell

I am trying to make it when the users clicks on Button in a table view cell it takes them to a new view controller related to the current cell.
Try the below code,
Do this in cellForRowAt indexPath,
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell_Identifier", for: indexPath) as! UITableViewCell
cell.myButton.isUserInteractionEnabled = true
cell.myButton.tag = indexPath.row
cell.myButton.addTarget(self, action: #selector(didTapMyButton(_:)), for: UIControlEvents.touchUpInside)
return cell
}
And here is the button action,
func didTapMyButton(_ sender : UIButton) {
let selectedIndex = sender.tag // You can get index here, so according to the index you can navigate to particular ViewController
let nextVC = self.storyboard?.instantiateViewController(withIdentifier: "UIViewController_identifier") as! UIViewController
self.present(nextVC, animated: true, completion: nil)
}
Hope it helps ..

Getting Values from UISliders alongside UserID (linking User to section number?)

I'm working on creating a 'Rate' feature in my app where it pulls the users to rate from an external API. I then create a TableView which adds cells that contain a section name (the name of the User) and a UISlider.
What I'm trying to do is gather the data from the UISliders along with the User ID associated with the User who's section it is so that I can call a POST action to the API to send the rating data too.
Here's the code I have: (For the record, first time with Swift, the comments are me trying to figure out how to do this). Any ideas?
import UIKit
import Alamofire
class RateViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var submitButton: UIButton!
#IBOutlet weak var activeSurveyLabel: UILabel!
private var myTableView: UITableView!
var usersToRate = [User]()
var activeSurveyId: String?
var rateData = [String: Int]()
var sectionIdToUserId = [Int: String]()
var userIdSection = [Int: String]()
override func viewDidLoad() {
super.viewDidLoad()
submitButton.isHidden = true
submitButton.addTarget(self, action: #selector(pressSubmitButton(button:)), for: UIControlEvents.touchUpInside)
activeSurveyLabel.isHidden = false
self.loadUsersToRate()
Alamofire.request(RateRouter.getSurvey()).responseJSON { response in
debugPrint(response)
if let string = response.result.value as? [[String: Any]]{
if string.count == 0 {
return
}
self.submitButton.isHidden = false
self.activeSurveyLabel.isHidden = true
for survey in string {
if let survey = Survey(json: survey) {
self.activeSurveyId = survey._id!
print(survey._id!)
}
}
//if there's a result, show slider bars for each person on the team with rating scales (except for the person logged in)
//have a submit button with a post request for the votes
let barHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
let displayWidth: CGFloat = self.view.frame.width
let displayHeight: CGFloat = barHeight * CGFloat(self.usersToRate.count * 5)
self.myTableView = UITableView(frame: CGRect(x: 0, y: barHeight, width: displayWidth, height: displayHeight - barHeight))
self.myTableView.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
self.myTableView.dataSource = self
self.myTableView.delegate = self
self.view.addSubview(self.myTableView)
} else {
print(response.result.error!)
let label = UILabel()
label.center = self.view.center
label.text = "No Active Surveys"
self.view.addSubview(label)
}
}
// Do any additional setup after loading the view.
}
func loadUsersToRate() {
Alamofire.request(RateRouter.getUsers()).responseJSON { response in
guard let jsonArray = response.result.value as? [[String: Any]] else {
print("didn't get array, yo")
return
}
for item in jsonArray {
if let user = User(json: item) {
self.usersToRate.append(user)
self.rateData.updateValue(5, forKey: user.userId!) //initialize user average data at 5 in case they don't change it
print (self.rateData)
}
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Num: \(indexPath.row)")
print("Value: \(usersToRate[indexPath.row])")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
self.sectionIdToUserId.updateValue(usersToRate[section].userId!, forKey: section)
print(self.sectionIdToUserId)
return "\(usersToRate[section].name!)"
}
func numberOfSections(in tableView: UITableView) -> Int {
return usersToRate.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath as IndexPath)
let slider = UISlider(frame: CGRect(x: 0, y:0, width: 400, height: 44))
slider.isUserInteractionEnabled = true
slider.minimumValue = 1
slider.maximumValue = 10
slider.value = 5
slider.tag = indexPath.section
slider.addTarget(self, action: #selector(sliderValueChange(sender:)), for: UIControlEvents.valueChanged)
cell.addSubview(slider)
return cell
}
func sliderValueChange(sender: UISlider) {
//get slider value
var currentValue = Int(sender.value)
print(currentValue)
//self.rateData.updateValue(currentValue, forKey: self.sectionIdToUserId.values[sender.])
//get user id for that value
//self.rateData.updateValue(currentValue, rateData[section])
// self.rateData.updateValue(currentValue, forKey: 0)
}
func pressSubmitButton(button: UIButton) {
for user in usersToRate {
// Alamofire.request(RateRouter.vote(voteFor: user.userId!, survey: activeSurveyId, rating:))
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
To answer my own question, what I did was create a different model called 'Votes' that stored the information for each vote.
Then to fill that array I created a tag for the slider in the tableView based on the section ID and used that to update the current value.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath as IndexPath)
let slider = UISlider(frame: CGRect(x: 0, y:0, width: cell.frame.width , height: 44))
slider.tag = indexPath.section
slider.addTarget(self, action: #selector(sliderValueChange(sender:)), for: UIControlEvents.valueChanged)
cell.addSubview(slider)
return cell
}
func sliderValueChange(sender: UISlider) {
//get slider value
var currentValue = Int(sender.value)
print(currentValue)
voteData[sender.tag].rating = currentValue
}

Swift 3 tableViewCell Button, strange behaviour, submit one button and others of the same Cell change as well?

Repeated on two projects and searched in vain so I hope someone can help...
Created a basic TableViewController and one TableViewCell. Run and press the first button, scroll down and other random buttons also show the same change?
TableViewController
import UIKit
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 99
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ButtonCell", for: indexPath) as! ButtonCell
cell.rowLabel.text = "\(indexPath.row)"
cell.tapAction = { (cell) in
self.showAlertForRow(tableView.indexPath(for: cell)!.row)
}
return cell
}
// MARK: - Extracted method
func showAlertForRow(_ row: Int) {
let alert = UIAlertController(
title: "BEHOLD",
message: "Cell at row \(row) was tapped!",
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Gotcha!", style: UIAlertActionStyle.default, handler: { (test) -> Void in
self.dismiss(animated: true, completion: nil)
}))
self.present(
alert,
animated: true,
completion: nil)
}
}
Table View Cell
// ButtonCell.swift
import UIKit
class ButtonCell: UITableViewCell {
var tapAction: ((UITableViewCell) -> Void)?
#IBOutlet weak var rowLabel: UILabel!
#IBOutlet weak var button: UIButton!
#IBAction func buttonTap(_ sender: AnyObject) {
button.setTitle("Correct", for: .normal)
tapAction?(self)
}
}
Lets begin with the basics. You don't need this:
cell.tapAction = { (cell) in
self.showAlertForRow(tableView.indexPath(for: cell)!.row)
}
You better use the apple framework:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
showAlertForRow(indexPath.row)
}
Second create a method in you cell like
import UIKit
class ButtonCell: UITableViewCell {
var tapAction: ((UITableViewCell) -> Void)?
#IBOutlet weak var rowLabel: UILabel!
#IBOutlet weak var button: UIButton!
#IBAction func buttonTap(_ sender: AnyObject) {
button.setTitle("Correct", for: .normal)
tapAction?(self)
}
func createCell() {
button.setTitle("Your button title", for: normal)
}
}
and call the createCell method in your
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
cell.createCell()
...
}
Hope I could help but try to start with the basics here: https://developer.apple.com/reference/uikit/uitableviewdelegate
This is happening because you are dequeuing your UITableViewCells. In order to solve the problem you should use the prepareForReuse() method in your ButtonCell. You can save the states of all your buttons and when a cell is being dequeued you can just set the title of the button in this cell in the prepareForReuse() method.

Issue with "didSelectRowAt indexPath" in Swift 3

I'm pretty new to swift but I've managed to follow along for the most part. However, there's an issue with my code that the code itself can't even identity apparently(I'm not receiving any error signs). I'm trying to click on a row from the table view to make it go to the following page but I don't think the code is recognizing the didselectrow method. Maybe one of you more experienced persons can help me out.
Here's the code:
import UIKit
import Foundation
class OnePercentersViewController: UIViewController, UITableViewDataSource
{
var copiedExecutiveArray : [String] = NSArray() as! [String]
var identities : [String] = NSArray() as! [String]
override func viewDidLoad()
{
super.viewDidLoad()
let copyExecutiveNames : ExecutiveArray = ExecutiveArray()
copiedExecutiveArray = copyExecutiveNames.executiveNames
identities = copyExecutiveNames.executiveNames
}
//how many sections in table
func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
//returns int (how many rows)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return copiedExecutiveArray.count
}
//contents of each cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")//UITableViewCell()
let personName = copiedExecutiveArray[indexPath.row]
cell?.textLabel?.text = personName
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let execName = identities[indexPath.row]
let viewController = storyboard?.instantiateViewController(withIdentifier: execName)
self.navigationController?.pushViewController(viewController!, animated: true)
print("button clicked")
tableView.deselectRow(at: indexPath as IndexPath, animated: true)
}
}
I haven't checked other parts of your code, but to make tableView(_:didSelectRowAt:) work, your ViewController needs to conform to UITableViewDelegate:
class OnePercentersViewController: UIViewController, UITableViewDataSource, UITableViewDelegate
Also you need to connect the delegate of the UITableView to your ViewController in your storyboard. Or you can do it in code, if you have an #IBOutlet to the UITableView. Anyway, not only dataSource, but also delegate.
And this is not a critical issue, but you can write the first two lines of the class as:
var copiedExecutiveArray : [String] = []
var identities : [String] = []
After spending a day and a half watching Youtube videos and reading comments to similar problems, tt took trial and error but I finally got it thanks to you guys!It finally clicked when I read that I had to connect the delegate to the ViewController through the storyboard, so thanks for that!
Here's the final code for those with similar issue:
import UIKit
import Foundation
class OnePercentersViewController: UIViewController,UITableViewDataSource, UITableViewDelegate
{
var copiedExecutiveArray : [String] = []
var identities : [String] = []
override func viewDidLoad()
{
super.viewDidLoad()
let copyExecutiveNames : ExecutiveArray = ExecutiveArray()
copiedExecutiveArray = copyExecutiveNames.executiveNames
identities = copyExecutiveNames.executiveNames
}
//how many sections in table
func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
//returns int (how many rows)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return copiedExecutiveArray.count
}
//contents of each cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let personName = copiedExecutiveArray[indexPath.row]
cell.textLabel?.text = personName
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let execName = identities[indexPath.row]
let viewController = storyboard?.instantiateViewController(withIdentifier: execName)
self.navigationController?.pushViewController(viewController!, animated: true)
tableView.deselectRow(at: indexPath as IndexPath, animated: true)
}
}