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.
Related
I am creating single Tableview custom cell row with top and bottom two textview. Here, bottom textview need to adjust height dynamically based on textview text length.
My Storyboard
enter image description here
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 280.0
}
//Choose your custom row height
private func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 280.0;
}
// number of rows in table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.animals.count
}
// create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:MyCustomCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! MyCustomCell
cell.sourceTextView.text = “ABCD….”
return cell
}
Just do this
func adjustUITextViewHeight(arg : UITextView)
{
arg.translatesAutoresizingMaskIntoConstraints = true
arg.sizeToFit()
arg.scrollEnabled = false
}
In Swift 4 the syntax of arg.scrollEnabled = false has changed to
arg.isScrollEnabled = false.
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
}
}
I have a UITableViewController that I'm using to override the inputView of a UITextField subclass. Sometimes the tableView didSelectRowAtIndexPath stops getting fired until a user scolls the table and tries to click again. I can't seem to get a fix on what would cause the cells to become unresponsive. Most of the time it works great, but randomly the cells stop responding to touches until the user scrolls the tableview a bit.
I don't have any TapGestureRecognizers in the view to my knowledge, and have played around with overriding HitTest in the UITableViewCell, but can't seem to figure out the issue.
Any ideas on how to better debug this?
Here is the tableView controller code:
protocol MultiSelectPickerDelegate
{
func didSelectOption(optionIndex:Int, group:Int)
}
class MultiSelectPicker: UITableViewController {
var multiSelectGroups = [MultiSelectGroup](){
didSet{
self.tableView.reloadData()
}
}
var delegate:MultiSelectPickerDelegate?
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return multiSelectGroups.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return multiSelectGroups[section].multiSelectOptions.count
}
//MARK: UITableView datasourse function
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
delegate?.didSelectOption(indexPath.row, group: indexPath.section)
}
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView(frame: CGRectMake(0, 0, tableView.frame.size.width, 40))
let label = UILabel(frame: CGRectMake(10, 0, tableView.frame.size.width, 40))
label.font = UIFont.systemFontOfSize(14)
label.text = multiSelectGroups[section].name
label.textColor = UIColor.whiteColor()
view.addSubview(label)
view.backgroundColor = UIColor.MyAppBlue()
return view
}
override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 40
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier")
if cell == nil{
cell = UITableViewCell(style: .Default, reuseIdentifier: "reuseIdentifier")
}
cell?.textLabel?.text = multiSelectGroups[indexPath.section].multiSelectOptions[indexPath.row].name
return cell!
}
}
I was having same problem. On my table view it was not clicking very last row of my table on the first tap. It highlights it, but didSelectRowAtIndexPath isn't being called. All the other rows work fine. But if I turn the tableview bounce on then it seems to solve the problem
Also try removing Show Selection on Touch in Interface Builder as follows:
Hope it helps
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.
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
}
}