Swift: How to animate the rowHeight of a UITableView? - swift

I'm trying to animate the height of tableViewCell rows by calling startAnimation() inside the tableView function:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! TableViewCell
tableView.rowHeight = 44.0
startAnimation(tableView)
return cell
}
//MARK: Animation function
func startAnimation(tableView: UITableView) {
UIView.animateWithDuration(0.7, delay: 1.0, options: .CurveEaseOut, animations: {
tableView.rowHeight = 88.0
}, completion: { finished in
print("Row heights changed!")
})
}
The result: The row height does change but without any animation occurring. I don't understand why the animation doesn't work. Should I perhaps define some beginning and end state somewhere?

Don't change the height that way. Instead, when you know you want to change the height of a cell, call (in whatever function):
self.tableView.beginUpdates()
self.tableView.endUpdates()
These calls notify the tableView to check for height changes. Then implement the delegate override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat, and provide the proper height for each cell. The change in height will be animated automatically. You can return UITableViewAutomaticDimension for items you don't have an explicit height for.
I would not suggest doing such actions from within cellForRowAtIndexPath, however, but in one that responds to a tap didSelectRowAtIndexPath, for example. In one of my classes, I do:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath == self.selectedIndexPath {
self.selectedIndexPath = nil
}else{
self.selectedIndexPath = indexPath
}
}
internal var selectedIndexPath: NSIndexPath? {
didSet{
//(own internal logic removed)
//these magical lines tell the tableview something's up, and it checks cell heights and animates changes
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath == self.selectedIndexPath {
let size = //your custom size
return size
}else{
return UITableViewAutomaticDimension
}
}

Related

Inside Subclassed UITableViewCell Override Func LayoutSubviews Being Called Double

I have a subclass of UITableViewCell that has is having override func layoutSubviews() called twice for each cell. This is creating multiple copies of elements within the cell.
The UITableView returns the proper count of cells and displays the correct number of cells but the layout function resets a handful of properties to nil. Therefore rendering a lot of the data incorrectly.
TableView Inside UIViewController:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userMatches.count // return 2 correctly
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return self.view.bounds.height * 0.20
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: MatchTableViewCell = self.matchTableView.dequeueReusableCellWithIdentifier("matchCell", forIndexPath: indexPath) as! MatchTableViewCell
cell.matchDetails = userMatches[indexPath.row]
cell.userInteractionEnabled = true
cell.selectionStyle = UITableViewCellSelectionStyle.None
return cell // creates 2 correctly
}
UITableView Subclass:
override func layoutSubviews() {
super.layoutSubviews()
// runs 4 times
let userHelperIconArray = [userZoom, userTakeTwo, userStopper]
let opponentHelperIconArray = [opponentZoom, opponentTakeTwo, opponentStopper]
layoutHelperInventoryIcons(self, opponentHelperIconArray: opponentHelperIconArray, userHelperIconArray: userHelperIconArray, opponentNameLabel: opponentName)
layoutMiniGameboard(self)
self.layer.borderWidth = 1.0
self.layer.borderColor = UIColor.blackColor().CGColor
print("one")
turnIdentifier(self, matchDetails: matchDetails, opponentNameLabel: opponentName)
}
You should 't be doing this in layoutSubviews anyway. Instead, move the configuration to a separate method, and have your table view data source call that configuration method after dequeing the cell.

Apply an IBAction only to a single cell

I have a tableView with prototypes cell; with this func I set cell height
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
return cellHeight
}
with this action I change cell height touching up inside the UIButton
#IBAction func changeCellHeight(sender: UIButton)
{
if cellHeight == 44
{
cellHeight = 88
} else {
cellHeight = 44
}
tableView.beginUpdates()
tableView.endUpdates()
}
Now I need to change height only for selected cell (not for every cells); so I define
var index: NSIndexPath! and I implement this func
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
index = self.tableView.indexPathForSelectedRow()!
println("Cella " + "\(index)")
}
As I expected in console Xcode prints the selected cell (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0} ).
So my trouble is how to use var index in the IBAction.
Thanks in advance.
To change your height based on selection, you don't need an IBAction if you are already implementing the didSelectRowAtIndexPath method.
Just make a little change in your didSelectRowAtIndexPath method first-
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
// You don't need to do this -> index=self.tableView.indexPathForSelectedRow()!
index = indexPath;
println("Cella " + "\(index)")
tableView.beginUpdates()
tableView.endUpdates()
}
And then a little change to your heightForRowAtIndexPath method-
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
if index == indexPath {
return 88
}
else{
return 44
}
}

