Updating Constraints in UITableViewCell are not properly - ios

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

Related

UITableViewCell size does not change

I cannot understand why the cells do not resize according to the given .xib file
This is my table controller
import Foundation
import UIKit
class RecipeTableView: UIViewController {
let cellIdentifier = "RecipeTableViewCell"
#IBOutlet weak var recipeTableView: UITableView!
private let localDatabaseManager: LocalDatabaseManager = LocalDatabaseManager.shared
private var recipes = [Recipe]()
override func viewDidLoad() {
super.viewDidLoad()
recipeTableView.dataSource = self
recipeTableView.delegate = self
//recipeTableView.rowHeight = UITableView.automaticDimension
//recipeTableView.estimatedRowHeight = UITableView.automaticDimension
self.recipeTableView.register(UINib(nibName: cellIdentifier, bundle: nil), forCellReuseIdentifier: cellIdentifier)
localDatabaseManager.loadRecipes { [weak self] (recipes) in
guard let recipes = recipes else {
return
}
self?.recipes = recipes
DispatchQueue.main.async {
self?.recipeTableView.reloadData()
}
}
}
// override func viewWillAppear(_ animated: Bool) {
// recipeTableView.estimatedRowHeight = 256
// recipeTableView.rowHeight = UITableView.automaticDimension
// }
}
extension RecipeTableView: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
recipes.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? RecipeTableViewCell else {
return UITableViewCell()
}
let recipe = recipes[indexPath.row]
cell.configure(with: recipe)
//cell.layer.cornerRadius = 32
//cell.layer.masksToBounds = true
return cell
}
}
extension RecipeTableView: UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
}
And my cell swift file
import Foundation
import UIKit
import Kingfisher
import Cosmos
class RecipeTableViewCell: UITableViewCell {
#IBOutlet weak var recipeNameLabel: UILabel!
#IBOutlet weak var recipeDescriptionLabel: UILabel!
#IBOutlet weak var recipeImageView: UIImageView!
#IBOutlet weak var recipeCosmosView: CosmosView!
override func prepareForReuse() {
super.prepareForReuse()
recipeNameLabel.text = nil
recipeDescriptionLabel.text = nil
recipeImageView.image = nil
}
func configure(with recipe: Recipe) {
recipeNameLabel?.text = recipe.name
recipeDescriptionLabel?.text = recipe.description
//let imageBytes = recipe.imageData
//let imageData = NSData(bytes: imageBytes, length: imageBytes.count)
//let image = UIImage(data: imageData as Data)
//recipeImageView?.image = image
let imageUrl = URL(string: recipe.imageData)
recipeImageView?.kf.setImage(with: imageUrl)
recipeCosmosView.settings.fillMode = .precise
recipeCosmosView.rating = recipe.rating
}
}
here is what my custom cell looks like
here is how these cells are shown in the app
I already found similar questions, but everywhere the same answer. Need to add the following lines. So I tried.
override func viewWillAppear(_ animated: Bool) {
recipeTableView.estimatedRowHeight = 256
recipeTableView.rowHeight = UITableView.automaticDimension
}
But it did not work
I think you need to call another tableView method for set the height for each cell according to his content
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// add estimated height here ....
//...
return indexPath.row * 20
}

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.

Layout and sizing issues with TableViewCells that change height based on content inside - Swift 3

I'm building a simple messenger app that uses a tableview to display the messages. Each cell contains text and a stretchable background image. When messages are added to the tableview, they do change height to accommodate the text. However, whenever a single-line message is entered, the table view cell appears to be way too long for just a single line of text.
I think it has to do with the initial height and width of the tableviewcell, but I am not sure. How can I fix this to ensure the text bubble image encompasses the text but does not expand too much over it?
Screenshot of single and multi-lined texts:
Screenshot of long single-lined text for comparison:
I am using auto layout, if it helps.
ViewController code:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
var texts: [String] = ["Hey, how are you?", "Good, you?", "Great!"]
var user: [Int] = [1, 0, 1]
let screenSize = UIScreen.main.bounds
#IBOutlet weak var tableView: UITableView!
#IBAction func sendMessage(_ sender: Any) {
if textBox.text != ""
{
let str:String = textBox.text
let retstr:String = insert(seperator: "\n", afterEveryXChars: 27, intoString: str)
let rand:UInt32 = arc4random_uniform(2)
addText(text: String(retstr), user: Int(rand))
}
}
#IBOutlet weak var textBox: UITextView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.texts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (self.user[indexPath.row]==1)
{
let cell:CustomCell = self.tableView.dequeueReusableCell(withIdentifier: "cell") as! CustomCell
cell.myCellLabel.text = self.texts[indexPath.row]
cell.myCellLabel.textAlignment = NSTextAlignment.left
cell.myCellLabel.sizeToFit()
cell.myBackgroundImage.image = UIImage(named: "bubbleReversed")?.resizableImage(withCapInsets: UIEdgeInsetsMake(60, 50, 60, 50))
return cell
}
else
{
let cell:CustomCellOther = self.tableView.dequeueReusableCell(withIdentifier: "cell2") as! CustomCellOther
cell.myCellLabel.text = self.texts[indexPath.row]
cell.myCellLabel.textAlignment = NSTextAlignment.right
cell.myCellLabel.sizeToFit()
cell.myBackgroundImage.image = UIImage(named: "bubble")?.resizableImage(withCapInsets: UIEdgeInsetsMake(60, 50, 60, 50))
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped cell number \(indexPath.row).")
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = 10.0
tableView.rowHeight = UITableViewAutomaticDimension
tableView.reloadData()
}
func addText(text:String, user:Int)
{
if (self.texts.count > 20)
{
self.texts.remove(at: 0)
self.user.remove(at: 0)
}
self.texts.append(text)
self.user.append(user)
tableView.reloadData()
let indexPath = NSIndexPath(row: self.texts.count-1, section: 0)
tableView.scrollToRow(at: indexPath as IndexPath, at: .top, animated: true)
}
func insert(seperator: String, afterEveryXChars: Int, intoString: String) -> String {
var output = ""
intoString.characters.enumerated().forEach { index, c in
if index % afterEveryXChars == 0 && index > 0 {
output += seperator
}
output.append(c)
}
return output
}
}
My tableviewcell classes just contain a UIImageView and a label.

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)

Modify custom TableViewCell size based on contents

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.

Resources