Add Label To Cell on Certain Rows only - ios

I have a lot of rows with a few different layouts - that all works fine. Now I want to add a Custom UILabel on just some of the rows. I know that there is a "problem" working with a reuse identifier, so the UITableView will try to reuse my UILabel on the next Cells again. To prevent this, I've checked a lot of suggestions here and tried it this way:
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell? {
var cell = tableView .dequeueReusableCellWithIdentifier(myQuestions.getQuestion(indexPath.row).qTemplate, forIndexPath: indexPath) as CustomTableViewCell
// if there is a saved Notice, add this to this Cell
if(myQuestions.getQuestion(indexPath.row).qNotice != nil) {
cell.addNoticeToCell("This is a Test")
}
return cell
}
And my CustomTableViewCell class is:
class CustomTableViewCell: UITableViewCell {
#IBOutlet var LabelCellTitle: UILabel!
#IBOutlet var TextView: UITextView!
#IBOutlet weak var LabelCellContent: UILabel!
var noticeButton:UIButton!
func addNoticeToCell(noticeText: String) {
noticeButton = UIButton.buttonWithType(UIButtonType.System) as UIButton
noticeButton.frame = CGRectMake(330,44,600,44)
noticeButton.titleLabel.textColor = UIColor.blueColor()
noticeButton.tag = 100
noticeButton.setTitle(noticeText, forState: UIControlState.Normal)
self.contentView.addSubview(noticeButton)
}
override func prepareForReuse() {
println("CELL BEFORE REUSE")
if(self.contentView.subviews.count > 0) {
for mySubView in self.contentView.subviews {
if mySubView.tag == 100 {
mySubView.removeFromSuperview()
....
It works now as expected - but I don't know if its a good idea to go through all subviews. Is there a better way? I would add 2-3 UIImageViews on certain cells too, and would create it with the same procedure.

In prepareForReuse(), why not just use the property you added to your cell class to remove it rather than enumerating all of the subviews like so:
var noticeButton:UIButton?
override func prepareForReuse() {
super.prepareForReuse()
if let notice = self.noticeButton {
notice.removeFromSuperview()
self.notice = nil
}
}

Related

is there a way of refreshing the whole UITableView through a button that is in one of the cells?

I have a dynamically generated UITableView with many dynamic UITableViewCells and one static UITableViewCell.
The static one has a button and I want to refresh the whole table view when user presses it.
My code attached to the cell is simple:
class MyStaticCell: UITableViewCell {
#IBOutlet weak var sendCommentButton: UIButton!
#IBAction func sendCommentButtonAction(sender: AnyObject) {
//from here I want to refresh the table
}
}
How can I refresh the parent table from that button? In the class MyStaticCell I don't have any instance of the table, so that's my problem for now :|
The cleanest way to do this is through delegation. This ensures that the cell class doesn't need to know what should happen when the button is pressed; that logic can remain in your view controller where it belongs.
protocol CommentButtonProtocol {
func commentButtonTapped(sender: MyStaticCell)
}
class MyStaticCell: UITableViewCell {
#IBOutlet weak var sendCommentButton: UIButton!
var delegate: CommentButtonProtocol?
#IBAction func sendCommentButtonAction(sender: AnyObject) {
self.delegate?.commentButtonTapped(self)
}
}
Then in your view controller you can set it as the delegate in cellForRowAtIndexPath and comply with the protocol in order to handle the event:
class ViewController: UIViewController, CommentButtonProtocol {
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("staticCell", forIndexPath: indexPath) as! MyStaticCell
cell.delegate = self
return cell
}
func commentButtonTapped(sender: MyStaticCell) {
// Do whatever you need to do when the button is tapped
}
}
You could access the tableView using superview.
class MyStaticCell: UITableViewCell {
#IBOutlet weak var sendCommentButton: UIButton!
#IBAction func sendCommentButtonAction(sender: AnyObject) {
(superview as? UITableView)?.reloadData()
}
}
This isn't as stable as it could be so maybe consider this extension:
extension UIResponder {
func nextResponder<T: UIResponder>(ofType type: T.Type) -> T? {
switch nextResponder() {
case let responder as T:
return responder
case let .Some(responder):
return responder.nextResponder(ofType: type)
default:
return nil
}
}
}
It allows you to find the next parent of a particular type, in the cells case, a UITableView.
class MyStaticCell: UITableViewCell {
#IBOutlet weak var sendCommentButton: UIButton!
#IBAction func sendCommentButtonAction(sender: AnyObject) {
nextResponder(ofType: UITableView.self)?.reloadData()
}
}

