Swift- Using Delegation to append data to TableView within CollectionView - ios

I have a CollectionViewController named yearOnethat looks something like this:
When you click the add button for each quarter cell it leads you to SearchPage:
I used delegation to append the data clicked on SearchPage to yearOne
The delegation seems to work and data seems to append as I tried the print(vc.data) but it doesn't seem to show up on the tableView when backed into yearOne viewcontroller through navigationController?.popViewController(animated: true) Am I doing something wrong?
import UIKit
class yearOne: UICollectionViewController, coursesDelegate {
let customCellIdentifier = "cellID"
let customCellIdentifier2 = "cellID2"
let customCellIdentifier3 = "cellID3"
let customCellIdentifier4 = "cellID4"
let quarters = [
customLabel (title: "Fall Quarter"),
customLabel (title: "Winter Quarter"),
customLabel (title: "Spring Quarter"),
customLabel (title: "Summer Quarter")
]
let vc = fallQuarterCell()
let vc2 = winterQuarterCell()
let vc3 = springQuarterCell()
let vc4 = summerQuarterCell()
func sendDataBackFall(data: String) {
vc.data.append(data)
print(vc.data)
//UserDefaults.standard.set(vc.data, forKey: "SavedArray")
}
func sendDataBackWinter(data2: String) {
vc2.data.append(data2)
}
func sendDataBackSpring(data3: String) {
vc3.data.append(data3)
}
func sendDataBackSummer(data4: String) {
vc4.data.append(data4)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
vc.tableView.reloadData()
collectionView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
self.collectionView!.register(fallQuarterCell.self, forCellWithReuseIdentifier: customCellIdentifier)//
self.collectionView!.register(winterQuarterCell.self, forCellWithReuseIdentifier: customCellIdentifier2)
self.collectionView!.register(springQuarterCell.self, forCellWithReuseIdentifier: customCellIdentifier3)
self.collectionView!.register(summerQuarterCell.self, forCellWithReuseIdentifier: customCellIdentifier4)
navigationItem.title = "Year One"
navigationController?.navigationBar.prefersLargeTitles = true
collectionView?.backgroundColor = .lightGray
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return quarters.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if (indexPath.row == 0){
let cell1 = collectionView.dequeueReusableCell(withReuseIdentifier: customCellIdentifier, for: indexPath) as! fallQuarterCell
cell1.layer.borderColor = UIColor.orange.cgColor
cell1.layer.borderWidth = 2
cell1.layer.cornerRadius = 5
cell1.quarters = self.quarters[0]
return cell1
}
else if (indexPath.row == 1){
let cell2 = collectionView.dequeueReusableCell(withReuseIdentifier: customCellIdentifier2, for: indexPath) as! winterQuarterCell
cell2.layer.borderColor = UIColor.blue.cgColor
cell2.layer.borderWidth = 2
cell2.layer.cornerRadius = 5
cell2.quarters = self.quarters[1]
return cell2
}
else if (indexPath.row == 2){
let cell3 = collectionView.dequeueReusableCell(withReuseIdentifier: customCellIdentifier3, for: indexPath) as! springQuarterCell
cell3.layer.borderColor = UIColor.green.cgColor
cell3.layer.borderWidth = 2
cell3.layer.cornerRadius = 5
cell3.quarters = self.quarters[2]
return cell3
}
else if (indexPath.row == 3){
let cell4 = collectionView.dequeueReusableCell(withReuseIdentifier: customCellIdentifier4, for: indexPath) as! summerQuarterCell
cell4.layer.borderColor = UIColor.red.cgColor
cell4.layer.cornerRadius = 5
cell4.quarters = self.quarters[3]
return cell4
}
else{
return UICollectionViewCell()
}
}
#objc func buttonAction(sender: UIButton!) {
switch sender.tag {
case 0:
let destination = SearchPage()
destination.delegate = self
destination.tag = sender.tag
navigationController?.pushViewController(destination, animated: true)
case 1:
let destination = SearchPage()
destination.delegate = self
destination.tag = sender.tag
navigationController?.pushViewController(destination, animated: true)
case 2:
let destination = SearchPage()
destination.delegate = self
destination.tag = sender.tag
navigationController?.pushViewController(destination, animated: true)
case 3:
let destination = SearchPage()
destination.delegate = self
destination.tag = sender.tag
navigationController?.pushViewController(destination, animated: true)
default:
let destination = SearchPage()
destination.delegate = self
destination.tag = sender.tag
navigationController?.pushViewController(destination, animated: true)
}
}
}
extension yearOne : UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = (view.frame.width - 30)
return CGSize(width: width, height: 200)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 8
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 1
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
UIEdgeInsets(top: 30, left: 10, bottom: 30, right: 10)
}
}
class fallQuarterCell: UICollectionViewCell, UITableViewDelegate, UITableViewDataSource {
//var data = UserDefaults.standard.object(forKey: "SavedArray") as? [String] ?? [String]()
var data : [String] = []
// func load(){
// if let loadeddata: [String] = UserDefaults.standard.object(forKey: "SavedArray") as? [String] {
// data = loadeddata
// tableView.reloadData()
// }
// }
let cellId = "coursesName"
let tableView:UITableView = {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.backgroundColor = UIColor.white
return tableView
}()
override init(frame: CGRect){
super.init(frame: frame)
addSubview(tableView)
setupView()
}
func setupView(){
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
tableView.delegate = self
tableView.dataSource = self
self.backgroundColor = UIColor.white
contentView.addSubview(quarterLabel)
contentView.addSubview(addButton)
quarterLabel.translatesAutoresizingMaskIntoConstraints = false
quarterLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10).isActive = true
quarterLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10).isActive = true
addButton.translatesAutoresizingMaskIntoConstraints = false
addButton.topAnchor.constraint(equalTo: quarterLabel.topAnchor, constant: -5).isActive = true
addButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10).isActive = true
addButton.heightAnchor.constraint(equalToConstant: 25).isActive = true
addButton.widthAnchor.constraint(equalToConstant: 25).isActive = true
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 35).isActive = true
tableView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 5).isActive = true
tableView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10).isActive = true
tableView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5).isActive = true
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
cell.textLabel?.text = data[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 40
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete) {
if (indexPath.row == 0){
data.remove(at: 0)
//UserDefaults.standard.set(data, forKey: "SavedArray")
self.tableView.reloadData()
}
else if (indexPath.row == 1){
data.remove(at: 1)
//UserDefaults.standard.set(data, forKey: "SavedArray")
self.tableView.reloadData()
}
else if (indexPath.row == 2){
data.remove(at: 2)
//UserDefaults.standard.set(data, forKey: "SavedArray")
self.tableView.reloadData()
}
else if (indexPath.row == 3){
data.remove(at: 3)
//UserDefaults.standard.set(data, forKey: "SavedArray")
self.tableView.reloadData()
}
else if (indexPath.row == 4){
data.remove(at: 4)
//UserDefaults.standard.set(data, forKey: "SavedArray")
self.tableView.reloadData()
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//add class information???
}
var quarters: customLabel? {
didSet {
guard let quarters = quarters else {return}
quarterLabel.text = quarters.title
}
}
let quarterLabel : UILabel = {
let label = UILabel()//frame: CGRect(x: 15, y: -75, width: 300, height: 50))
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.black
label.font = UIFont.boldSystemFont(ofSize: 16)
//label.textAlignment = .center
return label
}()
let addButton : UIButton = {
let button = UIButton()//frame: CGRect(x: 345, y: 10, width: 30, height: 30))
button.setImage(UIImage(named: "addicon"), for: .normal)
button.imageEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
button.tag = 0
button.addTarget(self, action: #selector(yearOne.buttonAction), for: .touchUpInside)
return button
}()
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I also have the
class winterQuarterCell: UICollectionViewCell, UITableViewDelegate, UITableViewDataSource
class springQuarterCell: UICollectionViewCell, UITableViewDelegate, UITableViewDataSource
class summerQuarterCell: UICollectionViewCell, UITableViewDelegate, UITableViewDataSource
similar to fallQuarterCell but excluded it.
import UIKit
protocol coursesDelegate {
func sendDataBackFall(data: String)
func sendDataBackWinter(data2: String)
func sendDataBackSpring(data3: String)
func sendDataBackSummer(data4: String)
}
class SearchPage: UITableViewController {
var delegate: coursesDelegate?
let cellId = "course"
var allCourses : NSArray = NSArray()
var filteredCourses = [String]()
var resultSearchController = UISearchController()
var tag: Int?
let vc = fallQuarterCell()
let vc2 = winterQuarterCell()
let vc3 = springQuarterCell()
let vc4 = summerQuarterCell()
override func viewDidLoad() {
super.viewDidLoad()
let courses : CourseList = CourseList()
allCourses = courses.coursesList
navigationItem.title = "Select Courses"
navigationController?.navigationBar.prefersLargeTitles = true
//let button1 = UIBarButtonItem(title: "Add Course", style: .plain, target: self, action: #selector(addTapped))
//self.navigationItem.rightBarButtonItem = button1
self.view.backgroundColor = .systemBackground
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
tableView.delegate = self
tableView.dataSource = self
self.tableView.tableFooterView = UIView()
resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.obscuresBackgroundDuringPresentation = false
controller.searchBar.placeholder = "Search Courses"
controller.searchBar.sizeToFit()
tableView.tableHeaderView = controller.searchBar
return controller
})()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (resultSearchController.isActive) {
return filteredCourses.count
}
else{
return allCourses.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
if (resultSearchController.isActive) {
cell.textLabel?.text = filteredCourses[indexPath.row]
return cell
}
else {
let courses = self.allCourses[indexPath.row]
cell.textLabel?.text = courses as? String
return cell
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
tableView.deselectRow(at: indexPath, animated: true)
if (tag == 0){
if (resultSearchController.isActive){
// vc.data.append(filteredCourses[indexPath.row])
// UserDefaults.standard.set(vc.data, forKey: "SavedArray")
// self.navigationController?.popViewController(animated: true)
let data = filteredCourses[indexPath.row]
delegate?.sendDataBackFall(data: data)
self.navigationController?.popViewController(animated: true)
}
else{
// vc.data.append(allCourses[indexPath.row] as! String)
// UserDefaults.standard.set(vc.data, forKey: "SavedArray")
// self.navigationController?.popViewController(animated: true)
let data = allCourses[indexPath.row] as! String
delegate?.sendDataBackFall(data: data)
self.navigationController?.popViewController(animated: true)
}
}
else if (tag == 1){
if (resultSearchController.isActive){
// vc2.data.append(filteredCourses[indexPath.row])
// UserDefaults.standard.set(vc2.data, forKey: "SavedArray2")
// self.navigationController?.popViewController(animated: true)
let data = filteredCourses[indexPath.row]
delegate?.sendDataBackWinter(data2: data)
self.navigationController?.popViewController(animated: true)
}
else{
// vc2.data.append(allCourses[indexPath.row] as! String)
// UserDefaults.standard.set(vc2.data, forKey: "SavedArray2")
// self.navigationController?.popViewController(animated: true)
let data = allCourses[indexPath.row] as! String
delegate?.sendDataBackWinter(data2: data)
self.navigationController?.popViewController(animated: true)
}
}
else if (tag == 2){
if (resultSearchController.isActive){
// vc3.data.append(filteredCourses[indexPath.row])
// UserDefaults.standard.set(vc3.data, forKey: "SavedArray3")
// self.navigationController?.popViewController(animated: true)
}
else{
// vc3.data.append(allCourses[indexPath.row] as! String)
// UserDefaults.standard.set(vc3.data, forKey: "SavedArray3")
// self.navigationController?.popViewController(animated: true)
}
}
else if (tag == 3){
if (resultSearchController.isActive){
// vc4.data.append(filteredCourses[indexPath.row])
// UserDefaults.standard.set(vc4.data, forKey: "SavedArray4")
// self.navigationController?.popViewController(animated: true)
}
else{
// vc4.data.append(allCourses[indexPath.row] as! String)
// UserDefaults.standard.set(vc4.data, forKey: "SavedArray4")
// self.navigationController?.popViewController(animated: true)
}
}
}
var isSearchBarEmpty: Bool {
return resultSearchController.searchBar.text?.isEmpty ?? true
}
}
extension SearchPage: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
filteredCourses.removeAll(keepingCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (allCourses as NSArray).filtered(using: searchPredicate)
filteredCourses = array as! [String]
self.tableView.reloadData()
}
}
I commented out the UsersDefault.standard.set.... because the data itself doesn't seem to refresh and display in the tableView atm

Check at your cellForRow method. I don't believe you set the text anywhere.

Related

Swift5 Get the "invalid number of rows in section" error when trying to delete a row in tableview (task manager with core data)

Trying to create the Task manager App with core data. I have an array with all done and current tasks. I divide it for two arrays to load a tableview (it shows either current tasks or tasks are done). When I'm trying to delete a task with swipe on the table, I receive "invalid number of rows in section" error. My code is below. Please advise how to fix it? My thanks in advance.
import UIKit
import Foundation
protocol TaskNotesDelegate: AnyObject {
func refreshTasks()
func deleteTask(with id: UUID)
}
class TasksViewController: UIViewController {
#IBOutlet weak var doneTaskSwitcher: UIBarButtonItem!
#IBOutlet weak var addTaskButton: UIBarButtonItem!
static let identifier = "TasksViewController"
let tableView: UITableView = {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
return tableView
}()
let idTasksCell = "idTasksCell"
//константа для реализации функции поиска через searc controllr
let searchController = UISearchController(searchResultsController: nil)
var allTasks: [Task] = []
var doneTasks: [Task] = []
var currentTask: [Task] = []
private var filteredTasks: [Task] = []
var currentSearch = ""
var doneTasksCheck = false
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.separatorStyle = .none
tableView.register(TaskTableViewCell.self, forCellReuseIdentifier: idTasksCell)
configureSearchBar()
setConstraints()
fetchTasksFromStorage()
for i in 0..<allTasks.count {
if allTasks[i].taskIsDone {
doneTasks.append(allTasks[i])
} else {
currentTask.append(allTasks[i])
}
}
for i in 0..<allTasks.count {
print("all \(allTasks[i].id)")
}
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.navigationBar.backgroundColor = .white
self.tabBarController?.tabBar.backgroundColor = .white
tableView.reloadData()
}
#IBAction func showDoneTasks(_ sender: UIBarButtonItem) {
if doneTasksCheck == false {
doneTasksCheck = true
doneTaskSwitcher.title = "current"
doneTasks = doneTasks.sorted { $0.taskDate > $1.taskDate }
tableView.reloadData()
addTaskButton.isEnabled = false
} else {
doneTasksCheck = false
doneTaskSwitcher.title = "is done"
tableView.reloadData()
addTaskButton.isEnabled = true
}
}
private func indexForTask(id: UUID, in list: [Task]) -> IndexPath {
let row = Int(list.firstIndex(where: { $0.id == id }) ?? 0)
return IndexPath(row: row, section: 0)
}
#IBAction func addTaskButton(_ sender: UIBarButtonItem) {
goToEditTask(createTask())
}
private func goToEditTask(_ task: Task) {
let controller = storyboard?.instantiateViewController(identifier: TaskDetailsViewController.identifier) as! TaskDetailsViewController
controller.task = task
controller.delegate = self
navigationController?.pushViewController(controller, animated: true)
}
// MARK:- Methods to implement
private func createTask() -> Task {
let task = CoreDataManager.shared.createTask()
// Update table
allTasks.insert(task, at: 0)
tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
return task
}
private func fetchTasksFromStorage() {
allTasks = CoreDataManager.shared.fetchTasks()
}
private func deleteTaskFromStorage(_ task: Task) {
deleteTaskSviped(with: task.id)
CoreDataManager.shared.deleteTask(task)
}
}
//UITableViewDelegate, UITableViewDataSource
extension TasksViewController: UITableViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if doneTasksCheck == true {
if currentSearch == "" {
filteredTasks = doneTasks.sorted { $0.taskDate > $1.taskDate }
}
} else {
if currentSearch == "" {
filteredTasks = currentTask.sorted { $0.taskDate < $1.taskDate }
}
}
return filteredTasks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: idTasksCell, for: indexPath) as! TaskTableViewCell
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd.MM.YYYY"
cell.taskLabel.text = filteredTasks[indexPath.row].task
cell.taskDateLabel.text = dateFormatter.string(from: filteredTasks[indexPath.row].taskDate)
if filteredTasks[indexPath.row].taskDate < Date().onlyDate! {
cell.taskDateLabel.textColor = .red
} else {
cell.taskDateLabel.textColor = .black
}
cell.taskTegLabel.text = filteredTasks[indexPath.row].taskTag
if filteredTasks[indexPath.row].taskReminder != nil {
cell.taskRemindTimeLabel.isHidden = false
dateFormatter.dateFormat = "HH:mm"
cell.taskRemindTimeLabel.text = dateFormatter.string(from: filteredTasks[indexPath.row].taskReminder!)
} else {
cell.taskRemindTimeLabel.isHidden = true
}
if filteredTasks[indexPath.row].taskNote == nil || filteredTasks[indexPath.row].taskNote == "" {
cell.taskNoteLabel.isHidden = true
} else {
cell.taskNoteLabel.isHidden = false
}
if filteredTasks[indexPath.row].taskIsDone == false {
cell.taskDoneButton.setBackgroundImage(UIImage(systemName: "circle"), for: .normal)
} else {
cell.taskDoneButton.setBackgroundImage(UIImage(systemName: "chevron.down.circle.fill"), for: .normal)
}
cell.layer.masksToBounds = true
cell.layer.cornerRadius = 15
cell.layer.borderWidth = 2
cell.layer.borderColor = UIColor.white.cgColor
cell.selectionStyle = .none
cell.cellTaskDelegate = self
cell.index = indexPath
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 75
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
goToEditTask(filteredTasks[indexPath.row])
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
deleteTaskFromStorage(filteredTasks[indexPath.row])
}
}
}
extension TasksViewController {
func setConstraints() {
view.addSubview(tableView)
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0)
])
}
}
// MARK:- TaskNotes Delegate
extension TasksViewController: TaskNotesDelegate {
func refreshTasks() {
allTasks = allTasks.sorted { $0.taskDate < $1.taskDate }
currentTask = currentTask.sorted { $0.taskDate < $1.taskDate }
tableView.reloadData()
}
func deleteTaskSviped(with id: UUID) {
let indexPath = indexForTask(id: id, in: filteredTasks)
filteredTasks.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
// just so that it doesn't come back when we search from the array
allTasks.remove(at: indexForTask(id: id, in: allTasks).row)
}
}

