1

I am using SideMenu and I am trying to add an item to that SideMenu from another View Controller.

I store the items for SideMenu in habits object. But I have no idea how to add a new item from ADD VIEW CONTROLLER as there is no segue.

To sum up; How can I access/edit habits object in SideMenu from "Add View Controller"

Here is my code for SideMenu;

import UIKit
import SideMenu
import FSCalendar

class MenuListController: UITableViewController {
    
    var habits = [Habit]()
    var selectedHabitIndex = 0
        
    let darkColor = UIColor(red: 33/255.0, green: 33/255.0, blue: 33/255.0, alpha: 1)
    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.reloadData()
        tableView.backgroundColor = darkColor
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        
        
        // MARK: - HABIT INITILIZAR
        var dateStrings = ["2020-12-25","2020-12-24","2020-12-23","2020-12-22"]
        var dateObjects = [Date]()
        let dateFormatter = DateFormatter()
        for date in dateStrings{
            dateFormatter.dateFormat = "yyyy-MM-dd"
            let dateObject = dateFormatter.date(from: date)
           dateObjects.append(dateObject!)
         }
        
        
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy/MM/dd"
        let someDate = formatter.date(from: "2020/12/29")
        
            
        habits = [Habit(name: "Read a book", selectedDatesArray: dateObjects),
                   Habit(name: "Go for a walk", selectedDatesArray: dateObjects)
        ]
    }
    
    
    // MARK: - Number of Habits in the Table View
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return habits.count
    }
    
    
    // MARK: - Display Names of the Habits in the Table View
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = habits[indexPath.row].name
        cell.textLabel?.textColor = .white
        cell.backgroundColor = darkColor
        return cell
    }
    
}

Main View Controller

import UIKit
import SideMenu
import FSCalendar

class ViewController: UIViewController, FSCalendarDelegate, FSCalendarDelegateAppearance {
    
    var selectedDateArray : [Date] = []
    var habits = [Habit]()
    
    
    
    var menu: SideMenuNavigationController?
    var selectedHabit: Habit?
    
    
    @IBOutlet weak var calendar: FSCalendar!
    
    var selectedDate = NSDate()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        calendar.delegate = self
        calendar.scrollDirection = .vertical
        calendar.allowsMultipleSelection = true
        calendar.locale = NSLocale(localeIdentifier: "tr") as Locale
        
        menu = SideMenuNavigationController(rootViewController: MenuListController())
        menu?.leftSide = true
        menu?.setNavigationBarHidden(true, animated: false)
        SideMenuManager.default.leftMenuNavigationController = menu
        SideMenuManager.default.addPanGestureToPresent(toView: self.view)
     
        
        var dateStrings = ["2020-12-25","2020-12-24","2020-12-23","2020-12-22"]
        var dateObjects = [Date]()
        let dateFormatter = DateFormatter()
        for date in dateStrings{
            dateFormatter.dateFormat = "yyyy-MM-dd"
            let dateObject = dateFormatter.date(from: date)
           dateObjects.append(dateObject!)
         }
        
            
        habits = [Habit(name: "Read a book", selectedDatesArray: dateObjects),
                   Habit(name: "Go for a walk", selectedDatesArray: dateObjects)
        ]
        
    }
    
    func toggleSideBar() {
        present(menu!, animated: true, completion: nil)
    }
    
    @IBAction func didMenuTapped(_ sender: UIButton) {
        present(menu!, animated: true, completion: nil)
    }
    
    func showSelectedDates (habit: Habit) {
        calendar.select(habit.selectedDatesArray)
    }
    
    func setTitle(habit: Habit) {
        title = habit.name
    }
    

   
    func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
        selectedDateArray.append(date)
    }
    
    func updateUI() {
    
        setTitle(habit: selectedHabit ?? habits[0] )
        showSelectedDates(habit: selectedHabit ?? habits[0])
        
    }
    
}
    

Add Habit View Controller

import UIKit

class AddHabitViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet weak var textField: UITextField!
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
    
    
    

    

    @IBAction func addbuttonTapped(_ sender: UIButton) {
        
        // NO IDEA WHAT TO PUT HERE?
        self.dismiss(animated: true, completion: nil)
        
    }
}
  • Use delegate pattern like this: Define one or multiple protocol for each ViewController (based on whether you need one or multiple viewControllers to communicate). Define a weak variable of that protocol type in the secondViewController and set it to firstViewController. Then simply call the methods of the protocol and implement the protocol in the firstViewController. Is it clear or Do you need an example? – Abbas Sabeti Dec 28 '20 at 16:56
  • Hi @AbbasSabeti, thanks for the reply. I would appreciate it if you give me an example or update the code I shared. – Evrim Demir Dec 28 '20 at 16:59
  • I found this example below. @IBAction func actionAddDetail(_ sender: UIButton) { guard let secondView = self.storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController else { fatalError("View Controller not found")} secondView.delegate = self //Protocol conformation here navigationController?.pushViewController(secondView, animated: true)} } – Evrim Demir Dec 28 '20 at 17:16

3 Answers3

0

You can use closure or delegates pattern to pass your habit object. Implement the code below by checking, you can use this sample pattern.

// AddHabitViewController

typealias SuccessListener = (Habit) -> ()
var successListener: SuccessListener?

@IBAction func addbuttonTapped(_ sender: UIButton) {
    self.dismiss(animated: false, completion: {
        
        passTheHabitObj = Habit(name: "yourString", selectedDatesArray: dateObject)
        self.successListener?(passTheHabitObj)
        
    })
}

// ViewController

var habit = [HabitArray]() // use your array for data
let sb = UIStoryboard(name: "Main", bundle: nil)

if let destVC = sb.instantiateViewController(withIdentifier: "AddHabitViewController") as? AddHabitViewController {
          destVC.successListener = { habitObj in
            habit.append(habitObj)
            
}
zeytin
  • 5,545
  • 4
  • 14
  • 38
  • Almost correct. There's nothing wrong with using listeners. (Unless the retain cycle between closure and the viewController which can be resolved with capturing self). But when you have optional variables in swift, you can simply leverage delegate pattern for communication between different objects and have corresponding protocols for working with each component (here: ViewController) – Abbas Sabeti Dec 28 '20 at 17:31
  • Thanks for comment. Yeah, in real usage sometime delegates are better sometimes not. I can not say one of them is better than other :) – zeytin Dec 28 '20 at 17:34
0
protocol MenuVCDataFillDelegate : class {
    func addNewData(data: Habit)
}

class MenuListController : UITableViewController,MenuVCDataFillDelegate {
    func addNewData(data: Habit){
        habits.append(data)
    }
}

Then, define your main ViewController like this:

class ViewController : UIViewController, AddHabitDelegate {
     weak var menuDelegate : MenuVCDataFillDelegate?

    func addMenuVC(){
        let vc = MenuListController()
        self.menuDelegate = vc
    }

    func navigateToAddHabit(){
        let vc = AddHabitViewController()
        vc.delegate = self
    }

    func newHabitAdded(data: Habit){
        delegate?.addNewData(data: data)
    }
}

And modify your AddHabitViewController to fill this data when added:

protocol AddHabitDelegate : class {
    func newHabitAdded(data: Habit)
}

class AddHabitViewController: UIViewController {
    weak var delegate : AddHabitDelegate?

    func needsAddItem(data: Habit){
        delegate?.newHabitAdded(data: data)
    }
}
Abbas Sabeti
  • 141
  • 2
  • 9
0

thanks all for the answers. I tried both approaches but could not make it work.

Using the notification center solved my problem;

In AddHabitVC;

NotificationCenter.default.post(name: Notification.Name("ourCustom"), object: textField.text)

In SideMenuVC:

    var observer : NSObjectProtocol?
  observer = NotificationCenter.default.addObserver(forName: Notification.Name("ourCustom"), object: nil, queue: .main, using: { (notification) in
            guard let object = notification.object as? String else {
                return
            }
            
            self.items.append(object)
            self.tableView.reloadData()
        })

Thanks.