Swift TableViewCell empty when cell has values

I have a custom cell with some simple labels and a UIImage. Everything appears to be set correctly, and stepping through the debugger shows that everything is getting a value and even using the print in the debugger shows that the labels have text. However my table view is still empty when executed. I have been looking at this for too long and cannot figure out the problem.
Here is the cell code
class CurrentFileCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var statusImage: UIImageView!
#IBOutlet weak var dateLabel: UILabel!
var currentContent: AircraftContent! {
didSet{
setStyles(Constants.appStyleSetting)
self.nameLabel.text = currentContent.contentName
self.dateLabel.text = currentContent.contentStatus
self.statusImage.image = UIImage(named: "color_label_circle_green")
}
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
private func setStyles(settings: StyleSettings) {
let tableFont = UIFont(name: settings.bodyFont, size: CGFloat(settings.tableFontSize))
nameLabel.font = tableFont
dateLabel.font = tableFont
// Text color
let tableFontColor = settings.tableFontColor
nameLabel.textColor = tableFontColor
dateLabel.textColor = tableFontColor
}
Here is the ViewController code with a tableview inside.
class CurrentFilesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var content: AircraftContent?
#IBOutlet weak var currentFiles: UILabel!
#IBOutlet weak var downloadingLabel: UILabel!
#IBOutlet weak var readyLabel: UILabel!
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
self.content = loadContent()
setStyles(Constants.appStyleSetting)
//self.tableView.reloadData()
// Do any additional setup after loading the view.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CurrentFileCell", forIndexPath: indexPath) as? CurrentFileCell
cell?.currentContent = content
return cell!
}
func loadContent() -> AircraftContent {
return (NSKeyedUnarchiver.unarchiveObjectWithFile(AircraftContent.ArchiveURL.path!) as? AircraftContent)!
}
private func setStyles(settings: StyleSettings) {
let titleFont = UIFont(name: settings.bodyFont, size: CGFloat(settings.titleFontSize))
let key = UIFont(name: settings.bodyFont, size: CGFloat(settings.tableFontSize))
currentFiles.font = titleFont
downloadingLabel.font = key
readyLabel.font = key
// Text color
let titleFontColor = settings.titleFontColor
currentFiles.textColor = titleFontColor
downloadingLabel.textColor = titleFontColor
readyLabel.textColor = titleFontColor
}
Here are some images showing the debug location where the cell is not empty, and also printing out the label which has a value, but isn't being shown during simulation.
http://imgur.com/a/dBkpe
This is an image showing the prototype cell. The cell has the correct class set as well as the identifier.
http://imgur.com/PKtFTeQ
Lastly another image showing that the prototype cell is linked to the labels within the CurrentFileCell.
http://imgur.com/nW0QUjM
Any help at all with this would be appreciated. I have tried recreating everything but continue to be stumped as it seems like everything is how it should be.
You have to implement the 'heightForRowAtIndexPath' method for the table view
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let height:CGFloat = 75
return height
}
You may consider registering the custom class as it does not appear that you did. You can do that by using the following code in the viewDidLoad of your View Controller.
tableView.registerClass(CurrentFileCell.self, forCellReuseIdentifier: "cell")
If you are using an external nib you will want to use registerNib instead like so:
tableView.registerNib(UINib(name:"ReplaceWithYourNibName", bundle: nil), forCellReuseIdentifier: "ReuseIdentifier")
Of course, replace ReplaceWithYourNibName and ReuseIdentifier with the appropriate values. In addition, if your nib is in a different bundle specify that instead of nil (nil defaults to the main bundle).
However, do not use both registerClass and registerNib as whichever one you call last will be used and they were designed to be mutually exclusive. Whenever you make a custom UITableViewCell you must use either of the two for it to work unless you have set it explicitly in the storyboard.
Also, you could instead, use prototype cells to define your custom cell, which would, I believe, automatically register the cell. But only if you did not use prototype cells make sure to use registerClass or registerNib.
Good luck! Hope this helps!
If your cell is a static cell, then you need to comment out these methods in UITableViewDataSource:
/* override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 0
} */
I had the same issue.
Data has values and cell rows are showing empty.
I figured it by adding
contentView.addSubview(titleLabel)
where cell values are being set.

