FSCalendar searchbar disappears - tableview

I'm using FSCalendar in my app. I placed the searchbar, which works properly with tableview below the FSCalendar. I select any row in the table and go to the next viewcontroller to edit selected row. As soon as I returned to the main VC (with FSCalndar), my searchbar disappeared. I have the same searchbar with tableview in other VC (without FSCalendar), and it works properly every time. My code for FSCalendar, tableview and searchbar are below.
Does anybody have any suggestions? Probably somebody faced with similar issue before?
Thanks in advance!
class CalendarViewController: UIViewController {
var calendarHeightConstraint: NSLayoutConstraint!
private var calendar: FSCalendar = {
let calendar = FSCalendar()
calendar.translatesAutoresizingMaskIntoConstraints = false
return calendar
}()
let tableView: UITableView = {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
return tableView
}()
let searchController = UISearchController(searchResultsController: nil)
var currentSearch = ""
override func viewDidLoad() {
super.viewDidLoad()
calendar.delegate = self
calendar.dataSource = self
calendar.scope = .week
calendar.firstWeekday = 2
tableView.delegate = self
tableView.dataSource = self
tableView.separatorStyle = .none
tableView.register(CalendarTableViewCell.self, forCellReuseIdentifier: idCalendarCell)
configureSearchBar()
setConstraints()
}
private func configureSearchBar() {
searchController.searchBar.delegate = self
searchController.delegate = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "search for events"
navigationItem.searchController = searchController
definesPresentationContext = true
}
extension CalendarViewController: UITableViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredEvents.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: idCalendarCell, for: indexPath) as! CalendarTableViewCell
.................. (code)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 75
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
goToEditEvent(filteredEvents[indexPath.row])
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
.......... (code)
}
}
}
extension CalendarViewController: FSCalendarDataSource, FSCalendarDelegate {
func calendar(_ calendar: FSCalendar, boundingRectWillChange bounds: CGRect, animated: Bool) {
calendarHeightConstraint.constant = bounds.height
view.layoutIfNeeded()
}
func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
selectedDate = date
refreshEvent()
}
func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int {
for i in 0..<allEvents.count {
if date.onlyDate == allEvents[i].eventDate.onlyDate {
return 1
}
}
return 0
}
}
extension CalendarViewController {
func setConstraints() {
view.addSubview(calendar)
calendarHeightConstraint = NSLayoutConstraint(item: calendar, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 300)
calendar.addConstraint(calendarHeightConstraint)
NSLayoutConstraint.activate([
calendar.topAnchor.constraint(equalTo: view.topAnchor, constant: 130),
calendar.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0),
calendar.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0),
])
view.addSubview(showHideButton)
NSLayoutConstraint.activate([
showHideButton.topAnchor.constraint(equalTo: calendar.bottomAnchor, constant: 0),
showHideButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 15),
showHideButton.widthAnchor.constraint(equalToConstant: 200),
showHideButton.heightAnchor.constraint(equalToConstant: 20)
])
view.addSubview(tableView)
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: showHideButton.bottomAnchor, constant: 10),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0)
])
}
}
'''

Related

How to fix: Application tried to present a nil modal view controller on target

I tried using the simulator and real phone, I still get the same error.
ERROR: Thread 1: Exception: Application tried to present a nil modal view controller on target
Here's the complete ViewController. Please see the codes below.
import UIKit
import MessageUI
class CellClass: UITableViewCell {
}
class ContactUsViewController: UIViewController, MFMailComposeViewControllerDelegate {
#IBOutlet weak var messageField: UITextField!
#IBOutlet weak var subjectField: UIButton!
let transparentView = UIView()
let tableView = UITableView()
var selectedButton = UIButton()
var dataSource = [String]()
var MFMailComposeResultCancelled = MFMailComposeResult(rawValue: 0)
var MFMailComposeResultFailed = MFMailComposeResult(rawValue: 0)
var MFMailComposeResultSaved = MFMailComposeResult(rawValue: 0)
var MFMailComposeResultSent = MFMailComposeResult(rawValue: 0)
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.register(CellClass.self, forCellReuseIdentifier: "Cell")
}
func addTransparentView(frames: CGRect) {
let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
transparentView.frame = window?.frame ?? self.view.frame
self.view.addSubview(transparentView)
tableView.frame = CGRect(x: frames.origin.x, y: frames.origin.y + frames.height, width: frames.width, height: 0)
self.view.addSubview(tableView)
tableView.layer.cornerRadius = 5
transparentView.backgroundColor = UIColor.black.withAlphaComponent(0.9)
tableView.reloadData()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(removeTransparentView))
transparentView.addGestureRecognizer(tapGesture)
transparentView.alpha = 0
UIView.animate(withDuration: 0.4, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: {self.transparentView.alpha = 0.5
self.tableView.frame = CGRect(x: frames.origin.x, y: frames.origin.y + frames.height + 5, width: frames.width, height: CGFloat(self.dataSource.count * 50))
}, completion: nil)
}
#IBAction func subjectList(_ sender: Any) {
dataSource = [" General Inquiries", " Purchase STL File"," Purchase an Eternity Band Ring", " Inquire about Measurements"]
selectedButton = subjectField
addTransparentView(frames: subjectField.frame)
}
#objc func removeTransparentView() {
let frames = selectedButton.frame
UIView.animate(withDuration: 0.4, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: { self.transparentView.alpha = 0
self.tableView.frame = CGRect(x: frames.origin.x, y: frames.origin.y + frames.height, width: frames.width, height: 0)
}, completion: nil)
}
#IBAction func sendTapped(_ sender: Any) {
let toCS = ["info#ezbandcalculator.com"]
let mc: MFMailComposeViewController = MFMailComposeViewController()
mc.mailComposeDelegate = self
mc.setToRecipients(toCS)
mc.setSubject(selectedButton.currentTitle!)
mc.setMessageBody("Message: \(String(describing: messageField?.text))", isHTML: false)
self.present(mc, animated: true, completion: nil)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
switch result.rawValue {
case MFMailComposeResultCancelled!.rawValue:
print ("Cancelled")
case MFMailComposeResultFailed!.rawValue:
print ("Failed")
case MFMailComposeResultSaved!.rawValue:
print ("Saved")
case MFMailComposeResultSent!.rawValue:
print ("Sent")
default:
break
}
controller.dismiss(animated: true, completion: nil)
}
#IBAction func dismissKeyboard(_ sender: Any) {
self.resignFirstResponder()
}
}
extension ContactUsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = dataSource[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedButton.setTitle(dataSource[indexPath.row], for: .normal)
removeTransparentView()
}
}
You may want to check whether your current device can send mail or not before presenting.
if MFMailComposeViewController.canSendMail() {
// present
}
Refer: https://developer.apple.com/documentation/messageui/mfmailcomposeviewcontroller/1616879-cansendmail

tableView didset does not reload

I am trying the to reload the tableview whenever the didset is countRow is called. It has never worked.
I have tried guard and if let in the didset, it has never work out.
I realised it is calling another object of tableview. it is very weird,
import UIKit
class ViewController:UIViewController, UITableViewDelegate, UITableViewDataSource {
// Global Variable
var tableView: UITableView!
var tableView2 : UITableView!
var countRow : Int = 0 {
didSet{
//if let tableview == tableview
guard tableView2 == tableView else
{
tableView2 = UITableView(frame: self.view.bounds)
tableView2.delegate = self
tableView2.dataSource = self
self.tableView2.reloadData()
self.tableView.reloadData()
print ("3countRow: \(countRow)")
return
}
self.tableView2.reloadData()
self.tableView.reloadData()
print ("2countRow: \(countRow)")
}
}
override func viewDidLoad() {
super.viewDidLoad()
tableView = UITableView(frame: self.view.bounds)
tableView.delegate = self
tableView.dataSource = self
self.view.addSubview(tableView)
tableView.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
tableView.register(TableViewCell.self, forCellReuseIdentifier: "TableViewCell")
tableView.register(AppCell.self, forCellReuseIdentifier: "NormalCell")
tableView.register(appDays.self, forCellReuseIdentifier: "textField")
tableView.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print (countRow)
return 5 + countRow
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 80
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0
{
let cell = tableView.dequeueReusableCell(withIdentifier: "NormalCell", for: indexPath) as! AppCell
cell.backgroundColor = UIColor.groupTableViewBackground
return cell
}
if indexPath.row == 1 || indexPath.row == 2
{
var cell: TableViewCell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell
cell.backgroundColor = UIColor.groupTableViewBackground
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: "textField", for: indexPath) as! appDays
cell.backgroundColor = UIColor.groupTableViewBackground
return cell
}
}
countrow will be printed in the program
You never set countRow to a value. You only add 5 + countRow which does not mean you set 5 in countRow.
didSet is called immediately after the new value is stored. Swift official documentation

UISwitch added to a tableView is selected when scrolling

I have embed a UISwitch on a UITableView cell.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
if self.users.count > 0 {
let eachPost = self.users[indexPath.row]
let postDate = (eachPost.date as? String) ?? ""
let postTitle = (eachPost.title as? String) ?? ""
cell.detailTextLabel?.text = postDate
cell.textLabel?.text = postTitle
}
if cell.accessoryView == nil{
let switchView : UISwitch = UISwitch(frame: .zero)
switchView.setOn(false, animated: true)
cell.accessoryView = switchView
}
return cell
}
My table has 30 rows. When I select a switch on the visible cells and then scroll down, on the cells from the bottom of the list the switch is selected by default. What can I do to have correct selections for my list?
My solution without creating a custom class:
class MyTableViewController: UITableViewController {
var users: [[String: Any]] = [[String: Any]]()
// This array is used for storring the state for switch from each cell. Otherwise when the cell is reused the state is displayed incorrectly
var switchArray = [Bool]()
override func viewDidLoad() {
super.viewDidLoad()
self.users = result
for _ in 0 ..< self.users.count {
self.switchArray.append(false)
}
self.tableView?.reloadData()
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let eachPost = self.users[indexPath.row]
let postTitle = (eachPost.title as? String) ?? ""
cell.textLabel?.text = postTitle
let switchView : UISwitch = UISwitch(frame: .zero)
switchView.isOn = self.switchArray[indexPath.row]
cell.accessoryView = switchView
return cell}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
self.switchArray[indexPath.row] = true
let switchView : UISwitch = (tableView.cellForRow(at: indexPath)?.accessoryView) as! UISwitch;
switchView.isOn = true
}
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
self.switchArray[indexPath.row] = false
let switchView : UISwitch = (tableView.cellForRow(at: indexPath)?.accessoryView) as! UISwitch;
switchView.isOn = false
}
Note: Multiple selection has to be enabled

Did Select method in Table View not Working in Swift

I'm using two table view in one view controller to populate my different data. All the other delegates methods are working fine but didselect method is not performing an action neither its printing any value in console. I hae checked through story board also all the things looks fine and i have set its delegate and datasources methods also but still it isn't working. Searched a lot from net but still unsuccessful in it. My code is this,
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.isNavigationBarHidden = true
self.searchBar.delegate = self
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(LoginViewController.dismissKeyboard))
view.addGestureRecognizer(tap)
dishDetailTableView.delegate = self
dishDetailTableView.dataSource = self
self.addOnTableView.allowsMultipleSelection = true
addOnTableView.delegate = self
addOnTableView.dataSource = self
dishDetailTableView.reloadData()
sectionArray = [String(MenuService.instance.subCategoryModelInstance[0].sub_categoryName)]
print(sectionArray)
cancelBtn.cornerButton()
cancelBtn.layer.borderColor = #colorLiteral(red: 1, green: 0, blue: 0.03742904276, alpha: 1)
cancelBtn.layer.borderWidth = 1.5
nextBtn.layer.borderColor = #colorLiteral(red: 0.09759091236, green: 0.5779017098, blue: 1, alpha: 1)
nextBtn.layer.borderWidth = 1.5
nextBtn.cornerButton()
menuTypeLbl.layer.cornerRadius = 5
addOnView.borderView(view: addOnView)
addOnView.isHidden = true
shadowView.isHidden = true
}
extension DishDetailViewController : UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
var count:Int?
if tableView == dishDetailTableView {
count = MenuService.instance.subCategoryItemModelInstance.count
}
if (tableView == addOnTableView) {
count = AddOnService.instance.AddOnModelInstance.count
}
return count!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var cell:UITableViewCell?
if tableView == dishDetailTableView{
let cell = tableView.dequeueReusableCell(withIdentifier: "menuCell", for: indexPath) as! MenuDetailTableViewCell
cell.titleLbl?.text = MenuService.instance.subCategoryItemModelInstance[indexPath.row].itemName
cell.descriptionLbl?.text = MenuService.instance.subCategoryItemModelInstance[indexPath.row].itemdescription
cell.priceLbl?.text = MenuService.instance.subCategoryItemModelInstance[indexPath.row].itemprice
cell.backgroundColor = UIColor.clear
cell.delegate = self
cell.selectionStyle = .none
return cell
}
if tableView == addOnTableView{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! AddOnTableViewCell
cell.addOnLbl.text = AddOnService.instance.AddOnModelInstance[indexPath.row].itemName
print(cell.addOnLbl?.text! as Any)
return cell
}
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView == dishDetailTableView{
print("Hi")
dishDetailTableView.reloadData()
}
if tableView == addOnTableView {
print("Hello")
addOnTableView.reloadData()
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
var count : Int?
if tableView == dishDetailTableView{
count = 120
}
if tableView == addOnTableView{
count = 60
}
return CGFloat(count!)
}
}
How can i perform any action on cell?

iOS 11 reloadSections create duplicate header section

I'm working on a company's project and I have this problem when testing my table view on iOS 11 GM. It did work well on iOS 10. It is simple, I have three sections with header. When I tap on section header, my section will collapse/extend. Here's how I do it:
ViewController:
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var sectionsOpened: [String: Bool] = [
"section1": true,
"section2": true,
"section3": true
]
func isSectionOpened(section: String) -> Bool {
return sectionsOpened[section]!
}
#objc func toggleSection1() {
sectionsOpened["section1"] = !sectionsOpened["section1"]!
toggle(sectionIndex: 0)
}
#objc func toggleSection2() {
sectionsOpened["section2"] = !sectionsOpened["section2"]!
toggle(sectionIndex: 1)
}
#objc func toggleSection3() {
sectionsOpened["section3"] = !sectionsOpened["section3"]!
toggle(sectionIndex: 2)
}
func toggle(sectionIndex: Int) {
self.tableView.reloadSections([sectionIndex], with: .automatic)
self.tableView.scrollToRow(at: IndexPath(row: 0, section: sectionIndex), at: .top, animated: true)
}
Table view dataSource:
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "testCell", for: indexPath)
let label = cell.viewWithTag(1) as! UILabel
label.text = "TEST \(indexPath.section) - \(indexPath.row)"
return cell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UILabel(frame: CGRect(x: 0, y: 0, width: 320, height: 60))
headerView.backgroundColor = UIColor.green
headerView.text = "Header \(section)"
var gesture: UITapGestureRecognizer?
if section == 0 {
gesture = UITapGestureRecognizer(target: self, action: #selector(toggleSection1))
} else if section == 1 {
gesture = UITapGestureRecognizer(target: self, action: #selector(toggleSection2))
} else if section == 2 {
gesture = UITapGestureRecognizer(target: self, action: #selector(toggleSection3))
}
headerView.addGestureRecognizer(gesture!)
headerView.isUserInteractionEnabled = true
return headerView
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 60
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return CGFloat.leastNormalMagnitude
}
Table view delegate:
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if !isSectionOpened(section: "section\(indexPath.section+1)") {
return 1
}
if indexPath.section == 0 {
return 350
} else if indexPath.section == 1 {
return 400
} else if indexPath.section == 2 {
return 350
}
return 500
}
First notice is that scrollToRow behave weird, it go to top, then scroll down to the position.
Then, after trying to open/close the 3 headers, scrolling up/down, sometimes I got this duplicate header problem:
Duplicate header when reloading sections (photo)
'
Thank you in advance for your help. I really need to make this to work because iOS 11 will come next Tuesday...
I had the same problem and fixed it by using:
self.tableView.estimatedRowHeight = 0;
self.tableView.estimatedSectionHeaderHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0;
The issue has to do with the estimated row height when using self sizing cells. It seems if we do not provide very accurate estimations a couple of issues may appear across different version of iOS when reloading table view rows. On iOS 9 and 10 the table view may jump towards the top. On iOS 11 it seems section headers may duplicate if they are sticky at the top when reloading a row.
One solution is to cache the hight of the row as it is displayed and then provide this height back to the tableview as the estimated height, when required. Annoying to have to resort to something like this, but it solves both issues for me across iOS 9, 10 and 11.
See also this issue

Resources