I am trying to figure out how Swift works. and right now I'm working on a table view cell, but there seems to be a problem.
import UIKit
class ProfileTableViewCell : UITableViewCell{
#IBOutlet var profilePicImageView: UIImageView!
#IBOutlet var profileNameText: UILabel!
}
ove
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cellResult :Dictionary<CellType, Dictionary<String, String>> = menuList[indexPath.row]
var cellType: CellType = Array(cellResult.keys)[0]
var cellData: Dictionary<String, String> = cellResult[cellType] as Dictionary<String, String>!
if(cellType == CellType.Profile){
var cell = tableView.dequeueReusableCellWithIdentifier("DisplayPicCellIdentifier") as? ProfileTableViewCell
if (cell == nil) {
cell = ProfileTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "DisplayPicCellIdentifier")
}
cell.profileNameText!.text! = cellData["displayName"] // Error 'ProfileTableViewCell? does not have member named 'profileNameText'
// cell.setProfileNameText(cellData["displayName"])
return cell!
}
I keep getting this error does not have member named in profileNameText, may I ask how I am able to fix such issue?
Thanks
You should change the line to
cell!.profileNameText.text = cellData["displayName"]
Related
I would really appreciate any help on this. I'm very new to coding, and have no luck implementing this feature so far. I'm looking to populate a UITableViewCell with information gathered from Firestore, namely: title, username and content. I've been able to print the 'title' array successfully, but have not been able to actually populate this into the cells.
This is the HomeViewController, where my UITableView is:
class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var logoutButton: UIButton!
var postArray: [String] = []
var documents: [DocumentSnapshot] = []
let db = Firestore.firestore()
let currentUserID = Auth.auth().currentUser?.uid
// Find the UserIDs of people following
// Where Field for those UserIDs in "Posts"
override func viewDidLoad() {
super.viewDidLoad()
getFollowingPosts()
configureTableView()
}
func getFollowingPosts() {
let searchForFollowing = db.collection("users").document(currentUserID!).collection("Following")
searchForFollowing.getDocuments { (snapshot, error) in
for documents in snapshot!.documents {
let followedUID = documents.get("uid")
print(followedUID!)
self.db.collection("posts").whereField("uid", isEqualTo: followedUID!).getDocuments { (querySnapshot, error) in
for documents in querySnapshot!.documents {
let uid = documents.get("uid") as! String
let title = documents.get("Title") as! String
let ProfilePictureURL = documents.get("ProfilePictureURL") as! String
let username = documents.get("username") as! String
let content = documents.get("Content") as! String
self.postArray.append(title)
print(self.postArray)
}
self.tableView.reloadData()
}
}
}
}
func configureTableView() {
tableView.delegate = self
tableView.dataSource = self
tableView.register(PostTableViewCell.self, forCellReuseIdentifier: "PostCell")
// remove separators for empty cells
tableView.tableFooterView = UIView()
// remove separators from cells
tableView.separatorStyle = .none
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
postArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath) as! PostTableViewCell
let post = postArray[indexPath.row]
return cell
}
}
This is my PostTableViewCell:
class PostTableViewCell: UITableViewCell {
#IBOutlet weak var usernameLabel: UILabel!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var contentLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
addSubview(usernameLabel)
addSubview(titleLabel)
addSubview(contentLabel)
}
}
If anyone could help, this would be massively appreciated. Like I said, I've been struggling a lot with this one.
You don't seem to be setting the data onto anything in the cell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath) as! PostTableViewCell
let post = postArray[indexPath.row]
cell.titleLabel.text = post
return cell
}
Also, modify the register method if you're using nib
func configureTableView() {
//...
tableView.register(UINib(nibName: "PostCell", bundle: nil), forCellReuseIdentifier: "PostCell")
//...
}
Note: Make sure that the nib file has nib's identifier set as "PostCell".
The issue is as follows: I have a tableview with a custom cell. That cell contains a label and a UISwitch. I have set the label.text value to an array, but the UISwitch is getting reused.
Example: If I toggle the switch in the first row, the 5th row gets enabled, and if I scroll it continues to reuse the cells and cause issue.
Video : https://vimeo.com/247906440
View Controller:
class ViewController: UIViewController {
let array = ["One","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten"]
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell
cell.label.text = array[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
}
Custom Cell:
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var label: UILabel!
#IBOutlet weak var toggleSwitch: UISwitch!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
I realize there isn't code trying to store this data because I haven't been successful. Any ideas would be helpful. The project currently uses the MVC model and I believe that is the answer but just need some help.
I would recommend to you create cellViewModel class and keep array of it instead of just string. You cellViewModel may look like,
class CellViewModel {
let title: String
var isOn: Bool
init(withText text: String, isOn: Bool = false /* you can keep is at by default false*/) {
self.title = text
self.isOn = isOn
}
Now, build array of CellViewModel
let array =["One","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten"]
var cellViewModels = [CellViewModel]()
for text in array {
let cellViewModel = CellViewModel(withText: text)
cellViewModels.append(cellViewModel)
}
Change your tableVieDelegate function to :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell
let cellViewModel = cellViewModels[indexPath.row]
cell.label.text = cellViewModel.title
cell.toggleSwitch.isOn = cellViewModel.isOn
cell.delegate = self
return cell
}
In you Custom Cell class, add this protocol :
protocol CellActionDelegate: class {
func didChangeSwitchStateOnCell(_ cell: CustomTableViewCell)
}
Add delegate as property in your custom cell,
weak var delegate: CellActionDelegate?
Also, on switch change, add this line,
delegate?.didChangeSwitchStateOnCell(self)
Now, your viewController should register and listen to this delegate :
I have added line cellForRowAtIndexPath to register for delegates. To listen this delegate, add this function in your VC.
func didChangeSwitchStateOnCell(_ cell: CustomTableViewCell) {
let indexPath = tableView.indexPath(for: cell)
cellViewModels[indexPath.row].isOn = cell.toggleSwitch.isOn
}
start creating a model for example :
struct item {
var id: String
var name: String
var isActivated: Bool
init(id: String, name: String, isActivated: Bool) {
self.id = id
self.name = name
self.isActivated = isActivated
}
}
let item1 = item(id: "1", name: "One", isActivated: false)
let item2 = ...........
let item3 = ...........
let items [item1, item2, item3]
With that you can trigger the boolean if it's activated or not.
You will also have to take a look to https://developer.apple.com/documentation/uikit/uitableviewcell/1623223-prepareforreuse I think.
I have a ViewController called notificationsVC with two tableViews - notifTable and messageTable. In notifTable I have two types of UITableViewCells -
As you can see there's a followed you and commented/liked your post patterns.
Until now I have been using two UITableViewCell's like this. (reuse identifier - cell1 and cell3)
The first one I made a coach touch file named notification_cell.swift
and another notification2_cell.swift
notification_cell.swift
import UIKit
class notification_cell: UITableViewCell {
#IBOutlet weak var profilePic: UIImageView!
#IBOutlet weak var username: UILabel!
#IBOutlet weak var followNotif: UILabel!
}
notification2_cell.swift
import UIKit
class notification2_cell: UITableViewCell {
#IBOutlet weak var profilePic: UIImageView!
#IBOutlet weak var username: UILabel!
#IBOutlet weak var c_l_notif: UILabel!
#IBOutlet weak var postImage: UIImageView!
}
I have four arrays in all -
var username = [String]()
var notif = [String]()
var u_id = [String]()
var p_id = [String]()
For eg, when I do a API call I get this
username-> ["Anton Griezmann", "Anonymous", "Anonymous"]
u_id-> ["2", "30", "31"]
notif-> ["followed you", "liked your post", "liked your post"]
p_id-> ["", "9", "9"]
What I'm trying to do is whenever there's a blank "" in p_id I know I have to initialise cell1 and otherwise cell3
This is my code for cellForRowAtIndexpath
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) ->
UITableViewCell{
var cell = UITableViewCell()
var index = [Int]()
if(tableView==self.notifTable)
{
for i in 0..<p_id.count {
if p_id[indexPath.row].isEmpty {
index.append(i)
//cell for followed you
let cell = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! notification_cell
cell.username.text = username[indexPath.row]
cell.followNotif.text =
notif[indexPath.row]
return cell
}
else {
//cell for commented/liked
let cell = tableView.dequeueReusableCellWithIdentifier("cell3", forIndexPath: indexPath) as! notification2_cell
cell.username.text = username[indexPath.row]
cell.c_l_notif.text = notif[indexPath.row]
return cell
}
}
}
else
{
let cell = tableView.dequeueReusableCellWithIdentifier("cell2", forIndexPath: indexPath)
cell.textLabel?.text = "hola"
return cell
}
}
What I get when I run it is this
That is the first cell is getting overwritten again. I need a way to find out which cell should be initialised at what indexPath but can't think of how. Any suggestions are welcome!
First of all you are returning wrong cell in your cellForRowAtIndexPath try to return cell inside the if - else block, and if you want to check for p_id[indexPath.row] is "" means it is empty so you can check its length or use isEmpty function of String, There is no need to go through a loop, Also.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) ->
UITableViewCell{
if(tableView==self.notifTable)
{
if (p_id[indexPath.row].isEmpty) {
//cell for followed you
let cell = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! notification_cell
cell.username.text = username[indexPath.row]
cell.followNotif.text =
notif[indexPath.row]
return cell
}
else {
//cell for commented/liked
let cell = tableView.dequeueReusableCellWithIdentifier("cell3", forIndexPath: indexPath) as! notification2_cell
cell.username.text = username[indexPath.row]
cell.c_l_notif.text = notif[indexPath.row]
return cell
}
}
else
{
let cell = tableView.dequeueReusableCellWithIdentifier("cell2", forIndexPath: indexPath)
cell.textLabel?.text = "hola"
return cell
}
}
I have a UITableViewController with three labels. The first two are from the .textLabel and .detailTextLabel, I have added a third label into the storyboard and hooked it up to a UITableViewCell file. When I run, the app crashes unless the label is set as an optional with a "?", but nothing is still presented in the table. If it does not have the optional sign, it crashes and I get a response saying "unexpectedly found nil while unwrapping an optional value". The other two .textLabel and .detailTextLabel work fine. I would appreciate any help!
Here is my TableViewController File,
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.groupscores.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier(cellidentifier) as? ScoresPageCell
if (cell != nil) {
cell = ScoresPageCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: cellidentifier)
}
let groupscorelist: PFObject = self.groupscores.objectAtIndex(indexPath.row) as! PFObject
var scores: AnyObject = groupscorelist.objectForKey("Score")!
var user: AnyObject = groupscorelist.objectForKey("User")!
var info: AnyObject = groupscorelist.objectForKey("Info")!
cell!.textLabel?.text = "\(scores)"
cell?.detailTextLabel?.text = "\(user)"
cell!.UserNameCellLabel?.text = "\(info)"
return cell!
}
And my UITableViewCell File,
class ScoresPageCell: UITableViewCell {
#IBOutlet var UserNameCellLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
}
Follow this my friend:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier(cellidentifier, forIndexPath: indexPath) as! ScoresPageCell
let groupscorelist = self.groupscores[indexPath.row]
var scores = groupscorelist["Score"] as! String // cast it to String if they are string.
var user = groupscorelist["User"] as! String
var info = groupscorelist["Info"] as! String
cell!.text1?.text = scores
cell?.text2?.text = user
cell!.UserNameCellLabel?.text = info
return cell
}
class ScoresPageCell: UITableViewCell {
#IBOutlet weak var text1: UILabel!
#IBOutlet weak var text2: UILabel!
#IBOutlet weak var UserNameCellLabel: UILabel!
}
I'm trying to retrieve core data and display it in a custom cell class. I think it will be easier if I present my code first.
This is my "original code", with a regular cell:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let CellID:NSString = "cell"
var cell: UITableViewCell = self.tv.dequeueReusableCellWithIdentifier(CellID) as UITableViewCell
if let ip = indexPath as Optional {
var data:NSManagedObject = myList[ip.row] as NSManagedObject
cell.textLabel!.text = data.valueForKeyPath("username") as String!
}
return cell
}
This is what I change my code to when trying to use the custom cell:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let CellID:NSString = "cell"
var cell: CustomCell = tv.dequeueReusableCellWithIdentifier(CellID) as CustomCell
if let ip = indexPath as Optional {
var data:NSManagedObject = myList[ip.row] as NSManagedObject
cell.titleLabel.text = data.valueForKeyPath("username") as String!
cell.dateLabel.text = data.valueForKeyPath("date") as String!
}
return cell
}
The first code works perfectly, but when using the second one I get the (lldb) runtime error.
Both "username" and "date" are saved as strings.
Any suggestions would be appreciated.
EDIT:
Additional information:
var myList: Array<AnyObject> = []
The error that pops up is just "(lldb)" and "Thread 1: EXC_BREAKPOINT (code = EXC_l386_BPT, subcode = 0x0)".
My model-file:
#objc(Model)
class Model: NSManagedObject {
#NSManaged var username: String
#NSManaged var date: String
#NSManaged var isAnonymousMessage: Bool
}
My cellForRowAtIndexPath-function:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let CellID:NSString = "cell"
var cell: CustomCell = tv.dequeueReusableCellWithIdentifier(CellID) as CustomCell
if let ip = indexPath as Optional {
let data = myList[indexPath.row] as Model
cell.titleLabel.text = data.username
cell.dateLabel.text = data.date
}
return cell
}
My viewDidAppear-function:
override func viewDidAppear(animated: Bool) {
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext!
let freq = NSFetchRequest(entityName: "Message")
let en = NSEntityDescription.entityForName("Message", inManagedObjectContext: context)
let fetchRequest = NSFetchRequest(entityName: "Message")
myList = context.executeFetchRequest(fetchRequest, error: nil) as [Model]
tv.reloadData()
}
my CustomCell class looks like this:
import UIKit
class CustomCell: UITableViewCell {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var dateLabel: 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
}
}
I would suggest to change your code a little bit.
Load your NSManagedObjects into an Array of your Core Data Class, for example like this:
var myList = [ListObject]() // Where ListObject is your NSManagedClass
let fetchRequest = NSFetchRequest(entityName: "List")
myList = context.executeFetchRequest(fetchRequest, error: nil) as [ListObject]
// You should load this once (maybe in ViewDidLoad) so every Core Data object gets only fetched once (you could easy refresh this if needed).
Then use in your cellForRowAtIndexPath:
let data = myList[indexPath.row] as ListObject
cell.titleLabel.text = data.name
cell.dateLabel.text = data.date
// You dont need to use "valueForKeyPath" - just use the property as shown above.
var cell: CustomCell = tv.dequeueReusableCellWithIdentifier(CellID) as CustomCell
Crashing on this line means that you're not getting a CustomCell back when you dequeue from the tableview.
You need to register the class with that reuse identifier, either by setting it in the storyboard or xib, or calling registerClass(_ cellClass: AnyClass, forCellReuseIdentifier identifier: String) on the table view, normally in view did load.
If you've added a new cell to the storyboard and want to use it instead of the default one, make sure the reuse identifier is set correctly.