Programmatically change the image of a button in a table cell - ios

//The objectsAry is NSManagedObjects
//main view controller
var nowImg = UIImage(named: "img30.png")
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var NuTblCellInst = NuTableViewCell()
var maincell:UITableViewCell = self.mainList.dequeueReusableCellWithIdentifier("maincell") as NuTableViewCell
maincell.textLabel.text = self.objectsAry[indexPath.row].valueForKey("itemname") as? String
//NuTblCellInst.doImgLoad(nowImg!)
//uncomment the line above = fatal error: unexpectedly found nil while unwrapping an Optional value
// it doesn't matter if i use the method or use NuTblCellInst.btnWImg.setBackgroungImage directly (unwrapping error either way)
return maincell
}
// custom tableview cell class
class NuTableViewCell: UITableViewCell {
#IBOutlet weak var btnWImg: UIButton!
var nowImg = UIImage(named: "img30.png")
func doImgLoad(theImg: UIImage) {
btnWImg.setBackgroundImage(theImg, forState: UIControlState.Normal)
}
override func awakeFromNib() {
super.awakeFromNib()
//doImgLoad(nowImg!)
//uncomment the line above and it loads the image to the button no problem
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
I'm populating a reusable cell w/text as above, but not sure how to access the image (or background image) for a button placed in the table cell using storyboard where the image will vary with the table row.

From '... will vary with the table row,' it sounds like you're building a reusable template cell.
CustTableViewCell must be derived from UITableViewCell
Editing your template cell in the storyboard editor...
in the Identity Inspector, set the class to your custom class
create an outlet for the button in CustTableViewCell
In tableview:cellForRowAtIndexPath
create/dequeue maincell as a CustTableViewCell -- not UITableViewCell
set properties on the button via the outlet
return your instance (of course :-) )
cf custom uitableviewcells in storyboard

Related

indexPath is nil when passing cell from delegate to UITableViewController

I have a button called addSet at the end of each section of my tableView, it is used as a footerView and it is supposed to tell the UITableViewController of when it is pressed and in which section. My code for the custom table view cell is as follows
import UIKit
class FooterTableViewCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
var footerDelegate:FooterTableViewCellDelegate?
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
#IBAction func addSetIsPressed(_ sender: AnyObject) {
print("Add Set is pressed")
footerDelegate?.didAddSetIsPressed(cell:self)
}
}
protocol FooterTableViewCellDelegate {
func didAddSetIsPressed(cell:FooterTableViewCell)
}
And in my TableViewController, I implement it like so
func didAddSetIsPressed(cell: FooterTableViewCell) {
let indexPath = tableView.indexPath(for: cell)
print("Index path is \(indexPath)")
}
I want to get the indexPath (the section specifically) when the user taps my button, however it always returns nil. What am I doing wrong?
To put things in context. I am using this cell as a footerView, so the cell is implemented like so
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "footerCell") as! FooterTableViewCell
cell.footerDelegate = self
return cell
}
so it isn't implemented in cellForRow at indexPath like it would normally be
Thanks in advance.
The thing is you put the cell FooterTableViewCell as a viewForFooterInSection,
so it's not used as a UITableViewCell in the UITableView, so the UITableView is not holding the indexPath of this UITableViewCell "Cause i said previously, the cell's view only is used as a footerView"
You need to add the button inside the cell that's being rendered on the UITableView. "The one that's being returned in the tableView(_:cellForRowAt:) method"
On a side note i noticed that you have a variable named footerDelegate in your cell, it needs to be weak to avoid memory leaks as you assign your TableViewController as this delegate,
so the UITableViewCell holds a strong reference of the TableViewController that leads to memory leak cause also in the view hierarchy the TableViewController contains the UITableView as a subView.
I found out how to do it, in order to detect the section in which the button was tapped. There must be an outlet reference in the FooterCell and in the tableViewController, in viewForFooter in Section, just add the following line
cell.addSetOutlet.tag = section

How do I prevent getting nil interface elements while using custom cell in UITableViewController?

