I have developed something very similar to DateCell Demo from Apple. https://developer.apple.com/library/ios/samplecode/DateCell/Introduction/Intro.html
The only difference is that I have a UIPickerView instead of a UIDatePicker. And whenever the cell is touched a new UIPickerView is instantiated and added to the cell below once touched again it is removed from the cell. And the part I have problem with is to select a specific item in the UIPickerView.
Here is some code:
var selectedPlatform = "Mac"
var platformPickerDataSource = ["iOS", "Mac", "PC", "Android"]
func updatePlatformPicker()
{
if let indexPath = platformPickerIndexPath {
let associatedPlatformPickerCell = tableView.cellForRowAtIndexPath(indexPath)
if let targetedPlatformPicker = associatedPlatformPickerCell?.viewWithTag(Constants.kPlatformPickerTag) as! UIPickerView? {
targetedPlatformPicker.dataSource = self
targetedPlatformPicker.delegate = self
// I cannot do selectRow(selectedPlatform, inComponent: 0,
animated : true) here since the row since the rows in the UIPickerView are not set
}
}
}
So my questions is, if you refer to the code from Apple, where can I set?
selectRow(selectedPlatform, inComponent: 0,
animated : true)
Edit: (I added the whole code, which is basically the swift version of Apple's DateCell Example and DatePicker replaced with a UIPickerView)
import UIKit
class PickerTableViewController: UITableViewController, UIPickerViewDataSource, UIPickerViewDelegate {
var platformPickerIndexPath : NSIndexPath?
var pickerCellRowHeight: CGFloat = 140
var selectedPlatform = "Mac"
var platformPickerDataSource = ["iOS", "Mac", "PC", "Android"]
private struct Constants {
static let kPlatformCellID: String = "platformCell"
static let kPlatformPickerID: String = "platformPicker"
static let kOtherCell: String = "otherCell"
static let kPlatformRow : Int = 1
static let kPlatformPickerTag : Int = 99
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var rows = 3
if hasInlinePlatformPicker()
{
rows = 4
}
return rows
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell : UITableViewCell?
print("\(indexPath.row)")
var cellID = Constants.kOtherCell
if indexPathHasPicker(indexPath)
{
cellID = Constants.kPlatformPickerID
}
else if indexPathHasPlatform(indexPath)
{
cellID = Constants.kPlatformCellID
}
cell = tableView.dequeueReusableCellWithIdentifier(cellID)
var modelRow = indexPath.row
if (platformPickerIndexPath != nil && platformPickerIndexPath?.row <= indexPath.row) {
modelRow -= 1
}
if cellID == Constants.kPlatformCellID
{
cell?.textLabel?.text = "Select a platform"
cell?.detailTextLabel?.text = selectedPlatform
}
else if cellID == Constants.kOtherCell
{
cell?.textLabel?.text = "Other Cell"
}
return cell!
}
func indexPathHasPicker(indexPath: NSIndexPath) -> Bool
{
return hasInlinePlatformPicker() && platformPickerIndexPath?.row == indexPath.row
}
func indexPathHasPlatform(indexPath : NSIndexPath) -> Bool
{
var hasPlatform = false
if indexPath.row == Constants.kPlatformRow || (hasInlinePlatformPicker() && (indexPath.row == Constants.kPlatformRow + 1))
{
hasPlatform = true
}
return hasPlatform
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath)
if cell?.reuseIdentifier == Constants.kPlatformCellID
{
displayInlinePlatformPickerForRowAtIndexPath(indexPath)
}
else
{
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
}
func displayInlinePlatformPickerForRowAtIndexPath(indexPath : NSIndexPath)
{
// display the platform picker inline with the table content
tableView.beginUpdates()
var before = false
if hasInlinePlatformPicker()
{
before = platformPickerIndexPath?.row < indexPath.row
}
let sameCellClicked = (platformPickerIndexPath?.row == indexPath.row + 1)
// remove any picker cell if it exists
if hasInlinePlatformPicker()
{
tableView.deleteRowsAtIndexPaths([NSIndexPath(forRow: platformPickerIndexPath!.row, inSection: 0)], withRowAnimation: .Fade)
platformPickerIndexPath = nil
}
if (!sameCellClicked)
{
// hide the old picker and display the new one
let rowToReveal = (before ? indexPath.row - 1 : indexPath.row)
let indexPathToReveal = NSIndexPath(forRow: rowToReveal, inSection: 0)
togglePlatformPickerForSelectedIndexPath(indexPathToReveal)
platformPickerIndexPath = NSIndexPath(forRow: indexPathToReveal.row + 1, inSection: 0)
}
tableView.deselectRowAtIndexPath(indexPath, animated: true)
tableView.endUpdates()
updatePlatformPicker()
}
func updatePlatformPicker()
{
if let indexPath = platformPickerIndexPath {
let associatedPlatformPickerCell = tableView.cellForRowAtIndexPath(indexPath)
if let targetedPlatformPicker = associatedPlatformPickerCell?.viewWithTag(Constants.kPlatformPickerTag) as! UIPickerView? {
targetedPlatformPicker.dataSource = self
targetedPlatformPicker.delegate = self
}
}
}
func togglePlatformPickerForSelectedIndexPath(indexPath: NSIndexPath)
{
tableView.beginUpdates()
let indexPaths = [NSIndexPath(forRow: indexPath.row + 1, inSection: 0)]
// check if 'indexPath' has an attached picker below it
if hasPickerForIndexPath(indexPath)
{
// found a picker below it, so remove it
tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: .Fade)
}
else
{
// didn't find a picker below it, so we should insert it
tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: .Fade)
}
tableView.endUpdates()
}
func hasPickerForIndexPath(indexPath: NSIndexPath) -> Bool
{
var hasPlatformPicker = false
let targetedRow = indexPath.row + 1
let checkPlatformPickerCell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: targetedRow, inSection: 0))
let checkPlatformPicker = checkPlatformPickerCell?.viewWithTag(Constants.kPlatformPickerTag)
hasPlatformPicker = checkPlatformPicker != nil
return hasPlatformPicker
}
func hasInlinePlatformPicker() -> Bool
{
return platformPickerIndexPath != nil
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return (indexPathHasPicker(indexPath) ? pickerCellRowHeight : tableView.rowHeight)
}
// MARK: - Platform Picker View Data Source and Delegates
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return platformPickerDataSource.count
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return platformPickerDataSource[row]
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
print("Platform Selected: \(platformPickerDataSource[row])")
var targetedCellIndexPath: NSIndexPath?
if hasInlinePlatformPicker() {
// inline picker: update the cell's above the picker cell
//
targetedCellIndexPath = NSIndexPath(forRow: platformPickerIndexPath!.row - 1, inSection: 0)
}
let cell = tableView.cellForRowAtIndexPath(targetedCellIndexPath!)
cell?.detailTextLabel?.text = platformPickerDataSource[row]
}
}
Related
I have custom UITableViewController with two custom UITableViewCell
Each of them contains UIPickerView
I want to compare values of these UIPickerViews
(I make the Maps App, and i wanna make the route, so i need do compare two points, and if they are different then "Make The Route" button will be set to Enabled State)
How can i do this? I tried to use didSet values, but it didn't work
Here are my classes:
class PlaceForRouteCell: UITableViewCell, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var picker: UIPickerView!
// Выбираем все места в базе данных
let places = realm.objects(Place.self)
var selectedPlace = Place()
func getSelectedPlace() -> Place {
return selectedPlace
}
//число "барабанов"
func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 }
//число элементов в "барабане"
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
//если БД пуста, то предупредить об этом
return places.count != 0 ? places.count : 1
}
//содержимое "барабанов"
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if (places.count != 0) {
if (places.count == 1) { return "Добавьте еще одно место" }
return places[row].userName
}
return "Сохраненных мест нет"
}
//обрабатываем выбранный элемент
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if (places.count != 0) {
print(picker.tag)
selectedPlace = places[row]
// print("выделена строка №\(row), элемент \(places[row])'")
}
}
func setup() { picker.delegate = self }
}
And
class TableForRouteViewController: UIViewController, UITableViewDataSource, UIPickerViewDelegate{
// Выбираем все места в базе данных
let places = realm.objects(Place.self)
#IBOutlet weak var makeTheRouteButton: UIBarButtonItem!
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView(frame: CGRect(x: 0, y: 0,
width: tableView.frame.size.width, height: 1))
tableView.dataSource = self
let cell = tableView.cellForRow(at: IndexPath(row: 0, section: 1)) as! PlaceForRouteCell
cell.picker.selectRow(1, inComponent: 0, animated: true)
var cellA = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as! PlaceForRouteCell {
didSet {
print("первая ячейка изменилось")
}
}
var cellB = tableView.cellForRow(at: IndexPath(row: 0, section: 1)) as! PlaceForRouteCell
// print("tag of A: \(cellA.picker)")
// print("tag of B: \(cellB.picker.tag)")
var valueOfA = cellA.getSelectedPlace() {
didSet {
print("значение в первой ячейке изменилось")
}
}
print(valueOfA.userName)
var valueOfB = cellB.getSelectedPlace() {
didSet {
print("значение в первой ячейке изменилось")
}
}
print(valueOfB.userName)
if (places.count < 2 ) { makeTheRouteButton.isEnabled = false }
}
#IBAction func makeTheRouteButtonPressed(_ sender: UIBarButtonItem) {
let cellA = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as! PlaceForRouteCell
let pointA = cellA.selectedPlace
let cellB = tableView.cellForRow(at: IndexPath(row: 0, section: 1)) as! PlaceForRouteCell
let pointB = cellB.selectedPlace
print("point A: \(pointA.userName), point B: \(pointB.userName)")
}
// var arePlacesEqual : Bool = comparing() {
// didSet {
// print("значения поменялись")
// }
// }
func comparing() -> Bool {
let cellA = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as! PlaceForRouteCell
let cellB = tableView.cellForRow(at: IndexPath(row: 0, section: 1)) as! PlaceForRouteCell
let valueOfA = cellA.selectedPlace.userName
let valueOfB = cellB.selectedPlace.userName
if (valueOfA == valueOfB) { return true }
return false
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1 }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "placeForRouteCell", for: indexPath) as! PlaceForRouteCell
cell.picker.tag = indexPath[0]
cell.setup()
return cell
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch(section) {
case 0:return "Точка А"
case 1:return "Точка Б"
default :return ""
}
}
// количество секциий
func numberOfSections(in tableView: UITableView) -> Int { return 2 }
}
I created tableView with pickerView in cell.
In didSelectRow method i added NotificationCenter sender
My tableViewCell class:
let picker_array = ["one", "two", "three"]
class TableViewCell: UITableViewCell, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var picker: UIPickerView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.picker.delegate = self
self.picker.dataSource = self
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return picker_array.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return "\(picker_array[row])"
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
NotificationCenter.default.post(name: Notification.Name("Notification"), object: nil)
}
}
In viewDidLoad i added NotificationCenter listener.
My ViewController class:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
let dummy_data = [1,2]
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(methodOfReceivedNotification(notification:)), name: Notification.Name("Notification"), object: nil)
}
#objc func methodOfReceivedNotification(notification: Notification) {
for index in dummy_data {
let indexPath = IndexPath(row: index - 1, section: 0)
let cell = tableView.cellForRow(at: indexPath) as! TableViewCell
let selectRow = picker_array[cell.picker.selectedRow(inComponent: 0)]
print(selectRow)
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dummy_data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
return cell
}
}
When i change pickerView, console print current value from cells
I think it will help
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I'm trying to make expandable UITableView and make a simple rotate animation to UIImage in UITableViewCell from didSelectRowAt when I click the cell the image rotate 180° clockwise and when I click the cell again it will rotate -180° back to its normal state but the animation not working and it has a bug for the first time to click on cell the arrow not rotate I must click in twice to make it rotate as the gif.
TestModel:
struct Titles {
var opened = Bool()
var title = String()
var sectionData = [String]()
}
ViewControllerView:
class ViewControllerView: UIView {
var data = [Titles]()
override init(frame: CGRect) {
super.init(frame: frame)
layoutUI()
data = [
Titles(opened: false, title: "Title1", sectionData: ["Cell1", "Cell2", "Cell3"]),
Titles(opened: false, title: "Title2", sectionData: ["Cell1", "Cell2", "Cell3"]),
Titles(opened: false, title: "Title3", sectionData: ["Cell1", "Cell2", "Cell3"])
]
}
lazy var recipesTableView: UITableView = {
let recipesTableView = UITableView()
recipesTableView.register(TableViewCell.self, forCellReuseIdentifier: "TableViewCell")
recipesTableView.register(TableViewCellTwo.self, forCellReuseIdentifier: "TableViewCellTwo")
return recipesTableView
}()
}
extension ViewControllerView: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if data[section].opened == true {
return data[section].sectionData.count + 1
} else {
return 1
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell
cell.title.text = data[indexPath.section].title
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCellTwo", for: indexPath) as! TableViewCellTwo
cell.title.text = data[indexPath.section].sectionData[indexPath.row - 1]
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if data[indexPath.section].opened == true {
data[indexPath.section].opened = false
let section = IndexSet.init(integer: indexPath.section)
let cell = tableView.cellForRow(at: indexPath) as! TableViewCell
UIView.animate(withDuration: 1, animations: {
cell.arrowImage.transform = CGAffineTransform.init(rotationAngle: CGFloat.pi)
})
tableView.reloadSections(section, with: .none)
} else {
data[indexPath.section].opened = true
let section = IndexSet.init(integer: indexPath.section)
let cell = tableView.cellForRow(at: indexPath) as! TableViewCell
UIView.animate(withDuration: 1, animations: {
cell.arrowImage.transform = CGAffineTransform.identity
})
tableView.reloadSections(section, with: .none)
}
}
}
TableViewCell:
class TableViewCell: UITableViewCell {
lazy var arrowImage: UIImageView = {
var arrow = UIImageView(image: UIImage(systemName: "arrowtriangle.down.fill"))
arrow.tintColor = .black
arrow.translatesAutoresizingMaskIntoConstraints = false
return arrow
}()
}
First we declare an enum with available chevron directions.
enum ChevronDirection: Double {
case up = -180
case down = 0
}
Also we could add an extension to make working with radians much easier
extension Double {
var degreesToRadians: CGFloat {
return CGFloat(self) * (CGFloat(Double.pi) / 180.0)
}
}
Then you should create a UITableViewHeaderFooterView header
class AnyNameHeader: UITableViewHeaderFooterView {
#IBOutlet weak var imgChevron: UIImageView!
var tapDelegate: DelegateToGetNotified?
var currentSection: Int!
func configure(_ text: String, section: Int, isExpanded: Bool) {
currentSection = section
self.set(isExpanded: isExpanded, animated: false)
}
func set(isExpanded: Bool, animated: Bool) {
if isExpanded {
setChevronDirection(ChevronDirection.up, animated: animated)
} else {
setChevronDirection(ChevronDirection.down, animated: animated)
}
}
func setChevronDirection(_ direction: ChevronDirection, animated: Bool) {
if animated {
UIView.animate(withDuration: 0.4, delay: 0.0, options: UIViewAnimationOptions(), animations: { [weak self] in
self?.imgChevron.transform = CGAffineTransform(rotationAngle: direction.rawValue.degreesToRadians)
}, completion: nil)
} else {
self.imgChevron.transform = CGAffineTransform(rotationAngle: direction.rawValue.degreesToRadians)
}
}
//Note: You can add your way to detect user tap over header for me i add a button over the header
#IBAction func headerAction() {
tapDelegate?.didTapHeader(sectionIndex: currentSection)
}
} // End of header
Then we goes to the Final part in our View controller
class MyViewController: UIViewController {
var expandedSectionIndex : Int? = nil {
didSet{
tableView.beginUpdates()
// Collapse previously expanded section
if let oldValue = oldValue {
tableView.deleteRows(at: indexPathsFor(section: oldValue), with: UITableViewRowAnimation.top)
if let header = tableView.headerView(forSection: oldValue) as? AnyNameHeader {
header.set(isExpanded: false, animated: true)
}
}
// Don't continue to Expand new section if already collapsing opened section
if let oldValue = oldValue, let sectionIndex = expandedSectionIndex, sectionIndex == oldValue {
expandedSectionIndex = nil
tableView.endUpdates()
return
}
// Expand new section
if let expandedSectionIndex = expandedSectionIndex {
tableView.insertRows(at: indexPathsFor(section: expandedSectionIndex), with: UITableViewRowAnimation.top)
if let header = tableView.headerView(forSection: expandedSectionIndex) as? AnyNameHeader {
header.set(isExpanded: true, animated: true)
}
}
tableView.endUpdates()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isExpandedSection(section: section) {
return data[section].sectionData.count
} else {
return 0
}
}
}
// MARK: Expanded Section
extension MyViewController {
/// Returns Indexpaths to be Inserted or Removed for a given section Index
fileprivate func indexPathsFor(section: Int) -> [IndexPath] {
var newIndexPaths = [IndexPath]()
for index in 0 ..< data[section].sectionData.count {
let newIndexPath = IndexPath(row: index , section: section)
newIndexPaths.append(newIndexPath)
}
return newIndexPaths
}
fileprivate func isExpandedSection(section: Int) -> Bool {
return expandedSectionIndex == section
}
}
extension MyViewController: DelegateToGetNotified {
func didTapHeader(sectionIndex: Int) {
expandedSectionIndex = sectionIndex
}
}
I have tried n number of solutions, but still no luck. I designed my own Custom cell for Header of a Section. So my table renders properly.
Then for each header cell in func viewForHeaderInSection(), I've added tap gesture to handle click on header. But every time I click on any header it disappears from the table. I haven't written any code of deleting sections.
func numberOfSections(in tableView: UITableView) -> Int {
return ModelFacade.sharedInstanceModelFacade.getGenericModel().faqsArray.count
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "AccordianHeaderPrototypeCell") as! AccordianHeaderPrototypeCell
let cellinfo = ModelFacade.sharedInstanceModelFacade.getGenericModel().faqsArray[section]
cell.headingLabel.text = cellinfo.title
cell.arrowImage.tag = kHeaderSectionTag + section
cell.arrowImage.image = Constants.DOWNARROW_IMAGE
cell.tag = section
let headerTapGesture = UITapGestureRecognizer()
headerTapGesture.addTarget(self, action: #selector(self.sectionHeaderWasTouched(_:)))
cell.addGestureRecognizer(headerTapGesture)
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (self.expandedSectionHeaderNumber == section) {
return ModelFacade.sharedInstanceModelFacade.getGenericModel().faqsArray[section].contentArray.count
} else {
return 0;
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ProductDetailingsPrototypeCell", for: indexPath) as! ProductDetailingsPrototypeCell
cell.separatorInset = .zero
let cellinfo = ModelFacade.sharedInstanceModelFacade.getGenericModel().faqsArray[indexPath.section].contentArray[indexPath.row]
cell.descriptionLabel.attributedText = cellinfo.html2Attributed
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
#objc func sectionHeaderWasTouched(_ sender: UITapGestureRecognizer) {
let headerView = sender.view as! AccordianHeaderPrototypeCell
let section = headerView.tag
let eImageView = headerView.viewWithTag(kHeaderSectionTag + section) as? UIImageView
if (self.expandedSectionHeaderNumber == -1) {
self.expandedSectionHeaderNumber = section
tableViewExpandSection(section, imageView: eImageView!)
} else {
if (self.expandedSectionHeaderNumber == section) {
tableViewCollapeSection(section, imageView: eImageView!)
} else {
let cImageView = self.view.viewWithTag(kHeaderSectionTag + self.expandedSectionHeaderNumber) as? UIImageView
tableViewCollapeSection(self.expandedSectionHeaderNumber, imageView: cImageView!)
tableViewExpandSection(section, imageView: eImageView!)
}
}
}
func tableViewCollapeSection(_ section: Int, imageView: UIImageView) {
let sectionData = ModelFacade.sharedInstanceModelFacade.getGenericModel().faqsArray[section].contentArray
self.expandedSectionHeaderNumber = -1
if (sectionData.count == 0) {
return
} else {
UIView.animate(withDuration: 0.4, animations: {
imageView.transform = CGAffineTransform(rotationAngle: (0.0 * CGFloat(Double.pi)) / 180.0)
})
var indexesPath = [IndexPath]()
for i in 0 ..< sectionData.count {
let index = IndexPath(row: i, section: section)
indexesPath.append(index)
}
self.itemsTableView!.beginUpdates()
self.itemsTableView!.deleteRows(at: indexesPath, with: UITableView.RowAnimation.fade)
self.itemsTableView!.endUpdates()
}
}
func tableViewExpandSection(_ section: Int, imageView: UIImageView) {
let sectionData = ModelFacade.sharedInstanceModelFacade.getGenericModel().faqsArray[section].contentArray
if (sectionData.count == 0) {
self.expandedSectionHeaderNumber = -1
return
} else {
UIView.animate(withDuration: 0.4, animations: {
imageView.transform = CGAffineTransform(rotationAngle: (180.0 * CGFloat(Double.pi)) / 180.0)
})
var indexesPath = [IndexPath]()
for i in 0 ..< sectionData.count {
let index = IndexPath(row: i, section: section)
indexesPath.append(index)
}
self.expandedSectionHeaderNumber = section
self.itemsTableView.beginUpdates()
self.itemsTableView.insertRows(at: indexesPath, with: UITableView.RowAnimation.fade)
self.itemsTableView.endUpdates()
}
}
Adding two images to explain what happens before and after click.
I have been stuck on this problem for a few days now. I have an image view set as invisible until the long press gesture is triggered. However, I can not wrap my head around on how to make the UIImageView to become visible after such. This is within a Cell in tableView. I have used reloadData(), but the issue is the table loses its place. It scrolls upwards by the time you release your finger. The long gesture triggers a boolean on the database side that toggles saveShow. When data is loaded, the query checks and displays the UIImageView if true. So I believe I would have to reload the data since it is based on the query..? Any help will be greatly appreciated!
class SecondViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UIPickerViewDelegate, UIPickerViewDataSource {
let db = DatabaseController()
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var cityPicker: UIPickerView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
cityPicker.delegate = self
cityPicker.dataSource = self
self.tableView.autoresizingMask = UIView.AutoresizingMask.flexibleHeight;
self.db.createTable()
//////////////////////////////////////////////////////////////////
let longPressGesture:UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPress))
longPressGesture.minimumPressDuration = 1.0 // 1 second press
longPressGesture.delegate = self as? UIGestureRecognizerDelegate
self.tableView.addGestureRecognizer(longPressGesture)
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////
Alamofire.request("http://url.com/jsonfile").responseJSON { response in
if (response.result.value != nil) {
let strOutput = JSON(response.result.value ?? "nil")
if let resObj = strOutput.arrayObject {
arrRes2 = resObj as! [[String:AnyObject]]
self.db.addShowsToDatabase(arrRes: arrRes2)
}
}
self.db.deleteOldShows()
self.grabData(id: 1)
self.tableView.reloadData()
}
////////////////////////////////////////////////////////////////////
}
func grabData(id: Int) -> Void {
regionResults = self.db.listAllShows(id: id)
self.tableView.reloadData()
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return cities.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return cities[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
cheapFix = row + 1
grabData(id: row + 1)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if regionResults.isEmpty {
return 1
} else {
return regionResults.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "bandCustomCell") as! BandTableViewCell
if regionResults.isEmpty {
cell.bandPic.image = UIImage(named: "flyer.jpg")
cell.bandSummary?.text = "No Shows Announced!"
cell.bandVenue?.text = "None"
cell.bandDate?.text = "None"
} else {
let show = regionResults[indexPath.row]
//print(try! show.get(Expression<String>("show_summary")))
let strDateFull = try! show.get(Expression<String>("show_date"))
var strDate: Array = ((strDateFull as AnyObject).components(separatedBy: "T"))
// Setting up the date
var dateArr: Array = strDate[0].components(separatedBy: "-")
let dateStr: String = "\(dateArr[1])-\(dateArr[2])-\(dateArr[0])"
// Setting up the time
var timeArr: Array = strDate[1].components(separatedBy: ":")
var timeStr: Int? = Int(timeArr[0])
// Changing military time to standard
if timeStr! > 12 {
timeStr = timeStr! - 12
}
let saved = try! show.get(Expression<Bool>("save_show"))
if (saved == true) {
cell.bandSavedShow.isHidden = false
} else if (saved == false) {
cell.bandSavedShow.isHidden = true
}
cell.bandSummary?.text = "\(try! show.get(Expression<String>("show_summary"))), \(timeStr!):\(timeArr[1])PM"
cell.bandVenue?.text = try! show.get(Expression<String>("venue_name"))
/*if (show["shows_img"] as? String != "null") {
let url = URL(string: show["shows_img"] as! String)
cell.bandPic.kf.setImage(with: url)
} else {*/
cell.bandPic.image = UIImage(named: "flyer.jpg")
//}
cell.bandDate?.text = dateStr
tableView.isScrollEnabled = true
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
myIndex2 = indexPath.row
//performSegue(withIdentifier: "segue2", sender: self)
}
#objc func longPress(_ longPressGestureRecognizer: UILongPressGestureRecognizer) {
if longPressGestureRecognizer.state == UIGestureRecognizer.State.began {
let touchPoint = longPressGestureRecognizer.location(in: self.tableView)
if let indexPath = self.tableView.indexPathForRow(at: touchPoint) {
let show = regionResults[indexPath.row]
let summary = try! show.get(Expression<String>("show_summary"))
let date = try! show.get(Expression<String>("show_date"))
let saved = try! show.get(Expression<Bool>("save_show"))
self.db.toogleSaveShows(summary: summary, date: date, saved: saved)
reload(tableView: self.tableView, indexPath: indexPath)
}
//grabData(id: cheapFix)
}
}
func reload(tableView: UITableView, indexPath: IndexPath) {
//found on stackoverflow
let contentOffset = tableView.contentOffset
tableView.beginUpdates()
tableView.reloadRows(at: [indexPath], with: UITableView.RowAnimation.none)
tableView.setContentOffset(contentOffset, animated: false)
tableView.endUpdates()
}
}
update the longPressGesture method as below:
let touchPoint = longPressGestureRecognizer.location(in: self.tableView)
if let indexPath = self.tableView.indexPathForRow(at: touchPoint) {
let cell = tableView.cellForRow(at: indexPath) as? BandTableViewCell;
cell.bandPic.image = ....//provide image here.
}
Here, it is not required to reload full table, instead just access the cell and set imageview there.
Firstly you must add gesture recogniser on the cell being used instead of the whole table view as in selector method of gesture you are trying to get the indexpath of cell being pressed.
Then try reloading the cell with found indexpath.
self.tableView.reloadRows(at: [indexpath], with: .automatic)
I have a UITableView with about 5 sections. I am trying to collapse and expand one of those section by the click of a button, but I am seeing an issue where the code I'm using to do so results in the collapsing of other sections as well. Specifically, the first row of all visible sections are collapsed.
Here is what that code looks like:
func didClickSectionCollapseButton() {
shouldCollapseSection = !shouldCollapseSection
tableView.beginUpdates()
tableView.reloadSections(NSIndexSet(index: 1), withRowAnimation: .Fade)
tableView.endUpdates()
}
And here is the numberOfRowInSection method:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
return 1
case 1:
// collapsible section
return shouldCollapse ? 0 : collapsibleSectionCellCount
case 2:
return getCellCount()
case 3:
return 1
case 4:
return 1
default:
return 0
}
}
Is there anything I'm missing here? I've gone through various tutorials and questions, but I haven't been able to find a solution yet.
Hi after a lot of research, i found a solution which worked for me perfectly using storyboard.
View controller code:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tblView: UITableView!
var sections = ["section1","section2","section3"]
var cells = ["cell1","cell2","cell3","cell4"]
var selectedIndx = -1
var thereIsCellTapped = false
override func viewDidLoad() {
super.viewDidLoad()
tblView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
return 2
case 1:
return 3
default:
return 4
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == selectedIndx && thereIsCellTapped{
return 50
}else{
return 0
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerCell = tableView.dequeueReusableCell(withIdentifier: "SectionTableViewCell") as! SectionTableViewCell
headerCell.lblHeader.text = sections[section]
headerCell.btnSelection.tag = section
headerCell.btnSelection.addTarget(self, action: #selector(ViewController.btnSectionClick(sender:)), for: .touchUpInside)
return headerCell
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ExpandeTableViewCell") as! ExpandeTableViewCell
cell.lblCell.text = cells[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.section)
}
#objc func btnSectionClick(sender:UIButton!){
print("selected index",sender.tag)
if selectedIndx != sender.tag {
self.thereIsCellTapped = true
self.selectedIndx = sender.tag
}
else {
// there is no cell selected anymore
self.thereIsCellTapped = false
self.selectedIndx = -1
}
tblView.reloadData()
}
}
If you don't want to do select and unselect on the same selection then, see code below.
#objc func btnSectionClick(sender:UIButton!){
print("selected index",sender.tag)
selectedIndx = sender.tag
tblView.reloadData()
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == selectedIndx{
return 50
}else{
return 0
}
}
It works for me, i referred lot of answers and made it. I hope it will help you.
I've Used this code long time ago it is in Swift 2.3. I don't know if this will help or not but worth to mention it.
class DriversVC : UIViewController , UITableViewDelegate , UITableViewDataSource {
//-----------------------------------------------------------------------
//MARK: - Outlets
#IBOutlet var tvDriverList: UITableView! {
didSet {
tvDriverList.delegate = self
tvDriverList.dataSource = self
}
}
//-----------------------------------------------------------------------
//MARK: - Variables
var arrDriverList : NSArray? //Section data
var arrWorkerList : NSArray? //Section data
var collapseSection0 : Bool = false
var collapseSection1 : Bool = false
var btnSection0Headder : UIButton = UIButton()
var btnSection1Headder : UIButton = UIButton()
//------------------------------------------------------
func btnSection0HeadderTapped () {
if collapseSection0 {
collapseSection0 = false
} else {
collapseSection0 = true
}
tvDriverList.reloadSections(NSIndexSet(index: 0), withRowAnimation: UITableViewRowAnimation.Fade)
}
//------------------------------------------------------
func btnSection1HeadderTapped () {
if collapseSection1 {
collapseSection1 = false
} else {
collapseSection1 = true
}
tvDriverList.reloadSections(NSIndexSet(index: 1), withRowAnimation: UITableViewRowAnimation.Fade)
}
//-----------------------------------------------------------------------------------
//MARK:- Table delegate and data sources
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
//------------------------------------------------------
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 20
}
//------------------------------------------------------
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.mainScreen().bounds.width, height: 50))
view.backgroundColor = OrangeColor //Set your color
let lbl = UILabel(frame: CGRect(x: 10, y: 5, width: UIScreen.mainScreen().bounds.width - 20, height: 40))
lbl.font = UIFont(name: OpenSansRegular, size: 18) //Set your font
lbl.textColor = UIColor.whiteColor()
view.addSubview(lbl)
if section == 0 {
lbl.text = "D R I V E R"
btnSection0Headder.addTarget(self, action: #selector(self.btnSection0HeadderTapped), forControlEvents: .TouchUpInside)
btnSection0Headder.frame = view.frame
view.addSubview(btnSection0Headder) // uncomment to apply collapse effect
} else {
lbl.text = "W O R K E R"
btnSection1Headder.addTarget(self, action: #selector(self.btnSection1HeadderTapped), forControlEvents: .TouchUpInside)
btnSection1Headder.frame = view.frame
view.addSubview(btnSection1Headder) // uncomment to apply collapse effect
}
return view
}
//------------------------------------------------------
func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
return UIView()
}
//------------------------------------------------------
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if arrWorkerList != nil && arrWorkerList?.count > 0 {
return 2
}
return 1
}
//------------------------------------------------------
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
if !collapseSection0 {
guard arrDriverList != nil else {return 0}
return arrDriverList!.count
} else {
return 0
}
} else {
if !collapseSection1 {
guard arrWorkerList != nil else {return 0}
return arrWorkerList!.count
} else {
return 0
}
}
}
//------------------------------------------------------
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCellWithIdentifier(NSStringFromClass(DriversCVC).componentsSeparatedByString(".").last!) as? DriversCVC else { fatalError("unexpected DriversCVC dequeued from tableView") }
cell.superViewController = self
if indexPath.section == 0 {
guard let dict = arrDriverList![indexPath.row] as? NSDictionary else {return cell}
cell.data = dict
} else {
guard let dict = arrWorkerList![indexPath.row] as? NSDictionary else {return cell}
cell.data = dict
}
cell.setup()
return cell
}
//----------------------------------------------------------------------
//MARK: - Action Method
#IBAction func btnBackTapped(sender: AnyObject) {
guard self.navigationController != nil else {
self.dismissViewControllerAnimated(true, completion: nil)
return
}
guard self.navigationController?.popViewControllerAnimated(true) != nil else {
guard self.navigationController?.dismissViewControllerAnimated(true, completion: nil) != nil else {
AppDelegate.sharedInstance().loginCall()
return
}
return
}
}
//-----------------------------------------------------------------------
//MARK: - View Life Cycle Methods
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
//----------------------------------------------------------------------
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
setUpVC()
}
//----------------------------------------------------------------------
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
} }
You can use:
func didClickSectionCollapseButton() {
shouldCollapseSection = !shouldCollapseSection
tableView.beginUpdates()
tableView.deleteSections(NSIndexSet(index: 1), withRowAnimation: .Fade)
tableView.endUpdates()
}
beginUpdates() and endUpdates() works in pair if you want subsequent insertions, deletion, and selection operations, but not for the reloadData.
In your code, remove beginUpdates() and endUpdates().
Is there a difference between the shouldCollapseSection variable being set in the button action and the shouldCollapse variable used in the numberOfRowsInSection method ?
It would seem that you are not setting the same variable you are using in the data source delegate.