Presenting UICollectionView from a UIButton swift

first, I hope I don't break any rules or make it long but I'm really stuck and spent the whole day about that so I would love to get help with that :).
Okay so I'm having a popped up view controller using Panmodel 3rd party and I've got a UIButton inside of a UITableView custom cell, now I want to present my custom calendar when the user press the button, now I followed Ray tutorial(Link) on how to do a custom calendar and it still doesn't work for me, for some reason when I press the button it just shows a clear view + freezes my screen :|, I'm going to post the code of my tableView setup + the cell of the UIButton, not going to post the code of the calendar because it's long and I don't want to spam, if needed I will post that :) once again thanks for the help really spent the whole day about it and found myself with no solution, so here's my code:
Code: TableViewVC:
import UIKit
import PanModal
class FilterTableViewController: UITableViewController, PanModalPresentable {
var panScrollable: UIScrollView? {
return tableView
}
var albumsPickerIndexPath: IndexPath? // indexPath of the currently shown albums picker in tableview.
var datesCell = DatesCell()
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
// registerTableViewCells()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// tableView.frame = view.bounds
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
// MARK: - View Configurations
func setupTableView() {
tableView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
tableView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
tableView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
tableView.separatorStyle = .singleLine
tableView.isScrollEnabled = false
tableView.allowsSelection = true
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 600
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
}
func indexPathToInsertDatePicker(indexPath: IndexPath) -> IndexPath {
if let albumsPickerIndexPath = albumsPickerIndexPath, albumsPickerIndexPath.row < indexPath.row {
return indexPath
} else {
return IndexPath(row: indexPath.row + 1, section: indexPath.section)
}
}
// MARK: - UITableViewDataSource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// If datePicker is already present, we add one extra cell for that
if albumsPickerIndexPath != nil {
return 5 + 1
} else {
return 5
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.row {
case 0:
let byActivityCell = UINib(nibName: "byActivityCell",bundle: nil)
self.tableView.register(byActivityCell,forCellReuseIdentifier: "byActivityCell")
let activityCell = tableView.dequeueReusableCell(withIdentifier: "byActivityCell", for: indexPath) as! byActivityCell
activityCell.selectionStyle = .none
return activityCell
case 1:
let byTypeCell = UINib(nibName: "ByType",bundle: nil)
self.tableView.register(byTypeCell,forCellReuseIdentifier: "byTypeCell")
let typeCell = tableView.dequeueReusableCell(withIdentifier: "byTypeCell", for: indexPath) as! ByType
typeCell.selectionStyle = .none
return typeCell
case 2:
let byHashtagsCell = UINib(nibName: "ByHashtags",bundle: nil)
self.tableView.register(byHashtagsCell,forCellReuseIdentifier: "byHashtagsCell")
let hashtagsCell = tableView.dequeueReusableCell(withIdentifier: "byHashtagsCell", for: indexPath) as! ByHashtags
hashtagsCell.selectionStyle = .none
return hashtagsCell
case 3:
let byDatesCell = UINib(nibName: "DatesCell",bundle: nil)
self.tableView.register(byDatesCell,forCellReuseIdentifier: "byDatesCell")
let datesCell = tableView.dequeueReusableCell(withIdentifier: "byDatesCell", for: indexPath) as! DatesCell
datesCell.selectionStyle = .none
datesCell.datesTableViewCellDelegate = self
return datesCell
case 4:
let byAlbumCell = UINib(nibName: "AlbumCell",bundle: nil)
self.tableView.register(byAlbumCell,forCellReuseIdentifier: "byAlbumCell")
let albumCell = tableView.dequeueReusableCell(withIdentifier: "byAlbumCell", for: indexPath) as! AlbumCell
albumCell.configureCell(choosenAlbum: "Any")
albumCell.selectionStyle = .none
return albumCell
case 5:
let albumPickerCell = UINib(nibName: "AlbumsPickerTableViewCell", bundle: nil)
self.tableView.register(albumPickerCell, forCellReuseIdentifier: "albumPickerCell")
let albumsPicker = tableView.dequeueReusableCell(withIdentifier: "albumPickerCell", for: indexPath) as! AlbumsPickerTableViewCell
return albumsPicker
default:
return UITableViewCell()
}
}
// MARK: - footer Methods:
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
return getfooterView()
}
func getfooterView() -> UIView
{
let footerView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 400))
let applyFiltersBtn = UIButton(frame: CGRect(x: 0, y: 0, width: 380, height: 35))
applyFiltersBtn.center = footerView.center
applyFiltersBtn.layer.cornerRadius = 12
applyFiltersBtn.layer.masksToBounds = true
applyFiltersBtn.setTitle("Apply Filters", for: .normal)
applyFiltersBtn.backgroundColor = #colorLiteral(red: 0.1957295239, green: 0.6059523225, blue: 0.960457623, alpha: 1)
// doneButton.addTarget(self, action: #selector(hello(sender:)), for: .touchUpInside)
footerView.addSubview(applyFiltersBtn)
return footerView
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 10
}
// MARK: TableViewDelegate Methods:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false)
tableView.beginUpdates()
// 1 - We Delete the UIPicker when the user "Deselect" the row.
if let datePickerIndexPath = albumsPickerIndexPath, datePickerIndexPath.row - 1 == indexPath.row {
tableView.deleteRows(at: [datePickerIndexPath], with: .fade)
self.albumsPickerIndexPath = nil
} else {
// 2
// if let datePickerIndexPath = albumsPickerIndexPath {
// tableView.deleteRows(at: [datePickerIndexPath], with: .fade)
// }
albumsPickerIndexPath = indexPathToInsertDatePicker(indexPath: indexPath)
tableView.insertRows(at: [albumsPickerIndexPath!], with: .fade)
tableView.deselectRow(at: indexPath, animated: true)
}
tableView.endUpdates()
if indexPath.row == 4 {
let pickerController = CalendarPickerViewController(
baseDate: Date(),
selectedDateChanged: { [weak self] date in
guard let self = self else { return }
// self.item.date = date
self.tableView.reloadRows(at: [IndexPath(row: 3, section: 0)], with: .fade)
})
present(pickerController, animated: true, completion: nil)
}
}
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
if indexPath.row == 4 {
return indexPath
} else {
return nil
}
}
}
extension FilterTableViewController: DatesTableViewCellDelegate {
func didButtonFromPressed() {
print("Button From is Pressed")
let pickerController = CalendarPickerViewController(
baseDate: Date(),
selectedDateChanged: { [weak self] date in
guard let self = self else { return }
// self.item.date = date
self.tableView.reloadRows(at: [IndexPath(row: 3, section: 0)], with: .fade)
})
present(pickerController, animated: true, completion: nil)
}
func didButtonToPressed() {
print("Button To is Pressed")
//TODO: Present our custom calendar
let vcToDisplay = CalendarPickerViewController(baseDate: Date()) { (date) in
self.tableView.reloadRows(at: [IndexPath(row: 3, section: 0)], with: .fade)
}
self.present(vcToDisplay, animated: true, completion: nil)
}
}
custom cell code:
import UIKit
// MARK: - Class Protocols:
protocol DatesTableViewCellDelegate { // a delegate to tell when the user selected the button:
func didButtonFromPressed()
func didButtonToPressed()
}
class DatesCell: UITableViewCell {
#IBOutlet var fromDate: UIButton!
#IBOutlet var toDate: UIButton!
var datesTableViewCellDelegate: DatesTableViewCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
fromDate.layer.cornerRadius = 5
toDate.layer.cornerRadius = 5
fromDate.layer.borderWidth = 1
toDate.layer.borderWidth = 1
fromDate.layer.borderColor = #colorLiteral(red: 0.2005972862, green: 0.6100016236, blue: 0.9602670074, alpha: 1)
toDate.layer.borderColor = #colorLiteral(red: 0.2005972862, green: 0.6100016236, blue: 0.9602670074, alpha: 1)
fromDate.layer.masksToBounds = true
toDate.layer.masksToBounds = true
self.preservesSuperviewLayoutMargins = false
self.separatorInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15)
self.layoutMargins = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15)
}
// MARK: - UIButton Methods:
#IBAction func fromDateButtonIsPressed(_ sender: UIButton) {
datesTableViewCellDelegate?.didButtonFromPressed()
}
#IBAction func toDateButtonIsPressed(_ sender: UIButton) {
datesTableViewCellDelegate?.didButtonToPressed()
}
}
Found the problem, the problem was with the auto-layout, I changed the auto layout to be like this:
override func viewDidLoad() {
super.viewDidLoad()
collectionView.backgroundColor = .yellow
view.addSubview(dimmedBackgroundView)
view.addSubview(collectionView)
var constraints = [
dimmedBackgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
dimmedBackgroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
dimmedBackgroundView.topAnchor.constraint(equalTo: view.topAnchor),
dimmedBackgroundView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
]
constraints.append(contentsOf: [
//1
collectionView.leadingAnchor.constraint(
equalTo: view.readableContentGuide.leadingAnchor),
collectionView.trailingAnchor.constraint(
equalTo: view.readableContentGuide.trailingAnchor),
//2
collectionView.centerYAnchor.constraint(
equalTo: view.centerYAnchor,
constant: 10),
//3
collectionView.heightAnchor.constraint(
equalTo: view.heightAnchor,
multiplier: 0.5)
])
NSLayoutConstraint.activate(constraints)
// NSLayoutConstraint.activate([
// dimmedBackgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
// dimmedBackgroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
// dimmedBackgroundView.topAnchor.constraint(equalTo: view.topAnchor),
// dimmedBackgroundView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
// ])
}

