How To Update Data in Another View Controller without Segue (SideMenu) - swift

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)
}
}

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)
}
}

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)
}

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.

Related

Creating a JTAppleCalendar inside a xib view

I'm trying to create a calendar using JTAppleCalendar inside a xib view because I want a paging type thing for it. On the first page is some information, and the second page is the calendar. I followed the tutorials for JTAppleCalendar on multiple sites, but my project always crashes, probably because I'm doing something wrong.
I have 3 files, a DetailViewController for setting up and displaying the pages, a dataViewController to connect the xib vars, and a CollectionViewCell for the calendar cells.
DetailViewController
import UIKit
import JTAppleCalendar
class DetailViewController: UIViewController {
var sportName: String = ""
var numClasses: Int = 0
var pages : [dataViewController]{
get {
let page1: dataViewController = Bundle.main.loadNibNamed("dataView", owner: self, options: nil)?.first as! dataViewController
page1.label.text = sportName
page1.classDuration.text = String(numClasses)
let page2: dataViewController = Bundle.main.loadNibNamed("secondDataView", owner: self, options: nil)?.first as! dataViewController
page2.calendar.calendarDataSource = self as? JTACMonthViewDataSource
page2.calendar.calendarDelegate = self as? JTACMonthViewDelegate
page2.calendar.reloadData()
return [page1, page2]
}
}
#IBOutlet weak var detailDescriptionLabel: UILabel!
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var pageControl: UIPageControl!
func configureView() {
// Update the user interface for the detail item.
if let detail = detailItem {
if let label = detailDescriptionLabel {
label.text = detail.description
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let back = UIBarButtonItem(title: "Back",style: .plain,target: self,action: #selector(backButton(_:)))
self.navigationItem.leftBarButtonItem = back
configureView()
view.bringSubviewToFront(pageControl)
setupScrollView(pages: pages)
pageControl.numberOfPages = pages.count
pageControl.currentPage = 0
}
var detailItem: String? {
didSet {
// Update the view.
configureView()
}
}
#objc
func backButton (_ sender: Any) {
performSegue(withIdentifier: "detailToList", sender: self)
}
func setupScrollView(pages: [dataViewController]){
scrollView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
scrollView.contentSize = CGSize(width: view.frame.width * CGFloat(pages.count), height: view.frame.height)
scrollView.isPagingEnabled = true
for i in 0 ..< pages.count {
pages[i].frame = CGRect(x: view.frame.width * (CGFloat(i)), y: 0, width: view.frame.width, height: view.frame.height)
scrollView.addSubview(pages[i])
}
}
}
extension DetailViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let pageIndex = round(scrollView.contentOffset.x/view.frame.width)
pageControl.currentPage = Int(pageIndex)
}
}
extension dataViewController: JTACMonthViewDataSource, JTACMonthViewDelegate {
func configureCalendar(_ calendar: JTACMonthView) -> ConfigurationParameters {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy MM dd"
formatter.timeZone = Calendar.current.timeZone
formatter.locale = Calendar.current.locale
let startDate = formatter.date(from: "2020 01 01")!
let endDate = Date()
return ConfigurationParameters(startDate: startDate, endDate: endDate)
}
func calendar(_ calendar: JTACMonthView, cellForItemAt date: Date, cellState: CellState, indexPath: IndexPath) -> JTACDayCell {
let cell = calendar.dequeueReusableJTAppleCell(withReuseIdentifier: "dateCell", for: indexPath) as! CollectionViewCell
cell.dateLabel.text = cellState.text
return cell
}
func calendar(_ calendar: JTACMonthView, willDisplay cell: JTACDayCell, forItemAt date: Date, cellState: CellState, indexPath: IndexPath) {
let cell = cell as! CollectionViewCell
cell.dateLabel.text = cellState.text
}
}
dataViewController
import JTAppleCalendar
class dataViewController: UIView {
//Page 1
#IBOutlet weak var label: UILabel!
#IBOutlet weak var classDuration: UILabel!
//Page 2
#IBOutlet weak var calendar: JTACMonthView!
}
CollectionViewCell
import UIKit
import JTAppleCalendar
class CollectionViewCell: JTACDayCell {
#IBOutlet weak var dateLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
And of course, the xib files. In DetailViewController, I moved the last extension part to dataViewController, but it didn't change anything. I'm also pretty sure I did many things wrong or inefficiently.
Can anyone help successfully implement the JTAppleCalendar inside the xib view, that would be appreciated! Thanks!
In your DetailViewController You are setting delegate and dataSource of calendar to self which is not conforming those protocols.
page2.calendar.calendarDataSource = self as? JTACMonthViewDataSource
page2.calendar.calendarDelegate = self as? JTACMonthViewDelegate
If thoose are needed to be handled in DetailViewController, I suggest you to start with implementing methods in DetailViewController not in dataViewController
extension DetailViewController: JTACMonthViewDataSource, JTACMonthViewDelegate

How to create a new collectionView when switching to another calendarView

I am having trouble trying to create a new collection view. I want to effectively have a collectionView with unique properties and then when I click on a date in my view controller it changes to a effectively a blank collectionView template so that the user can then put it there recipes.
How would I go about changing the collectionView (when user clicked on a date), change to a collection-view template and then save when the user puts in data?
Here is the code for the controller along with a picture example:
import UIKit
import GCCalendar
class CalendarViewController123: UIViewController, UICollectionViewDelegate {
// MARK: Properties
fileprivate var calendarView: GCCalendarView!
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var datelabel: UILabel!
#IBOutlet weak var open: UIBarButtonItem!
#IBAction func trytoday(_ sender: Any) {
self.calendarView.select(date: Date())
}
}
// MARK: - View
extension CalendarViewController123 {
override func viewDidLoad() {
super.viewDidLoad()
open.target = revealViewController()
open.action = #selector(SWRevealViewController.revealToggle(_:))
self.addToolbar()
self.addCalendarView()
self.addConstraints()
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for:.default)
self.navigationController?.navigationBar.shadowImage = UIImage()
}
}
// MARK: - Toolbar
extension CalendarViewController123 {
fileprivate func addToolbar() {
self.navigationController!.isToolbarHidden = false
let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let displayMode = UIBarButtonItem(title: "Display Mode", style: .plain, target: self, action: #selector(self.displayMode))
self.toolbarItems = [ space, displayMode]
}
#objc func displayMode() {
self.calendarView.displayMode = (self.calendarView.displayMode == .month) ? .week : .month
}
}
// MARK: - Calendar View
fileprivate extension CalendarViewController123 {
func addCalendarView() {
self.calendarView = GCCalendarView()
self.calendarView.delegate = self
self.calendarView.displayMode = .week
self.calendarView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(self.calendarView)
}
}
// MARK: - Constraints
fileprivate extension CalendarViewController123 {
func addConstraints() {
self.calendarView.topAnchor.constraint(equalTo: self.topLayoutGuide.bottomAnchor, constant: 2).isActive = true
self.calendarView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
self.calendarView.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
self.calendarView.heightAnchor.constraint(equalToConstant: 325).isActive = true
}
}
// MARK: - GCCalendarViewDelegate
extension CalendarViewController123: GCCalendarViewDelegate {
func calendarView(_ calendarView: GCCalendarView, didSelectDate date: Date, inCalendar calendar: Calendar) {
let dateFormatter = DateFormatter()
dateFormatter.calendar = calendar
dateFormatter.dateFormat = DateFormatter.dateFormat(fromTemplate: "MMM dd", options: 0, locale: calendar.locale)
//yyyy
// self.navigationItem.title = dateFormatter.string(from: date)
self.datelabel.text = dateFormatter.string(from: date)
}
}
The Green is the collectionView on a view controller
It depends on what data you are showing and how you are retrieving it. If you can provide more information I will update my answer accordingly.
1. Select Date
#IBAction func trytoday(_ sender: Any) {
self.calendarView.select(date: Date())
self.fetchData(forDate: selectedDate)
}
2. Fetch Data
func fetchData(forDate date: Date) {
//Fetch data and return array of objects
self.objects = retreivedObjects
self.collectionView.reloadData()
}
3. Load Data
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return objects.count
}
4. Setup Cell with Data
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? Cell else { return UICollectionViewCell}
cell.configure(withObject: objects[indexPath.item])
return cell
}

