Modify custom TableViewCell size based on contents - ios

I want to be able to resize my custom TableViewCell to wrap its contents, which can be a variably-sized image and/or any number of lines of text, followed by a vote bar.
My OMCFeedTableViewCell looks like this:
I have tried setting up contraints, modifying frames, nothing works correctly.
This is my code so far:
In FeedViewController:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("OMCFeedCell", forIndexPath: indexPath) as! OMCFeedTableViewCell
let p = posts[indexPath.row]
cell.selectionStyle = .None
let data = p.content!.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let content = try! JSON(NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers))
let image = content["image"].string
let uploads = uploadAPI()
uploads.tryGetImage(image) {
(result: UIImage?) in
if let i = result {
cell.postImage.image = i
cell.postImage.contentMode = .ScaleAspectFill
let containerWidth = cell.postImage.frame.width
let aspectRatio = i.size.height / i.size.width
let scaledHeight = aspectRatio * containerWidth
// do something with the calculated height
} else {
// kill the imageview
}
}
if let text = content["text"].string {
cell.postText.text = text
// calculate the textview height
} else {
// kill the text section
}
return cell
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if heights[indexPath.row] == nil {
// calculate the correct overall height
}
return heights[indexPath.row]!
}
In OMCFeedTableViewCell:
import UIKit
class OMCFeedTableViewCell: UITableViewCell {
#IBOutlet weak var postImage: UIImageView!
#IBOutlet weak var postText: UILabel!
#IBOutlet weak var voteLabel: UILabel!
#IBOutlet weak var downVoteButton: UIButton!
#IBOutlet weak var upVoteButton: UIButton!
}

You can use UITableViewAutomaticDimension and it would automatically adjust your cell size depending on contents.
self.tableView.rowHeight = UITableViewAutomaticDimension
also set your estimatedRowHeigh
self.tableView.estimatedRowHeight = 120
Also from experience, sometime the bottom constraint on main content view has given me a problem, you can remove it if it bothered you.

Related

Why is the shadow being applied to the text in my view and not the view itself?

I have a table view populated with 2 labels of text. I want the views to be outlined with a shadow, however, with my current code, only the text gets a shadow. Here is my code:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: K.reuseIdentifier, for: indexPath) as! NoteTableViewCell
cell.titleLabel?.text = titleArray[indexPath.row]
cell.mainTextLabel?.text = noteArray[indexPath.row]
cell.titleLabel?.font = UIFont(name:"HelveticaNeue-Bold", size: 16.0)
cell.mainTextLabel?.font = UIFont(name:"HelveticaNeue", size: 14.0)
cell.cardView?.layer.shadowOpacity = 1
cell.cardView?.layer.shadowOffset = .zero
cell.cardView?.layer.shadowRadius = 1
cell.cardView?.layer.cornerRadius = 8
cell.cardView?.layer.shadowOffset = CGSize(width: 10,
height: 10)
return cell
}
NoteTableViewCell
class NoteTableViewCell: UITableViewCell {
#IBOutlet weak var cardView: UIView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var mainTextLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
Here is a screenshot of my app:
I figured out that it was because I had not set a height or width for the view. After doing so it displayed properly

iOS Scrolling Parallax Effect in Table View Cell Swift 4

I create Scrolling Parallax Effect in Table View Cell in my project but i have some freeze when i scrolling.
My code in custom Table View Cell :
class newsTableViewCell: UITableViewCell {
#IBOutlet weak var imageNews: UIImageView!
#IBOutlet weak var titleLable: UILabel!
#IBOutlet weak var imageHeight: NSLayoutConstraint!
#IBOutlet weak var imageTop: NSLayoutConstraint!
override func awakeFromNib() {
super.awakeFromNib()
imageNews.clipsToBounds = true
}
}
Code in my Main View Controller:
var paralaxOfSetSpeed: CGFloat = 70
var cellHeignt: CGFloat = 170
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! newsTableViewCell
let good = data[indexPath.row]
cell.titleLable.text = (good["title"] as! String)
cell.imageHeight.constant = paralaxImageHeight
cell.imageTop.constant = paralaxOfSet(newOfSetY: tableView.contentOffset.y, cell: cell)
let newsImage = good["image"] as! PFFile
newsImage.getDataInBackground{ (imageData, error) in
if imageData != nil {
let image = UIImage(data: imageData!)
cell.imageNews.image = image
} else {
print(error ?? "getDataInBackground error")
}
}
return cell
}
func paralaxOfSet(newOfSetY: CGFloat, cell: UITableViewCell) -> CGFloat {
return (newOfSetY - cell.frame.origin.y) / paralaxImageHeight * paralaxOfSetSpeed
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let ofSetY = tableView.contentOffset.y
for cell in tableView.visibleCells as! [newsTableViewCell] {
cell.imageTop.constant = paralaxOfSet(newOfSetY: tableView.contentOffset.y, cell: cell)
}
}
var paralaxImageHeight: CGFloat {
let maxOffSet = (sqrt(pow(cellHeignt, 2) + 4 * paralaxOfSetSpeed * self.tableView.frame.height ) - cellHeignt) / 1.5
return maxOffSet + self.cellHeignt
}
This is my code, main problem it's scrolling because it's a not smooth.
UPDATE:
So in the end, i want to get something like this:
I would suggest you to look into the below libraries:
They have implemented the parallax view in TableViewCell
https://github.com/marty-suzuki/SAParallaxViewControllerSwift
Hope it helps.

