4
votes

I'm showing a View Controller as a Popup on a Parent ViewController(VC).

On Dismiss of ModalVC, data from ModalVC is passed to a function that reloads TableView in ParentVC with fresh data.

Things I've tried

  1. Calling 'reloadData' function before dismiss-statement from ModalVC
  2. Dismiss Completion handler
  3. Notification Center
  4. Singleton - To call reload-TableView function
  5. Dismiss Handler

Each time I get an error for this statement.

self.myTableView.reloadData()

FYI, 'myTableView' is an @IBOutlet with a valid connection

The Error Message:

Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
2019-11-30 02:40:30[31116:1071948] Fatal error: Unexpectedly found nil while implicitly 
unwrapping an Optional value

I can confirm that I'm able to print data in the console that is being sent to Table Cell

I've also checked that on dismissing ModalVC, the ViewWillAppear/ViewDidAppear doesn't load and thanks to Sean for confirming it.

I need help reloading TableView in ParentVC on dismissing ModalVC/Popup.

ModalVC.swift

import UIKit
import ACFloatingTextfield_Swift
import Alamofire
import SwiftyJSON

class UISearchViewController: UIViewController,UITextViewDelegate,UITextFieldDelegate {

@IBOutlet weak var searhView: UIView!
@IBOutlet weak var patientUMRIpNo: ACFloatingTextfield!
@IBOutlet weak var fromDateTF: UITextField!
@IBOutlet weak var toDateTF: UITextField!
@IBOutlet weak var submitButtonOutlet: UIButton!


let logBookRef:LogbookViewController = LogbookViewController()
let datePickerView = UIDatePicker()
let datePicker = UIDatePicker()

let searchVC = "SearchPatient"

var rawSearchData : JSON!

var patientSearchDetails:[String:Any] = [:]

var patientNameArr:[String] = []
var patientAgeArr: [String] = []
var patientGenderArr:[String] = []
var patientUmrIdsArr:[String] = []
var patientMobileNoArr:[String] = []
var patientDatesArr:[String] = []

override func viewDidLoad() {
    super.viewDidLoad()

    submitButtonOutlet.layer.borderColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
    submitButtonOutlet.layer.cornerRadius = 10
    submitButtonOutlet.layer.masksToBounds = true
    patientUMRIpNo.delegate = self
    fromDateTF.delegate = self
    toDateTF.delegate = self

    searhView.layer.cornerRadius = 10
    searhView.layer.masksToBounds = true


    textFieldPlaceHolder()

    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard(_sender:)))
    self.view.addGestureRecognizer(tapGesture)



}
@objc func dismissKeyboard(_sender:UITapGestureRecognizer){
    patientUMRIpNo.resignFirstResponder()
    fromDateTF.resignFirstResponder()
    toDateTF.resignFirstResponder()


}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    let touch = touches.first
    if (touch!.view != self.searhView) {
            self.dismiss(animated: true, completion: nil)
        }
}

// TextField placeholder method
func textFieldPlaceHolder(){
    placeHolderWhite(textFieldName: fromDateTF, placeHolderText: "Select from date")
    placeHolderWhite(textFieldName: toDateTF, placeHolderText: "Select to date")
}

func placeHolderWhite(textFieldName:UITextField, placeHolderText:String){
    textFieldName.attributedPlaceholder = NSAttributedString(string: placeHolderText ,attributes: [NSAttributedString.Key.foregroundColor: UIColor.white])

}