Push UIViewController from UICollectionView programmatically

I know it's a dummy request but I'm stuck in it from yesterday (It's my first time to use programmatically method).
I have ViewController that contains UITableView, and the UITableView has UICollectionView init.
What I want to do, to push a new UIViewController when I click on UICollectionView. I tried using push from navigationController but it didn't work.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = RecipesTableViewDetails()
navigationController.show(vc, sender: nil)
}
CategoriesTableViewCellCollectionViewCell:
class CategoriesTableViewCellCollectionViewCell: UITableViewCell, UICollectionViewDelegateFlowLayout {
let navigationController = UINavigationController()
let categories: [String] = [
"Main course",
"Beef",
"Chicken",
"Seafood",
"Vegetarian",
"Breakfast",
"Side dish",
"Drink",
"Sauce",
"Soup",
"Snacks",
"Dessert"
]
let categoriesImages: [UIImage] = [
UIImage(named: "maincourse")!,
UIImage(named: "beef")!,
UIImage(named: "chicken")!,
UIImage(named: "seafood")!,
UIImage(named: "vegetarian")!,
UIImage(named: "breakfast")!,
UIImage(named: "sidedish")!,
UIImage(named: "drink")!,
UIImage(named: "sauce")!,
UIImage(named: "soup")!,
UIImage(named: "snacks")!,
UIImage(named: "dessert")!
]
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
layoutUI()
selectionStyle = .none
self.backgroundColor = .clear
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var containerView: UIView = {
let containerView = UIView()
containerView.backgroundColor = .clear
containerView.translatesAutoresizingMaskIntoConstraints = false
return containerView
}()
lazy var categoriesNameLabel: UILabel = {
let categoriesNameLabel = UILabel()
categoriesNameLabel.text = "Categories"
categoriesNameLabel.textColor = .customDarkGray()
categoriesNameLabel.textAlignment = .left
categoriesNameLabel.font = UIFont(name: "AvenirNext-DemiBold", size: 16)
categoriesNameLabel.translatesAutoresizingMaskIntoConstraints = false
return categoriesNameLabel
}()
lazy var seeAllCategoriesButton: UIButton = {
let seeAllCategoriesButton = UIButton()
seeAllCategoriesButton.setTitle("See all", for: .normal)
seeAllCategoriesButton.setTitleColor(.CustomGreen(), for: .normal)
seeAllCategoriesButton.titleLabel?.font = UIFont(name: "AvenirNext-Regular", size: 14)
seeAllCategoriesButton.translatesAutoresizingMaskIntoConstraints = false
seeAllCategoriesButton.addTarget(self, action: #selector(test), for: .touchUpInside)
return seeAllCategoriesButton
}()
#objc func test() {
print("Test worked")
}
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.backgroundColor = .clear
collectionView.showsHorizontalScrollIndicator = false
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(CategoriesCollectionViewCell.self, forCellWithReuseIdentifier: "CategoriesCollectionViewCell")
return collectionView
}()
func setupContainerViewConstraints() {
NSLayoutConstraint.activate([
containerView.topAnchor.constraint(equalTo: topAnchor, constant: 8),
containerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
containerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
containerView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0)
])
}
func setupCategoriesNameLabelConstraints() {
NSLayoutConstraint.activate([
categoriesNameLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
categoriesNameLabel.centerYAnchor.constraint(equalTo: seeAllCategoriesButton.centerYAnchor)
])
}
func setupSeeAllCategoriesButtonConstraints() {
NSLayoutConstraint.activate([
seeAllCategoriesButton.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
seeAllCategoriesButton.topAnchor.constraint(equalTo: containerView.topAnchor)
])
}
func setupCollectionViewConstraints() {
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: seeAllCategoriesButton.bottomAnchor, constant: 0),
collectionView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0),
collectionView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
collectionView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
])
}
func addSubviews() {
addSubview(containerView)
containerView.addSubview(categoriesNameLabel)
containerView.addSubview(seeAllCategoriesButton)
containerView.addSubview(collectionView)
}
func layoutUI() {
addSubviews()
setupCollectionViewConstraints()
setupContainerViewConstraints()
setupCategoriesNameLabelConstraints()
setupSeeAllCategoriesButtonConstraints()
}
}
extension CategoriesTableViewCellCollectionViewCell: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return categories.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CategoriesCollectionViewCell", for: indexPath) as! CategoriesCollectionViewCell
cell.categoriesImage.image = categoriesImages[indexPath.row]
cell.categoryName.text = categories[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = RecipesTableViewDetails()
navigationController.show(vc, sender: nil)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let w: CGFloat = self.frame.width * 0.4
let h: CGFloat = collectionView.frame.size.height - 6.0
return CGSize(width: w, height: h)
}
}
HomeView that contains UITableView:
class HomeView: UIView {
var recipes: Recipes?
var recipesDetails = [Recipe]()
let indicator = ActivityIndicator()
let categories = ["italian food", "chinese food", "korean food", "italian food", "chinese food", "korean food", "italian food", "chinese food", "korean food", "italian food", "chinese food", "korean food"]
override init( frame: CGRect) {
super.init(frame: frame)
layoutUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var foodTableView: UITableView = {
let foodTableView = UITableView()
foodTableView.translatesAutoresizingMaskIntoConstraints = false
foodTableView.backgroundColor = #colorLiteral(red: 0.9568627451, green: 0.9568627451, blue: 0.9568627451, alpha: 1)
foodTableView.delegate = self
foodTableView.dataSource = self
foodTableView.register(CategoriesTableViewCellCollectionViewCell.self, forCellReuseIdentifier: "CategoriesTableViewCellCollectionViewCell")
foodTableView.register(PopularRecipesTableViewCellCollectionViewCell.self, forCellReuseIdentifier: "PopularRecipesTableViewCellCollectionViewCell")
foodTableView.register(HomeTableViewCell.self, forCellReuseIdentifier: "HomeTableViewCell")
foodTableView.rowHeight = UITableView.automaticDimension
foodTableView.estimatedRowHeight = 100
foodTableView.showsVerticalScrollIndicator = false
foodTableView.separatorStyle = .none
return foodTableView
}()
func setupFoodTableView() {
NSLayoutConstraint.activate([
foodTableView.topAnchor.constraint(equalTo: topAnchor),
foodTableView.bottomAnchor.constraint(equalTo: bottomAnchor),
foodTableView.leadingAnchor.constraint(equalTo: leadingAnchor),
foodTableView.trailingAnchor.constraint(equalTo: trailingAnchor)
])
}
func addSubview() {
addSubview(foodTableView)
}
func layoutUI() {
indicator.setupIndicatorView(self, containerColor: .white, indicatorColor: .customDarkGray())
addSubview()
setupFoodTableView()
fetchData()
}
func fetchData() {
AF.request("apilink.com").responseJSON { (response) in
if let error = response.error {
print(error)
}
do {
if let data = response.data {
self.recipes = try JSONDecoder().decode(Recipes.self, from: data)
self.recipesDetails = self.recipes?.recipes ?? []
DispatchQueue.main.async {
self.foodTableView.reloadData()
}
}
} catch {
print(error)
}
self.indicator.hideIndicatorView(self)
}
}
}
extension HomeView: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 1
} else if section == 1 {
return 1
} else {
return recipesDetails.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "CategoriesTableViewCellCollectionViewCell", for: indexPath) as! CategoriesTableViewCellCollectionViewCell
cell.collectionView.reloadData()
return cell
} else if indexPath.section == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: "PopularRecipesTableViewCellCollectionViewCell", for: indexPath) as! PopularRecipesTableViewCellCollectionViewCell
cell.collectionView.reloadData()
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
let url = URL(string: recipesDetails[indexPath.row].image ?? "Error")
cell.foodImage.kf.setImage(with: url)
cell.foodTitle.text = recipesDetails[indexPath.row].title
if let readyInMin = recipesDetails[indexPath.row].readyInMinutes {
cell.cookingTimeInfoLabel.text = "\(readyInMin) Minutes"
}
if let pricePerServing = recipesDetails[indexPath.row].pricePerServing {
cell.priceInfoLabel.text = "$\(Int(pricePerServing))"
}
if let serving = recipesDetails[indexPath.row].servings {
cell.servesInfoLabel.text = "\(serving)"
}
return cell
}
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 2 {
return "Random recipes"
} else {
return ""
}
}
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
(view as! UITableViewHeaderFooterView).contentView.backgroundColor = #colorLiteral(red: 0.9568627451, green: 0.9568627451, blue: 0.9568627451, alpha: 1)
(view as! UITableViewHeaderFooterView).textLabel?.font = UIFont(name: "AvenirNext-DemiBold", size: 16)
(view as! UITableViewHeaderFooterView).textLabel?.textColor = .customDarkGray()
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 30.0
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 0 {
return 130
} else if indexPath.section == 1 {
return 180
} else {
return UITableView.automaticDimension
}
}
}
HomeViewController:
class HomeViewController: UIViewController {
let searchController = UISearchController(searchResultsController: nil)
let leftMenuNavigationController = SideMenuNavigationController(rootViewController: SideMenuTableViewController())
lazy var mainView: HomeView = {
let view = HomeView(frame: self.view.frame)
return view
}()
override func loadView() {
super.loadView()
view = mainView
}
override func viewDidLoad() {
super.viewDidLoad()
}
override var preferredStatusBarStyle: UIStatusBarStyle {
.lightContent
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.isNavigationBarHidden = false
setNeedsStatusBarAppearanceUpdate()
setupNavigationWithLargeTitle()
setupLeftSideMenu()
}
func setupLeftSideMenu() {
SideMenuManager.default.leftMenuNavigationController = leftMenuNavigationController
leftMenuNavigationController.leftSide = true
leftMenuNavigationController.statusBarEndAlpha = 0
leftMenuNavigationController.presentationStyle = .viewSlideOut
leftMenuNavigationController.allowPushOfSameClassTwice = false
leftMenuNavigationController.menuWidth = view.frame.width * (3/4)
leftMenuNavigationController.navigationBar.isHidden = true
}
}
extension HomeViewController: UISearchControllerDelegate, UISearchBarDelegate {
func setupNavigationWithLargeTitle() {
navigationController?.navigationBar.prefersLargeTitles = true
searchController.delegate = self
searchController.searchBar.delegate = self
searchController.searchBar.searchTextField.backgroundColor = .white
searchController.searchBar.searchTextField.textColor = .customDarkGray()
searchController.searchBar.searchTextField.font = UIFont(name: "AvenirNext-Regular", size: 14)
searchController.searchBar.tintColor = UIColor.CustomGreen()
self.navigationItem.searchController = searchController
self.title = "Home"
let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.configureWithOpaqueBackground()
navBarAppearance.titleTextAttributes = [.foregroundColor: UIColor.CustomGreen()]
navBarAppearance.largeTitleTextAttributes = [.foregroundColor: UIColor.CustomGreen(), .font: UIFont(name: "AvenirNext-Heavy", size: 36)!]
navigationController?.navigationBar.tintColor = .white
navigationController?.navigationBar.standardAppearance = navBarAppearance
navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance
navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "heart.fill"), style: .plain, target: self, action: #selector(saveButtonTapped))
navigationItem.rightBarButtonItem?.tintColor = UIColor.CustomGreen()
navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(named: "menu"), style: .plain, target: self, action: #selector(menuButtonTapped))
navigationItem.leftBarButtonItem?.tintColor = UIColor.CustomGreen()
}
#objc func saveButtonTapped() {
print("OK")
}
#objc func menuButtonTapped() {
self.present(leftMenuNavigationController, animated: true, completion: nil)
}
}
All I want to push the new ViewController.
So you should suppose to use a protocol for this !
Follow these steps
Initially you need to make a protocol in your CategoriesTableViewCellCollectionViewCell:
protocol RecipesDidselectActionDelegate: class {
func recipesSelectionAction(data: categories?, indexPath: IndexPath)
}
then add a property
weak var recipesDidselectActionDelegate: RecipesDidselectActionDelegate?
Then inside the didSelectItemAt
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
recipesDidselectActionDelegate?.recipesSelectionAction(data: categories[indexPath.item], indexPath: indexPath)
}
Likewise You need to implement a protocol in HomeView
protocol HomeViewDidSelectActionDelegate: class {
func recipesSelectionAction(data: categories?, indexPath: IndexPath)
}
then add a property
weak var homeViewDidSelectActionDelegate: HomeViewDidSelectActionDelegate?
Then you should make the following delegate in HomeView like below
extension HomeView: RecipesDidselectActionDelegate{
func recipesSelectionAction(data: categories?, indexPath: IndexPath) {
homeViewDidSelectActionDelegate?.recipesSelectionAction(data: data indexPath: indexPath)
}
}
and also Add delegate in your cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "CategoriesTableViewCellCollectionViewCell", for: indexPath) as! CategoriesTableViewCellCollectionViewCell
cell.recipesDidselectActionDelegate = self
cell.collectionView.reloadData()
return cell
}
....// other cells
}
Then at last in your HomeViewController, you need to add delegate
var mainView: HomeView = {
let view = HomeView(frame: self.view.frame)
view.homeViewDidSelectActionDelegate = self
return view
}()
then the delegate function in HomeViewController
extension HomeViewController: HomeViewDidSelectActionDelegate{
func recipesSelectionAction(data: categories?, indexPath: IndexPath) {
// Here you can push your destination View Controller
let vc = RecipesTableViewDetails()
navigationController.show(vc, sender: nil)
}
}
This should work for sure!
Add this line at collectionview didSelectItemAt function
navigationController?.pushViewController(viewController, animated: true)
and don’t forget to asign collectionView delegate
collectionView.delegate = self
User closer like Inside your collectionView add button now create button action in your collectionView Cell class.
class CategoriesCollectionViewCell : UICollectionViewCell {
var btnTap : (()->())?
#IBAction func btntapAction() {
btnTap?()
}
}
Now inside your cellForItemAt method
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
cell.btnTap = {
()in
let vc = RecipesTableViewDetails()
//You can also pass selected cell data like cell.categoryName.tex
self.navigationController?.pushViewController(vc, animated: true)
}
}
And last Remove DidSelect method
Your tableCell doesn't have a navigationController. You need to call this code in the ViewController.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = RecipesTableViewDetails()
self.navigationController?.pushViewController(vc, animated: true)
}
set the CollectionViewDelegate to self in your cellForRowAtIndexpath method
cell.collectionView.delegate = self.superclass as? HomeViewController
You can just change the extension declaration to the following
extension HomeViewController: UICollectionViewDelegate, UICollectionViewDataSource
1). Need to call collectionView.delegate = self on viewDidLoad method.
2). Then,
In selectItemAtRowmethod declare ViewControllerto navigate to:-
let storyBoard = UIStoryboard(name: <Name Of your StoryBoard>, bundle: nil)
let viewController = storyBoard.instantiateViewController(withIdentifier: <Controller Identifier Name>) as! <ControllerName>
self.navigationController?.pushViewController(viewController, animated: true)
// Now you can push "anyController" into navigationController just you need to check
let storyBoard = UIStoryboard(name: <Name Of your StoryBoard>, bundle: nil)
let viewController = storyBoard.instantiateViewController(withIdentifier: <Controller Identifier Name>) as! <ControllerName>
self.navigationController != nil{
self.navigationController?.pushViewController(viewController, animated: true)
}else{
// Add New NavigationController here
}

