VMF_MainViewController

class VMF_MainViewController : VMF_BaseViewController, VMF_MasterTableControllerProtocol
extension VMF_MainViewController: UIPageViewControllerDataSource
extension VMF_MainViewController: UIPageViewControllerDelegate
extension VMF_MainViewController: UIPickerViewDataSource
extension VMF_MainViewController: UIPickerViewDelegate

This is the main view controller for the weekday/time selector tab.

QUICK OVERVIEW

The way that the data is set up, we have “day indexes (1-7), and "time slots” (varies, per day).

All times are mapped to the user’s local timezone, regardless of the start time in the meeting’s “native” timezone.

The instance of this class will have a VMF_DayTimeSearchPageViewController embedded (also the VMF_AttendanceViewController class, but that one disables the page view controller), which allows the user to swipe-select pages of meetings.

DAY INDEXES

These represent 0 (In Progress), 1 (Sunday), through 7 (Saturday), or 8 (Search Mode).

Mode 0 (In Progress)

In this mode, there is no “time index,” and the screen displays all meetings that are in progress, regardless of when they started.

Mode 1 -> 7

If the day index is 1 -> 7, the screen displays whatever time index, within that day, is selected.

The day index is stored in Sunday -> Saturday (the database native scheme), but are mapped to the local week, upon display.

In this mode, the day index and time index are irrelevant. All possible meetings are displayed, then are filtered, “live,” as text is entered into the text entry field.

TIME INDEXES

Each day has “time slots.” These are groupings of times, where meetings start (1 or more). Each “time slot” is a separate group of meetings that all begin at the same time.

In regular weekdays (day index 1 -> 7), only one “time slot” is shown at a time. Only the meetings that begin at the same time are shown.

If possible, the user’s currently selected time index is honored, and we try to match the time, when changing selected days. It is also preserved, when entering Mode 0 or Mode 8.

SELECTING FOR ATTENDANCE

It is possible for a user to indicate that they attend a meeting, by double-tapping on a meeting in the list, or by selecting “I Attend,” in the meeting inspector screen.