CoreData not Persisting?

I am saving an exercise into core data and calling it into the table, this works in terms of carrying the info from the user input into the table, however the coredata doesnt persist so when i re open the app, the entry is lost.
It was actually working yesterday and seems to have broken, but I havent made a change that would effect this as far as im aware. The one thing i found when debugging is that when I loaded the app its meant to point at the sql database in my console, however its changed to a .configurationprofiles file? Could this be a cause and what would the fix be? I will include the code for the tableview and the code for the user entry form below to show the information flow. Let me know if any other data is needed to be added.
import Foundation
import UIKit
import CoreData
class ExerciseEditorController: UIViewController, UITextFieldDelegate {
var managedObjectContext: NSManagedObjectContext?
var userRepsCount = Int()
var userSetsCount = Int()
#IBOutlet weak var userExerciseName: UITextField!
#IBOutlet weak var userExerciseSetCounter: UILabel!
#IBOutlet weak var userExerciseRepsCounter: UILabel!
#IBOutlet weak var userExerciseWeight: UITextField!
#IBAction func userSetsStepper(_ sender: UIStepper) {
userExerciseSetCounter.text = Int(sender.value).description
self.userSetsCount = Int(sender.value)
}
#IBAction func userRepsStepper(_ sender: UIStepper) {
userExerciseRepsCounter.text = Int(sender.value).description
self.userRepsCount = Int(sender.value)
}
#IBAction func cancelExerciseEditor(_ sender: Any) {
self.performSegue(withIdentifier: "unwindToWorkoutDesignerWithSegue:", sender: self)
}
#IBAction func saveExerciseToWorkout(_ sender: Any) {
createExercise()
self.performSegue(withIdentifier: "unwindToWorkoutDesignerWithSegue:", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = (UIColor.customBackgroundGraphite())
userExerciseSetCounter.text = String(userSetsCount)
userExerciseRepsCounter.text = String(userSetsCount)
userExerciseWeight.delegate = self
userExerciseWeight.keyboardType = .numbersAndPunctuation
}
func createExercise() {
let userExerciseWeightSet = Double(self.userExerciseWeight.text!) //make this safe!
guard let managedObjectContext = managedObjectContext else { return }
let userExercise = UserExercise(context: managedObjectContext)
userExercise.name = userExerciseName.text
userExercise.sets = Int64(userSetsCount)
userExercise.reps = Int64(userRepsCount)
userExercise.weight = userExerciseWeightSet! //make this safe!
userExercise.createdAt = Date().timeIntervalSince1970
}
func animateTextField(textField: UITextField, up: Bool) {
let movementDistance:CGFloat = -130
let movementDuration: Double = 0.3
var movement:CGFloat = 0
if up {
movement = movementDistance
}
else {
movement = -movementDistance
}
UIView.beginAnimations("animateTextField", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(movementDuration)
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
UIView.commitAnimations()
}
func textFieldDidBeginEditing(_ textField: UITextField) {
self.animateTextField(textField: textField, up:true)
}
func textFieldDidEndEditing(_ textField: UITextField) {
self.animateTextField(textField: textField, up:false)
}
}
And this is the tableview:
import Foundation
import UIKit
import CoreData
class WorkoutDesignerController: UIViewController, UITableViewDataSource, UITableViewDelegate, NSFetchedResultsControllerDelegate {
#IBAction func unwindToWorkoutDesigner(segue: UIStoryboardSegue) {}
#IBOutlet weak var workoutDesignerTable: UITableView!
#IBOutlet weak var tapToAddExercise: UILabel!
#IBOutlet weak var activityIndicatorView: UIActivityIndicatorView!
#IBAction func cancelWorkoutDesigner(_ sender: Any) {
self.performSegue(withIdentifier: "unwindToTemplatesWithSegue", sender: self)
}
private let persistentContainer = NSPersistentContainer(name: "Lift")
override func viewDidLoad() {
super.viewDidLoad()
setupView()
workoutDesignerTable.delegate = self
workoutDesignerTable.dataSource = self
view.backgroundColor = (UIColor.customBackgroundGraphite())
persistentContainer.loadPersistentStores { (persistentStoreDescription, error) in
if let error = error {
print("Unable to Load Persistent Store")
print("\(error), \(error.localizedDescription)")
} else {
self.setupView()
do {
try self.fetchedResultsController.performFetch()
} catch {
let fetchError = error as NSError
print("Unable to Perform Fetch Request")
print("\(fetchError), \(fetchError.localizedDescription)")
}
self.updateView()
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let userExercises = fetchedResultsController.fetchedObjects else { return 0 }
return userExercises.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? RoutineTableViewCell else {
fatalError("Unexpected Index Path")
}
cell.backgroundColor = UIColor.customBackgroundGraphite()
cell.textLabel?.textColor = UIColor.white
let userExercise = fetchedResultsController.object(at: indexPath)
cell.nameLabel.text = userExercise.name
cell.repsLabel.text = String(userExercise.reps)
cell.setsLabel.text = String(userExercise.sets)
cell.weightLabel.text = String(userExercise.weight)
return cell
}
private func setupView() {
setupMessageLabel()
updateView()
}
private func setupMessageLabel() {
tapToAddExercise.text = "Tap + To Add An Exercise To The Routine"
}
fileprivate func updateView() {
var hasUserExercises = false
if let UserExercise = fetchedResultsController.fetchedObjects {
hasUserExercises = UserExercise.count > 0
}
workoutDesignerTable.isHidden = !hasUserExercises
tapToAddExercise.isHidden = hasUserExercises
activityIndicatorView.stopAnimating()
}
fileprivate lazy var fetchedResultsController: NSFetchedResultsController<UserExercise> = {
// Create Fetch Request
let fetchRequest: NSFetchRequest<UserExercise> = UserExercise.fetchRequest()
// Configure Fetch Request
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: true)]
// Create Fetched Results Controller
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
// Configure Fetched Results Controller
fetchedResultsController.delegate = self
return fetchedResultsController
}()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addNewExerciseSegue" {
if let destinationViewController = segue.destination as? ExerciseEditorController {
// Configure View Controller
destinationViewController.managedObjectContext = persistentContainer.viewContext
}
}
}
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
workoutDesignerTable.beginUpdates()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
workoutDesignerTable.endUpdates()
updateView()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch (type) {
case .insert:
if let indexPath = newIndexPath {
workoutDesignerTable.insertRows(at: [indexPath], with: .fade)
}
break;
default:
print("...")
}
}
}
You need to call context.save().