func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
    fromDateTF.inputView = datePickerView
    toDateTF.inputView = datePicker
    datePickerView.datePickerMode = .date
    datePicker.datePickerMode = .date

    let toolBar = UIToolbar().ToolbarPiker(mySelect: #selector(UISearchViewController.dismissPicker))
    fromDateTF.inputAccessoryView = toolBar
    toDateTF.inputAccessoryView = toolBar

    datePickerView.addTarget(self, action: #selector(handleDatePicker(sender:)), for: .valueChanged)
    datePicker.addTarget(self, action: #selector(handleDatePickerTwo(sender:)), for: .valueChanged)

    return true
}
@objc func dismissPicker() {
     view.endEditing(true)
    }
@objc func handleDatePicker(sender: UIDatePicker) {
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "dd-MM-yyyy"
    fromDateTF.text = dateFormatter.string(from: sender.date)

}
@objc func handleDatePickerTwo(sender: UIDatePicker){
           let formatDate = DateFormatter()
            formatDate.dateFormat = "dd-MM-yyyy"
            toDateTF.text = formatDate.string(from: sender.date)
}
func getPatientData(_ completion: @escaping (JSON?) -> Void){

    let parameters:[String:Any] = [KeyConstants.mobileNo:self.getUserMobileNumberFromUserDefaults(),
        KeyConstants.keyword:self.patientUMRIpNo.text ?? "" ,
        KeyConstants.datedvalue:self.fromDateTF.text ?? "",
        KeyConstants.todatedValue: self.toDateTF.text ?? ""]

    print(parameters)
    Alamofire.request(AppUrl.searchPatientUrl, method: .post, parameters: parameters).responseJSON { (response) in
        if response.result.isSuccess{

            let patientSearchJSON : JSON = JSON(response.result.value!)
            // print(patientSearchJSON)
            print("Raw Search Results......\(patientSearchJSON)")

            if patientSearchJSON["status"] == "3"{
             completion(patientSearchJSON)
                //self.bindSearchedData(json: patientSearchJSON)

            }

}
    }}

func bindSearchedData(json:JSON){
    for i in 0..<json["status_messsage"].count{
        let patientNames = json["status_messsage"][i]["patient_name"].stringValue
        let patientUmrNos = json["status_messsage"][i]["patient_umr_ipno"].stringValue
        let patientGenderNames = json["status_messsage"][i]["patient_gender"].stringValue
        let patientMobileNos = json["status_messsage"][i]["patient_mobile"].stringValue
        let patientDateValues = json["status_messsage"][i]["patient_date"].stringValue
        let patiendAgeValues = json["statue_message"][i]["patient_age"].stringValue


        patientNameArr.append(patientNames)
        patientUmrIdsArr.append(patientUmrNos)
        patientGenderArr.append(patientGenderNames)
        patientMobileNoArr.append(patientMobileNos)
        patientDatesArr.append(patientDateValues)
        patientAgeArr.append(patiendAgeValues)

    }



}


@IBAction func submitButtonAction(_ sender: Any) {
    showToast(message: "Submit func calling", font: UIFont.systemFont(ofSize: 15))
    getPatientData() { value in


        // Modal/Popup - DISMISS
        self.dismiss(animated: true, completion: {

            let logbookObj = LogbookViewController()

            logbookObj.updatePatientSearch22(json: value!)


        })


}    
}
}

ParentVC.swift

import UIKit
import FSCalendar
import Alamofire
import SwiftyJSON
import ACFloatingTextfield_Swift

class LogbookViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, FSCalendarDataSource, FSCalendarDelegate,FSCalendarDelegateAppearance,LogbookCellDelegate{
@IBOutlet weak var calendar: FSCalendar!
@IBOutlet weak var headerTitle: UILabel!
@IBOutlet weak var calendarHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var logBookTableView: UITableView!
@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var logBookTableViewHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var overViewHeightConstraint: NSLayoutConstraint!

var patientUrl = [String]()


fileprivate var lunar: Bool = false {
    didSet {
        self.calendar.reloadData()
    }
}

fileprivate let formatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateFormat = "MMMM yyyy"

    return formatter
}()

fileprivate lazy var dateFormatter: DateFormatter = {
    let formatter = DateFormatter()
   formatter.dateFormat = "yyyy-MM-dd"

    return formatter
}()

fileprivate let gregorian: NSCalendar! = NSCalendar(calendarIdentifier:NSCalendar.Identifier.gregorian)

fileprivate let gregorianmove:NSCalendar =  NSCalendar(calendarIdentifier:NSCalendar.Identifier.gregorian)!

var datedValue = ""


var patientNamesList:[String] = []
var patientName = PatientSearchDataModel()

var patientAgeList: [String] = []
var patientAge = PatientSearchDataModel()

var patientGenderList:[String] = []
var patientGender = PatientSearchDataModel()

var patientUmrIdsList:[String] = []
var patientUmrId = PatientSearchDataModel()

var patientMobileNoList:[String] = []
var patientMobileNo = PatientSearchDataModel()

var patientCategoryDiagnosysList:[String] = []
var patientCategoryDiagnosys = PatientSearchDataModel()

var patientHospitalNameList:[String] = []
var hospitalName = PatientSearchDataModel()

var patientRefDoctorNameList:[String] = []
var patientRefDocName = PatientSearchDataModel()

var patientRefDoctorMobileNoList:[String] = []
var patientRefDocMobileNo = PatientSearchDataModel()

var patientOperationDoneList:[String] = []
var patientOperationDone = PatientSearchDataModel()

var patientDatesList:[String] = []
var patientDate = PatientSearchDataModel()

var dateValue = CalendarLogDataModel()
var dateValuesList:[String] = []

var logBookMonth = ""
var logBookYear = ""

//Declaring the variables for disply search options
var patientType:String?
var searchPatientIP:String?
var searchPatientFromDate:String?
var searchPatientToDate:String?

override func viewDidLoad() {
    super.viewDidLoad()
    self.calendar.appearance.headerMinimumDissolvedAlpha = 0.0
    self.logBookTableView.isHidden = true
    self.logBookTableView.separatorStyle = .none
    self.logBookTableView.reloadData()
    self.scrollView.bounces = false
    if UIDevice.current.model.hasPrefix("iPad") {
        self.calendarHeightConstraint.constant = 400

    }

    self.calendar.appearance.caseOptions = [.headerUsesUpperCase,.weekdayUsesUpperCase]

   //self.calendar.select(self.formatter.date(from: "2017/08/10")!)

    let scopeGesture = UIPanGestureRecognizer(target: self.calendar, action: #selector(self.calendar.handleScopeGesture(_:)))
    self.calendar.addGestureRecognizer(scopeGesture)

    // For UITest
    self.calendar.accessibilityIdentifier = "calendar"
    self.curentDate()

}

override func viewWillAppear(_ animated: Bool) {
    callCalenderApi()
    self.calendar.reloadData()

    //calendar.appearance.eventColor = UIColor.greenColor
    self.logBookTableView.reloadData()

}

override func viewDidAppear(_ animated: Bool) {
    self.logBookTableView.reloadData()

}

@IBAction func rightArrowButton(_ sender: Any) {
    calendar.setCurrentPage(getNextMonth(date: calendar.currentPage), animated: true)
}

@IBAction func leftArrowButton(_ sender: Any) {
   calendar.setCurrentPage(getPreviousMonth(date: calendar.currentPage), animated: true)
}

func getNextMonth(date:Date)->Date {
    self.logBookTableView.isHidden = true
    return  Calendar.current.date(byAdding: .month, value: 1, to:date)!
}
func getPreviousMonth(date:Date)->Date {
     self.logBookTableView.isHidden = true
    return  Calendar.current.date(byAdding: .month, value: -1, to:date)!
}   

@IBAction func backButton(_ sender: Any) {
    let dashBoard = self.storyboard?.instantiateViewController(withIdentifier: "HomeVC") as? HomeViewController
    self.navigationController?.pushViewController(dashBoard!, animated: true)

}

@IBAction func searchButton(_ sender: Any) {
    let searchPatientVC = self.storyboard?.instantiateViewController(withIdentifier:"SearchVC") as? UISearchViewController
    searchPatientVC!.modalTransitionStyle   = .crossDissolve
    searchPatientVC!.modalPresentationStyle = .overCurrentContext

    self.present(searchPatientVC!, animated: true, completion: nil)

}



func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return patientDatesList.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = logBookTableView.dequeueReusableCell(withIdentifier: "LogbookTVC", for: indexPath) as? LogbookTableViewCell
    cell?.delegate = self

    cell?.selectionStyle = .none

    cell?.patientNameLabel.text = patientNamesList[indexPath.row]
    cell?.patientIpNumberLabel.text = patientUmrIdsList[indexPath.row]
    cell?.patientGenderLabel.text = patientGenderList[indexPath.row]
    cell?.patientMobileNumberLabel.text = patientMobileNoList[indexPath.row]
    cell?.patientDateLabel.text = patientDatesList[indexPath.row]


    cell?.shareButtOL.tag = indexPath.row
    cell?.messageButtOL.tag = indexPath.row
    cell?.deleteButtOL.tag = indexPath.row
    cell?.printButtOL.tag = indexPath.row
    cell?.viewButtOL.tag = indexPath.row
    return cell!
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 200
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    let cell = logBookTableView.dequeueReusableCell(withIdentifier: "LogbookTVC") as? LogbookTableViewCell
    logBookTableViewHeightConstraint.constant = logBookTableView.contentSize.height
    overViewHeightConstraint.constant = logBookTableView.contentSize.height + CGFloat(340)
    cell?.contentView.backgroundColor = UIColor(red: 59, green: 87, blue: 157, alpha: 1.0)
}



func updatePatientSearch22(json: JSON){




        self.patientNamesList = []
        self.patientUmrIdsList = []
        self.patientGenderList = []
        self.patientMobileNoList = []
        self.patientDatesList = []
        self.patientAgeList = []


        for i in 0..<json["status_messsage"].count{

            let patientNamee = json["status_messsage"][i]["patient_name"].stringValue
            let patientUmrNumber = json["status_messsage"][i]["patient_umr_ipno"].stringValue
            let patientGenderName = json["status_messsage"][i]["patient_gender"].stringValue
            let patientMobileNumber = json["status_messsage"][i]["patient_mobile"].stringValue
            let patientDateValue = json["status_messsage"][i]["patient_date"].stringValue
            let patiendAgeValue = json["statue_message"][i]["patient_age"].stringValue


            self.patientName.patientname = patientNamee
            self.patientUmrId.patientumripno = patientUmrNumber
            self.patientGender.patientgender = patientGenderName
            self.patientMobileNo.patientmobile = patientMobileNumber
            self.patientDate.patientdate = patientDateValue
            self.patientAge.patientage = patiendAgeValue


            self.patientNamesList.append(patientNamee)
            self.patientUmrIdsList.append(patientUmrNumber)
            self.patientGenderList.append(patientGenderName)
            self.patientMobileNoList.append(patientMobileNumber)
            self.patientDatesList.append(patientDateValue)
            self.patientAgeList.append(patiendAgeValue)


        }

        print("Patient Name List", self.patientNamesList)
        print(self.patientUmrIdsList)
        print(self.patientGenderList)
        print(self.patientMobileNoList)
        print(self.patientDatesList)

    // ERROR-POINT
     self.logBookTableView.reloadData()

} 

}

}
3
can you add the code of your table view, and the ModalVC? pleaseAndres Gomez
Where are calling self.myTableView.reloadData()?Mojtaba Hosseini
@AndresGomez I've added codeuzair
@MojtabaHosseini That statement 'reloadData()' is in ParentVC.swift, last fourth line. I just edited the question with code, It will be great if you can refer it.uzair