You can bring in a separate screen, that contains only the meetings that you attend. This is accessed from the chackmark bar button item, in the upper right.

  • The segue ID of our open attendance.

    Declaration

    Swift

    static let openAttendanceSegueID: String
  • The image that we use for search mode.

    Declaration

    Swift

    static let searchImage: UIImage?
  • The image to use, when attendance is selected.

    Declaration

    Swift

    static let checkedImage: UIImage?
  • The image to use, when we do not attend.

    Declaration

    Swift

    static let uncheckedImage: UIImage?
  • The color of the “mercury” in our “thermometer.”

    Declaration

    Swift

    static let mercuryColor: UIColor
  • The font for the “I Attend” button.

    Declaration

    Swift

    static let barButtonLabelFont: UIFont
  • The width of each of the components of our direct picker view.

    Declaration

    Swift

    static let pickerViewComponentWidthInDisplayUnits: CGFloat
  • The container view needs to be wider than this, to show short (as opposed to “very” short) weekdays.

    Declaration

    Swift

    static let shortWidthThreshold: CGFloat
  • The container view needs to be wider than this, to show full (as opposed to short) weekdays.

    Declaration

    Swift

    static let fullWidthThreshold: CGFloat
  • This is used to restore the bottom of the stack view, when the keyboard is hidden.

    Declaration

    Swift

    var atRestConstant: CGFloat
  • This is used during the long-press slide. It holds the previous location of the gesture. We use it to determine if the finger has moved.

    Declaration

    Swift

    var oldLocation: CGFloat
  • This holds the last time of the selected page. We use this to set the time, when directly navigating away from Now.

    Declaration

    Swift

    var lastTime: Int
  • This tracks the current embedded table controller.

    Declaration

    Swift

    var tableDisplayController: VMF_EmbeddedTableControllerProtocol?
  • This is our page view controller.

    Declaration

    Swift

    weak var pageViewController: VMF_DayTimeSearchPageViewController?
  • This is set to true, if we were (past tense) in name search mode.

    Declaration

    Swift

    var wasNameSearchMode: Bool
  • Storage for our search meeting source

    Declaration

    Swift

    var searchMeetings: [MeetingInstance]
  • This has the “mostly organized” meeting data.

    The meetings are organized in ascending local time, arranged by weekday, with [0] being Sunday.

    Declaration

    Swift

    var organizedMeetings: [[MeetingInstance]]
  • This is set to true, if we are in name search mode.

    Declaration

    Swift

    var isNameSearchMode: Bool { get set }
  • True, if this is the direct weekday/time selection mode (PickerView displayed).

    Declaration

    Swift

    var isDirectSelectionMode: Bool { get set }
  • Contains the search text filter. Only relevant, if in Name Search Mode.

    Declaration

    Swift

    var searchText: String { get set }
  • This is set to true, if the “throbber” is shown (hiding everything else).

    Declaration

    Swift

    var isThrobbing: Bool { get set }
  • The bar button item that brings in the Settings Screen.

    Declaration

    Swift

    @IBOutlet
    weak var settingsBarButtonItem: UIBarButtonItem?
  • The bar button item that brings in the My Attendance Screen.

    Declaration

    Swift

    @IBOutlet
    weak var myAttendanceBarButtonItem: UIBarButtonItem?
  • The segmented switch that controls the mode.

    Declaration

    Swift

    @IBOutlet
    weak var weekdayModeSelectorSegmentedSwitch: UISegmentedControl?
  • The embedded table controller container view. This hosts an instance of VMF_DayTimeSearchPageViewController.

    Declaration

    Swift

    @IBOutlet
    weak var tableContainerView: UIView?
  • This contains the search items.

    Declaration

    Swift

    @IBOutlet
    weak var searchItemsContainerView: UIStackView?
  • The text field for entering a search.

    Declaration

    Swift

    @IBOutlet
    weak var searchTextField: UITextField?
  • The button to close the search mode.

    Declaration

    Swift

    @IBOutlet
    weak var searchCloseButton: UIButton?
  • This contains the time selector items.

    Declaration

    Swift

    @IBOutlet
    weak var timeSelectorContainerView: UIView?
  • The decrement time button

    Declaration

    Swift

    @IBOutlet
    weak var leftButton: VMF_TapHoldButton?
  • The increment time button

    Declaration

    Swift

    @IBOutlet
    weak var rightButton: VMF_TapHoldButton?
  • This displays the current time and day.

    Declaration

    Swift

    @IBOutlet
    weak var timeDayDisplayLabel: UILabel?
  • The bottom constraint of the table display area. We use this to shrink the table area, when the keyboard is shown.

    Declaration

    Swift

    @IBOutlet
    weak var bottomConstraint: NSLayoutConstraint?
  • This is a narrow bar, along the top, that shows how far into the day we are.

    Declaration

    Swift

    @IBOutlet
    weak var completionBar: UIView?
  • The “Throbber” view

    Declaration

    Swift

    @IBOutlet
    weak var throbber: UIView?
  • The double-tap gesture for bring us back to today.

    Declaration

    Swift

    @IBOutlet
    weak var labelDoubleTapGesture: UITapGestureRecognizer?
  • The long-press gesture for selecting a weekday.

    Declaration

    Swift

    @IBOutlet
    weak var weekdayLongPressGesture: UILongPressGestureRecognizer?
  • The long-press gesture for selecting a time.

    Declaration

    Swift

    @IBOutlet
    weak var timeDayLongPressGesture: UILongPressGestureRecognizer?
  • The tap recognizer for entering direct selection mode.

    Declaration

    Swift

    @IBOutlet
    weak var directSelectionTapRecognizer: UITapGestureRecognizer?
  • The container, for the Direct Selection Mode items.

    Declaration

    Swift

    @IBOutlet
    weak var directSelectionItemsContainer: UIView?
  • The picker view, for the Direct Selection Mode.

    Declaration

    Swift

    @IBOutlet
    weak var directSelectionPickerView: UIPickerView?
  • The button to exit Direct Selection Mode.

    Declaration

    Swift

    @IBOutlet
    weak var directSelectionCloseButton: UIButton?

Computed Properties

  • These are the meetings that are currently in progress. They are sorted in ascending local start time.

    Declaration

    Swift

    var inProgressMeetings: [MeetingInstance] { get }
  • Returns now, in terms understood by the meeting search.

    weekday is the current weekday, transformed to the meeting data (1 is Sunday) currentIntegerTime is the current time, as an integer (hours * 100 + minute).

    Declaration

    Swift

    var nowIs: (weekday: Int, currentIntegerTime: Int) { get }
  • Returns true, if we need to force a reload from the server.

    Declaration

    Swift

    var needsReload: Bool { get }