TableViewCell overlaps text

I'm having a problem with my TableViewCell
I have two type of cell in my storyboard.
when i scroll, the text overlaps in some cells. I Try everything but I do not know how else to do. thank you very much for the help
public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var storeNew = systemsBlog.getStore(listNews[indexPath.row].getIdStore())
var newNotice = listNews[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("TimelineCell", forIndexPath: indexPath) as? TimelineCell
cell!.nameLabel.text = storeNew.getName()
cell!.postLabel?.text = newNotice.getText()
cell!.postLabel?.numberOfLines = 0
cell!.dateLabel.text = newNotice.getDate()
cell!.typeImageView?.tag = indexPath.row;
return cell!
}
class TimelineCell : UITableViewCell {
#IBOutlet var nameLabel : UILabel!
#IBOutlet var postLabel : UILabel?
#IBOutlet var dateLabel : UILabel!
override func awakeFromNib() {
postLabel?.font = UIFont(name: "Roboto-Thin", size: 14)
}
override func layoutSubviews() {
super.layoutSubviews()
}
I can fix the problem. In the storyboard, the label have unchacked "Clears Graphics Context". I checked and for now it solved! Thanks for the help!
I had a similar issue with one of my UITableViews in the past. There are a bunch of things that could cause this, but maybe it is the same thing that happened to me.
I see that you are using a custom tableViewCell. What could be happening is when you set the text of the cell, it adds a label view with that text. Now say you scroll through the tableview and that cell disappears. If you were to reuse that cell and you did not remove the label from the subview, or set the text of that label again to the desired text, you will be reusing the tableviewcell with a previous label on it and adding a new label with new text to it, overlapping the text.
My suggestion would be to make sure you do not keep adding UIlabels as subviews in the TimelineCell class unless no label exists. if a label exists edit the text of that label not of the cell.
As per apple documentation:
The table view’s data source implementation of
tableView:cellForRowAtIndexPath: should always reset all content when
reusing a cell.
It seems that your problem is that you not always setting postLabel, causing it to write on top of the other cells, try this:
//reuse postLabel and set to blank it no value is returned by the function
let cell = tableView.dequeueReusableCellWithIdentifier("TimelineCell", forIndexPath: indexPath) as? TimelineCell
cell!.nameLabel.text = storeNew.getName()
if newNotice.getText() != nil{
cell!.postLabel.text = newNotice.getText()
} else {cell!.postLabel.text = ''}
cell!.postLabel.numberOfLines = 0
cell!.dateLabel.text = newNotice.getDate()
cell!.typeImageView?.tag = indexPath.row;
return cell!
}
//Make postLabel mandatory and set the font details in the xcode
class TimelineCell : UITableViewCell {
#IBOutlet var nameLabel : UILabel!
#IBOutlet var postLabel : UILabel!
#IBOutlet var dateLabel : UILabel!
override func awakeFromNib() {
//set this in xcode
}
override func layoutSubviews() {
super.layoutSubviews()
}
Also be sure that you are not creating any UI element and appending to the cell, as if you are you need to dispose it before you recycle the cell.
You can try setting:
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 44.0 // Set this value as a good estimation according to your cells
}
In the View Controller containing your tableView.
Make sure the layout constraints in your TimelineCell define a clear line of height constraints
Another option is responding to:
tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 44.0 // Your height depending on the indexPath
}
Always in the ViewController that contains your tableView and, I assume, is the tableView's UITableViewDelegate
Hope this helps
Set cell to nil that will fix some error.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
var cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as? ImageCellTableViewCell
cell = nil
if cell == nil {
cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for:indexPath) as? ImageCellTableViewCell
}
cell.yourcoustomtextTextLabel.text = "this is text"
cell.yourcoustomtextImageView.image = image
return cell
}

How do I create a custom UITableViewCell class in Swift?