There are many, many questions on this site and others addressing the issue of getting a Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value from trying to set an interface element in a custom table view cell.
How can I prevent the interface elements from being nil when it's time to set them?
I've done my homework and checked out the answers to these related questions.
I've made sure I don't register the class in the viewDidLoad, as I'm using a UITableViewController.
The UITableViewController is referenced properly in the Main.storyboard file.
The cells in the UITableViewController are given the proper reuseIdentifier and class.
My dequeueReusableCell call to the tableView has the proper identifier, matching the one in the storyboard.
I've broken the nib's outlets and reconnected them.
I'm setting up a UITableViewController, "FindTableViewController."
class FindTableViewController: UITableViewController {
var services = Fetch().getServices()
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
/*Sections & rows defined here*/
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Monk", for:indexPath) as! Monk
let service = services[indexPath.row]
cell.set(service)
return cell
}
}
In another file, I define the custom cell Monk.
import UIKit
class Monk: UITableViewCell {
#IBOutlet weak var titleImage: UIImageView!
//other outlets defined here
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 set(_ service:Service) {
if let imgv = self.titleImage {
imgv.image = service.image
} else {
print(self.titleImage)
}
self.titleLabel.text = service.title
/*other elements set here*/
}
}
I have a feeling that I did something really, really stupid.
I've set the custom class name to Monk, and also the cell identifier to Monk, because I didn't want that confusion to be the source of the issue and making the names different didn't solve the problem.
I've even printed the values in the Service object and they match what was expected.
if let imgv = self.titleImage {
imgv.image = service.image
} else {
print(self.titleImage)
}
Prints "nil" and the error is thrown on the next line,
self.titleLabel.text = service.title
I also set the identifier and class of the nib to "Monk".
This is something simple.
If you are using a custom NIB for your cell, then you do need to register it.
Something like this in viewDidLoad:
let myNib = UINib(nibName: "MonkCell", bundle: nil)
tableView.register(myNib, forCellReuseIdentifier: "Monk")
This is unnecessary if you choose to layout the cell directly in the storyboard for your view controller.

How to subclass TWTRTweetTableViewCell in Swift Twitterkit?

I am trying to find out if it is possible to subclass TWTRTweetTableViewCell from the TwitterKit library. So far I have my custom cell class inherit from TWTRTweetTableViewCell. The xib has a UIView in it which has an outlet to the cell class and the UIView class is set to
TWTRTweetView. Like this-
class UserTweetViewCell: TWTRTweetTableViewCell {
#IBOutlet weak var tweetViewCustom: TWTRTweetView!
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
}
}
The cell's class in property inspector is set to UserTweetViewCell and the UIVIew's class in the cell is set to TWTRTweetView.
In the main view controller I have this
tableView.register(UserTweetViewCell.self, forCellReuseIdentifier: tweetTableReuseIdentifier)
and then
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let tweet = tweetsarr[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: tweetTableReuseIdentifier, for: indexPath) as! UserTweetViewCell
cell.tweetViewCustom.showActionButtons = false
cell.tweetViewCustom.linkTextColor = UIColor(red:0.12, green:0.53, blue:0.90, alpha:1.0)
cell.tweetViewCustom.configure(with: tweet as? TWTRTweet)
cell.tweetViewCustom.theme = .light
cell.tweetViewCustom.delegate = self
return cell
}
However, i get an error at line cell.tweetViewCustom.showActionButtons = false and the error is Unexpectedly found nil while unwrapping an Optional value. What am I missing here?
I finally did it and it's working like a charm. The trick is not to subclass TWTRTweetTableViewCell but instead just subclass a regular UITableViewCell and use a TWTRTweetView inside of it. Which is basically what TWTRTweetTableViewCell does, it has tweetView property which is essentially an IBOutlet of type TWTRTweetView. The custom cell Nib should contain a UIView with TWTRTweetView set as it's class in the identity inspector. Here goes the code-
class CustomTweetCell: UITableViewCell{
#IBOutlet weak var customTweetView: TWTRTweetView!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func configureCell(with tweet: TWTRTweet){
self.customTweetView.showActionButtons = false
self.customTweetView.configure(with: tweet)
}
}
For the cell's height, the following needs to be done for the tableview-
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let tweet = tweets[indexPath.row]
let tweetheight = TWTRTweetTableViewCell.height(for: tweet as! TWTRTweet, style: .compact, width: self.view.frame.width, showingActions: false) + 30 //this 30 should be the height of any additional views that you put in the cell Nib file
return tweetheight
}
NOTE: Its extremely important to have autolayout constraints enabled within the tableview cell with the TWTRTweetView and any other views that you may have and also make sure the Table view cell row height is set to Default or blank in the cell's Size inspector.Failing to do so will mess up the tweet view height and will cause undesirable results.
I wanted to Subclass TWTRTweetTableViewCell so that I could add the likes count, retweets count, reply button etc. so far it hasn't worked. So next I am going to give it a try Subclassing TWTRTweetView and use that in the tableview cell instead. I think I have tried it once with partial success. The challenge is the tweet height
This is how I am calculating the tweet height in Objective-c:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
TWTRTweet * tweet = self.tweets[indexPath.row];
if (self.tweets.count > indexPath.row) {
[self.prototypeCell configureWithTweet:tweet];
}
CGFloat tweetHeight = [CustomTweetTableViewCell heightForTweet:tweet style:TWTRTweetViewStyleCompact width:[tableView bounds].size.width showingActions:YES];
self.tweetHeights[indexPath.row] = [NSNumber numberWithFloat:tweetHeight];
return tweetHeight;
}