Can't call object from another class

I have a table view with expanding cells. The expanding cells come from a xib file. In the class of the table is where all of the code is that controls the expansion and pulling data from plist. I'm trying to add a close button but only want it to show when the cell is expanded. As it stands, I can't reference the button to hide it because it's in another class. Here is how I am trying to access it:
import UIKit
class SecondPolandViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var customTableViewCell:CustomTableViewCell? = nil
var items = [[String:String]]()
override func viewDidLoad() {
super.viewDidLoad()
**REFERENCING CLASS**
customTableViewCell = CustomTableViewCell()
let nib = UINib.init(nibName: "CustomTableViewCell", bundle: nil)
self.tableView.register(nib, forCellReuseIdentifier: "cell")
self.items = loadPlist()
}
func loadPlist()->[[String:String]]{
let path = Bundle.main.path(forResource: "PolandResourceList", ofType: "plist")
return NSArray.init(contentsOf: URL.init(fileURLWithPath: path!)) as! [[String:String]]
}
var selectedIndex:IndexPath?
var isExpanded = false
func didExpandCell(){
self.isExpanded = !isExpanded
self.tableView.reloadRows(at: [selectedIndex!], with: .automatic)
}
}
extension SecondPolandViewController:UITableViewDataSource, UITableViewDelegate{
***HIDING BUTTON***
let button = customTableViewCell?.closeButton
button?.isHidden = true
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.selectedIndex = indexPath
self.didExpandCell()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell
cell.selectionStyle = .none
let item = self.items[indexPath.row]
cell.titleLabel.text = item["title"]
cell.shortLabel.text = item["short"]
cell.otherImage.image = UIImage.init(named: item["image"]!)
cell.thumbImage.image = UIImage.init(named: item["image"]!)
cell.longLabel.text = item["long"]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let height = UIScreen.main.bounds.height
if isExpanded && self.selectedIndex == indexPath{
//return self.view.frame.size.height * 0.6
return 400
}
return 110
//return height * 0.2
}
}
This does not hide it though.
Here is the xib that I am calling from if it helps. It is probably simple, I am just a newly self taught developer.
import UIKit
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var closeButton: UIImageView!
#IBOutlet weak var otherImage: UIImageView!
#IBOutlet weak var thumbImage: UIImageView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var shortLabel: UILabel!
//#IBOutlet weak var longLabel: UITextView!
#IBOutlet weak var longLabel: UITextView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
//let width = UIScreen.main.bounds.width
//let height = UIScreen.main.bounds.height
//thumbImage.frame.size.width = height * 0.19
//thumbImage.frame.size.height = height * 0.19
}
}
It seems like that you just need to add these lines into cellForRowAt:indexPath method:
if indexPath == selectedIndexPath {
cell.closeButton.isHidden = false
} else {
cell.closeButton.isHidden = true
}
You may add them right before return line
The normal iOS answer for this is a delegate, but you could get away with a simple closure in this case.
In CustomTableViewCell, add
public var closeTapped: ((CustomTableViewCell) -> ())?
Then in that class, when close is tapped, call
self.closeTapped?(self)
In the VC, in cellForRowAt,
cell.closeTapped = { cell in
// do what you want with the VC
}
For delegates, this might help: https://medium.com/#jamesrochabrun/implementing-delegates-in-swift-step-by-step-d3211cbac3ef
The quick answer to why to prefer delegates over the closure is that its a handy way to group a bunch of these together. It's what UITableViewDelegate is (which you are using). Also, it's a common iOS idiom.
I wrote about this here: https://app-o-mat.com/post/how-to-pass-data-back-to-presenter for a similar situation (VC to VC communication)

Updating Constraints in UITableViewCell are not properly