3 Answers

3
votes

ParentVC.swift

@IBAction func searchButton(_ sender: Any) {
    let searchPatientVC = self.storyboard?.instantiateViewController(withIdentifier:"SearchVC") as? UISearchViewController
    searchPatientVC!.modalTransitionStyle   = .crossDissolve
    searchPatientVC!.modalPresentationStyle = .overCurrentContext
    searchPatientVC.searchCompletion = {(model,flag) in
   if(flag){
    self.updatePatientSearch22(json:model)
    self.logBookTableView.reloadData()

   }
 }
    self.present(searchPatientVC!, animated: true, completion: nil)

}

ModalVC.swift

typealias completion = (NSArray,Bool)->Void
var searchCompletion:completion!


@IBAction func submitButtonAction(_ sender: Any) {
    showToast(message: "Submit func calling", font: UIFont.systemFont(ofSize: 15))
    getPatientData() { value in

     self.dismiss(animated: true, completion: nil)
    self.searchCompletion(value,true)


}    

}
2
votes

Here is the error:

let logbookObj = LogbookViewController()

You are creating a new instance and you are not loading your view controller from the storyboard (that’s why the tableView is nil), you can access it in this way:

    // Modal/Popup - DISMISS
self.dismiss(animated: true, completion: {
    if let navigationController = self.presentingViewController as? UINavigationController, 
       let logbookObj = self.navigationController.viewControllers.first as? LogbookViewController, 
       let value = value {
        logbookObj.updatePatientSearch22(json: value)
    }
})
2
votes

Create Protocal delegate in ModelVC

protocal RefreshDataDelegate() {
  func refreshData()
}

create delegate variable

var viewDelegate: RefreshDataDelegate?

use this delegate to reload your data like and you can call this method while dismissing ModalVC

viewDelegate.refreshData()

In parentVC When you show ModalVC add this line

let vc = your modalvc 
 vc.viewDelegate = self

for Delegate add this line

extension ParentVC: RefreshDataDelegate {
func refreshData() {
    // reload or load data here
 }
}