UICollectionView disappear when refreshing after pop back

I have a problem when pull-to-refresh with UICollectionView. The refreshing control work well but when I click on a cell, push another view controller, and pop back, try refreshing, UICollectionView is disappeared( I don't use storyboard).
Screen record video:
https://www.youtube.com/watch?v=3bsaqqv6x_8
Here is my code:
AccountController
class AccountController: UICollectionViewController, UICollectionViewDelegateFlowLayout{
var menuData: [Any]? {
didSet{
self.collectionView?.reloadData()
}
}
let collectionLayout = UICollectionViewFlowLayout()
let refreshControl = UIRefreshControl()
override func viewDidLoad() {
...
setupCollectionView()
self.collectionView?.isHidden = false
refreshControl.addTarget(self, action: #selector(refresh), for: .valueChanged)
collectionView!.addSubview(refreshControl)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
super.collectionViewLayout.invalidateLayout()
}
func setupCollectionView(){
self.collectionView?.isHidden = true
collectionView?.backgroundColor = UIColor(white: 0.95, alpha: 1)
collectionView?.alwaysBounceVertical = true
collectionView?.register(MenuCell.self, forCellWithReuseIdentifier: cellId)
collectionView?.register(MenuSupplementary.self, forCellWithReuseIdentifier: supplementaryId)
collectionLayout.minimumLineSpacing = 0
collectionView?.collectionViewLayout = collectionLayout
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let count = menuData?.count {
return count
}
return 0
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let dt = menuData?[indexPath.item] {
if dt is MenuCellData{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! MenuCell
cell.data = dt as? MenuCellData
return cell
}
if dt is MenuSupplementaryData {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: supplementaryId, for: indexPath) as! MenuSupplementary
cell.data = dt as? MenuSupplementaryData
return cell
}
}
return UICollectionViewCell()
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize.init(width: view.frame.width, height: 45)
}
#objc func refresh(sender:UIRefreshControl){
let api = AccountApi(jwt: appDelegate.userDefault.string(forKey: "jwt"))
api.getMenuData { (data) in
var dt: [Any] = []
if data.isEmpty { return }
for d in data as! [MenuData] {
if d.type == "supplementary" {
dt.append(MenuSupplementaryData( d.text ))
}
if d.type == "cell" {
dt.append(MenuCellData(d.icon!, d.text, isTheLastItem: d.isTheLastItem ?? false, url: d.url, httpMethod: d.httpMethod ))
}
}
if !dt.isEmpty {
self.menuData = dt
}
sender.endRefreshing()
}
}
}
TabBarController
class TabBarController: UITabBarController, UITabBarControllerDelegate {
...
private func setUpNavControllerFrom(viewController: UIViewController, title: String, imageName: String)-> UINavigationController{
let navController = UINavigationController(rootViewController: viewController)
navController.tabBarItem.title = title
navController.tabBarItem.image = UIImage(named: imageName)
let imageView = UIImageView.init(frame: CGRect())
imageView.image = UIImage(named: "logo")
imageView.contentMode = .scaleAspectFit
imageView.heightAnchor.constraint(equalToConstant: 20).isActive = true // Set logo height
viewController.navigationItem.titleView = imageView
navController.navigationBar.barTintColor = themeColor
return navController
}
// Set up color, logo, font ...
private func setupViewControllers(){
tabBar.tintColor = UIColor.white
tabBar.barTintColor = themeColor
let accountVC = AccountController(collectionViewLayout: UICollectionViewFlowLayout())
let accNavController = setUpNavControllerFrom(viewController: accountVC, title: "Account", imageName: "account-icon")
...
}
}
Additional info:
Refreshing without pop back: numberOfItemsInSection, cellForItemAt, sizeForItemAt of delegate is invoked when call collectionView?.reloadData().
But, when refreshing after pop back, cellForItemAt, sizeForItemAt of delegate is not invoked when call collectionView?.reloadData().
Thanks for help!
Thanks for trying help me!
I finally find out my mistake.
When handling menu is selected, I create new controller with current collectionViewLayout, and I think this current layout is changed when pop back, make menu is disappeared.
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let dt = menuData?[indexPath.item]{
if dt is MenuCellData {
let d = dt as! MenuCellData
// let controller = AccountMenuController(collectionViewLayout: collectionView.collectionViewLayout)
let controller = AccountMenuController(collectionViewLayout: UICollectionViewLayout())
controller.menuData = d
navigationController?.pushViewController(controller, animated: true)
}
}
}