I want to make a dynamic cell in my UITableView in case of image height. Everything works properly, but when I scroll my UITableView, debugger is yelling me about bad constraints in my cell, but cells look exactly what I want. I can't be calm when I see this debug info ;)
My CustomCell class looks like that:
class CustomCell: UITableViewCell {
//MARK: - Outlets
#IBOutlet weak var photoImageView: UIImageView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var heightConstraint: NSLayoutConstraint!
//MARK: - Variables
var dream: MyDream? {
didSet {
if let item = dream, let image = UIImage(data: item.photoData) {
scaleImageView(image: image)
photoImageView.image = image
titleLabel.text = item.title
}
}
}
func scaleImageView(image: UIImage) {
let imageWidth = image.size.width
let imageScale = image.size.height / image.size.width
if imageWidth > photoImageView.bounds.width {
heightConstraint.constant = photoImageView.bounds.size.width * imageScale
} else {
heightConstraint.constant = imageWidth * imageScale
}
}
}
my cell in .xib looks like that:
TitleLabel doesn't have a height constraint. Leading, Top, Trailing, Bottom only.
and my ViewController is quite simple and looks like that:
class ViewController: UIViewController {
//MARK: - Outlets
#IBOutlet var tableView: UITableView!
//MARK: - Variables
lazy var dreams = [MyDream]()
override func viewDidLoad() {
super.viewDidLoad()
createModel() // I mock data here :)
setTableView()
}
func setTableView() {
tableView.delegate = self
tableView.dataSource = self
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
tableView.register(UINib(nibName: String(describing: CustomCell.self), bundle: nil), forCellReuseIdentifier: "cell")
}
func createModel() {
for i in 0..<12 {
if i < 6 {
if let image = UIImage(named: "\(i + 1)"), let imageData = UIImageJPEGRepresentation(image, 100) {
let dream = MyDream(photoData: imageData, title: "Image number \(i + 1)")
dreams.append(dream)
}
} else {
if let image = UIImage(named: "\(i - 5)"), let imageData = UIImageJPEGRepresentation(image, 100) {
let dream = MyDream(photoData: imageData, title: "Image number \(i + 1)")
dreams.append(dream)
}
}
}
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dreams.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomCell
cell.dream = dreams[indexPath.row]
cell.selectionStyle = .none
return cell
}
}
How it looks like in simulator:
every thing look fine, but console gives me this kind of error:
I don't know why? I tried to use layoutIfNeeded(), updateConstraints() and setNeedsLayout() in didSet in CustomCell but is doesn't help. Any suggestions?
I guess you're having some redundant constraints on your image.
They could be height / bottom / top constraints. One should be removed

How do I make a custom table cell work with dynamically sized labels within?

I'm not sure the best way to show the issue here with multiple files in an xCode project, so forgive me if this isn't the best way--and please let me know what else I can show!
In any case, I have a custom table view displaying from a nib (.xib), and have multiple dynamic height labels within that custom table view. Everything is displaying great, except for the fact that the table cell height overall is not accommodating for the increased height of the elements within.
What I can see happening is that the row height value for the Table View is being used as an absolute, and I'm not sure how to override that. I have that set as 200, and it's fixed for every table, even if the content overflows that height:
I'm using auto-layout, which is working great among the cell elements themselves... it's just the cell height that's not being set.
Here's my code for the elements within the cell:
class EventTableViewCell: UITableViewCell {
func loadItem (#date:String, title:String, description:String, image:UIImage) {
dateLabel.text = date
titleLabel.text = title
eventImage.image = image
eventDescription.text = description
titleLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
titleLabel.numberOfLines = 0
self.contentView.addSubview(titleLabel)
eventDescription.setTranslatesAutoresizingMaskIntoConstraints(false)
eventDescription.numberOfLines = 0
self.contentView.addSubview(eventDescription)
}
#IBOutlet weak var eventImage: UIImageView!
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var eventDescription: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
And the class that handles the data:
class Contacts: UITableViewController, UITableViewDataSource, UITableViewDelegate {
var eventData:[AnyObject] = []
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return eventData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("customCell", forIndexPath: indexPath) as EventTableViewCell
if (eventData.count != 0) {
var event: AnyObject? = eventData[indexPath.row]
if let unwrappedEvent: AnyObject = event {
let eventTitle = unwrappedEvent["title"] as? String
let eventDate = unwrappedEvent["date"] as? String
let eventDescription = unwrappedEvent["description"] as String
let eventImage = unwrappedEvent["image"] as? UIImage
if (eventImage != nil){
cell.loadItem(date: eventDate!, title: eventTitle!, description: eventDescription, image: eventImage!)}
else {
let testImage: UIImage = UIImage(named: "test-image.png")
cell.loadItem(date: eventDate!, title: eventTitle!, description: eventDescription, image: testImage )
}
}
}
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
var nib = UINib(nibName: "CustomTableViewCell", bundle: nil)
tableView.registerNib(nib, forCellReuseIdentifier: "customCell")
let manager = AFHTTPRequestOperationManager()
manager.GET( "http://[my-json-file].json",
parameters: nil,
success: {
(operation: AFHTTPRequestOperation!,responseObject: AnyObject!) in
println("JSON: " + responseObject.description)
self.eventData = responseObject!["events"] as [AnyObject]
self.tableView.reloadData()
},
failure: { (operation: AFHTTPRequestOperation!,error: NSError!) in
println("Error: " + error.localizedDescription)
})
}
I'm just not sure what else I can do at this point to get the cell height to work dynamically. I've tried just about everything. Any help would be greatly appreciated, and again, please let me know if there's anything else I need to show from my code, etc.
Thanks so much in advance!
I notice that in your Contacts class, you are not implementing the tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) method. I suspect that if you do that, then the code will work.

Resources