How to navigate to viewController from tableview cell, which is placed in other tableview Cell? - ios

I have placed a tableview2 in tableview1 Cell, Now when I click on the tableview2 cell I need to navigate to a new viewController. Please help me... I was struggling with one whole day :(
here is the code, the second table view is placed in SegmentedCell...
when i am trying to push, its unable to go next controller..
import UIKit
import XMSegmentedControl
import Alamofire
import SwiftyJSON
class segmentedCell: UITableViewCell, XMSegmentedControlDelegate, UITableViewDelegate, UITableViewDataSource{
let byndrColor : UIColor = UIColor( red: 224/255, green: 0/255, blue: 115/255, alpha: 1.0 )
let fontStyle = UIFont(name: "Lato-bold", size: 12)
#IBOutlet weak var segmentedControl: XMSegmentedControl!
#IBOutlet weak var feedTableView: UITableView!
var getApi = UIApplication.shared.delegate as! AppDelegate
var course_id = String()
var materialListObjects = [MaterialsInSingleCourseGetSet]()
var assignmentExamAndQuizListObjects = [AssignmentAndExamsQuizGetSet]()
override func awakeFromNib() {
super.awakeFromNib()
feedTableView.delegate = self
feedTableView.dataSource = self
segmentedControl.delegate = self
segmentedControl.segmentTitle = ["LATEST", "MATERIALS", "COURSEWORK", "PROGRESS"]
segmentedControl.font = fontStyle!
segmentedControl.selectedItemHighlightStyle = XMSelectedItemHighlightStyle.BottomEdge
segmentedControl.backgroundColor = UIColor.white
segmentedControl.tint = UIColor.black
segmentedControl.highlightTint = byndrColor
segmentedControl.highlightColor = byndrColor
segmentedControl.edgeHighlightHeight = 2
segmentedControl.selectedSegment = 0
let share = UIApplication.shared.delegate as! AppDelegate
materialListObjects = share.materialListInSingleCourse as! [MaterialsInSingleCourseGetSet]
assignmentExamAndQuizListObjects = share.assignmentsExamsAndQuizListInSingleCourse as! [AssignmentAndExamsQuizGetSet]
// Initialization code
}
func xmSegmentedControl(xmSegmentedControl: XMSegmentedControl, selectedSegment: Int) {
if xmSegmentedControl == segmentedControl {
print("SegmentedControl1 Selected Segment: \(selectedSegment)")
switch segmentedControl.selectedSegment
{
case 0:
feedTableView.reloadData()
case 1:
feedTableView.reloadData()
case 2:
feedTableView.reloadData()
case 3:
feedTableView.reloadData()
default :
break
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if segmentedControl.selectedSegment == 0
{
return 0
}
else
if segmentedControl.selectedSegment == 1
{
return materialListObjects.count
}
else
if segmentedControl.selectedSegment == 2
{
return assignmentExamAndQuizListObjects.count
}
else
{
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if segmentedControl.selectedSegment == 0
{
let cell = Bundle.main.loadNibNamed("TypeOneCell", owner: self, options: nil)?.first as! TypeOneCell
return cell
}
else
if segmentedControl.selectedSegment == 1
{
if materialListObjects[indexPath.row].type == "file"
{
let cell = Bundle.main.loadNibNamed("materialCellOne", owner: self, options: nil)?.first as! materialCellOne
cell.materialNameLabel.text = materialListObjects[indexPath.row].title
let image = materialListObjects[indexPath.row].title
cell.contentImage.image = image.documentType(givenType: image)
return cell
}else
{
let cell = Bundle.main.loadNibNamed("materialCellTwo", owner: self, options: nil)?.first as! materialCellTwo
cell.materialNameLabel.text = materialListObjects[indexPath.row].title
cell.contentImage.image = #imageLiteral(resourceName: "material_hyperlink")
return cell
}
}
else
if segmentedControl.selectedSegment == 2
{
let cell = Bundle.main.loadNibNamed("CourseWorkCell", owner: self, options: nil)?.first as! CourseWorkCell
print("assignment title : \(assignmentExamAndQuizListObjects[indexPath.row].title)")
cell.titleLabel.text = assignmentExamAndQuizListObjects[indexPath.row].title
if assignmentExamAndQuizListObjects[indexPath.row].type == ""
{
cell.contentImage.image = #imageLiteral(resourceName: "assignment_large")
}else
{
cell.contentImage.image = #imageLiteral(resourceName: "exam_inline")
}
var time = assignmentExamAndQuizListObjects[indexPath.row].start
time = time.dateRange(dateString: time)
time = time.days(givenDate: time)
cell.timeLabel.text = time
return cell
}
else
if segmentedControl.selectedSegment == 3
{
let cell = Bundle.main.loadNibNamed("TypeOneCell", owner: self, options: nil)?.first as! TypeOneCell
return cell
}
else
{
let cell = Bundle.main.loadNibNamed("TypeOneCell", owner: self, options: nil)?.first as! TypeOneCell
return cell
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if segmentedControl.selectedSegment == 2
{
return 70
}
else
{
return 100
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView(frame: CGRect.zero)
let label = UILabel(frame: CGRect(x: 8, y: 8, width: 150, height: 20))
view.addSubview(label)
label.font = UIFont(name: "Lato-Heavy", size: 17)
if segmentedControl.selectedSegment == 1
{
switch section {
case 0:
label.text = "All Materials"
case 1:
label.text = "From Your Courses"
default:
break
}
}
else
if segmentedControl.selectedSegment == 2
{
switch section {
case 0:
label.text = "All CourseWork"
case 1:
label.text = "From Your Courses"
default:
break
}
}
else
{
}
return view
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
//How to perform from here
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if segmentedControl.selectedSegment == 1
{
let storyboard = UIStoryboard(name: "Main", bundle : nil)
let nextViewController = storyboard.instantiateViewController(withIdentifier: "QuickLook") as! QuickLook
if materialListObjects[indexPath.row].type == "url"
{
nextViewController.id = materialListObjects[indexPath.row].body
nextViewController.type = "url"
}
else
{
nextViewController.id = materialListObjects[indexPath.row].id
}
nextViewController.course_id = String(describing: materialListObjects[indexPath.row].course_id)
let naviControl = UINavigationController(rootViewController: nextViewController)
naviControl.pushViewController(nextViewController, animated: true)
}
}
}

I have created a similar scenario as yours and this is how you can get it working.
1. View Hierarchy
I have used tag property to uniquely identify both UITableViews, i.e.
Outer tableView tag = 0
Inner tableView tag = 1
2. Now implement UITableViewDataSource, UITableViewDelegate methods for both the tableViews. Set the dataSource and delegate of both the tableViews as the ViewController.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if tableView.tag == 0
{
return 1
}
else if tableView.tag == 1
{
return 5
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
if tableView.tag == 0
{
return tableView.dequeueReusableCell(withIdentifier: "outercell", for: indexPath)
}
else if tableView.tag == 1
{
return tableView.dequeueReusableCell(withIdentifier: "innercell", for: indexPath)
}
return UITableViewCell()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if tableView.tag == 1
{
//TODO: Write your code for navigating to another ViewController here
print("Inner cell tapped")
}
}
Edit:
In the Interface Builder, you can find a tag attribute corresponding to each element in the attributes inspector, i.e.
For outer tableView set it to 0 and for inner tableView set it to 1.
Let me know if you still face any issues. Happy Coding..🙂

Use performSegue(withIdentifier: "ViewController", sender: self); in didSelectrow method of you tableView2.

Related

Switch between tableViews using segmented Control

I have a tableView showing multiple tasks and i would like to programmatically switch between 2 dataSources. I have created a segmented control that appear at the top but when i click on the buttons there is no change and i don't know how to link my segmented Control to my dataSources, here's my code:
class MyTasksCollectionCell: UICollectionViewCell, UITableViewDelegate, UITableViewDataSource {
var tasks = [Add]()
var pastTasks = [Add]()
static let identifier = "MyTasksCollectionCell"
private let cellID = "CellID"
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tasks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! MyTasksTableCell
cell.accessoryType = .disclosureIndicator
cell.categoryLabel.text =
"\(tasks[indexPath.row].category)"
cell.dateLabel.text =
"\(tasks[indexPath.row].date)"
cell.hourLabel.text =
"\(tasks[indexPath.row].hour)"
if cell.categoryLabel.text == "Urgent" {
cell.categoryIcon.image = #imageLiteral(resourceName: "red.png")
}
if cell.categoryLabel.text == "Important" {
cell.categoryIcon.image = #imageLiteral(resourceName: "orange.png")
}
if cell.categoryLabel.text == "Not Important" {
cell.categoryIcon.image = #imageLiteral(resourceName: "green.png")
}
cell.dateIcon.image = UIImage(systemName: "calendar.badge.clock")
return cell
}
func addControl() {
let segmentItems = ["Present Tasks", "Past Tasks"]
let control = UISegmentedControl(items: segmentItems)
control.frame = CGRect(x: 10, y: 0, width: (self.tableView.frame.width - 20), height: 30)
control.addTarget(self, action: #selector(segmentControl(_:)), for: .valueChanged)
control.selectedSegmentIndex = 0
tableView.addSubview(control)
}
#objc func segmentControl(_ segmentedControl: UISegmentedControl) {
switch (segmentedControl.selectedSegmentIndex) {
case 0:
// First segment tapped
print("Present Tasks")
self.tableView.reloadData()
break
case 1:
// Second segment tapped
print("Past Tasks")
self.tableView.reloadData()
break
default:
break
}
}
}
Use an enum to know what to display in your tab depending on segment control value :
enum DispkayedTasks {
case current
case past
}
var displayedTask = DisplayedTasks.current
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch (displayedTask) {
case .current:
// First segment tapped
return self.tasks.count
case .past:
// Second segment tapped
return self.pastTasks.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! MyTasksTableCell
cell.accessoryType = .disclosureIndicator
let task = {() -> Add in
switch (displayedTask) {
case .current:
// First segment tapped
return self.tasks[indexPath.row]
case past:
// Second segment tapped
return self.pastTasks[indexPath.row]
}
}()
cell.categoryLabel.text =
"\(task.category)"
cell.dateLabel.text =
"\(task.date)"
cell.hourLabel.text =
"\(task.hour)"
if cell.categoryLabel.text == "Urgent" {
cell.categoryIcon.image = #imageLiteral(resourceName: "red.png")
}
if cell.categoryLabel.text == "Important" {
cell.categoryIcon.image = #imageLiteral(resourceName: "orange.png")
}
if cell.categoryLabel.text == "Not Important" {
cell.categoryIcon.image = #imageLiteral(resourceName: "green.png")
}
cell.dateIcon.image = UIImage(systemName: "calendar.badge.clock")
return cell
}
#objc func segmentControl(_ segmentedControl: UISegmentedControl) {
switch (segmentedControl.selectedSegmentIndex) {
case 0:
// First segment tapped
print("Present Tasks")
displayedTasks = .current
self.tableView.reloadData()
case 1:
// Second segment tapped
print("Past Tasks")
displayedTasks = .past
self.tableView.reloadData()
default:
break
}
}

Problem with Custom View for header in section not rendering on tap gesture UITableView

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.

How to show a parent view which is outside tableview and is scrollable?

I have a scenario where I need to show a parent view with shadow and corner radius containing a long list of reusable items. I used a tableView to display items. But I stuck at making my tableview expand as much as its contentSize. It works but not accurate. Any solutions?
Edit:
Desired result:
I used the following reference for self sizing tableview.
Self Sizing UITableView
I made a few modifications as below:
final class SelfSizedTableView: UITableView {
var maxHeight = CGFloat.greatestFiniteMagnitude
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
self.layoutIfNeeded()
}
override var intrinsicContentSize: CGSize {
let height = min(contentSize.height, maxHeight)
let size = CGSize(width: contentSize.width, height: height)
return size
}
}
I used a parent tableView with a cell having my containerView and embedding this self sized tableView.
class MyContainerViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
// MARK: - IBOutlets
#IBOutlet weak var parentTableView: UITableView!
// MARK: - Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
private func estimateDataHeight() -> CGFloat {
let detailCellHeight: CGFloat = 32
let headingCellHeight: CGFloat = 43
let headings: CGFloat = headingCellHeight*2
let detailsHeight: CGFloat = detailCellHeight*4
let baseHeight = headings + detailsHeight
let membersHeight =
CGFloat(sectionsArray.count) * detailCellHeight
return baseHeight + membersHeight
}
}
// MARK: - UITableViewDataSource
extension MyContainerViewController {
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let id = String(describing: MyContainerTVCell.self)
guard let cell = tableView
.dequeueReusableCell(withIdentifier: id, for: indexPath)
as? MyContainerTVCell else {
return UITableViewCell()
}
cell.policyDetails = dataSource
// my cheat/trick doesn't work on large data.
DispatchQueue.main.asyncAfter(deadline: .now()+0.4) {
tableView.beginUpdates()
cell.tableView.layoutIfNeeded()
cell.tableView.reloadData() // the overridden one
tableView.endUpdates()
}
return cell
}
}
extension MyContainerViewController {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return estimateDataHeight()
}
}
My cell class which has the self size tableView and containerView:
class MyContainerTVCell: UITableViewCell, UITableViewDataSource, UITableViewDelegate {
// MARK: - IBOutlets
#IBOutlet weak var containerView: UIView!
#IBOutlet weak var shadowView: UIView!
#IBOutlet weak var tableView: SelfSizedTableView!
// MARK: - Properties
let titles = ["Email ID:", "Mobile Number:", "Address:", "ID: "] // first section data array
let moreData: [String] = [] // remaining reusable sections array
// no of subsequent sections for moreData array type
var numberOfSections: Int {
return 4
}
// MARK: -
var dataSource: MyDataSource!
// MARK: - Life Cycle
override func awakeFromNib() {
super.awakeFromNib()
setupView()
}
override func layoutSubviews() {
super.layoutSubviews()
}
// MARK: - Setup
func setupView() {
containerView.rounded(with: 10)
shadowView.layer.applyShadow()
tableView.dataSource = self
tableView.delegate = self
}
}
// MARK: - UITableViewDataSource
extension MyContainerTVCell {
func numberOfSections(in tableView: UITableView) -> Int {
return numberOfSections + 1
}
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
if section == 0 { return titles.count + 1 }
else if section == 1 { return moreData.count + 1 }
else { return moreData.count }
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let headerID = String(describing: MyHeaderTVCell.self)
let itemID = String(describing: MyItemTVCell.self)
switch indexPath.section {
case 0:
if indexPath.row == 0 {
guard let cell = tableView
.dequeueReusableCell(withIdentifier: headerID, for: indexPath)
as? MyHeaderTVCell else {
return UITableViewCell()
}
cell.titleLabel.text = dataSource.title
return cell
} else {
guard let cell = tableView
.dequeueReusableCell(withIdentifier: itemID, for: indexPath)
as? MyItemTVCell else {
return UITableViewCell()
}
let item = titles[indexPath.row-1]
cell.titleLabel.text = item
cell.separatorView.isHidden = true
let data: String
switch indexPath.row {
case 1:
data = dataSource.emailID
case 2:
data = dataSource.mobileNo
case 3:
data = dataSource.address
case 4:
data = dataSource.name
case 5:
data = dataSource.age
case 6:
data = dataSource.id
case 7:
data = dataSource.office
case 8:
data = dataSource.academic
default: data = String()
}
cell.detailLabel.text = data
return cell
}
case 1:
if indexPath.row == 0 {
guard let cell = tableView
.dequeueReusableCell(withIdentifier: headerID, for: indexPath)
as? MyHeaderTVCell else {
return UITableViewCell()
}
cell.titleLabel.text = "More Data"
return cell
} else {
guard let cell = tableView
.dequeueReusableCell(withIdentifier: itemID, for: indexPath)
as? MyItemTVCell else {
return UITableViewCell()
}
let sectionIndex = indexPath.section-1
guard sectionIndex <= numberOfSections-1,
let section = sectionsArray?[indexPath.section-1] else {
return UITableViewCell()
}
cell.titleLabel.text = moreData[indexPath.row-1]
cell.separatorView.isHidden = true
switch indexPath.row {
case 1:
cell.detailLabel.text = section.a
case 2:
cell.detailLabel.text = section.b
case 3:
cell.detailLabel.text = "\(section.c ?? 0)"
case 4:
cell.detailLabel.text = section.d
case 5:
cell.detailLabel.text = section.e
case 6:
cell.detailLabel.text = section.f
if indexPath.section < numberOfSections {
cell.separatorView.isHidden = false
}
default: break
}
return cell
}
default:
guard let cell = tableView
.dequeueReusableCell(withIdentifier: itemID, for: indexPath)
as? MyItemTVCell else {
return UITableViewCell()
}
let sectionIndex = indexPath.section-1
guard sectionIndex <= numberOfSections-1,
let section = sectionsArray?[indexPath.section-1] else {
return UITableViewCell()
}
cell.titleLabel.text = moreData[indexPath.row]
cell.separatorView.isHidden = true
switch indexPath.row {
case 0:
cell.detailLabel.text = section.a
case 1:
cell.detailLabel.text = section.b
case 2:
cell.detailLabel.text = "\(section.c ?? 0)"
case 3:
cell.detailLabel.text = section.d
case 4:
cell.detailLabel.text = section.e
case 5:
cell.detailLabel.text = section.f
if indexPath.section < numberOfSections {
cell.separatorView.isHidden = false
}
default: break
}
return cell
}
}
}
// MARK: - UITableViewDelegate
extension MyContainerTVCell {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 0 && indexPath.row == 0 { return 43 }
if indexPath.section == 1 && indexPath.row == 0 { return 43 }
return 32
}
}
Why would you want to expand tableView as much as its content size to make it scrollable, when tableView is already scrollable?
However, if you have some other content, aside from table, on the screen and you want them to scroll together, then you need to embed all your content into UIScrollView.
Then, make a height constraint for you tableView in xib/storyboard with any value.
Then you might do something like this:
// in your view controller
private var heightObservation: NSKeyValueObservation?
// called once, for example, in viewDidLoad()
private func setupTableView() {
...
observation = tableView.constraintFrameHeightToContentSizeHeight()
}
extension UITableView {
func constraintFrameHeightToContentSizeHeight() -> NSKeyValueObservation {
return observe(\.contentSize, changeHandler: { (tableView, _) in
tableView.heightConstraint?.constant = tableView.contentSize.height
})
}
}
// find height constraint
extension UIView {
var heightConstraint: NSLayoutConstraint? {
return constraints.first(where: { $0.firstAttribute == .height })
}
}
Don't forget to uncheck "Scrolling Enabled" in xib/storyboard for that table view.