I'm trying to follow along the following Using Auto Layout in UITableView for dynamic cell layouts & variable row heights top answer in Swift but for the IOS 7 version to mitigate some bugs in the IOS 8.
Right now, my custom cell class is:
class PostCell: UITableViewCell {
#IBOutlet var name: UILabel!
#IBOutlet var timestamp: UILabel!
#IBOutlet var postBody: 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
}
}
In my Storyboard I set my custom class as the class for the cell. I don't know if I have to use my reuseIdentifier anywhere. I don't know how to get that working in the next piece of code.
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
var cell = PostCell()
var post = self.posts[indexPath.row]
cell.name.text = post.name
cell.timestamp.text = post.timestamp
cell.postBody.text = post.body
cell.setNeedsUpdateConstraints()
cell.updateConstraintsIfNeeded()
cell.bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(feed.bounds), CGRectGetHeight(cell.bounds))
cell.setNeedsLayout()
cell.layoutIfNeeded()
var height = cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
height += 1.0
return height
}
I thought that would work, but when I call cell.name.text = post.name, I get an EXC_BAD_INSTRUCTION. I'm guessing the cell isn't properly hooked up to the storyboard somehow or something. How do I fix this?
The problem is that you don't understand how nibs work. You have set up your PostCell to be configured when it is loaded from its nib:
class PostCell: UITableViewCell {
#IBOutlet var name: UILabel!
}
So that means that name is nil - until this cell is instantiated from the nib, at which time the outlet will be hooked up. But then later you say:
var cell = PostCell()
But that is the wrong PostCell! You didn't load it from the nib; you just made one out of whole cloth. So since there was no nib-loading, the outlet (as you rightly suspect) was naturally never connected, and so cell.name is nil.
So the solution is: do not make one out of whole cloth. Load it from the nib.

Accessing custom table cell labels

I have created two custom labels in a table cell in order to be able to dynamically resize the cell(s) to it's content. I first tried using the "Subtitle" style and this worked out great except that the cell(s) didn't resize the way i wanted to and it looked really messy.
My question is: how do I access these labels in order to append my value's from my API to them?
View controller code:
import UIKit
class nyheterViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, APIControllerProtocol {
#IBOutlet weak var nyheterTableView: UITableView!
#IBOutlet weak var titleLabel: UILabel!
var searchResultsData: NSArray = []
var api: APIController = APIController()
func JSONAPIResults(results: NSArray) {
dispatch_async(dispatch_get_main_queue(), {
self.searchResultsData = results
print(self.searchResultsData)
self.nyheterTableView.reloadData()
})
}
override func viewDidLoad() {
super.viewDidLoad()
var APIBaseUrl: String = "http://*.se/*/*.php"
var urlString:String = "\(APIBaseUrl)"
//Call the API by using the delegate and passing the API url
self.api.delegate = self
api.GetAPIResultsAsync(urlString, elementName:"news")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//print(self.searchResultsData.count)
return self.searchResultsData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier: String = "nyheterResultsCell"
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as UITableViewCell
//nyheterTableViewCell.cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier);
//Create a variable that will contain the result data array item for each row
var cellData: NSDictionary = self.searchResultsData[indexPath.row] as NSDictionary
//Assign and display the Title field
var releaseDate: String = cellData["date"] as String
var titleVar: String = cellData["title"] as String
var titleMix: String = "\(titleVar)" + " - " + "\(releaseDate)"
cell.textLabel?.text = titleMix //textLabel worked out fine using "Subtitle" style.
// Get the release date string for display in the subtitle
cell.detailTextLabel?.text = cellData["content"] as String? //Same
return cell
}
}
I understand that I can't access these labels without somehow connecting them to the ViewController. Creating outlets to the ViewController generates an error about that I can't use connections from the prototype cell to the ViewController.
So, i created a new class, called nyheterTableViewCell which i connect to the table cell and connected outlets to my labels.
nyhterTableViewCell code:
import UIKit
class nyheterTableViewCell: UITableViewCell {
#IBOutlet weak var nyhetLabel: UILabel!
#IBOutlet weak var titleLabel: 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'm an beginner at Swift-programming and Xcode.
Cheers!
You don't need the labels connected to the view controller. Your example of the custom table view cell looks correct.
To access the label properties, you're going to want to change the line
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as UITableViewCell
to
let cell: nyheterTableViewCell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as nyheterTableViewCell

Resources