I have a tableview with buttons, and I would like to create a UIActionSheet here when I click on the 3 dots button. It is a custome tableview cell.
My UITableViewCell:
import UIKit
class UserTableViewCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func view(with user: User){
nameLabel.text = user.getName();
}
#IBAction func btnMenu(_ sender: UIButton) {
//here I want to execute the UIActionSheet
}
#IBAction func btnDial(_ sender: UIButton) {
}
}
and in my view controller:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "userCell", for: indexPath) as? UserTableViewCell;
cell?.view(with: users[indexPath.row]);
return cell!;
}
Try this and do some changes in UserTableViewCell
class UserTableViewCell: UITableViewCell {
weak var myVC : UIViewController?
#IBAction func btnMenu(_ sender: UIButton) {
//here I want to execute the UIActionSheet
let actionsheet = UIAlertController(title: nil, message: nil, preferredStyle: UIAlertControllerStyle.actionSheet)
actionsheet.addAction(UIAlertAction(title: "Take a Photo", style: UIAlertActionStyle.default, handler: { (action) -> Void in
}))
actionsheet.addAction(UIAlertAction(title: "Choose Exisiting Photo", style: UIAlertActionStyle.default, handler: { (action) -> Void in
}))
actionsheet.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: { (action) -> Void in
}))
myVC?.present(actionsheet, animated: true, completion: nil)
}
}
And modify this method
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "userCell", for: indexPath) as? UserTableViewCell;
cell?.view(with: users[indexPath.row]);
cell?.myVC = self
return cell!;
}
make outlets of button in cell class
then in tableView where you are using this cell write the code below in cellForRowAtIndexPath
cell.yourButton.addTarget(self, action: #selector(yourButtonTapped), for: .touchDown)
and now in your yourButtonTapped method write actionSheet code following the link :
UIActionSheet iOS Swift
hope its help
Closure Approach
1 - Declare your actionBlock in your UserTableViewCell
var actionClosure : (()->Void)? = nil
2 - Execute your action block in your Cell Action
#IBAction func btnMenu(_ sender: UIButton) {
//here I want to execute the UIActionSheet
self.actionClosure?()
}
3 - Setup your cell block action adjusting your cellForRowAtIndexPath method
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "UserTableViewCell", for: indexPath) as! UserTableViewCell
cell.actionClosure = { [weak self] in
//SHow your ActionSheet Here
}
return cell
}
Full Code
CellForRow implementation
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "UserTableViewCell", for: indexPath) as! UserTableViewCell
cell.actionClosure = { [weak self] in
//SHow your ActionSheet Here
}
return cell
}
TableView Cell
import UIKit
class UserTableViewCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
var actionClosure : (()->Void)? = nil
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func view(with user: User){
nameLabel.text = user.getName();
}
#IBAction func btnMenu(_ sender: UIButton) {
//here I want to execute the UIActionSheet
self.actionClosure?()
}
#IBAction func btnDial(_ sender: UIButton) {
}
}
just add onMenubtnClick method in your ViewControler instead of cell.
add this in your cellForRowAt method
cell.youtBtn.addTarget(self, action: #selector(self.btnMenu(_:)), for: .touchUpInside)
add this code in your ViewController
#IBAction func btnMenu(_ sender: UIButton) {
//here I want to execute the UIActionSheet
}
Related
I'm trying to have the following animation in my custom UITableViewCell:
When a user checks off a task, the progressBar should go from 0.0 to 1.0 in 5 seconds
What I've tried
Doing the animation in the delegate method in the TableVC:
let cell = tableView.cellForRow(at: IndexPath(item: indexSection!, section: indexRow!)) as? TaskCell
cell?.progressBar.setProgress(1.0, animated: true)
This doesn't work because it seems like the cell doesn't exist (print(cell!) gives a fatal error)
Doing the animation in TaskCell.swift
#IBAction func checkBoxAction(_ sender: Any) {
if items![indexRow!].checked {
delegate?.changeButton(state: false, indexSection: indexSection!, indexRow: indexRow!, itemID: itemID)
UIView.self.animate(withDuration: 1.0) {
self.progressBar.setProgress(0.0, animated: true)
}
} else {
delegate?.changeButton(state: true, indexSection: indexSection!, indexRow: indexRow!, itemID: itemID)
UITableViewCell.animate(withDuration: 5.0) {
self.progressBar.setProgress(1.0, animated: true)
}
}
}
This does set the progress bar, but it doesn't animate it. The progress bar abruptly changes. This is what happens
Could anyone shine a light on what I'm doing wrong? Am I calling the animation function incorrectly or am I doing it in the wrong place?
Thanks,
Matt
I have just created a simplified example and for me the following code works:
CustomTableViewCell
class CustomTableViewCell: UITableViewCell
#IBOutlet weak var progressView: UIProgressView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
#IBAction func startButtonDidTap(_ sender: UIButton) {
UIView.animate(withDuration: 2) {
self.progressView.setProgress(0.5, animated: true)
}
}
}
ViewController
class ViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "reuseID", for: indexPath) as? CustomTableViewCell else { return UITableViewCell() }
return cell
}
}
Of course you need a proper storyboard configuration in order to run my code...
This question already has answers here:
Get button click inside UITableViewCell
(18 answers)
Closed 3 years ago.
I have a UITableView list with a button which says "Click Me!". I tried following this answer below: https://stackoverflow.com/a/53043358/7746248 to tie the button to an action, but that didn't work for unknown reasons.
I have checked other ways to tie a button to an event, I have had no luck.
import UIKit
class SampleTableViewCell: UITableViewCell {
#IBOutlet weak var name: UILabel!
#IBOutlet weak var button: UIButton!
var tapCallback: (() -> Void)?
#IBAction func didTap(_ sender: Any) {
tapCallback?()
}
}
class TableViewController: UITableViewController {
var tableArray = ["New York", "Chicago", "North Island"]
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
self.clearsSelectionOnViewWillAppear = false
self.tableView.dataSource = self
self.tableView.delegate = self
self.tableView.reloadData()
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return self.tableArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SampleCell", for: indexPath) as! SampleTableViewCell
// Configure the cell...
cell.selectionStyle = UITableViewCell.SelectionStyle.none
let names = self.tableArray[indexPath.row]
cell.name.text = names
cell.tapCallback = {
// do stuff
DispatchQueue.main.async {
let alert = UIAlertController(title: "title", message: "Button Clicked!", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Close", style: .cancel, handler: nil))
self.present(alert, animated: true)
}
}
return cell
}
}
Any other simple way to do this?
Add target inside cellForRowAt like below
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SampleCell", for: indexPath) as! SampleTableViewCell
// Configure the cell...
cell.selectionStyle = UITableViewCell.SelectionStyle.none
let names = self.tableArray[indexPath.row]
cell.name.text = names
cell. button.addTarget(self, action: #selector(alertMethod), for: .touchUpInside)
return cell
}
#objc fileprivate func alertMethod() {
let alert = UIAlertController(title: "title", message: "Button Clicked!", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Close", style: .cancel, handler: nil))
self.present(alert, animated: true)
}
}
So I have a custom SwipeCellTableView class that I inherited from when using UITableViewControllers. Now I want to just use that class for an ib outlet table view controller in a regular View Controller. It is proving to be very difficult and seemingly not worth it anymore. Can this be done?
Here is the superclass which inherits from a TableViewController, I have tried to change it to inherit from a view controller but it just doesn't work out
class SwipeTableViewController: UITableViewController, SwipeTableViewCellDelegate {
var cell: UITableViewCell?
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = 80.0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! SwipeTableViewCell
cell.delegate = self
return cell
}
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
self.updateModel(at: indexPath)
}
deleteAction.image = UIImage(named: "delete-icon")
return [deleteAction]
}
func tableView(_ tableView: UITableView, editActionsOptionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> SwipeOptions {
var options = SwipeTableOptions()
options.expansionStyle = .destructive
//options.transitionStyle = .reveal
return options
}
func updateModel(at indexPath: IndexPath){
//update data model
print("Item deleted from super class")
}
Here is the View Controller I'm trying to access it from:
class GoalsViewController: UIViewController, SwipeTableViewController {
#IBOutlet weak var categoryTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func addCategoryPressed(_ sender: UIButton) {
performSegue(withIdentifier: "showgoalsSeg", sender: self)
}
For reference on how I was using it before when using an actual TableViewController:
class CategoryViewController: SwipeTableViewController {
var categories: Results<Category>? //optional so we can be safe
override func viewDidLoad() {
super.viewDidLoad()
loadCategory()
tableView.rowHeight = 80.0
tableView.separatorStyle = .none
}
//MARK: - Tableview Datasource Methods
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//Only get the count of categories if it's nil, else 1
return categories?.count ?? 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//fetching cell from super view
let cell = super.tableView(tableView, cellForRowAt: indexPath)
cell.textLabel?.text = categories?[indexPath.row].name ?? "No Categories Added Yet"
cell.backgroundColor = UIColor(hexString: categories?[indexPath.row].color ?? "000000")
return cell
}
//MARK: - Tableview Delegate Methods
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "goToItems", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destinationVC = segue.destination as! ToDoListViewController
if let indexPath = tableView.indexPathForSelectedRow {
destinationVC.selectedCategory = categories?[indexPath.row]
}
}
//MARK: - Add New Categories
#IBAction func addButtonPressed(_ sender: Any) {
var textField = UITextField()
let alert = UIAlertController(title: "Add New Category", message: "", preferredStyle: .alert)
let action = UIAlertAction(title: "Add Category", style: .default) { (action) in
let newCategory = Category()
newCategory.name = textField.text!
newCategory.color = UIColor.randomFlat.hexValue()
self.save(category: newCategory)
}
alert.addAction(action)
alert.addTextField { (field) in
textField = field
textField.placeholder = "Add a new category"
}
present(alert, animated: true, completion: nil)
}
func save(category: Category){
let realm = try! Realm()
do {
try realm.write{
realm.add(category)
}
} catch {
print("error saving context")
}
tableView.reloadData()
}
override func updateModel(at indexPath: IndexPath) {
super.updateModel(at: indexPath)
let realm = try! Realm()
if let categoryForDeletion = self.categories?[indexPath.row]{
do{
try realm.write{
realm.delete(categoryForDeletion)
}
} catch {
print("error deleting cell")
}
//tableView.reloadData()
}
}
func loadCategory(){
let realm = try! Realm()
categories = realm.objects(Category.self)
tableView.reloadData()
}
Is this even worth persuing? Or doable?
I'm beginner at Xcode and I need some help with viewcell in table view.
This is the properties for my viewcell:
When i tried to run the simulator it become something like this:
my viewcontroller
import UIKit
import SwiftyJSON
import Kingfisher
class CakeViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
//MARK : Properties
var cakeArray = [Cake]()
#IBOutlet weak var testImage: UIImageView!
#IBOutlet weak var tableView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cakeArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "CakeTableViewCell"
// let cell = tableView.dequeueReusableCell(withIdentifier: "CakeCategoryTableViewCell"/*Identifier*/, for: indexPath)
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? CakeTableViewCell else {
fatalError("The dequeued cell is not an instance of cakeViewCell.")
}
//cell.textLabel?.text = cake_category[indexPath.row]
let cakeObj = cakeArray[indexPath.row]
cell.cakeLabel.text = cakeObj.product
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
print("Load Initial Data")
loadInitialDataFromJson(category : "Reguler Cake")
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadInitialDataFromJson(category:String)
{
APIManager.sharedInstance.getCakeByCategory(category: category, onSuccess: {json in DispatchQueue.main.async {
let status = json["status"].stringValue
//let message = json["message"].stringValue
if status == "OK"
{
for (key, subJson) in json["list_produk"] {
//print(subJson["id_product"])
var arrayVariant = [Variant]()
for(key,subsJson) in subJson{
arrayVariant.append(Variant(size:subsJson["size"].stringValue,price:subsJson["price"].intValue)!)
}
self.cakeArray.append(Cake(id_product:subJson["id_product"].stringValue,product:subJson["product"].stringValue,description:subJson["description"].stringValue,imageURL:subJson["images"].stringValue,variant:arrayVariant)!)
}
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 150
self.tableView.reloadData()
}else{
print("Not Ok")
}
}}, onFailure: {error in let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Dismiss", style: .default, handler: nil))
self.show(alert, sender: nil)
})
}
/*
// 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.
}
*/
}
and my tableviewcell
import UIKit
class CakeTableViewCell: UITableViewCell {
// MARK : Properties
#IBOutlet weak var cakeImage: UIImageView!
#IBOutlet weak var cakeLabel: UILabel!
#IBAction func addToCart(_ sender: UIButton) {
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Any idea why this happening??
I've created some views with tableview and it has worked normally.
to adjust your cell hight call in your cakeViewController
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
return 100.0;//Choose your custom row height
}
I tried to use this solution but is not working.
what I want i s a button in my customized cell that knows data from the array that is used from the tableview (later I'll apply it to CoreData), for example, print the value of the array that generated the tableview.
but I cannot understand how to do it
I have a customized cell class, where I tried to use both a button action or a outlet button (with tags):
import UIKit
class MyTableViewCell: UITableViewCell {
#IBOutlet weak var myCellImage: UIImageView!
#IBOutlet weak var myCellLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
and a ViewController where is my tableview with an extension
extension ViewController {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return comicsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = myTableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
cell.myCellLabel.text = String(indexPath.row)
cell.myCellButton.tag = indexPath.row
cell.myCellButton.addTarget(self, action: Selector("logAction:"), for: .touchUpInside)
return cell
}
func logAction(sender: UIButton) {
let titleString = self.comicsArray[sender.tag]
let firstActivityItem = "\(titleString)"
let activityVC = UIActivityViewController(activityItems: [firstActivityItem], applicationActivities: nil)
self.present(activityVC, animated: true, completion: nil)
}
}
EDIT:
solved with help of abdullahselek by adding in subclasses cell:
public var dataFromTableView : String!
and implementing:
#IBAction func myCellButtonTapped(_ sender: UIButton) {
guard dataFromTableView != nil else {return}
print("pushed \(dataFromTableView!)")
}
and in cellForRowAt :
cell.data = comicsArray[indexPath.row]
Add a dictionary or object model property to your custom tableviewcell like
public var data: Dictionary!
And set this data property
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
cell.data = comicsArray[indexPath.row]
}
Then you button can reach data with in your tableviewcell