How to navigate from UITableViewCell to UICollectionViewController programmatically in Swift 4

Here is what I want to achieve: When I tap a UITabViewCell (Picture 1), it will navigate to an UICollectionViewController (Picture 2). Just like the following two pictures showing:
I could navigate to an UIViewController when tap a UITableViewCell, but it doesn't work when I try to navigate to UICollectionView. Here are my codes:
import UIKit
class RealUserProfileController: UIViewController, UITableViewDelegate, UITableViewDataSource {
private let me = ["Vincent-St"]
private let followers = ["58 Followers"]
private let myPhotos = ["New Followers"]
private let others = ["My likes", "Notifications", "Clear Caches", "Drafts"]
private let settings = ["Settings"]
private let share = ["Share with friends"]
private let images = ["Hearts.png", "Footprint.png", "Notifications.png", "Trash-Empty.png"]
private let sections = ["me", "myPhotos", "others", "settings", "share"]
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.font: UIFont(name: "Arial", size: 18)!, NSAttributedStringKey.foregroundColor: UIColor.black]
navigationItem.title = "Profile"
let displayWidth: CGFloat = self.view.frame.width
let displayHeight: CGFloat = self.view.frame.height
let myTableView: UITableView = UITableView(frame: CGRect(x: 0, y: 0, width: displayWidth, height: displayHeight), style: .grouped)
myTableView.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
myTableView.dataSource = self
myTableView.delegate = self
self.view.addSubview(myTableView)
view.backgroundColor = UIColor(white: 1, alpha: 0.95)
myTableView.translatesAutoresizingMaskIntoConstraints = false
myTableView.topAnchor.constraint(equalTo: view.topAnchor, constant: -20).isActive = true
myTableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
myTableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
myTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 0 {
return 72
}
return tableView.rowHeight
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
let controller = UserProfileController()
self.navigationController?.pushViewController(controller, animated: true)
print("Value: \(me[indexPath.row])")
} else if indexPath.section == 1 {
print("Value: \(myPhotos[indexPath.row])")
} else if indexPath.section == 2 {
print("Value: \(others[indexPath.row])")
} else if indexPath.section == 3 {
let controller = ResultController()
navigationController?.pushViewController(controller, animated: true)
print("Value: \(settings[indexPath.row])")
} else if indexPath.section == 4 {
print("Value: \(share[indexPath.row])")
let shareText = "Share our app"
let shareImage = UIImage(named: "bell_unselected.png")
let activityViewController : UIActivityViewController = UIActivityViewController(activityItems: [shareText, shareImage as Any], applicationActivities: nil)
self.present(activityViewController, animated: true, completion: nil)
}
}
// return the number of cells each section.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return me.count
} else if section == 1 {
return myPhotos.count
} else if section == 2 {
return others.count
} else if section == 3 {
return settings.count
} else if section == 4 {
return share.count
} else {
return 0
}
}
// return cells
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: "MyCell")
if indexPath.section == 0 {
cell.textLabel?.text = "\(me[indexPath.row])"
cell.detailTextLabel?.text = "\(followers[indexPath.row])"
cell.imageView?.image = #imageLiteral(resourceName: "profile_image")
} else if indexPath.section == 1 {
cell.textLabel?.text = "\(myPhotos[indexPath.row])"
cell.imageView?.image = #imageLiteral(resourceName: "icon_friends")
} else if indexPath.section == 2 {
cell.textLabel?.text = "\(others[indexPath.row])"
cell.imageView?.image = UIImage.init(named: images[indexPath.row])
} else if indexPath.section == 3 {
cell.textLabel?.text = "\(settings[indexPath.row])"
cell.imageView?.image = #imageLiteral(resourceName: "Icon_Settings")
} else if indexPath.section == 4 {
cell.textLabel?.text = "\(share[indexPath.row])"
cell.imageView?.image = #imageLiteral(resourceName: "Share")
}
cell.accessoryType = .disclosureIndicator
return cell
}
}
The two lines of codes will crash the app:
let controller = UserProfileController()
self.navigationController?.pushViewController(controller, animated: true)
Here is the error message:
Cannot convert value of type 'UserProfileController.Type' to expected argument type 'UIViewController'
Here are the codes of the destination UICollectionViewController:
class UserProfileController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
}
After some research, I didn't find any useful information. Is there any way to make it happen? Or is there any way to implement the Picture 2 in another way, maybe: UIViewController, instead of UICollectionView?
Any suggestion would be appreciated.
I don't think the problem is with navigation, any subclass of UIViewController should be navigable.
I believe you need to initialize UICollectionViewController using init(collectionViewLayout: UICollectionViewLayout), collectionView has to have the layout set, otherwise the collectionView will crash if it gets presented.
Therefore instead of:
let controller = UserProfileController()
try using:
let controller = UserProfileController(collectionViewLayout: UICollectionViewFlowLayout())
Here I am assuming that the following code is your full implementation of UserProfileController, thus you directly inherit initializers from UICollectionViewController:
class UserProfileController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
}