Change cell height but not elements in it

I have a prototypes cell that changes height when I touch up inside it; below how I made it:
class SquadreController: UITableViewController
{
var index: NSIndexPath = NSIndexPath()
override func viewDidLoad()
{
super.viewDidLoad()
DataManager.sharedInstance.createCori()
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return DataManager.sharedInstance.arrayCori.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
let squadra = DataManager.sharedInstance.arrayCori[indexPath.row]
cell.backgroundColor = UIColor.clearColor()
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
index = indexPath
tableView.beginUpdates()
tableView.endUpdates()
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
if index == indexPath
{
return 176
} else
{
return 88
}
}
}
Ok, it works perfectly. Now I need to put an UIImage in that cell, but I need that image were partially visible like this:
The final effect I would like to realize it's quite similar to this mokeup https://dribbble.com/shots/1293959-LiveSport-App: like the mokeup in my cell there will placed UIImage, labels, views, button.
This is it, if someone could help me with a step by step guide to do this or with some tutorial, I would be grateful!!!
I solved simply with set top, left, right constraints (not bottom) and height constraint to the UIImageView; so the image would not be resized but partially covered by the cell.

Change UITableViewCell height touch up inside

As the title says, I'd like to make my cells dynamically change height. I try to be as clear that I can. When I touch up inside a cell (prototype cell), the cell change height (from 44 to 88);in the cell there is an UIImage (88px height) partially hidden and where the cell is hidden there is view, touching up starts a segue to a second tableView; when I touch up inside an other cell the previous cell return to 44px height an this other cell change height as the previous one. Follow a scheme of I need to do.
I think that this is quite difficult, so If someone could help me doing this feature I would be very grateful!! or if you know any examples to get ideas (by gitHub for example) please report me!
thanks a lot :-)
It is not so bad. I have done it like this, there might be smarter ways to do it.
The important stuff is tableView.beginUpdate and ....endUpdate.
//
// DynamicTableViewController.swift
// DynamicCellTest
//
// Created by Lars Christoffersen on 27/08/15.
// Copyright (c) 2015 Lars Christoffersen. All rights reserved.
//
import UIKit
class DynamicTableViewController: UITableViewController {
var dataCource = ["A","B","C","D","E","F","G","H"]
var extraHeight:CGFloat?
var selectedRowIndex:Int?
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if extraHeight != nil && selectedRowIndex != nil && selectedRowIndex == indexPath.row{
return 41 + extraHeight!
}else{
return 41
}
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.tableView.beginUpdates()
selectedRowIndex = indexPath.row
extraHeight = 100
self.tableView.endUpdates()
}
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
self.tableView.beginUpdates()
selectedRowIndex = indexPath.row
extraHeight = 0
self.tableView.endUpdates()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataCource.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel?.text = dataCource[indexPath.row]
return cell
}
}

How to make the height of tableviewcell self-adaption to the text-label in it?

I am creating a TableViewController, now there's some cells in it, the longth of texts in these cells are not the same, so I wanna make the height of the cells self-adaption to the text-label.
I was thinking to creat the label first, then set the height of cell to fix it, but it seems not work, here is the code.
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let cell = tableView.cellForRowAtIndexPath(indexPath) as! FuckGFWQATableViewCell
return cell.question.bounds.height
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("fuckGFWQATableViewCellId", forIndexPath: indexPath) as! FuckGFWQATableViewCell
cell.question.numberOfLines = 0
cell.question.text = fuckGFWQuestions[indexPath.section]
return cell
}
But when the heightForRowAtIndexPath called, the label was not exist. What should I do?
With iOS8 you can use self sizing cells in UITableView.Checkout here. You do not need to calculate your UILabel's height and set your cell. You just need to :
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension