UITableview and UICollectionView inside UIScrollView - Swift - swift

Can Anyone Suggest me an idea to create a UI like the below screenshot
https://drive.google.com/open?id=0B2JNfyRRcL0Gb2huQ3ZTV2N6Q1E
I used
ScrollView
imageView
Lable
TableView
Lable
Button
UiView
CollectionView
ImageView
Button
labels…
with this when I’m trying to scroll to down, only the UIScrollView is Scrolling not the UICollectionView.
I Also try to give the UiCollectionView height to its content height and made the scrollview height to fit that. Once i did this the collection view’s datasource and delegate methods are not calling when i use to scroll (means the first time when viewDidLoad - it loads the collection view cell) with the below below scene example the datasource and delegate methods are called only for the first two cells, since it is visible on the screen without scrolling whereas the others are not.
Im thinking to try it with tableview but i don’t know how…. because
the first content which is the banner image may not be in all categories (it may be in JSon response or may not be)
The Explore more label is static lable
The third thing “ Clothing, Bags belts and Wallets “ are the tableviewView Cells (cell count may be differ based on categories)
the forth is a Uiview which has the filter and sorting buttons and its a static one
the last is a collectionView cells (cell count may be differ based on categories)
So, Please Suggest me an Idea or an example or a sample code are highly welcomed….. thanks in Advance….!

Finally I found an idea,
I kept all within a TableView,
The first two (the banner image and the explore lable are the first cell of my tableview)
The second PrototypeCell is the category Titles(where the cloths, bags and belts)
The Third Prototype cell is a what with the filter and sort buttons and I used this cell as section Header View
And finally the last prototype cell is a one which have the collection View and its cell (I just design it and add it into a cell)
And sample code is as follows,
My CollectionViewCell
import UIKit
class CollectionViewCell: UICollectionViewCell {
#IBOutlet weak var imgFav: UIImageView!
#IBOutlet weak var title: UILabel!
#IBOutlet weak var pinImage: UIImageView!
#IBOutlet weak var priceLbl: UILabel!
#IBOutlet var splPriceLbl: UILabel!
#IBOutlet weak var addToFav: UIButton!
override func awakeFromNib()
{
super.awakeFromNib()
self.contentView.autoresizingMask = [UIViewAutoresizing.flexibleRightMargin, UIViewAutoresizing.flexibleLeftMargin, UIViewAutoresizing.flexibleBottomMargin, UIViewAutoresizing.flexibleTopMargin]
self.contentView.translatesAutoresizingMaskIntoConstraints = true
}
}
My TableViewCell
import UIKit
class SubCategoryTableViewCell: UITableViewCell {
#IBOutlet weak var productListCollectVw: UICollectionView!
#IBOutlet weak var btnSort: UIButton!
#IBOutlet weak var btnFilter: UIButton!
#IBOutlet weak var subCategryTitle: UILabel!
#IBOutlet weak var lblExplore: UILabel!
#IBOutlet weak var imgBanner: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
And Finally My ViewController
#IBOutlet weak var mainTableView: UITableView!
var subCategoryAry2 = NSMutableArray()
var imageUrl:URL!
var imageUrlStr:String = ""
var productListAry:NSMutableArray = []
func numberOfSections(in tableView: UITableView) -> Int
{
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if section == 0
{
return subCategoryAry2.count + 1
}
else
{
return 1
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
{
if section == 0
{
return nil
}
else
{
let CellIdentifier: String = "section2Cell"
let headerView: SubCategoryTableViewCell? = tableView.dequeueReusableCell(withIdentifier: CellIdentifier) as! SubCategoryTableViewCell?
headerView?.btnFilter.addTarget(self, action: #selector(self.filterAction(_:)), for: .touchUpInside)
headerView?.btnSort.addTarget(self, action: #selector(self.sortAction(_:)), for: .touchUpInside)
if headerView == nil
{
print("No cells with matching CellIdentifier loaded from your storyboard")
}
return headerView!
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
if indexPath.section == 0
{
if indexPath.row == 0
{
let cell:SubCategoryTableViewCell = self.mainTableView.dequeueReusableCell(withIdentifier: "bannerCell") as! SubCategoryTableViewCell!
if self.imageUrlStr == "no_image.png" || self.imageUrlStr == ""
{
cell.imgBanner.isHidden = true
cell.imgBanner.frame.size.height = 0
cell.lblExplore.frame.origin.y = 0
}
else
{
cell.imgBanner.isHidden = false
let imageUrl1 = "\(self.imageUrl!)"
let trimmedUrl = imageUrl1.trimmingCharacters(in: CharacterSet(charactersIn: "")).replacingOccurrences(of: " ", with: "%20") as String
cell.imgBanner.sd_setImage(with: URL(string: trimmedUrl), completed: { (image, error, imageCacheType, imageUrl) in
if image != nil
{
}
else
{
cell.imgBanner.isHidden = true
cell.imgBanner.frame.size.height = 0
cell.lblExplore.frame.origin.y = 0
}
})
}
return cell
}
else //if indexPath.row == 1
{
let cell:SubCategoryTableViewCell = self.mainTableView.dequeueReusableCell(withIdentifier: "listCell") as! SubCategoryTableViewCell!
cell.subCategryTitle.text = (subCategoryAry2.object(at: (indexPath as NSIndexPath).row - 1) as AnyObject).value(forKey: "name") as? String
return cell
}
}
else
{
let cell:SubCategoryTableViewCell = (self.mainTableView.dequeueReusableCell(withIdentifier: "collectionCell") as? SubCategoryTableViewCell!)!
// Load Your CollectionView
cell.productListCollectVw.dataSource = self
cell.productListCollectVw.delegate = self
cell.productListCollectVw.reloadData()
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if indexPath.section == 0
{
if indexPath.row != 0
{
//***************** Do whatever you need to do if user did selected the row but remain the indexpath.row or indexpath.row - 1 *****************//
subCategoryID = ((subCategoryAry2.object(at: (indexPath as NSIndexPath).row - 1) as! NSObject).value(forKey: "category_id") as? String)! as NSString
print("tableView - didSelectRowAt \(indexPath.row)")
}
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
if indexPath.section == 0
{
var height = CGFloat()
if indexPath.row == 0
{
if self.imageUrlStr == "no_image.png" || self.imageUrlStr == ""
{
//***************** Reduce banner image height if it is nil *****************//
height = 38
}
else
{
height = 175 + 38
}
}
else
{
height = 44
}
return height
}
else
{
let height = (255 * self.productListAry.count/2) + (2 * (self.productListAry.count) + 4)
//***************** increase height as per your need *****************//
return CGFloat(height)
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
{
if section == 0
{
return 0
}
else
{
return 44
}
}
func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int
{
return self.productListAry.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: self.reuseIdentifier, for: indexPath) as! CollectionViewCell
cell.title.text = (self.productListAry.object(at: (indexPath as NSIndexPath).row) as! NSDictionary).value(forKey: "name") as? String
//***************** Load your cell as per your need *****************//
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
print("didSelectItemAt \(indexPath.row)")
}
My Storyboard will look like the screenshot added
https://drive.google.com/file/d/0B2JNfyRRcL0GYjJsckpyaGpoMkE/view?usp=sharing

Related

Show hidden label by button click inside tableView Cell (expand/collapse cell)

I have a stackView(vertical) which contains labels and bottom description label is hidden by default. And I implemented an arrow button at the right side of the cell. By clicking the button, I just want to show the hidden description label and stackView should expand automatically and make cell bigger. This was my basic idea to implement expandable cell.
So this is the code I used to get desired results:
#objc func downArrowButtonClicked(_ sender: UIButton){
let indexPath = IndexPath(row: sender.tag, section: 0)
selectedIndex = indexPath
selectedCellIndex = sender.tag
isDescHidden = !isDescHidden
tableView.reloadRows(at: [indexPath], with: .automatic)
}
Above is the code for the button inside clicked cell. I went with the idea to reload that particular index. I created a variable named selectedCellIndex of in which I use in cellForRowAt method to make some changes.
I also had the implement some code in viewDidLayoutSubviews() as when I first clicked the cell wasn't getting expanded fully. here's that just in case:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let indexPath = selectedIndex
tableView.reloadRows(at: [indexPath ?? IndexPath(row: 0, section: 0)], with: .automatic)
}
And calling it in willDisplay method which finally fixed the cell expansion issue:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
viewDidLayoutSubviews()
}
And here is my cellForRowAt function:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell", for: indexPath) as! CustomTableViewCell
if indexPath.row == 0 {
cell.lblTitle.text = "Title 1"
cell.lblDesc.text = "Desc 1"
}
else if indexPath.row == 1 {
cell.lblTitle.text = "Title 2"
cell.lblDesc.text = "Desc 2"
}
else {
cell.lblTitle.text = "Title 3"
cell.lblDesc.text = "Desc 3"
}
if selectedCellIndex != nil {
if isDescHidden == false {
if cell.isDescHidden == true {
cell.lblDesc.isHidden = false
cell.btnArrow.setImage(UIImage(systemName: "chevron.up"), for: .normal)
}
else {
cell.lblDesc.isHidden = true
cell.btnArrow.setImage(UIImage(systemName: "chevron.down"), for: .normal)
}
}
else {
if cell.isDescHidden == true {
cell.lblDesc.isHidden = true
cell.btnArrow.setImage(UIImage(systemName: "chevron.down"), for: .normal)
}
else {
cell.lblDesc.isHidden = false
cell.btnArrow.setImage(UIImage(systemName: "chevron.up"), for: .normal)
}
}
cell.isDescHidden = !cell.isDescHidden
}
cell.btnArrow.tag = indexPath.row
cell.btnArrow.addTarget(self, action: #selector(downArrowButtonClicked(_:)), for: .touchUpInside)
return cell
}
This approach gets too confusing as you can see from the above code. The isDescHidden variable is defined in both Main view controller as well as table view cell class and I was trying to use both to expand or collapse a particular cell. However first time it works but if I have 3 cells expanded, collapsing button click doesn't work for 1-2 clicks then works.
Is there a better approach for this kind of problem? Or is there any way I can directly set cell.isDescHidden value from #objc func downArrowButtonClicked(_ sender: UIButton) function? So I can use that in cellForRowAt function?
I would be glad if I could directly make changes to cell variables from that.
Use the following function for automatic height for rows and provided top and bottom constraints to your stackView
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
Here is my CustomCell
class CustomCell: UITableViewCell {
#IBOutlet weak var titleCell: UILabel!
#IBOutlet weak var detail: UILabel!
#IBOutlet weak var arrowButton: UIButton!
let upArrow = UIImage(systemName: "chevron.up.circle.fill")
let downArrow = UIImage(systemName: "chevron.down.circle.fill")
var onArrowClick: ((UIButton)->())!
#IBAction func handleArrowButton(sender: UIButton){
onArrowClick(sender)
}
func updateArrowImage(expandStatus: Bool){
arrowButton.setImage(expandStatus ? downArrow : upArrow, for: .normal)
}
}
For sample Data
let data = [
["Nothing", "description is very long description is very long description is very long description is very "],
["Nothing", "description is very long "],
["Nothing", "description is very long "],
["Nothing", "description is very long "],
["Nothing", "description is very long "]
]
var eachCellStatus: [Bool] = []
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
for _ in data {
eachCellStatus.append(true)
}
}
TableView methods are like this
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! CustomCell
cell.titleCell.text = data[indexPath.row][0]
cell.detail.text = data[indexPath.row][1]
cell.detail.isHidden = eachCellStatus[indexPath.row]
cell.updateArrowImage(expandStatus: self.eachCellStatus[indexPath.row])
cell.onArrowClick = { button in
self.eachCellStatus[indexPath.row].toggle()
cell.updateArrowImage(expandStatus: self.eachCellStatus[indexPath.row])
tableView.reloadRows(at: [indexPath], with: .automatic)
}
return cell
}
First create a model to show data into cell. You have to preserve the state of cell.
struct CellData {
var title: String
var details: String
var isExpanded: Bool
}
In CustomTableViewCell add a property for cellData and assign Outlets data from it. Also create a protocol to reload row from UIViewController
protocol CustomTableViewCellDelegate {
func reloadRow(sender: CustomTableViewCell, flag: Bool)
}
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var detailsLabel: UILabel!
#IBOutlet weak var showButton: UIButton!
var indexPath: IndexPath?
var delegate: CustomTableViewCellDelegate?
var data: CellData? {
didSet {
if let data = data {
if data.isExpanded == false {
detailsLabel.isHidden = true
showButton.setImage(UIImage(systemName: "chevron.down"), for: .normal)
}else {
detailsLabel.isHidden = true
showButton.setImage(UIImage(systemName: "chevron.up"), for: .normal)
}
titleLabel.text = data.title
detailsLabel.text = data.details
}
}
}
override func awakeFromNib() {
super.awakeFromNib()
titleLabel.text = nil
detailsLabel.text = nil
}
#IBAction func showButtonAction(_ sender: UIButton) {
if var data = data {
data.isExpanded.toggle()
delegate?.reloadRow(cell: self, flag: data.isExpanded)
}
}
}
In UIViewController add an array of CellData type. You may assign it's data in viewDidLoad() method.
var tableData: [CellData]
Modify numberOfRowsInSection() and cellForRow() method like bleow.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell", for: indexPath) as! CustomTableViewCell
cell.data = tableData[indexPath.row]
cell.delegate = self
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
Then confirm CustomTableViewCellDelegate protocol to UIViewController
extension ViewController: CustomTableViewCellDelegate {
func reloadRow(sender: CustomTableViewCell, isExpanded: Bool) {
guard let tappedIndexPath = tableView.indexPath(for: sender) else { return }
tableData[tappedIndexPath.row].isExpanded = isExpanded
tableView.reloadRows(at: [indexPath], with: UITableView.RowAnimation.automatic)
}
}

How to hide a tableView after selecting a row

I have a textField, which when touch displays a tableView with some rows.
I'm trying to do this: when a user selects one of the rows, the value of row is placed in the textField and the tableView is closed.
The first part works well for me. The user touch on one row and the textField shows the value of that row. But if I want to close the tableview, I have to press twice on the row.
This is my code:
class Redactar_mensaje: UIViewController, UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate {
var values = ["123 Main Street", "789 King Street", "456 Queen Street", "99 Apple Street", "red", "orange", "yellow", "green", "blue", "purple", "owaldo", "ostras", "Apple", "Pineapple", "Orange", "Adidas"]
#IBOutlet weak var campo_para: UITextField!
#IBOutlet weak var tableView: UITableView!
var originalCountriesList:[String] = Array()
override func viewDidLoad() {
super.viewDidLoad()
tableView.isHidden = true
for country in values {
originalCountriesList.append(country)
}
campo_para.delegate = self
tableView.delegate = self
tableView.dataSource = self
campo_para.addTarget(self, action: #selector(textFieldActive), for: UIControlEvents.touchDown)
campo_para.addTarget(self, action: #selector(searchRecords(_ :)), for: .editingChanged)
}
#objc func searchRecords(_ textField: UITextField) {
self.values.removeAll()
if textField.text?.count != 0 {
for country in originalCountriesList {
if let countryToSearch = textField.text{
let range = country.lowercased().range(of: countryToSearch, options: .caseInsensitive, range: nil, locale: nil)
if range != nil {
self.values.append(country)
}
}
}
} else {
for country in originalCountriesList {
values.append(country)
}
}
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return values.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cellx")
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "cellx")
}
cell?.textLabel?.text = values[indexPath.row]
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
campo_para.text = values[indexPath.row]
tableView.isHidden = true //I need press twice for this. I want press only one
}
func textFieldActive() {
tableView.isHidden = false
}
}
Ideally, the user touches the textField, displays the tableView, chooses one of the values, and it close automatically the tableView. But this last one does not work well.
Any advice?
Details
xCode 8.3, Swift 3.1
Example to Detect Double tap and Single tap on TableViewCell
ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.tableFooterView = UIView()
}
}
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as! TableViewCell
cell.label.text = "\(indexPath)"
cell.delegate = self
return cell
}
}
extension ViewController:TableViewCellDelegate {
func tableViewCell(singleTapActionDelegatedFrom cell: TableViewCell) {
let indexPath = tableView.indexPath(for: cell)
print("singleTap \(String(describing: indexPath)) ")
}
func tableViewCell(doubleTapActionDelegatedFrom cell: TableViewCell) {
let indexPath = tableView.indexPath(for: cell)
print("doubleTap \(String(describing: indexPath)) ")
//You can hide your textfield here
}
}
TableViewCell.swift
import UIKit
class TableViewCell: UITableViewCell {
#IBOutlet weak var label: UILabel!
private var tapCounter = 0
var delegate: TableViewCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
let tap = UITapGestureRecognizer(target: self, action: #selector(tapAction))
addGestureRecognizer(tap)
}
func tapAction() {
if tapCounter == 0 {
DispatchQueue.global(qos: .background).async {
usleep(250000)
if self.tapCounter > 1 {
self.doubleTapAction()
} else {
self.singleTapAction()
}
self.tapCounter = 0
}
}
tapCounter += 1
}
func singleTapAction() {
delegate?.tableViewCell(singleTapActionDelegatedFrom: self)
}
func doubleTapAction() {
delegate?.tableViewCell(doubleTapActionDelegatedFrom: self)
}
}
TableViewCellDelegate.swift
import UIKit
protocol TableViewCellDelegate {
func tableViewCell(singleTapActionDelegatedFrom cell: TableViewCell)
func tableViewCell(doubleTapActionDelegatedFrom cell: TableViewCell)
}
Result
Here I put my solution, in case someone else would happen something similar.
Just change the order of the lines and add one more line. First it makes it invisible and then puts the result in the textField and, magically, it worked!
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.isHidden = true
campo_para.text = NombreUsuario[indexPath.row]
campo_asunto.becomeFirstResponder()
}
Thanks!

Segmented control and table view issue swift

so I am creating a table view with segmented control. at first - I didn't do segmented control but modified some code to at it. When I add numbers to my array, it is not actually populating the cells of the table view. In the table view every time I click a button that appends an amount to the array, the lines increase, but no data is shown. So I know it is working, but it is just not actually displaying the numbers in the table view cells. is there a specific reason why my code will not populate the views?
class SpendingHistoryViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var savedInformation = false
var newDefault : Double?
var selectedSegment = 1
let array2 = ["1","2","3"]
let userDefaults = UserDefaults.standard
#IBOutlet weak var tableViewSpending: UITableView!
#IBOutlet weak var youSpentLabel: UILabel!
#IBOutlet weak var youSavedLabel: UILabel!
#IBOutlet weak var segmentControl: UISegmentedControl!
override func viewDidLoad() {
super.viewDidLoad()
self.tableViewSpending.delegate = self
self.tableViewSpending.dataSource = self
//GETS user defaults that was set in ADDSUBTRACTMONEY VC - and then sets the default to Array- then gets total
let moneySpentArray = UserDefaults.standard.array(forKey: "moneySpent") as? [Double] ?? [Double]()
AddSubtractMoneyController.moneySpentArray = moneySpentArray
let arraySum = moneySpentArray.reduce(0.0, +)
youSpentLabel.text = String(format: "%.2f", arraySum)
}
#IBAction func spentSavedAction(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0 {
selectedSegment = 1
}
else {
selectedSegment = 2
}
self.tableViewSpending.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if selectedSegment == 1 {
return(AddSubtractMoneyController.moneySpentArray.count)
}
else {
return array2.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
//let cell2 = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell2")
let cell2 = tableViewSpending.dequeueReusableCell(withIdentifier: "cell2")! as UITableViewCell
let cell = tableViewSpending.dequeueReusableCell(withIdentifier: "cell")! as UITableViewCell
cell.textLabel?.text = String(format: "%.2f", AddSubtractMoneyController.moneySpentArray[indexPath.row])
cell2.textLabel?.text = array2[indexPath.row]
self.tableViewSpending.reloadData()
if selectedSegment == 1 {
return cell
}
else {
return cell2
}
}
}