How to hide the sections in the tableview after deleting a particular section from tableview?

I am having three sections (see the image) in which one section is to display the items and remaining two cells are designed by using stroyboard now if i delete all the items using delete button in first section then the remaining two sections need to be hidden and to display some text can anyone help me how to do this ?
func numberOfSections(in tableView: UITableView) -> Int{
// #warning Incomplete implementation, return the number of sections
return 3
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
if (section == 0){
return itemsArray.count
}else{
return 1
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! productTableViewCell
tableDetails.isHidden = false
myActivityIndicator.stopAnimating()
let arr = itemsArray[indexPath.row]
let urls = NSURL(string: arr["img"] as! String)
let data = NSData (contentsOf: urls! as URL)
cell.imageview.image = UIImage(data: data! as Data)
cell.nameLabel.text = arr["productName"]as! String
var price = arr["productPrice"] as! String
print(price)
var Quantity : Float = 1
let itemId : Int = arr["sku"] as! Int
print(itemId)
for aDic in CartArray{
if aDic["id"] == String(itemId){
Quantity = Float(String(aDic["quantity"]!))!
}
}
print(CartArray)
cell.stepper.value = Double(Int(Quantity))
cell.stepper.tag = indexPath.row
cell.stepper.addTarget(self, action: #selector(stepperValueChange(stepper:)), for:.valueChanged)
price = price.replacingOccurrences(of: "KD", with: "")
cartstring = String(Float(price)! * Quantity) + "0KD"
cell.priceLabel.text = cartstring
let quantityText = String(Quantity)
let endIndex = quantityText.index(quantityText.endIndex, offsetBy: -2)
let truncated = quantityText.substring(to: endIndex)
cell.quantityTextField.text = truncated
cell.price = price
cell.deleteButton.addTarget(self, action: #selector(deleteButtonAction(button:)), for: .touchUpInside)
cell.deleteButton.tag = indexPath.row
return cell
}else if indexPath.section == 1{
let cell = tableView.dequeueReusableCell(withIdentifier: "couponcell", for: indexPath) as! CouponTableViewCell
cell.applyButton.addTarget(self, action: #selector(applyButtonAction(button:)), for: .touchUpInside)
return cell
}else {
let cell = tableView.dequeueReusableCell(withIdentifier: "checkout", for: indexPath) as! checkoutTableViewCell
cell.finalCartpriceLabel.text = total
return cell
}
}
func deleteButtonAction(button : UIButton) {
let buttonPosition = button.convert(CGPoint(), to: tableDetails)
let index = tableDetails.indexPathForRow(at: buttonPosition)
self.itemsArray.remove(at: (index?.row)!)
self.tableDetails.deleteRows(at: [index!], with: .automatic)
tableDetails.reloadData()
}
Modify your numberOfSections with:
func numberOfSections(in tableView: UITableView) -> Int {
if self.itemsArray.count > 0 {
return 3
}
//Show Message List is Empty
return 1
}
You can manage something like,
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
if (section == 0){
return itemsArray.count
}else{
if self.itemsArray.count == 0 {
return 0
}
else{
return 1
}
}
}
You can just check for self.itemsArray.count > 0 inside your func numberOfSections(in:) and show all the three sections for this condition. Otherwise, return only the first section and this will automatically hide the other two.
Example:
func numberOfSections(in tableView: UITableView) -> Int {
if self.itemsArray.count > 0 {
return 3
}
return 1
}
in numberOfSection datasource methods, check if itemArray has no elements, return just one section. In cellForRowAtIndexPath, check the same condition again, and show the empty text in this.
func numberOfSections(in tableView: UITableView) -> Int {
if(itemArray.count > 0)
{
if (section == 0){
return itemsArray.count
}else{
return 1
}
} else {
return 1;
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if(itemArray.count > 0)
{
}else {
//Show empty text view
}
}
this code worked for me perfectly
func deleteButtonAction(button : UIButton) {
let buttonPosition = button.convert(CGPoint(), to: tableDetails)
let index = tableDetails.indexPathForRow(at: buttonPosition)
self.itemsArray.remove(at: (index?.row)!)
self.tableDetails.deleteRows(at: [index!], with: .automatic)
tableDetails.reloadData()
if (tableView(tableDetails, numberOfRowsInSection: 0) == 0){
tableDetails.isHidden = true
}
if (tableDetails.isHidden == true){
self.loadingLabel.textColor = UIColor.gray
self.loadingLabel.textAlignment = NSTextAlignment.center
self.loadingLabel.text = "Your shopping cart is empty"
self.loadingLabel.frame = CGRect(x: 130, y: 320, width: 140, height: 30)
view.addSubview(loadingLabel)
}
}

Resources