Parse: Retrieving Data by Row

Overview: How can I make it so that when the cell with the segue
identifier of 0 will load data in the row that has 0 as its ID, cell with segue identifier of 1 will load data in the row that has 1 as its ID, and so on and so forth. The data includes ID (responds to cell identifier), navTitle (title of the navigation bar), written (who the article was written by), date (date of the article), and article (the article itself).
I am trying to make it so that once a cell is tapped in my tableview it opens data unique to that cell. What would the best way to do that? I was thinking that maybe I should have it check the ID column I have on Parse and load data the data in that row, but I'm not sure how to do that. Is there a better way to do this? Any help is appreciated! Feel free to ask me for any additional information.
^ So here I have a tableview in which data is being taken from Parse and used for both labels.
^ This view controller is segued to the cell above with:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 0 {
self.performSegueWithIdentifier("0a", sender: nil)
self.tableview.deselectRowAtIndexPath(indexPath, animated: true)
}
}
Here is my EventsDetailViewController, the one that is called when a cell is tapped:
import UIKit
import Parse
import ParseUI
import Bolts
class EventDetailViewController: UIViewController {
#IBAction func eventDetail(sender: AnyObject) {
dismissViewControllerAnimated(true, completion: nil)
}
#IBOutlet weak var articleTitle: UILabel!
#IBOutlet weak var writtenBy: UILabel!
#IBOutlet weak var date: UILabel!
#IBOutlet weak var article: UITextView!
#IBOutlet weak var navBar: UINavigationBar!
var dateDetail = [String]()
var articleDetail = [String]()
override func viewDidLoad() {
super.viewDidLoad()
loadEvents()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadEvents () -> Void {
var query = PFQuery(className: "eventsdetail")
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error : NSError?) -> Void in
if error == nil {
if let objects = objects as [PFObject]! {
for object in objects {
var output1 = object.objectForKey("navTitle") as! String
self.navBar.topItem?.title = output1
var output2 = object.objectForKey("articleTitle") as! String
self.articleTitle.text = output2
var output3 = object.objectForKey("written") as! String
self.writtenBy.text = output3
var output4 = object.objectForKey("date") as! String
self.date.text = output4
var output5 = object.objectForKey("article") as! String
self.article.text = output5
}
}
}
}
}
}
Here is my EventsViewController:
import UIKit
import Parse
import Bolts
import ParseUI
class EventsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var timer: NSTimer!
var isAnimating = false
var currentColorIndex = 0
var currentLabelIndex = 0
var customView: UIView!
var labelsArray: Array<UILabel> = []
var refreshControl: UIRefreshControl!
var testArray = [String]()
var subArray = [String]()
#IBOutlet weak var tableview: UITableView!
#IBOutlet weak var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
refreshControl = UIRefreshControl()
tableview.delegate = self
tableview.dataSource = self
refreshControl.addTarget(self, action: Selector("loadEvents"), forControlEvents: UIControlEvents.ValueChanged)
self.tableview.addSubview(refreshControl)
loadEvents()
loadCustomRefreshContents()
//refreshControl colors
refreshControl.backgroundColor = UIColor.clearColor() //color of background
refreshControl.tintColor = UIColor.clearColor() //color of indicator
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadEvents () -> Void {
testArray = [String]()
subArray = [String]()
let query = PFQuery(className: "events")
let runkey = query.orderByDescending("eventTitle")
runkey.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error:NSError?) -> Void in
if error == nil {
if let objects = objects as [PFObject]! {
for object in objects {
let load = object.objectForKey("eventTitle") as! String
self.testArray.append(load)
let subload = object.objectForKey("date") as! String
self.subArray.append(subload)
//reload TableView
self.tableview.reloadData()
print(self.testArray)
}
}
} else {
print("error:\(error!) \(error!.userInfo)")
}
}
refreshControl.endRefreshing()
}
func do_table_refresh() {
dispatch_async(dispatch_get_main_queue()) {
self.tableview.reloadData()
return
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 0 {
self.performSegueWithIdentifier("0a", sender: nil)
self.tableview.deselectRowAtIndexPath(indexPath, animated: true)
}
}
func tableView(tableView:UITableView!, numberOfRowsInSection section:Int) -> Int {
return testArray.count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("eventcell", forIndexPath: indexPath) as! EventTableViewCell
cell.title.text = self.testArray[indexPath.row]
cell.subTitle.text = self.subArray[indexPath.row]
return cell
}
//refreshes tableview; starts refresh
func loadCustomRefreshContents() {
let refreshContents = NSBundle.mainBundle().loadNibNamed("RefreshControl", owner: self, options: nil)
customView = refreshContents[0] as! UIView
customView.frame = refreshControl.bounds
for var i=0; i<customView.subviews.count; ++i {
labelsArray.append(customView.viewWithTag(i + 1) as! UILabel)
}
refreshControl.addSubview(customView)
}
//stops refresh
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
do_table_refresh()
if refreshControl.refreshing {
if !isAnimating {
animateRefreshStep1()
}
}
}
//cycles through colors
func getNextColor() -> UIColor {
var colorsArray: Array<UIColor> = [UIColor.magentaColor(), UIColor.brownColor(), UIColor.yellowColor(), UIColor.redColor(), UIColor.greenColor(), UIColor.blueColor(), UIColor.orangeColor()]
if currentColorIndex == colorsArray.count {
currentColorIndex = 0
}
let returnColor = colorsArray[currentColorIndex]
++currentColorIndex
return returnColor
}
func doSomething() {
timer = NSTimer.scheduledTimerWithTimeInterval(4.0, target: self, selector: "endOfWork", userInfo: nil, repeats: true)
self.do_table_refresh()
}
func endOfWork() {
refreshControl.endRefreshing()
timer.invalidate()
timer = nil
}
//first part of animation
func animateRefreshStep1() {
isAnimating = true
UIView.animateWithDuration(0.1, delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: { () -> Void in
self.labelsArray[self.currentLabelIndex].transform = CGAffineTransformMakeRotation(CGFloat(M_PI_4))
self.labelsArray[self.currentLabelIndex].textColor = self.getNextColor()
}, completion: { (finished) -> Void in
UIView.animateWithDuration(0.05, delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: { () -> Void in
self.labelsArray[self.currentLabelIndex].transform = CGAffineTransformIdentity
self.labelsArray[self.currentLabelIndex].textColor = UIColor.blackColor()
}, completion: { (finished) -> Void in
++self.currentLabelIndex
if self.currentLabelIndex < self.labelsArray.count {
self.animateRefreshStep1()
}
else {
self.animateRefreshStep2()
}
})
})
}
//second part of animation
func animateRefreshStep2() {
UIView.animateWithDuration(0.35, delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: { () -> Void in
self.labelsArray[0].transform = CGAffineTransformMakeScale(1.5, 1.5)
self.labelsArray[1].transform = CGAffineTransformMakeScale(1.5, 1.5)
self.labelsArray[2].transform = CGAffineTransformMakeScale(1.5, 1.5)
self.labelsArray[3].transform = CGAffineTransformMakeScale(1.5, 1.5)
self.labelsArray[4].transform = CGAffineTransformMakeScale(1.5, 1.5)
self.labelsArray[5].transform = CGAffineTransformMakeScale(1.5, 1.5)
self.labelsArray[6].transform = CGAffineTransformMakeScale(1.5, 1.5)
self.labelsArray[7].transform = CGAffineTransformMakeScale(1.5, 1.5)
self.labelsArray[8].transform = CGAffineTransformMakeScale(1.5, 1.5)
self.labelsArray[9].transform = CGAffineTransformMakeScale(1.5, 1.5)
}, completion: { (finished) -> Void in
UIView.animateWithDuration(0.25, delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: { () -> Void in
self.labelsArray[0].transform = CGAffineTransformIdentity
self.labelsArray[1].transform = CGAffineTransformIdentity
self.labelsArray[2].transform = CGAffineTransformIdentity
self.labelsArray[3].transform = CGAffineTransformIdentity
self.labelsArray[4].transform = CGAffineTransformIdentity
self.labelsArray[5].transform = CGAffineTransformIdentity
self.labelsArray[6].transform = CGAffineTransformIdentity
self.labelsArray[7].transform = CGAffineTransformIdentity
self.labelsArray[8].transform = CGAffineTransformIdentity
self.labelsArray[9].transform = CGAffineTransformIdentity
}, completion: { (finished) -> Void in
if self.refreshControl.refreshing {
self.currentLabelIndex = 0
self.animateRefreshStep1()
}
else {
self.isAnimating = false
self.currentLabelIndex = 0
for var i=0; i<self.labelsArray.count; ++i {
self.labelsArray[i].textColor = UIColor.blackColor()
self.labelsArray[i].transform = CGAffineTransformIdentity
}
}
})
})
}
}
Since you have the data on the tableView just make sure you create a segue in your storyboard connecting the prototype cell and the destination view controller and add an identifier to the segue.
Next, on the controller with the tableView make sure you call:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "segueIdentifier"){
let detailScene = segue.destinationViewController as! EventDetailViewController
if let indexPath = self.tableView.indexPathForSelectedRow {
let row = Int(indexPath.row)
//here you can pass all the data to the destination viewController. But you CANT POPULATE YOUR LABELS. make sure you pass the data to some auxiliar variable(s). Next line is an example
detailScene.selectedData = (self.myData[row] as! PFObject)
}
}
}
Update:
detailScene.selectedData = (self.myData[row] as! PFObject)
detailScene is an instance of the destination viewController. selectedData is, in this case an instance of PFObject that will be use to populate the labels in my destination viewController. self.myData is an array in my source viewController with the data that is shown in my tableView.
So if you want to send the data you show in your cells. (
self.testArray[indexPath.row]
self.subArray[indexPath.row]
) make sure you declare the variables that will receive this data on your EventsDetailViewController something like this:
var testString:String!
var subString:String!
Then in your prepareForSegue (EventsViewController) make sure to instantiate this variables with the proper data:
detailScene.test = self.testArray[row]
detailScene.sub = self.subArray[row]
Finally on EventsDetailViewController you can pass the data to the labels you want on viewDidLoad():
self.articleTitle.text = testString
Let me know in case of any concerns.
What you are asking is the archetypical basic app: show a list of items in a tableview and being able to tap on a row to see more details.
What seems unusual to me is how you have two different classes: events and eventdetails.
Compare it with a database of books. You would not have a Book class and a BookDetails class. You would only have a Book class that contains all details about a book. So when you want to show a list of books, you fetch all the Book objects. In the tableviewcontroller you display a subset of fields from the Book objects (like i.e. title and author). When the user then taps on a specific book, you open a viewcontroller and pass it the Book object for that row. This detail viewcontroller then just shows MORE info/fields from that same Book object.

