I need to change the last UITAbleViewCell Bottom Constraints constant on runtime.
I've added an identifier on IB, and I've added the following extension to my UIView.
func getConstraintById(id:String) -> NSLayoutConstraint?{
let constraints = self.constraints()
for c in constraints{
if((c as! NSLayoutConstraint).identifier == id){
return c as? NSLayoutConstraint
}
}
return nil
}
All works fine, but it seems redundant to iterate all constraints for each cell.
Is there a better way?
I've solved this by adding a custom UITableView class with an outlet to the needed constraint, then I had a direct approach on my cellForRowAtIndexPath method.
class:
import UIKit
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var bottomPadding: NSLayoutConstraint!
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
}
}
Method:
if(index + 1 == ITEMS?.count){
cell.bottomPadding.constant = 5
}else{
cell.bottomPadding.constant = 0
}
Related
I have an app in which I have a tableview. Inside this tableview I currently have 8 different types of cells. Each of these cells are different but also have similarities.
As an example (using only 2 cells for simplicity). Imagine you have cell 1 which can be divided into 3 parts:
--------------A--------------
--------------B--------------
--------------C--------------
Now cell 2 also has 3 parts. Parts A and C are the same as in cell1 (different data of course, but same structure):
--------------A--------------
--------------D--------------
--------------C--------------
Currently, in my cellForRowAt method I just check if the cell type should be 1 or 2 and then I dequeue cell for that type. The challenge is that I would like to avoid setting part A and C two different places in the code.
Eg. instead of
if type == type1 {
//Set A
//Set B
//Set C
} else if type == type2 {
//Set A
//Set D
//Set C
}
I would like
//Set A
//Set C
if type == type1 {
//Set B
} else if type == type2 {
//Set D
}
I was wondering if there is a way to "abstract" these commonalities?
Please let me know if anything is unclear.
EDIT: IB Challenge
I probably should also mention that a tricky part for me is figuring out how a parent cell would fit in in regards to IB. I have separate xib files for each cell type, but if I would only have one parent swift file with section A and C, can both my other xib files have the same outlets to this parent and how would this even be done?
Parent TableViewCell
class BaseTableViewCell: UITableViewCell {
#IBOutlet weak var ALabel: UILabel!
#IBOutlet weak var CLabel: 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
}
}
Subclass of BaseCell
class TypeOneTableViewCell: BaseTableViewCell {
#IBOutlet weak var BLabel: 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
}
}
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.
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;
}
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!
I'm in trouble.I have a city list, I want to display cities in UITableView and I have disabled UITableView's scroll because i don't want to display scroll.
when I changed to UITableView's height dynamically, it doesn't changed and UITableView does not display all cities. Below the code. Please look it.
import UIKit
class FlightDetailsOneWayViewController: UIViewController {
var flightDetailArr:[FlightDetailsOneWay] = [FlightDetailsOneWay]()
#IBOutlet var tableView: UITableView!
#IBOutlet var scrollview: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
flightDetailArr.append(FlightDetailsOneWay(depCity: "London"))
flightDetailArr.append(FlightDetailsOneWay(depCity: "China"))
flightDetailArr.append(FlightDetailsOneWay(depCity: "Singapore"))
flightDetailArr.append(FlightDetailsOneWay(depCity: "Dubai"))
self.tableView.scrollEnabled = false
var cellHeight = CGFloat(self.tableView.rowHeight)
var totalCity = CGFloat(flightDetailArr.count)
var totalHeight = cellHeight * totalCity
self.tableView.frame.size.height = CGFloat(totalHeight)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return flightDetailArr.count
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!
{
let cell:FlightDetailsCell = tableView.dequeueReusableCellWithIdentifier("FlightDetail") as FlightDetailsCell
let flight = flightDetailArr[indexPath.row]
cell.setCell(flight.depCity)
return cell
}
}
class FlightDetailsOneWay
{
var depCity = ""
init(depCity: String)
{
self.depCity = depCity
}
}
class FlightDetailsCell: UITableViewCell {
#IBOutlet var depCity: 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
}
func setCell(depCity: String)
{
self.depCity.text = depCity
}
}
below the preview of above code.
As explanation required to this answer is very detailed.
Follow my other answer "Moving views with constraints" to get idea about how to update layout constraint.
And as per your comment about how to connect NSLayoutConstraint variable with height constraints? as below:
Go to Storyboard.
Select UIView in which you have added constraint. Click on Constraints icon to see all constraints. Right now in below image I have added only one constraint you may find more than that. Check for which constraint you have change it's properties.
Now click on view controller and Click on Show the connection inspector. here you can see all IBOutlets defined in your view controller. As in below image you can see I have created only one IBOutlet for sake of simplicity which is heightConstraint.
And this is how you can add connection to constraint variable.
You can use prototype cell if all your cell will have the same look:
a complete tutorial to make a project using prototype cell
or
a second which it more quick to read
You can can also use
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 42.0
}
This function is perfect if you resize dynamically the height cell.
You just need to use: beginUpdates() and endUpdates() for make it works.