Inset a new section in UICollectionView with new type of Cell

I am working on a chat app. I am using UICollectionView to display text and different type of text. I have 3 type of cells. In the first section, I am loading the first type of cell. in second, second type and in third, third type of cell loading. I have a button text field. When i click send button in text field i want to create new section of type first cell. I don't know how to create. Please help :)
Here is my Code:
// ChatLogController.swift
// Blubyn
//
// Created by JOGENDRA on 02/02/18.
// Copyright © 2018 Jogendra Singh. All rights reserved.
//
import UIKit
fileprivate enum DefaultConstants {
static let sendButtonImageName: String = "send-icon"
static let voiceButtonImageName: String = "microphone"
}
enum Cells {
case chatCell
case oneWayFlight
case twoWayFlight
}
class ChatLogController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
fileprivate var sideBar = SideBar()
lazy var inputTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "Send a message..."
textField.delegate = self
return textField
}()
let textView: UIView = {
let textFieldview = UIView()
textFieldview.backgroundColor = UIColor.white
textFieldview.translatesAutoresizingMaskIntoConstraints = false
return textFieldview
}()
let cellId = "cellId"
let oneWayCellId = "oneWayFlightCell"
let twoWayCellId = "twoWayFlightCell"
var numberOfSections: Int = 0
var numberOfItemsInASection: Int = 0
var cellType: Cells?
var keyboardHeight: CGFloat = 0.0
var textViewBottomAnchor: NSLayoutConstraint?
var userMessages: [String] = ["Hey there!", "I want to book flight for Pokhara, Nepal", "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s", "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.", "Book a hotel for me near lakeside", "book returning flight"]
var systemMessages: [String] = ["Hello Jogendra!", "Pluto at your service - your guide and concierge to help you with the planning ticket booking and finding new and exotic places.", "Go on! Try me out. Won't take long. I promise."]
lazy var messages: [String] = systemMessages + userMessages
override func viewDidLoad() {
super.viewDidLoad()
setupInputComponents()
sideBarSetup()
collectionView?.contentInset = UIEdgeInsets(top: 8.0, left: 0, bottom: 52.0, right: 0)
collectionView?.backgroundColor = UIColor.chatbackgroundColor
// Register Chat Cell
collectionView?.register(ChatMessagesCell.self, forCellWithReuseIdentifier: cellId)
// Regsiter One Way Flight Cell
let oneWayFlightCellNib = UINib(nibName: "OneWayFlightViewCell", bundle: nil)
collectionView?.register(oneWayFlightCellNib, forCellWithReuseIdentifier: oneWayCellId)
// Register Two Way Flight Cell
let twoWayFlightCellNib = UINib(nibName: "TwoWayFlightViewCell", bundle: nil)
collectionView?.register(twoWayFlightCellNib, forCellWithReuseIdentifier: twoWayCellId)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
// Register Notification, To know When Key Board Appear.
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: Notification.Name.UIKeyboardWillShow, object: nil)
// Register Notification, To know When Key Board Hides.
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
// De register the notifications
NotificationCenter.default.removeObserver(self)
}
fileprivate func setupInputComponents() {
// Text View setups
view.addSubview(textView)
textView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
textView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
textView.heightAnchor.constraint(equalToConstant: 44.0).isActive = true
textViewBottomAnchor = textView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
textViewBottomAnchor?.isActive = true
let voiceButton = UIButton(type: .system)
let voiceButtonImage = UIImage(named: DefaultConstants.voiceButtonImageName)
voiceButton.setImage(voiceButtonImage, for: .normal)
textView.addSubview(voiceButton)
voiceButton.translatesAutoresizingMaskIntoConstraints = false
voiceButton.topAnchor.constraint(equalTo: textView.topAnchor).isActive = true
voiceButton.trailingAnchor.constraint(equalTo: textView.trailingAnchor).isActive = true
voiceButton.bottomAnchor.constraint(equalTo: textView.bottomAnchor).isActive = true
voiceButton.widthAnchor.constraint(equalToConstant: 44.0).isActive = true
let sendButton = UIButton(type: .system)
let sendButtonImage = UIImage(named: DefaultConstants.sendButtonImageName)
sendButton.setImage(sendButtonImage, for: .normal)
textView.addSubview(sendButton)
sendButton.addTarget(self, action: #selector(didTapSend), for: .touchUpInside)
sendButton.translatesAutoresizingMaskIntoConstraints = false
sendButton.topAnchor.constraint(equalTo: textView.topAnchor).isActive = true
sendButton.trailingAnchor.constraint(equalTo: voiceButton.leadingAnchor).isActive = true
sendButton.bottomAnchor.constraint(equalTo: textView.bottomAnchor).isActive = true
sendButton.widthAnchor.constraint(equalToConstant: 44.0).isActive = true
textView.addSubview(inputTextField)
inputTextField.translatesAutoresizingMaskIntoConstraints = false
inputTextField.leadingAnchor.constraint(equalTo: textView.leadingAnchor, constant: 4.0).isActive = true
inputTextField.trailingAnchor.constraint(equalTo: sendButton.leadingAnchor, constant: -4.0).isActive = true
inputTextField.topAnchor.constraint(equalTo: textView.topAnchor).isActive = true
inputTextField.bottomAnchor.constraint(equalTo: textView.safeAreaLayoutGuide.bottomAnchor).isActive = true
let separator = UIView()
separator.backgroundColor = UIColor.black.withAlphaComponent(0.4)
textView.addSubview(separator)
separator.translatesAutoresizingMaskIntoConstraints = false
separator.leadingAnchor.constraint(equalTo: textView.leadingAnchor).isActive = true
separator.trailingAnchor.constraint(equalTo: textView.trailingAnchor).isActive = true
separator.topAnchor.constraint(equalTo: textView.topAnchor).isActive = true
separator.heightAnchor.constraint(equalToConstant: 1.0).isActive = true
}
// MARK: - Keyboard Events
#objc func keyboardWillShow(_ notification: Notification) {
if let keyboardFrame: NSValue = notification.userInfo![UIKeyboardFrameEndUserInfoKey] as? NSValue {
let keyboardRectangle = keyboardFrame.cgRectValue
keyboardHeight = keyboardRectangle.height
}
let keyboardDuration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? Double
textViewBottomAnchor?.constant = -keyboardHeight
UIView.animate(withDuration: keyboardDuration!, animations: {
self.view.layoutIfNeeded()
})
}
#objc func keyboardWillHide(_ notification: Notification) {
let keyboardDuration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? Double
textViewBottomAnchor?.constant = 0
UIView.animate(withDuration: keyboardDuration!, animations: {
self.view.layoutIfNeeded()
})
}
// MARK: - Collection View Methods
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
switch section {
case 0:
return messages.count
case 1:
numberOfItemsInASection = 5
return numberOfItemsInASection
case 2:
return 5
default:
numberOfItemsInASection = 5
return numberOfItemsInASection
}
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! ChatMessagesCell
let message = messages[indexPath.item]
cell.chatTextView.text = message
// Align chat cell according
if userMessages.contains(message) {
cell.bubbleView.backgroundColor = UIColor.white
cell.chatTextView.textColor = UIColor.black
cell.bubbleViewRightAnchor?.isActive = true
cell.bubbleViewLeftAnchor?.isActive = false
} else {
cell.bubbleView.backgroundColor = UIColor.chatThemeColor
cell.chatTextView.textColor = UIColor.white
cell.bubbleViewRightAnchor?.isActive = false
cell.bubbleViewLeftAnchor?.isActive = true
}
//Modify the width accordingly
cell.bubbleWidthAnchor?.constant = estimateFrameForText(text: message).width + 32.0
if indexPath.section == 0 {
cellType = Cells.chatCell
return cell
} else if indexPath.section == 1 {
let oneWayFlightCell = collectionView.dequeueReusableCell(withReuseIdentifier: oneWayCellId, for: indexPath) as! OneWayFlightViewCell
cellType = Cells.oneWayFlight
return oneWayFlightCell
} else {
let twoWayFlightCell = collectionView.dequeueReusableCell(withReuseIdentifier: twoWayCellId, for: indexPath) as! TwoWayFlightViewCell
cellType = Cells.twoWayFlight
return twoWayFlightCell
}
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
numberOfSections = 3
return numberOfSections
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
var cellHeight: CGFloat = 80.0
let text = messages[indexPath.item]
cellHeight = estimateFrameForText(text: text).height + 20.0
if indexPath.section == 1 {
cellHeight = 112
} else if indexPath.section == 2 {
cellHeight = 201
}
return CGSize(width: collectionView.frame.width, height: cellHeight)
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
inputTextField.resignFirstResponder()
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
//{top, left, bottom, right}
return UIEdgeInsetsMake(10, 0, 10, 0)
}
private func estimateFrameForText(text: String) -> CGRect {
let size = CGSize(width: 300.0, height: 1000)
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
return NSString(string: text).boundingRect(with: size, options: options, attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 17.0)], context: nil)
}
#objc fileprivate func didTapSend() {
if let enteredText = inputTextField.text, !enteredText.isEmpty {
messages.append(enteredText)
userMessages.append(enteredText)
}
collectionView?.reloadData()
inputTextField.text = nil
}
fileprivate func sideBarSetup() {
sideBar = SideBar(sourceView: self.view, menuItems: ["Chat", "Hot Deals", "My Trips", "Experiences", "Settings", "Profile"])
sideBar.delegate = self
}
#IBAction func didTapMenu(_ sender: Any) {
sideBar.showSideBar(!sideBar.isSideBarOpen)
}
#IBAction func didTapHelp(_ sender: Any) {
}
}
extension ChatLogController: SideBarDelegate {
func SideBarDidSelectButtonAtIndex(_ index: Int) {
switch index {
case 0:
sideBar.showSideBar(!sideBar.isSideBarOpen)
case 1:
sideBar.showSideBar(!sideBar.isSideBarOpen)
case 2:
sideBar.showSideBar(!sideBar.isSideBarOpen)
case 3:
sideBar.showSideBar(!sideBar.isSideBarOpen)
case 4:
let settingStoryboard = UIStoryboard(name: "Main", bundle: nil)
let settingsViewController = settingStoryboard.instantiateViewController(withIdentifier: "settingvc")
self.navigationController?.pushViewController(settingsViewController, animated: true)
case 5:
let profileStoryboard = UIStoryboard(name: "Main", bundle: nil)
let profileViewController = profileStoryboard.instantiateViewController(withIdentifier: "profilevc")
self.navigationController?.pushViewController(profileViewController, animated: true)
default:
break
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
inputTextField.resignFirstResponder()
inputTextField.endEditing(true)
}
}
extension ChatLogController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
inputTextField.endEditing(true)
return true
}
}
Designs:
See Screens Section of [this link][1].
Achieved Designs:
[![screenshot1][2]][2]
[![screenshot2][3]][3]
[![screenshot3][4]][4]
[![screenshot4][5]][5]
[1]: https://blubyn.com/
[2]: https://i.stack.imgur.com/S9ZLX.png
[3]: https://i.stack.imgur.com/wLvAu.png
[4]: https://i.stack.imgur.com/nBKbH.png
[5]: https://i.stack.imgur.com/Sota4.png

Resources