Adding items to tableview with sections

I display a shopping list for different product groups as a table view with multiple sections. I want to add items with an add button for each group. So I equipped the header cell with a UIToolbar and a + symbol as a UIBarButtonItem.
Now every product group has an add button of his own:
If one add button was pressed, I have a problem identifying which one was pressed.
If I connect the add button with a seque, the function prepareForSeque(...) delivers a sender of type UIBarButtomItem, but there is no connection to the header cell from were the event was triggered.
If I connect an IBAction to the UITableViewController, the received sender is also of type UIBarButtomItem, and there is no connection to the header cell, too.
If I connect an IBAction to my CustomHeaderCell:UITableViewCell class, I am able to identify the right header cell.
This line of code returns the header cell title:
if let produktTyp = (sender.target! as! CustomHeaderCell).headerLabel.text
However, now the CustomHeaderCell class has the information I need.
But this information should be available in the UITableViewController.
I couldn't find a way to feed the information back to the UITableViewController.
import UIKit
class CustomHeaderCell: UITableViewCell {
#IBOutlet var headerLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
#IBAction func neuesProdukt(sender: UIBarButtonItem) {
if let produktTyp = (sender.target! as! CustomHeaderCell).headerLabel.text
{
print(produktTyp)
}
}
}
Here's how I typically handle this:
Use a Closure to capture the action of the item being pressed
class CustomHeaderCell: UITableViewCell {
#IBOutlet var headerLabel: UILabel!
var delegate: (() -> Void)?
#IBAction func buttonPressed(sender: AnyObject) {
delegate?()
}
}
When the Cell is created, create a closure that captures either the Index Path or the appropriate Section.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let section = indexPath.section
let cell = createCell(indexPath)
cell.delegate = { [weak self] section in
self?.presentAlertView(forSection: section)
}
}

How to access custom cell created in xib

I have designed a custom cell in xib. And created a class for that as well. The code for that class is as given below-
class ProjectsCell : UITableViewCell {
#IBOutlet var projectNameLabel: UILabel! //This is outlet to which I will assign value.
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
}
}
Now I have a view controller there I am trying to access this cell. In storyboard I have given reusable identifier "Cell". Now I am using this cell like the following code-
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as ProjectsCell
let project = projects[indexPath.row]
cell.projectNameLabel?.text = project.ProjectName //********* Here I am getting exception for projectNameLabel.
return cell
I think that label is coming null. I have tried the following approach also but that is also not working.
var cell: ProjectsCell = tableView.dequeueReusableCellWithIdentifier("Cell") as ProjectsCell
tableView.registerNib(UINib(nibName: "ProjectsCell", bundle: nil), forCellReuseIdentifier: "Cell")
cell = tableView.dequeueReusableCellWithIdentifier("Cell") as ProjectsCell
What can be the issue if anyone has faced this same issue.
Your custom cell should inherit from the class UITableViewCell. So the class would look like this.
class ProjectsCell: UITableViewCell {
#IBOutlet var projectNameLabel: UILabel! //This is outlet to which I will assign value.
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
}
}
If you have it like this it should work. Because i am doing it like that in my Apps. For the future if you create a class you can use the 'File -> New Files...' menu. There you can select coca touch class and specify the class you want to inherit from and xcode will add all necessary functions.
You have dequeued the custom cell but not initialised it with the following method.
Besides this you also have to set your custom class as the sub-class of UITableViewCell this is because you are getting the null value for the cell.
if cell == nil {
cell = UITableViewCell(style: UITableViewCellStylePlain reuseIdentifier: "Cell")
}
I solved the problem using the following code-
var array = NSBundle.mainBundle().loadNibNamed("ProjectsCell", owner: self, options: nil)
var cell = array[0] as ProjectsCell
let project = projects[indexPath.row]
cell.nameLabel?.text = project.Name
return cell
Thanks everyone for contributing. :)
Maybe it set the dataSource and delegate :
(source: icodeblog.com)

Resources