Change UITableViewCell height touch up inside - swift

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
}
}

Related

Hide a specific cell in my table

I'm trying to hide a specific cell in my table view?
private var yourDataSource = [AccountData.AllTasksDB] //A Json Array originally
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
var rowHeight:CGFloat = 72
//AccountData.AllTasksDB[indexPath.row]["importantvariable"]
if (self.yourDataSource[indexPath.row]["importantvarible"] == "0")
{
print("Cell is mine")
rowHeight = 0
}
else
{
print("Cell is not mine")
rowHeight = 72.0
}
return rowHeight
}
Can anyone tell me what I'm doing wrong? I thought it was looping through each cell and checking a value i've setup before hand. But it doesn't seem to want to access it.
I figured this would be more legible in an answer
fileprivate var yourDataSource = [YourObject]()
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if self.yourDataSource[indexPath].isMine == true {
return 0
}
else
{
return 72.0 //or whatever you like
}
}
You are accessing cell in the tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) method.
At this point the cell is not yet visible (or dequeued). If the cell is not visible, it will be nil, hence your error.
Try putting a break point at let cell = tableView.cellForRowAtIndexPath(indexPath) as! TableViewCellMyTasks and you'll see that it is nil.

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.

Expand and contract tableview cell when tapped, in swift

I'm trying to expand a tableview cell when tapped. Then, when it is tapped again, I want it to go back to its original state.
If cellA is expanded and cellB is tapped, I want cellA to contract and cellB to expand at the same time. So that only one cell can be in its expanded state in any given moment.
My current code:
class MasterViewController: UITableViewController {
var isCellTapped = false
var currentRow = -1
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRowIndex = indexPath
tableView.beginUpdates()
tableView.endUpdates()
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row == selectedRowIndex.row {
if isCellTapped == false {
isCellTapped = true
return 140
} else if isCellTapped == true {
isCellTapped = false
return 70
}
}
return 70
}
Current code works well when:
You tap a row (it expands)
You tap it again (it contracts)
It fails when:
You tap a row (it expands)
You tap another row (it contracts, but the other row does not expand)
How can I solve this?
You need to take in account that you need to update your selected row when another is tapped, see the following code :
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row == selectedRowIndex {
return 140
}
return 44
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if selectedRowIndex != indexPath.row {
// paint the last cell tapped to white again
self.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: self.selectedRowIndex, inSection: 0))?.backgroundColor = UIColor.whiteColor()
// save the selected index
self.selectedRowIndex = indexPath.row
// paint the selected cell to gray
self.tableView.cellForRowAtIndexPath(indexPath)?.backgroundColor = UIColor.grayColor()
// update the height for all the cells
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
}
EDIT:
To handle that the cell where is selected and is tapped again return to its original state you need to check some conditions like the following:
var thereIsCellTapped = false
var selectedRowIndex = -1
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row == selectedRowIndex && thereIsCellTapped {
return 140
}
return 44
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.tableView.cellForRowAtIndexPath(indexPath)?.backgroundColor = UIColor.grayColor()
// avoid paint the cell is the index is outside the bounds
if self.selectedRowIndex != -1 {
self.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: self.selectedRowIndex, inSection: 0))?.backgroundColor = UIColor.whiteColor()
}
if selectedRowIndex != indexPath.row {
self.thereIsCellTapped = true
self.selectedRowIndex = indexPath.row
}
else {
// there is no cell selected anymore
self.thereIsCellTapped = false
self.selectedRowIndex = -1
}
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
With the above modifications in yourdidSelectRowAtIndexPath and heightForRowAtIndexPath functions you can see when a cell is tapped its background color it will be changed to gray when it's height grow and when another cell is tapped the cell is painted to white and the tapped to gray and again and again allowing only tap one cell at time.
I though you can benefit and learn how to do a Accordion Menu in this repo I have created and I plan to update very soon to handle better results, it's handle using a UITableView just like you want.
Any doubt in the repository you can post it here.
I hope this help you.
A simple approach for expanding only one cell at a time:
Swift 3
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
var selectedRowIndex = -1
#IBOutlet weak var tableView: UITableView!
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == selectedRowIndex {
return 90 //Expanded
}
return 40 //Not expanded
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if selectedRowIndex == indexPath.row {
selectedRowIndex = -1
} else {
selectedRowIndex = indexPath.row
}
tableView.reloadRows(at: [indexPath], with: .automatic)
}
}
This code will expand the clicked cell and close previously open cell with animation.
UITableViewCell:
import UIKit
protocol HierarchyTableViewCellDelegate {
func didTapOnMoreButton(cell:HierarchyTableViewCell)
}
class HierarchyTableViewCell: UITableViewCell {
#IBOutlet weak var viewBtn: UIButton!
//MARK:- CONSTANT(S)
var delegate:HierarchyTableViewCellDelegate!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
#IBAction func actionViewButtonTapped(_ sender: Any) {
delegate.didTapOnMoreButton(cell: self)
}
}
UIViewController:
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableViewObj: UITableView!
var HierarchyListArr = NSArray()
var selectedIndex:Int?
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return HierarchyListArr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableViewObj.dequeueReusableCell(withIdentifier: "HierarchyTableViewCell", for: indexPath) as! HierarchyTableViewCell
cell.delegate = self
if let index = selectedIndex,indexPath.row == index{
cell.viewBtn.isSelected = true
}else{
cell.viewBtn.isSelected = false
}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if let index = selectedIndex,indexPath.row == index{
return 190
}
return 80
}
}
extension ViewController: HierarchyTableViewCellDelegate{
func didTapOnMoreButton(cell: HierarchyTableViewCell) {
guard let index = tableViewObj.indexPath(for: cell)else{return}
if selectedIndex == index.row{
selectedIndex = -1
}else{
selectedIndex = index.row
}
tableViewObj.reloadRows(at: [index], with: .fade)
}
}