I have a UITableViewCell that I want to show as selected when the UIViewController is presented. vc.tableView.selectRow:atIndexPath is nice in theory but it bypasses the calls for willSelect and didSelect on the cell.
The cell has an exposed UIImageView that setSelected toggles, which is what I'm trying to show on the initial load.
Any help here would be appreciated. Thanks!
I'll give you an example of how change background color of your cells and select a initial one, so you can follow and put the code that you need:
In your UIViewController subclass, implement these methods, so you can put your logic for selected and deselected states:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell = tableView.cellForRow(at: indexPath)!
selectedCell.backgroundColor = UIColor.purple
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let deselectedCell = tableView.cellForRow(at: indexPath)!
deselectedCell.backgroundColor = UIColor.clear
}
In your custom UITableViewCell subclass you have to override the isSelected property, this is the key to avoid the method tableView.selectRow:atIndexPath bypassing didSelect:
class CustomTableViewCell: UITableViewCell {
override var isSelected: Bool {
didSet {
print(isSelected.description)
self.selectionStyle = .none
if isSelected{
self.backgroundColor = UIColor.purple
} else {
self.backgroundColor = UIColor.clear
}
}
}
}
Last, back to your UIViewController subclass, you can call selectRow:atIndexPath in your viewDidLoad method, for instance:
override func viewDidLoad(){
super.viewDidLoad()
tableView.selectRow(at: IndexPath(row: 0, section: 0) , animated: true, scrollPosition: UITableViewScrollPosition.none)
}
Related
I would like for the user to tap a cell within UITableViewController B and then have the text from the cell sent to display in a cell inside of UITableViewController A which would replace its default text. However I am having trouble doing so. Both of the UITableViewController A & B are in a UINavigationController. selectedName is the String I would like to send from B to A
Here is the code from UITableViewControllerB didSelectRowAt
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedIndexPath = indexPath
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath as IndexPath)
tableView.register(UINib(nibName: "BBcell", bundle: nil), forCellReuseIdentifier: "Cell")
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
selectedName = nameArray[indexPath.row]
print(selectedName)
let infoTVC = InfoTableViewController()
infoTVC.name = selectedName
}
Here is the code from UITableViewController A
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath as IndexPath) if indexPath.section == 1 {
cell.textLabel?.text = "Choose Name" // This is where I set the default text
cell.accessoryType = UITableViewCell.AccessoryType.disclosureIndicator
}
return cell
}
This is a very rudimentary example that has many shortcomings and utilizes some practices breaking SOLID principles, but fundamentally should work. Don't take this as the final end all answer, but instead as a starting point to move forward from:
import UIKit
// declare the delegate protocol
protocol TableViewControllerSubclassBDelegate: AnyObject {
func tableViewControllerB(_ controller: TableViewControllerSubclassB, touched indexPath: IndexPath, withStringInfo newStringValue: String)
}
// table view controller where the content ends up
class TableViewControllerSubclassA: UITableViewController {
var tableArrayContents: [String] = []
}
// table view controller where the user touches an entry
class TableViewControllerSubclassB: UITableViewController {
//...
weak var delegate: TableViewControllerSubclassBDelegate?
//...
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// this is quite prone to fail if everything is not perfectly set - beware
guard let stringInfo = tableView.cellForRow(at: indexPath)?.textLabel?.text else { return }
delegate?.tableViewControllerB(self, touched: indexPath, withStringInfo: stringInfo)
}
}
// you'll need to subclass your navigation controller with something like this
class NavigationControllerSubclass: UINavigationController {
// force unwrap is generally frowned upon. there are better solutions, but this is a simplified answer
var tvcA: TableViewControllerSubclassA!
var tvcB: TableViewControllerSubclassB!
override func viewDidLoad() {
super.viewDidLoad()
// populate the table view controller properties - not the cleanest way
for viewController in viewControllers {
if let typeA = viewController as? TableViewControllerSubclassA {
tvcA = typeA
} else if let typeB = viewController as? TableViewControllerSubclassB {
tvcB = typeB
}
}
tvcB.delegate = self
}
}
// conforming to delegates/protocols in an extension is a great way to organize your code
extension NavigationControllerSubclass: TableViewControllerSubclassBDelegate {
func tableViewControllerB(_ controller: TableViewControllerSubclassB, touched indexPath: IndexPath, withStringInfo newStringValue: String) {
tvcA.tableArrayContents[indexPath.row] = newStringValue
tvcA.tableView.reloadRows(at: [indexPath], with: .automatic)
// you could also go the heavyhanded approach of tvcA.tableView.reloadData() if necessary
}
}
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.
How do I detect if a table view cell is tapped. The guides and tutorial on the web follow the approach of adding a button to the table cell view - prototype cell. This button remains same for all rows and when tapped, returns the index of the row or cell.
I want to do this without the button. How can I invoke
#IBAction
when a cell is tapped and then perform a segue inside the function.
How should I go about it?
I added the following code, but this does not print anything
class MoreViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let moreOption = [
("My Profile"),
("Settings"),
("Logout")
]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.navigationController!.navigationBar.barTintColor = UIColor(red: (48/255.0), green: (187/255.0), blue: (197/255.0), alpha: 1.0);
self.navigationController!.navigationBar.tintColor = UIColor .whiteColor();
self.navigationController!.navigationBar.translucent = false;
self.navigationController!.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName : UIColor .whiteColor()]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func logout() {
print("Logout tapped");
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return moreOption.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
let (celloption) = moreOption[indexPath.row];
cell.textLabel!.text = celloption;
cell.textLabel!.textColor = UIColor(red: (74/255.0), green: (74/255.0), blue: (74/255.0), alpha: 1.0);
return cell;
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath)
print(cell);
}
}
Why aren't you using UITableViewDelegate method?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
In this method you will get the indexPath of cell tapped and here itself you can open the new controller if you want.
Try This
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedItem = items.objectAtIndex(indexPath.row) as String
print(selectedItem)
}
On the code you are not adding the tableview outlet (or variable if you are building it from code).
You need first to add the tableview outlet to your class and then set the apropiate delegate and datasource for it
for example
//this is connected to your storyboard
//It may have typing errors it is not tested
//at this on the top of your class
#IBOutlet weak var table: UITableView!
//at this on viewdidLoad or viewDidAppear
table.delegate = self
table.datasource = self
I am using SWRevealViewController, I have three cells like "first" "second" "last". And I have three different tableviews, "firstTableViewController", "secondTableViewController", "lastTableViewController".
I tried to use didSelectRowAtIndexPath, the prepareforsegue. But I couldn't implement multipe segues in didSelectRowAtIndexPath.
Is there a way to do this?
import UIKit
class SideBarTableViewController: UITableViewController {
var MenuArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
MenuArray = ["First","Second","Last"]
var view = UIView(frame: CGRectZero)
self.tableView.tableFooterView = view
self.tableView.backgroundColor = UIColor.whiteColor()
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return MenuArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier(MenuArray[indexPath.row], forIndexPath: indexPath) as UITableViewCell
cell.textLabel?.text = MenuArray[indexPath.row]
return cell
}
}
cell identifiers : First, Second, Last
Thanks,
Ok, I found the solution. Followed this tutorial, there is no need to set a segue in code, just kntrl+drag to the view controllers and select "SWRevealViewControllerSeguePushController" ...
http://www.appcoda.com/sidebar-menu-swift/
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet
var tableView: UITableView
var items: String[] = ["We", "Heart", "Swift"]
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "myCell")
}
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return self.items.count;
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("myCell") as UITableViewCell
cell.textLabel.text = self.items[indexPath.row]
cell.accessoryType = UITableViewCellAccessoryType.DetailDisclosureButton
cell.selectionStyle = UITableViewCellSelectionStyle.Blue
tableView.separatorStyle = UITableViewCellSeparatorStyle.None
return cell
}
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
println("You selected cell #\(indexPath.row)!")
}
}
My problem is that the accessoryType and the selectionStyle don't get changed.
The tableView.separatorStyle does get changed as well as the cell.textlabel.text.
How can I fix that?
UITableViewCell.SelectionStyle.blue
The cell has a default background color when selected.
In iOS 7, the selection color is no longer blue. Use
UITableViewCell.SelectionStyle.default instead.
As for the accessoryType, it should work fine as long as you don't change it later somewhere else. Make sure that the table width is correct, otherwise accessory views might be offscreen.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet
var tableView: UITableView
var items: String[] = ["We", "Heart", "Swift"]
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "myCell")
}
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return self.items.count;
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("myCell") as UITableViewCell
cell.textLabel.text = self.items[indexPath.row]
cell.selectionStyle = UITableView.CellSelectionStyle.blue
/*
enum UITableViewCellAccessoryType : Int {
case none // don't show any accessory view
case disclosureIndicator // regular chevron. doesn't track
case detailDisclosureButton // info button w/ chevron. tracks
case checkmark // checkmark. doesn't track
case detailButton // info button. tracks
}
*/
// Standard options
cell.accessoryType = UITableViewCell.AccessoryType.none
cell.accessoryType = UITableViewCell.AccessoryType.disclosureIndicator
cell.accessoryType = UITableViewCell.AccessoryType.detailDisclosureButton
cell.accessoryType = UITableViewCell.AccessoryType.checkmark
cell.accessoryType = UITableViewCell.AccessoryType.detailButton
// Custom view options
cell.accessoryType = UITableViewCell.AccessoryType.none
cell.accessoryView = UIView(frame: CGRectMake(0, 0, 20, 20))
cell.accessoryView.backgroundColor = UIColor.blueColor()
return cell
}
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
println("You selected cell #\(indexPath.row)!")
}
}
Note that it isn't a good solution to set separatorStyle of the table each time the cell is requested, instead do it once when the tableView is loaded: at viewDidLoad.
I didn't have any luck setting it in the cellForRowAtIndexPath method, moving it to willDisplayCell fixed the issue with it not showing up.
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
cell.accessoryType = .DisclosureIndicator
}
I would like to share my experience about this, I had the same issue, cell.accessoryType = IUTableViewCellAccessoryType.Checkmark
And I noticed my tableview didn't have constraints, so I added missing constraints then it worked for me
Below will set your accessoryView as an icon named "sentIcon". Just in case!!!
let sentImage = UIImage(named: "sentIcon")
let sentImageView = UIImageView(image: sentImage)
sentImageView.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
sentImageView.tintColor = .lightGray
cell.accessoryView = sentImageView