How to add a notification?

I want to add a local notification in my project. In the DetailViewController, I have two labels with date and hour.
Is it possible to add a notification based on this date and hour? If yes, can you explain how to set it?
Code:
DetailViewController :
import UIKit
class DetailViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
// MARK: - Outlet
#IBOutlet var imageProfilo: UIImageView!
#IBOutlet var labelNome: UILabel!
#IBOutlet var pillsTable: UITableView!
// MARK: - Variabili
var profilo: ProfiloModel!
// MARK: - Metodi standard del controller
override func viewDidLoad() {
super.viewDidLoad()
pillsTable.dataSource = self
pillsTable.delegate = self
imageProfilo.layer.cornerRadius = 30
DataManager.sharedInstance.detail = self
if let test = profilo {
//title = profilo!.nome
labelNome.text = profilo!.nome
imageProfilo.image = profilo!.immagine
if profilo!.immagine.size.width > profilo!.immagine.size.height {
imageProfilo.image = UIImage(CGImage: profilo!.immagine.CGImage, scale: 1.0, orientation: UIImageOrientation.Right)
} else {
imageProfilo.image = profilo!.immagine
}
} else {
if !DataManager.sharedInstance.storage.isEmpty {
profilo = DataManager.sharedInstance.storage[0]
//title = profiloSos.nome
labelNome.text = profilo.nome
imageProfilo.image = profilo.immagine
if profilo.immagine.size.width > profilo.immagine.size.height {
imageProfilo.image = UIImage(CGImage: profilo.immagine.CGImage, scale: 1.0, orientation: UIImageOrientation.Right)
} else {
imageProfilo.image = profilo.immagine
}
} else {
return
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: UITableView
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return profilo.therapyArra.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! PillsCell
var terapia = profilo.therapyArra[indexPath.row]
cell.nomeMedicina.text = terapia.nomeMedicina
cell.data.text = terapia.data
cell.ora.text = terapia.ora
cell.dosaggio.text = terapia.dosaggio
return cell
}
// MARK: - Azioni
// MARK: - Metodi
// MARK: - Navigazione
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "terapia" {
var cell = sender as! UITableViewCell
if let indexPath = self.pillsTable.indexPathForRowAtPoint(cell.center) {
var controller = segue.destinationViewController as! PillsViewController
controller.therapy = profilo.therapyArra[indexPath.row]
}
} else if segue.identifier == "addtherapy" {
var controller = segue.destinationViewController as! AddPillsController
controller.profilo = profilo
}
}
}'
PillsCell :
'import UIKit
class PillsCell: UITableViewCell {
#IBOutlet var nomeMedicina: UILabel!
#IBOutlet var ora: UILabel!
#IBOutlet var data: UILabel!
#IBOutlet var dosaggio: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
PillsModel :
import UIKit
class PillsModel: NSObject, NSCoding {
var nomeMedicina :String!
var data :String!
var ora :String!
var dosaggio :String!
init(nomeMedicinaIn:String, dataIn:String, oraIn:String, dosaggioIn:String) {
nomeMedicina = nomeMedicinaIn
ora = oraIn
data = dataIn
dosaggio = dosaggioIn
}
internal required init(coder aDecoder: NSCoder) {
self.nomeMedicina = aDecoder.decodeObjectForKey("nomeMedicina") as! String
self.ora = aDecoder.decodeObjectForKey("ora") as! String
self.data = aDecoder.decodeObjectForKey("data") as! String
self.dosaggio = aDecoder.decodeObjectForKey("dosaggio") as! String
}
func encodeWithCoder(encoder: NSCoder) {
encoder.encodeObject(self.nomeMedicina, forKey: "nomeMedicina")
encoder.encodeObject(self.ora, forKey: "ora")
encoder.encodeObject(self.data, forKey: "data")
encoder.encodeObject(self.dosaggio, forKey: "dosaggio")
}
}
AddPillsController :
import UIKit
class AddPillsController: UIViewController, UITextFieldDelegate, CameraManagerDelegate {
#IBOutlet var fieldNomeMedicina: UITextField!
#IBOutlet var fieldData: UITextField!
#IBOutlet var fieldOra: UITextField!
#IBOutlet var fieldDosaggio: UITextField!
var profilo: ProfiloModel!
override func viewDidLoad() {
super.viewDidLoad()
fieldNomeMedicina.delegate = self
fieldData.delegate = self
fieldOra.delegate = self
// Vista accessoria per la tastiera
var keyboardToolbar = UIToolbar(frame: CGRectMake(0, 0, self.view.bounds.size.width, 44))
keyboardToolbar.barStyle = UIBarStyle.BlackTranslucent
keyboardToolbar.backgroundColor = UIColor.redColor()
keyboardToolbar.tintColor = UIColor.whiteColor()
var flex = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil)
var save = UIBarButtonItem(title: "Fatto", style: UIBarButtonItemStyle.Done, target: fieldDosaggio, action: "resignFirstResponder")
keyboardToolbar.setItems([flex, save], animated: false)
fieldDosaggio.inputAccessoryView = keyboardToolbar
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder() // chiudere la tastiera nei campi di testo
return true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func annulla(sender: UIButton) {
dismissViewControllerAnimated(true, completion: nil) // chiude una modal
}
#IBAction func salva(sender: UIButton) {
if fieldNomeMedicina.text.isEmpty &&
fieldData.text.isEmpty &&
fieldOra.text.isEmpty &&
fieldDosaggio.text.isEmpty{
//alertView che avverte l'utente che tutti i campi sono obbligatori
return
}
var therapy = PillsModel(nomeMedicinaIn: fieldNomeMedicina.text,
dataIn: fieldData.text,
oraIn: fieldOra.text,
dosaggioIn : fieldDosaggio.text)
profilo.therapyArra.append(therapy)
DataManager.sharedInstance.salvaArray()
DataManager.sharedInstance.detail.pillsTable.reloadData()
dismissViewControllerAnimated(true, completion: nil)
}
}
You should use UILocalNotification. Use your date and time to create a NSDate object and then setup your notification like this:
let notification = UILocalNotification()
notification.fireDate = ... // Add your date here
notification.alertBody = "Alert alert alert!!!"
UIApplication.sharedApplication().scheduleLocalNotification(notification)
Handle the notification in your AppDelegate by overriding the didReceiveLocalNotification method:
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {
println(notification.alertBody); // "Alert alert alert!!!"
}