Instance Methods

  • This recalculates all the meeting data, breaking it into a couple of sorted arrays.

    Declaration

    Swift

    func reorganizeMeetings()
  • Called to load the meetings from the server.

    Declaration

    Swift

    func loadMeetings(completion inCompletion: @escaping () -> Void)

    Parameters

    completion

    A simple, no-parameter completion. It is always called in the main thread.

  • Get the meetings for a particular weekday.

    Declaration

    Swift

    func getDailyMeetings(for inWeekdayIndex: Int) -> [Int : [MeetingInstance]]

    Parameters

    for

    The 1-based (1 is Sunday) weekday index

    Return Value

    a Dictionary, with the weekday’s meetings, organized by localized start time (the key), which is expressed as military time (HHMM).

  • This returns a new destination controller to use for transitions.

    NOTE: If the controller is in text search mode, then the name search controller is returned.

    Declaration

    Swift

    func getTableDisplay(for inDayIndex: Int, time inTimeIndex: Int) -> VMF_EmbeddedTableController?

    Parameters

    for

    The 0-based “day index.” If it is 0, though, it is the “in-progress” display.

    time

    The 0-based time index. This is the index of the currently selected time slot.

    Return Value

    A new (or reused) view controller, for the destination of the transition.

  • This returns a the meetings we should display, given the day and time.

    NOTE: If the controller is in text search mode, then the search set is returned.

    Declaration

    Swift

    func getCurentMeetings(for inDayIndex: Int = 0, time inTimeIndex: Int = 0) -> [MeetingInstance]

    Parameters

    for

    The 0-based “day index.” If it is 0, though, it is the “in-progress” display.

    time

    The 0-based time index. This is the index of the currently selected time slot.

    Return Value

    A new (or reused) view controller, for the destination of the transition.

  • This opens the screen to a certain day index, and time (not index).

    Declaration

    Swift

    func openTo(dayIndex inDayIndex: Int = -1, time inMilitaryTime: Int = 0)

    Parameters

    dayIndex

    The 1-based (but 0 is in progress, 1-7 is Sunday through Saturday) day index. If omitted, then today/now is selected, and time is ignored.

    time

    The military time (HHMM), as an integer. If omitted, 12AM (0000) is assumed.

  • This returns the index of the time slot that is closest (before or after) to the given time and day.

    Declaration

    Swift

    func getNearestIndex(dayIndex inDayIndex: Int = -1, time inMilitaryTime: Int = 0) -> Int

    Parameters

    dayIndex

    The 1-based day index. If omitted, then today/now is selected, and time is ignored.

    time

    The military time (HHMM), as an integer. If omitted, 12AM (0000) is assumed.

    Return Value

    The index of the time slot closest to (or at the same time as) the given time. It may be prior.

  • This returns the time that corresponds to the day and time presented.

    Declaration

    Swift

    func getTimeOf(dayIndex inDayIndex: Int = -1, timeIndex inTimeIndex: Int = 0) -> Int?

    Parameters

    dayIndex

    The 1-based day index.

    timeIndex

    The index, as a 0-based integer.

    Return Value

    The time, as a military time integer, of the time slot for the given time. Returns nil, if the time can’t be found.

  • This updates the “thermometer” display in the time selector.

    Declaration

    Swift

    func updateThermometer(_ inTablePage: VMF_EmbeddedTableControllerProtocol?)
  • Sets the day picker to whatever the current day/time is.

    Declaration

    Swift

    func setDayPicker()
  • This enables or disables the attendance item.

    Declaration

    Swift

    func setAttendance()

Callbacks

  • The segmented switch that controls the current display mode, was hit.

    Declaration

    Swift

    @IBAction
    func weekdayModeSelectorSegmentedSwitchHit(_ inSwitch: UISegmentedControl)

    Parameters

    inSwitch

    The segmented switch.

  • Called when something changes in the search text field.

    Declaration

    Swift

    @IBAction
    func searchTextChanged(_ inTextField: UITextField)
  • Called when the search close button is hit.

    Declaration

    Swift

    @IBAction
    func searchCloseHit(_: Any)
  • The decrement time button was hit.

    Declaration

    Swift

    @IBAction
    func leftButtonHit(_: Any)
  • The increment time button was hit.

    Declaration

    Swift

    @IBAction
    func rightButtonHit(_: Any)
  • The double-tap gesture recognizer on the weekday/time display label was triggered.

    This resets the screen to today/now.

    Declaration

    Swift

    @IBAction
    func doubleTapOnDayTimeLabel(_: Any)
  • A long-press on the weekday switch was detected. We change the weekday (we do not change to in-progress or search).

    Declaration

    Swift

    @IBAction
    func longPressGestureInWeekdaySwitchDetected(_ inGestureRecognizer: UILongPressGestureRecognizer)

    Parameters

    inGestureRecognizer

    The gesture recognizer that was triggered.

  • A long-press on the day/time display label switch was detected. We change the time slot.

    Declaration

    Swift

    @IBAction
    func longPressGestureInDisplayLabelDetected(_ inGestureRecognizer: UILongPressGestureRecognizer)

    Parameters

    inGestureRecognizer

    The gesture recognizer that was triggered.

  • The weekday/time label was touched.

    Declaration

    Swift

    @IBAction
    func directSelectionOpen(_: Any)
  • The close button for the direct selection mode was hit.

    Declaration

    Swift

    @IBAction
    func directSelectionCloseButtonHit(_: Any)
  • This is called just before the keyboard shows. We use this to “nudge” the table bottom up.

    Declaration

    Swift

    @objc
    func keyboardWillShow(notification inNotification: NSNotification)

    Parameters

    notification

    The notification being passed in.

  • This is called just before the keyboard shows. We use this to return the table bottom to its original position.

    Declaration

    Swift

    @objc
    func keyboardWillHide(notification: NSNotification)

    Parameters

    notification

    The notification being passed in.

  • Reloads all the meetings.

    Declaration

    Swift

    func refreshCalled(completion inCompletion: @escaping () -> Void)

    Parameters

    completion

    A simple empty callback. Always called in the main thread.

