let cell = tableView.dequeueReusableCell(withIdentifier: CellButton.identifier(), for: indexPath) as! CellButton
cell.build(height: 40, color: .lighterGray())
cell.blockAction = {
let category = categories?[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier:
CellCheck.identifier(), for: indexPath) as!
CellCheck
}
return cell
This is the code I have and when blockAction called (function called when i tap button in cell) I need to build another TableViewCell and remove first that appear
Is there any way of loading it inside same cell and also expand the height by the height of dynamic cell content
You could remove IndexPath and then add add a new indexPath.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
private var buttonTapped: Bool = false
private var selectedIndexPath: IndexPath?
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "TableViewCell")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if buttonTapped == true,
let olderIndexPath = selectedIndexPath,
selectedIndexPath == olderIndexPath {
let cell = UITableViewCell()
cell.backgroundColor = .red
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell
cell.callback = {[weak self] in
self?.buttonTapped = true
self?.selectedIndexPath = indexPath
self?.insertNewCell(indexPath)
}
return cell
}
private func insertNewCell(_ indexPath: IndexPath) {
tableView.beginUpdates()
tableView.deleteRows(at: [indexPath], with: .right)
tableView.insertRows(at: [indexPath], with: .left)
tableView.endUpdates()
}
}
Related
By clicking on the red area I get a comment id. But I also want to get the id if I click on the blue button. How can I do that?
Right now I use this to detect a tap on a row. But tapping on button should run some other code.
extension FirstTabSecondViewComment: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
FirstTabSecondViewComment.subComment = table[indexPath.row].commentId ?? ""
print(FirstTabSecondViewComment.subComment)
self.performSegue(withIdentifier: "CommentDetail", sender: Any?.self)
}
}
After you have register your custom cell declare it in cellForRowAt and add target in button cell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! MyCell // your custom cell
// add target in buoon cell
cell.YourButtonCell.addTarget(self, action: #selector(submit(_:)), for: .touchUpInside)
return cell
}
after that add submit func:
#objc fileprivate func submit(_ sender: UIButton) {
var superview = sender.superview
while let view = superview, !(view is UITableViewCell) {
superview = view.superview
}
guard let cell = superview as? UITableViewCell else {
print("button is not contained in a table view cell")
return
}
guard let indexPath = tableView.indexPath(for: cell) else {
print("failed to get index path for cell containing button")
return
}
// We've got the index path for the cell that contains the button, now do something with it.
print("button is in index \(indexPath.row)")
}
This is a full code example, copy and paste in a new project and run:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let tableView = UITableView()
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
tableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
tableView.delegate = self
tableView.dataSource = self
tableView.register(MyCell.self, forCellReuseIdentifier: cellId)
}
#objc fileprivate func submit(_ sender: UIButton) {
var superview = sender.superview
while let view = superview, !(view is UITableViewCell) {
superview = view.superview
}
guard let cell = superview as? UITableViewCell else {
print("button is not contained in a table view cell")
return
}
guard let indexPath = tableView.indexPath(for: cell) else {
print("failed to get index path for cell containing button")
return
}
// We've got the index path for the cell that contains the button, now do something with it.
print("button is in index \(indexPath.row)")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 80
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! MyCell
// add target in buoon cell
cell.cancelButton.addTarget(self, action: #selector(submit(_:)), for: .touchUpInside)
return cell
}
}
class MyCell: UITableViewCell {
let cancelButton: UIButton = {
let b = UIButton(type: .system)
b.backgroundColor = .white
b.setTitle("get Index", for: .normal)
b.layer.cornerRadius = 10
b.clipsToBounds = true
b.titleLabel?.font = .systemFont(ofSize: 16, weight: .semibold)
b.tintColor = .black
b.translatesAutoresizingMaskIntoConstraints = false
return b
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.backgroundColor = .darkGray
contentView.addSubview(cancelButton)
cancelButton.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
cancelButton.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
cancelButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
cancelButton.widthAnchor.constraint(equalToConstant: 300).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I am working on a location-based reminder app. I show all reminders that user created on a table view. I have also UISwitch on every cell. I want that UISwitch disables/enables reminders individually, not all notifications. I couldn't figure it out.
extension MainViewController: UITableViewDataSource{
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath) as! itemTableViewCell
let item = self.items![indexPath.row]
cell.itemTitle?.text = item.itemName
cell.itemSubTitle?.text = item.itemDescription
//switch
let swicthView = UISwitch(frame: .zero)
swicthView.onTintColor = UIColor (named: "DingerBlue")
swicthView.setOn(true, animated: true)
swicthView.tag = indexPath.row
swicthView.addTarget(self, action: #selector(self.SwitchBtn(_:)), for: .valueChanged)
cell.accessoryView = swicthView
let itemToRemove = self.items![indexPath.row]
let notifToRemove: String = itemToRemove.notifID!
return cell
}
#objc func switchDidChanged(_ sender: UISwitch){
print("Switch value is \(sender.isOn)")
if(sender.isOn){
print("on")
UIApplication.shared.registerForRemoteNotifications()
}
else{
print("Off")
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [notifToRemove])
}
}
}
I believe your code is not working because cells are reused and actions on specific cells should be handled from the cell class rather than from ViewController. Move this code into the UITableViewCell code.
let swicthView = UISwitch(frame: .zero)
swicthView.onTintColor = UIColor (named: "DingerBlue")
swicthView.setOn(true, animated: true)
swicthView.tag = switchViewTag!
swicthView.addTarget(self, action: #selector(self.SwitchBtn(_:)), for: .valueChanged)
#objc func switchDidChanged(_ sender: UISwitch){
print("Switch value is \(sender.isOn)")
if(sender.isOn){
print("on")
UIApplication.shared.registerForRemoteNotifications()
}
else{
print("Off")
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [notifToRemove])
}
}
and add a new property in the UITableViewCell
weak var switchViewTag: Int?
Modify your cellForRowAt delegate method to
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath) as! itemTableViewCell
let item = self.items![indexPath.row]
cell.itemTitle?.text = item.itemName
cell.itemSubTitle?.text = item.itemDescription
cell.switchViewTag = indexPath.row
return cell
}
The code below creates a tableviewcell and places a object in it. The code to place data inside the tableviewcell is not present. All I want to do is when the user clicks on the tableviewcell it to be transfers to a basic view controller class. No need to show how to go back.
var itemName : [NSManagedObject] = []
var theField = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
theField.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let title = itemName[indexPath.row]
let cell = theField.dequeueReusableCell(withIdentifier: "MyCell", for : indexPath)
cell.selectionStyle = .default
let attr1 = title.value(forKey: "name") as? String
let text = [attr1].flatMap { $0 }.reduce("", +)
cell.textLabel?.text = "\(text)"
cell.textLabel?.textAlignment = .center
cell.layoutMargins = UIEdgeInsets.zero
cell.preservesSuperviewLayoutMargins = false
cell.separatorInset = UIEdgeInsets.zero
cell.layoutMargins = UIEdgeInsets.zero
return cell
}
#objc func moveRight() {
//pass tableview cell
let vce = fullScreen()
vce.text = UITableViewCell
vce.modalPresentationStyle = .overCurrentContext // actually .fullScreen would be better
vce.present(vce, animated: true)}
Assume you have an array titles
cell.textLabel?.text = titles[indexPath.row]
and then take action in this method:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
moveRight(titles[indexPath.row])
}
func moveRight(_ title:String) {
//pass tableview cell
let vc = fullScreen()
vc.text = title
self.navigationController!.pushViewController(vc, animated: true)
}
You need to implement the UITableViewDelegate protocol. So:
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { .. }
}
I want to delete tableview cell by clicking a button present in the same cell. But I am unable to access the cell in the button action function.
Please help me to Access this cell. My code is -
class MatchesViewController: UIViewController{
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "MatchingUsersTVCell") as? MatchingUsersTVCell else{
return UITableViewCell()
}
let likeUid = userIdArray[indexPath.row]
cell.heartBtn.tag = indexPath.row
cell.heartBtn.addTarget(self, action: #selector(userLikeButtonWasTappaed(sender:)), for: .touchUpInside)
}
#objc func userLikeButtonWasTappaed(sender: UIButton){
if let cell = sender.superview as? MatchingUsersTVCell{
CellAnimator.animate(cell: cell)
}
let tag = sender.tag
let userid = userIdArray[tag]
}
}
Try this code:
#objc func userLikeButtonWasTappaed(sender: UIButton){
guard let indexPath = tableView.indexPathForRow(at: sender.convert(sender.frame.origin, to: tableView)) else {
return
}
let cell = tableView.cellForRow(at: indexPath) as? MatchingUsersTVCell
}
And in your cellForRowAt function add the following code:
cell.yourBtn.tag = indexPath.row
cell.yourBtn.addTarget(self, action: #selector(userLikeButtonWasTappaed(sender:)), for: .touchUpInside)
I'd stay away from using tags, and instead implement protocol/delegate.
Using indexPath allows use of multiple sections, etc...
1) Create a protocol:
protocol MatchingUsersTVCellDelegate : class {
func didTapLikeButton(_ indexPath: IndexPath)
func didTapOtherButton(_ indexPath: IndexPath)
}
2) Create/Update your cell:
class MatchingUsersTVCell : UITableViewCell {
weak var delegate: MatchingUsersTVCellDelegate?
var indexPath: IndexPath!
// add target to your like button
func didTapLIkeButton(_ sender: UIButton) {
self.delegate?.didTapLikeButton(indexPath)
}
func didTapOtherButton() {
self.delegate?.didTapOtherButton(indexPath)
}
}
3) make sure your viewController conforms to the new delegate:
extension YourViewController: MatchingUsersTVCellDelegate {
func didTapLikeButton(_ indexPath: IndexPath) {
//Do something with the indexPath or indexPath.row
dataSource.remove(at: indexPath.row)
}
func didTapOtherButton(_ indexPath: IndexPath) {
//Do something else with the indexPath or indexPath.row
}
}
4) Set delegate and indexPath
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell...
cell.delegate = self
cell.indexPath = indexPath
return cell
}
Within MatchingUsersTVCell, add two properties, one named parentVC of type UIViewController and one named index of type Int:
class MatchingUsersTVCell: UITableViewCell {
var parentVC: UIViewController!
var index: Int!
...
}
Then, when creating each cell, set these two values appropriately:
class MatchesViewController: UIViewController, UITableViewDelegate, UITableViewDatasource {
...
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "MatchingUsersTVCell") as? MatchingUsersTVCell else {
return UITableViewCell()
}
cell.parentVC = self
cell.index = index
...
return cell
}
}
Now, you simply update your parentVC's tableView's data source and reload its data whenever the button is tapped:
class MatchingUsersTVCell: UITableViewCell {
...
#objc func userLikeButtonWasTappaed(sender: UIButton){
parentVC.userIdArray.remove(at: index)
parentVC.tableView.reloadData()
}
}
you can get it like this in your selector method
#objc func userLikeButtonWasTappaed(button:UIButton){
guard let indexPath = myTableView.indexPathForRow(at: button.convertPoint(button.frame.origin, toView: myTableView)) else {
print("Error: indexPath)")
return
}
print("indexPath.row: \(indexPath.row)")
}
I just cannot seem to update data in Swift! I am trying to build a radio player app for a friends radio station so when a song changes I need to update the playlist viewcontroller.
The data from my Main View Controler is a instance of a struct. I know there is data being generated and it is passed to the table but for whatever reason the array isn't updating. I am sure it is something simple.
I have tried directly injecting the data with a call, using a protocol and using a function. Using the protocol and function I can see the passed data via print statement.
class PlaylistVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
//Mark: Variables ~~~~~~~~~~~~###########################################
var sourceDatas = [PlaylistData]()
//Mark: View Containers ~~~~~############################################
private let bg = GradientBG()
private let closeButton = UIButton()
let playlistTable = UITableView()
//Mark: Overrides ~~~~~~~~~~~~###########################################
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
playlistTable.delegate = self
playlistTable.dataSource = self
layoutUI()
setupTable()
setupClose()
}
//Mark: objc func's ~~~~~~~~~~~###########################################
#IBAction func handleXButton() {
dismiss(animated: true, completion: nil)
}
func handleMoreInfo(_ playlist: PlaylistData) {
let vc = SongPopUp()
vc.buildLables(playlist)
vc.modalPresentationStyle = .overCurrentContext
self.present(vc, animated: true, completion: nil )
}
//##################################################################
//Mark: Pass Data ##################################################
//Mark: Not Working!! ##############################################
//##################################################################
func handlePlaylist(_ with: PlaylistData) {
print(with)
sourceDatas.append(with)
//sourceDatas.insert(with, at: 0)
playlistTable.reloadData()
}
//Mark: Table view ################################################
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sourceDatas.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:UITableViewCell = UITableViewCell(style: .subtitle, reuseIdentifier: "myCell")
cell.backgroundColor = .clear
cell.selectionStyle = .none
tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
if let text = cell.textLabel {
components.layoutHeadingLable(sender: text, title: sourceDatas[indexPath.row].heading, size: 20)
}
if let dtext = cell.detailTextLabel {
components.layoutSubheadingLable(sender: dtext, title: sourceDatas[indexPath.row].subheading, size: 14)
}
if sourceDatas[indexPath.item].hasStoreLink {
cell.accessoryType = .detailButton
cell.tintColor = .white
}
return cell
}
func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
let dataToPass = self.sourceDatas[indexPath.row]
print("Extended~~~~~~~~~~~~~~~~~~~~")
print(dataToPass)
handleMoreInfo(sourceDatas[indexPath.row])
}
//Mark: Setup Table ~~~~~~~~~~~~~~###########################################
func setupTable() {
playlistTable.backgroundColor = .clear
playlistTable.separatorStyle = .singleLine
playlistTable.rowHeight = 45
playlistTable.register(UITableViewCell.self, forCellReuseIdentifier: "myCell")
}
......
I think it's something wrong with your cellForRowAt
Try to make it simple first, like:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! UITableViewCell
cell.textLabel?.text = sourceDatas[indexPath.row]
return cell
}
See whether you can find the added object. Then dive into the detail settings of your cell.