Expand Cell at selectedIndex

Updated Code Below
I am working on comment cells who are limited to 100 characters and if they contain more a "show more button" will show up.
If pressed, the exact cell should reload itself with the number of lines changed to 0 and fully display the cell, no matter how big.
What I have achieved is that cells reload, but not the selected one and kinda arbitrary.
Below is my code for the enlarging process
NOTE: Updatet Code for My Function
Problem: I have to press the button twice to get the result, to minimize and to maximize the cell
#IBAction func readMore(_ sender: UIButton) {
self.state = !self.state
print("state" , state)
self.tapMore.setTitle(self.state ? self.decreaseState: self.expandState, for: .normal)
self.commentLabel.numberOfLines = (self.state ? self.expandedLines: self.numberOfLines)
print(self.commentLabel.numberOfLines)
let myIndexPath = IndexPath(row: sender.tag, section: 0)
UIView.animate(withDuration: 0.3, animations: {
self.parentViewControllerCommentCell?.tableView.reloadRows(at: [myIndexPath], with: UITableViewRowAnimation(rawValue: Int(UITableViewAutomaticDimension))!)
})
}
The index comes from
extension CommentTableViewCell {
var indexPath: IndexPath? {
return (superview as? UITableView)?.indexPath(for: self)
}
}
Note
The print statement prints out the chosen cell ( e.g. [0, 1] or [0,0] but it doesn't change then.
Whereas I hardcode my code and change
let myIndexPath = IndexPath(row: indexPath!.row, section: 0)
to
let myIndexPath = IndexPath(row: 0, section: 0)
The feature works, but arbitrarily reloads some cells and arbitrarily enlarges and decreases the cell.
In the variable version with row: indexPath!.row the lines state doesn't change as well, whereas with hardcoded the lines change between 3 and 0.
Thanks for your help :)
Addition
my commentCell
class CommentTableViewCell: UITableViewCell {
#IBOutlet weak var likeCountButton: UIButton!
#IBOutlet weak var profileImageView: UIImageView!
#IBOutlet weak var commentLabel: KILabel!
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var timeLabel: UILabel!
#IBOutlet weak var likeImageView: UIImageView!
#IBOutlet weak var tapMore: UIButton!
#IBOutlet weak var tapMoreButton: UIButton!
var delegate: CommentTableViewCellDelegate?
var postId : String!
Here is a better approach to get you the correct index path. First, in your cellForRow method, add the current index row as tag to your show more button, and then add click action to your button handler function.
Add an outlet of UIButton in you custom UITableViewCell class as
class CustomCell: UITableViewCell {
#IBOutlet var moreButton: UIButton! // Connect your button from storyboard
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell") as! CustomCell
cell.moreButton.tag = indexPath.row
/* Just add action normally from storyboard. No need to add target. cell.moreButton.addTarget(self, action:#selector(buttonUp(sender:)), for: .touchUpInside) */
return cell
}
Then in your handler function, you can get the correct index path by reading this tag
func tapForMore(sender: UIButton) {
let myIndexPath = IndexPath(row: sender.tag, section: 0)
print("myindex", myIndexPath)
//... other code here
}
You take class variable and track tap counts. Depending on these variables you can increase or decrease size of cell and reload it.
In YOURViewController declare variables as:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var CommentsTableView: UITableView!
var defaultSizeOfCell = 60.0
var newSize = 80.0
var selectedIndex = -1
var isExpanded = false
var expandCounter = 0
override func viewDidLoad() { ...
Connect button in cell to this action:
#IBAction func moreButtonAction(_ sender: UIButton) {
if !isExpanded {
if expandCounter == 0 {
expandCounter = expandCounter + 1
} else if expandCounter == 1 {
expandCounter = 0
isExpanded = true
selectedIndex = sender.tag
let myIndexPath = IndexPath(row: sender.tag, section: 0)
UIView.animate(withDuration: 0.3, animations: {
self.CommentsTableView.reloadRows(at: [myIndexPath], with: UITableViewRowAnimation(rawValue: Int(UITableViewAutomaticDimension))!)
})
print("Increase")
}
} else if isExpanded {
if expandCounter == 0 {
expandCounter = expandCounter + 1
} else if expandCounter == 1 {
expandCounter = 0
isExpanded = false
selectedIndex = -1
let myIndexPath = IndexPath(row: sender.tag, section: 0)
UIView.animate(withDuration: 0.3, animations: {
self.CommentsTableView.reloadRows(at: [myIndexPath], with: UITableViewRowAnimation(rawValue: Int(UITableViewAutomaticDimension))!)
})
print("Decrease")
}
}
}
In tableview datasource function add tag to button:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "testCell", for: indexPath) as! TestTableViewCell
cell.moreButton.tag = indexPath.row
return cell
}
And finally add this delegate method for height of cells:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if selectedIndex == indexPath.row {
return CGFloat(newSize)
} else {
return CGFloat(defaultSizeOfCell)
}
}
Not to mention, button should be in cell and connected to YOURCustomTableViewCell class as:
class TestTableViewCell: UITableViewCell {
#IBOutlet weak var moreButton: UIButton!
I have tested it against your requirements.

Retrieve information from Table Cell?

I have a custom UITableViewCell with 2 labels and a button. The cell has it's own class:
class personTableCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var emailLabel: UILabel!
#IBAction func inviteButtonPressed(_ sender: Any) {
self.accessoryType = .checkmark
}
}
Inside the view controller that contains the table view, I add the cells to the table in this method:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "person", for: indexPath) as? personTableCell
cell?.nameLabel.text = results[indexPath.row].name
cell?.emailLabel.text = results[indexPath.row].email
return cell!
}
When a user presses the button inside the cell that calls the #IBAction func inviteButtonPressed, I want to add the cell's labels' text into an array that's initialized in the same view controller as the table.
How can I achieve such a thing if the #IBAction func inviteButtonPressed is in a seperate file as the table's view controller?
I think using delegate is one of solutions.
In TableViewCell class
#objc protocol PersonTableViewCellDelegate {
func personTableViewCellInviteButtonPressed(cell: PersonTableViewCell)
}
class PersonTableViewCell: UITableViewCell {
weak var delegate: PersonTableViewCellDelegate?
#IBAction func inviteButtonPressed(_ sender: Any) {
delegate?.personTableViewCellInviteButtonPressed(cell: self)
}
}
In ViewController class
class TableViewController: UITableViewController, PersonTableViewCellDelegate {
var results: [Person] = []
var invited: [Person] = []
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "person", for: indexPath) as! PersonTableViewCell
cell.nameLabel.text = results[indexPath.row].name
cell.emailLabel.text = results[indexPath.row].email
cell.delegate = self
return cell
}
func personTableViewCellInviteButtonPressed(cell: PersonTableViewCell) {
guard let indexPath = tableView.indexPath(for: cell) else {
return
}
let person = results[indexPath.row]
invited.append(person)
}
}