Base Class Overrides

  • Called when the view hierarchy loads.

    Declaration

    Swift

    override func viewDidLoad()
  • Called just before the view is to appear.

    Declaration

    Swift

    override func viewWillAppear(_ inIsAnimated: Bool)

    Parameters

    inIsAnimated

    True, if the appearance is animated.

  • Called just after the view appears.

    Declaration

    Swift

    override func viewDidAppear(_ inIsAnimated: Bool)

    Parameters

    inIsAnimated

    True, if the appearance is animated.

  • Called when the subviews are laid out. We use this to ensure that our segmented switch is set up correctly.

    Declaration

    Swift

    override func viewDidLayoutSubviews()
  • Called just before the view disappears.

    Declaration

    Swift

    override func viewWillDisappear(_ inIsAnimated: Bool)

    Parameters

    inIsAnimated

    True, if the disappearance is animated.

  • Called before transitioning to another view controller.

    Declaration

    Swift

    override func prepare(for inSegue: UIStoryboardSegue, sender: Any?)

    Parameters

    for

    The segue instance.

    sender

    ignored.

UIPageViewControllerDataSource Conformance

  • Called to fetch the view controller before the current one.

    Declaration

    Swift

    func pageViewController(_: UIPageViewController, viewControllerBefore inBeforeViewController: UIViewController) -> UIViewController?

    Parameters

    viewControllerBefore

    The view controller after (to the right of) the one we want.

    Return Value

    A new (or reused) view controller to appear before (to the left of) the “before” controller.

  • Called to fetch the view controller after the current one.

    Declaration

    Swift

    func pageViewController(_: UIPageViewController, viewControllerAfter inAfterViewController: UIViewController) -> UIViewController?

    Parameters

    viewControllerBefore

    The view controller before (to the left of) the one we want.

    Return Value

    A new (or reused) view controller to appear after (to the right of) the “before” controller.

UIPageViewControllerDelegate Conformance

  • Called when a swipe transition is done. The only thing we do here, is reset our trackers.

    Declaration

    Swift

    func pageViewController(_: UIPageViewController, didFinishAnimating: Bool, previousViewControllers: [UIViewController], transitionCompleted inIsDone: Bool)

    Parameters

    didFinishAnimating

    The animation is complete (ignored)

    previousViewControllers

    A list of previous view controllers (also ignored)

    transitionCompleted

    True, if the transition completed, and was not aborted.

PickerView Component Enum

  • This enumerates the two PickerView components.

    See more

    Declaration

    Swift

    enum PickerViewComponents : Int
  • This returns the number of components in the picker view.

    Declaration

    Swift

    func numberOfComponents(in: UIPickerView) -> Int

    Parameters

    in

    The picker view (ignored).

    Return Value

    2 (always)

  • This returns the number of rows in each component.

    Declaration

    Swift

    func pickerView(_ inPickerView: UIPickerView, numberOfRowsInComponent inComponent: Int) -> Int

    Parameters

    numberOfRowsInComponent

    The 0-based component index. Component 0 is the weekday, and Component 1, is the time slots for that weekday.

    Return Value

    7 (Component 0), or the number of time slots for the selected weekday (usually around 70).

UIPickerViewDelegate Conformance

  • This returns the title for the selected row, as a label.

    Declaration

    Swift

    func pickerView(_ inPickerView: UIPickerView, viewForRow inRow: Int, forComponent inComponent: Int, reusing inReusing: UIView?) -> UIView

    Parameters

    inPickerView

    The picker view.

    titleForRow

    The 0-based row index.

    forComponent

    The 0-based component index.

    reusing

    Any previously allocated view, to be reused.

    Return Value

    a view (a label), with the title for the indicated row.

  • Get the width of a component.

    Declaration

    Swift

    func pickerView(_: UIPickerView, widthForComponent: Int) -> CGFloat

    Parameters

    widthForComponent

    The 0-based component index.

    Return Value

    The width, in display units, of the component.

  • Called when a row is selected in the picker.

    Declaration

    Swift

    func pickerView(_ inPickerView: UIPickerView, didSelectRow inRow: Int, inComponent: Int)

    Parameters

    inPickerView

    The picker view.

    didSelectRow

    The 0-based row index.

    inComponent

    The 0-based component index.