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;
}
Related
I have an issue in my UICollectionView where some cells seem to be neither part of the indexPathForVisibleItems, nor are they taken from the caching queue with dequeueReusableCell. The result is, that some cells don't receive required updates of data during a scroll and show old behavior.
For simplicity, I reduced the project to the neccessary Controllers and a minimized Storyboard. Basically, I've got a NavigationController as EntryPoint that contains the MainViewController, which itself contains a ContainerView with the CollectionViewController.
The NavigationController uses the default edit button to switch between edit and non-edit mode - this should result in an image displayed on the cells while in edit mode. Therefore I implemented setEditing and changed the images hidden property of all visible cells, and additionally i set the images hidden property while dequeuing - assuming that cells are either visible or they will be dequeued in the future.
This works fine while the CollectionView is scrolled from top to bottom. But when I switch back from Edit-Mode to Non-Edit-Mode while scrolled to the bottom and then scroll back to the top, some cells still display the image (more specific: at least the same row, which is the first non-visible row). Somehow I'd assume that the dequeued cells and the visible cells would be complementary parts of the displayed data, which should result in either the images being hidden/unhidden during the setEditing call (which works for the first 4 rows of cells) or being hidden/unhidden during the dequeuing (which works for the last few rows, except the third row in my example)
Code for the CollectionViewController:
import Foundation
import UIKit
import Photos
class CollectionViewController : UICollectionViewController {
fileprivate let CELL_ID = "PicCell"
fileprivate let IMAGE_VIEW_SIZE = 104
var selectedIndex = -1
var itemCount = 28
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
print(self.isEditing)
collectionView?.allowsMultipleSelection = editing
for indexPath in (collectionView?.indexPathsForSelectedItems)! {
collectionView?.deselectItem(at: indexPath, animated: true)
}
for indexPath in (collectionView?.indexPathsForVisibleItems)! {
let cell = collectionView?.cellForItem(at: indexPath) as? PicCell
if cell != nil {
cell?.editing = editing
}
}
}
override func viewDidLoad() {
self.navigationItem.rightBarButtonItem = self.editButtonItem
collectionView?.reloadData()
}
}
extension CollectionViewController {
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return itemCount;
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print(indexPath.row)
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CELL_ID, for: indexPath) as! PicCell
cell.editing = isEditing
cell.isSelected = false
return cell;
}
}
Code for PicCell
import Foundation
import UIKit
class PicCell : UICollectionViewCell {
#IBOutlet weak var deleteBtn: UIImageView!
var editing:Bool = false{
didSet {
self.deleteBtn.isHidden = !editing
self.deleteBtn.tintColor = UIColor.blue
}
}
override var isSelected: Bool{
didSet {
if isSelected && editing {
self.deleteBtn.tintColor = UIColor.red
} else {
self.deleteBtn.tintColor = UIColor.blue
}
}
}
override func prepareForReuse() {
super.prepareForReuse()
self.editing = false
self.isSelected = false
}
}
Code for MainViewController
import Foundation
import UIKit
class MainViewController: UIViewController {
override func viewDidLoad() {
self.navigationItem.rightBarButtonItem = self.editButtonItem
}
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
collectionController?.setEditing(editing, animated: animated)
}
var collectionController : CollectionViewController? = nil
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowCollectionView" {
collectionController = segue.destination as! CollectionViewController
}
}
}
as you can see I'm using constant values for the item count in a single section, which shouldn't be a big deal.
I don't know if it matters, but my cells are 104px in width and height, the image is rendered as template and the UIContainerView is 343 in width and 473 in height, i'm testing on an iPhone 7+ simulator with iOS 10.1
If there's anything missing from the storyboard, i might add some screenshots, but i'm unsure what to post exactly
Thanks in advance for your help and kind regards
Christian
Edit: just to be clear with my question: I'm looking for some advice for either obvious mistakes in my code or a way to access UICollectionViewCells that are neither in the list of visible items nor dequeued during scroll - there might be a way to access those cells i simplay don't know about
You can change this behavior if you turn off prefetching:
collectionView?.isPrefetchingEnabled = false
Or you can keep it on, but then either:
hook into UICollectionViewDelegate methods didEndDisplaying and willDisplay to know as cells appear and disappear independent of cellForItemAt; or
have cells do some KVO on some view controller property or observe some notification that the view controller will initiate in order to know whether to change their state.
Usually, We use the dequeueReuseableCellwithIdentifier method in ViewController class but I want to use this method in the UITableViewCell.I have tried but I got the exception like this.
fatal error: unexpectedly found nil while unwrapping an optional value
ViewController Class:
#IBOutlet var tableView: UITableView!
var tableData:[songData] = [songData]()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.backgroundColor = UIColor.orangeColor()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = TableViewCell()
cell.datas()
return cell
}
}
TableViewCell Class:
#IBOutlet var text1: UILabel!
#IBOutlet var text2: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func datas(){
let vc = ViewController()
let tableData = vc.tableData
print(tableData)
let tableview = vc.tableView
let indexpath:NSIndexPath = NSIndexPath()
let cell = tableview.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexpath) as! TableViewCell //The fatal error is showing exactly at this line.
let artistAndAlbum = tableData[indexpath.row]
cell.text1.text = artistAndAlbum.country
cell.text2.text = artistAndAlbum.currency
tableview.reloadData()
}
I need to customize my table data in the TableViewCell class.If it is possible help me or else why it is not possible?
You're going about this the wrong way. It honestly doesn't make any sense for your table cell subclass to be creating itself. It does make sense, however, for your cell subclass to be passed data and for it to populate itself from that.
You should have your view controller dequeue the cell as normal and then change your table cell function to take some data as a parameter and update itself.
In your view controller:
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerNib(UINib(nibName: "INSERT_NIB_NAME", bundle: nil), forCellReuseIdentifier: "Cell")
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! TableViewCell
cell.updateWithData(tableData[indexPath.row])
return cell
}
If your cell is a prototype cell in the storyboard then you have to set the reuse identifier there instead of registering in viewDidLoad.
In your table cell:
func updateWithData(artistAndAlbum: songData) {
text1.text = artistAndAlbum.country
text2.text = artistAndAlbum.currency
}
In your view controller's viewDidLoad(), register the class with a reuse identifier.
tableView.registerClass(TableViewCell.self, forCellReuseIdentifier: "ID")
Then your cellForRowAtIndexPath method can dequeue your custom cell.
tableView.dequeueReuseableCellWithIdentifier("ID", indexPath: indexPath)
This isn't just limited to view controllers. If you have a custom table view cell, then register the class for a reuse identifier wherever you setup the table view and then dequeue your custom cell with that identifier in its cellForRowAtIndexPath.
As a general rule of thumb, your view should not keep a reference to its view controller. The view shouldn't care about any view controllers or need to know what the view controller is doing. Either the entire table view and all of its workings should go in your view, hidden from the view controller, or you should keep all of your table view code in the view controller. This will make your life much easier.
Firstly you must set name for cell identifier
after it in cellForRowAtIndexPath method used this code:-
for custom cell
CustomCellTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CELL" forIndexPath:indexPath];
//-------------------------------------------------------------
for normal cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CELL" forIndexPath:indexPath];
Check your vc.tableView. It's probably nil
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)
I have a UILabel in a custom UITableViewCell called "ScheduleCell", like so:
Here is the code for ScheduleCell:
import UIKit
class ScheduleCell: UITableViewCell {
#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 want the tileLabel to expand based on its text and I want the ScheduleCell to expand with it. Here are the attributes of the titleLabel:
Here are the attributes of the cell prototype, which is linked to the ScheduleCell class:
Here is some relevant code for the ViewController:
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 160.0
And
func tableView(tableView: UITableView!,cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as! ScheduleCell
cell.backgroundColor = UIColor(rgb: 0xF6FBFE)
cell.titleLabel.text = (Globals.scheduleArr[indexPath.row][3] as! String)
cell.titleLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
return cell
}
All of the titles from "Globals.scheduleArr" fit on one line except one, which is "Jane Richards Grey Reads "To Kill a Mockingbird"". Here is what the ViewController looks like in the iOS Simulator:
As you can see, the label does not expand as necessary. Strangely enough, when I add a "\n" to the end of each of the titles, the label does expand. However, the cell doesn't seem to expand with the label:
Any help in resolving this issue would be greatly appreciated!
If you want to use UITableViewAutomaticDimension, you need to add Label-ContentView bottom constraint.
Add new bottom constraint and try again!
//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