From ecba5863ff9a4179e392829d8ee9e87fa6c112b8 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 28 Jul 2020 10:02:54 -0400 Subject: [PATCH] formatitgn and commenting --- .../ScrollingViewController.swift | 29 +++- .../BaseControllers/ViewController.swift | 146 ++++++++++++------ 2 files changed, 123 insertions(+), 52 deletions(-) diff --git a/MVMCoreUI/BaseControllers/ScrollingViewController.swift b/MVMCoreUI/BaseControllers/ScrollingViewController.swift index 0eea84cc..65b145c1 100644 --- a/MVMCoreUI/BaseControllers/ScrollingViewController.swift +++ b/MVMCoreUI/BaseControllers/ScrollingViewController.swift @@ -8,7 +8,12 @@ import Foundation + open class ScrollingViewController: ViewController { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public var dismissKeyboardTapGesture: UITapGestureRecognizer? @IBOutlet public var scrollView: UIScrollView! public var contentView: UIView? @@ -17,6 +22,10 @@ open class ScrollingViewController: ViewController { private var keyboardIsShowing = false private var preKeyboardContentInset: UIEdgeInsets? + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + public init(with scrollView: UIScrollView) { self.scrollView = scrollView super.init(nibName: nil, bundle: nil) @@ -30,7 +39,10 @@ open class ScrollingViewController: ViewController { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } - // MARK: - View Life Cycle + //-------------------------------------------------- + // MARK: - View Lifecycle + //-------------------------------------------------- + open override func viewDidLoad() { super.viewDidLoad() @@ -41,7 +53,7 @@ open class ScrollingViewController: ViewController { scrollView.alwaysBounceVertical = false scrollView.delegate = self } - + open override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() view.setNeedsUpdateConstraints() @@ -53,7 +65,10 @@ open class ScrollingViewController: ViewController { registerForKeyboardNotifications() } + //-------------------------------------------------- // MARK: - Keyboard Handling + //-------------------------------------------------- + open func registerForKeyboardNotifications() { if !keyboardNotificationsAdded { keyboardNotificationsAdded = true @@ -87,16 +102,18 @@ open class ScrollingViewController: ViewController { @objc open func keyboardWillBeHidden(notification: Notification) { keyboardIsShowing = false - + // Disables the tap gesture. dismissKeyboardTapGesture?.isEnabled = false - + MVMCoreUIUtility.setScrollViewInsetForKeyboardHide(notification, scrollView: scrollView, viewController: self, contentInset: preKeyboardContentInset ?? scrollView.contentInset) } - + open func rectToScrollToWhenKeyboardPopsUp() -> CGRect? { guard let field = selectedField, - let parent = selectedField?.superview else { return nil } + let parent = selectedField?.superview + else { return nil } + return scrollView.convert(field.frame, from: parent) } } diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index c46ef48f..076a6117 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -9,6 +9,10 @@ import UIKit @objc open class ViewController: UIViewController, MVMCoreViewControllerProtocol, MVMCoreViewManagerViewControllerProtocol, MoleculeDelegateProtocol, FormHolderProtocol, MVMCoreActionDelegateProtocol, MVMCoreLoadDelegateProtocol, UITextFieldDelegate, UITextViewDelegate, ObservingTextFieldDelegate, MVMCoreUIDetailViewProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + @objc public var pageType: String? @objc public var loadObject: MVMCoreLoadObject? public var pageModel: MVMControllerModelProtocol? @@ -33,16 +37,21 @@ import UIKit private var previousScreenSize = CGSize.zero public var selectedField: UIView? - + /// Checks if the screen width has changed open func screenSizeChanged() -> Bool { return !MVMCoreGetterUtility.cgfequalwiththreshold(previousScreenSize.width, view.bounds.size.width, 0.1) } + //-------------------------------------------------- // MARK: - Response handling + //-------------------------------------------------- + open func observeForResponseJSONUpdates() { guard !observingForResponses, - (pagesToListenFor()?.count ?? 0 > 0 || modulesToListenFor()?.count ?? 0 > 0) else { return } + (pagesToListenFor()?.count ?? 0 > 0 || modulesToListenFor()?.count ?? 0 > 0) + else { return } + observingForResponses = true NotificationCenter.default.addObserver(self, selector: #selector(responseJSONUpdated(notification:)), name: NSNotification.Name(rawValue: NotificationResponseLoaded), object: nil) } @@ -52,7 +61,7 @@ import UIKit NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NotificationResponseLoaded), object: nil) observingForResponses = false } - + open func pagesToListenFor() -> [String]? { guard let pageType = loadObject?.pageType else { return nil } return [pageType] @@ -69,7 +78,9 @@ import UIKit let pageType = pagesToListenFor()?.first(where: { (pageTypeListened) -> Bool in guard let page = pagesLoaded.optionalDictionaryForKey(pageTypeListened), let pageType = page.optionalStringForKey(KeyPageType), - pageType == pageTypeListened else { return false } + pageType == pageTypeListened + else { return false } + return true }) { newData = true @@ -90,6 +101,7 @@ import UIKit } guard newData else { return } + do { try parsePageJSON() MVMCoreDispatchUtility.performBlock(onMainThread: { @@ -134,8 +146,10 @@ import UIKit switch (registryError) { case .decoderErrorModelNotMapped(let identifier, let codingKey, let codingPath) where identifier != nil && codingKey != nil && codingPath != nil: MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Model identifier \"\(identifier!)\" is not mapped for \"\(codingKey!.stringValue)\" @ \(codingPath!.map { return $0.stringValue })") + case .decoderErrorObjectNotPresent(let codingKey, codingPath: let codingPath): MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Required model \"\(codingKey.stringValue)\" was not found @ \(codingPath.map { return $0.stringValue })") + default: MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Registry error: \(registryError)") } @@ -144,34 +158,39 @@ import UIKit switch (decodingError) { case .keyNotFound(let codingKey, let context): MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Key \(codingKey.stringValue) was not found @ \(context.codingPath.map { return $0.stringValue })") + case .valueNotFound(_, let context): MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Value not found @ \(context.codingPath.map { return $0.stringValue })") + case .typeMismatch(_, let context): MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Type mismatch @ \(context.codingPath.map { return $0.stringValue })") + case .dataCorrupted(let context): MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: Data corrupted @ \(context.codingPath.map { return $0.stringValue })") + @unknown default: MVMCoreLoggingHandler.shared()?.handleDebugMessage("Error parsing template: \(parsingError)") } } } - open func parsePageJSON() throws { - } + open func parsePageJSON() throws { } open class func verifyRequiredModulesLoaded(for loadObject: MVMCoreLoadObject?, error: AutoreleasingUnsafeMutablePointer) -> Bool { - guard let pageType = loadObject?.pageType, var modulesRequired = MVMCoreUIViewControllerMappingObject.shared()?.modulesRequired(forPageType: pageType), - !modulesRequired.isEmpty else { return true } - + guard let pageType = loadObject?.pageType, + var modulesRequired = MVMCoreUIViewControllerMappingObject.shared()?.modulesRequired(forPageType: pageType), + !modulesRequired.isEmpty + else { return true } + guard let loadedModules = loadObject?.modulesJSON else { return false } - + for case let key as String in Array(loadedModules.keys) { guard modulesRequired.count > 0 else { break } if let index = modulesRequired.firstIndex(where: {($0 as? String) == key}) { modulesRequired.remove(at: index) } } - + guard modulesRequired.count == 0 else { if let errorObject = MVMCoreErrorObject(title: nil, message: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorCritical), messageToLog: modulesRequired.description, code: ErrorCode.requiredModuleNotPresent.rawValue, domain: ErrorDomainNative, location: MVMCoreLoadHandler.sharedGlobal()?.errorLocation(forRequest: loadObject!)) { error.pointee = errorObject @@ -194,14 +213,14 @@ import UIKit navigationModel.title = pageModel?.screenHeading return navigationModel } - + /// Processes any new data. Called after the page is loaded the first time and on response updates for this page, open func handleNewData() { if formValidator == nil { let rules = pageModel?.formRules formValidator = FormValidator(rules) } - + if let backgroundColor = pageModel?.backgroundColor { view.backgroundColor = backgroundColor.uiColor } @@ -210,7 +229,9 @@ import UIKit setNavigationItem() } + //-------------------------------------------------- // MARK: - Navigation Item (Move to model base) + //-------------------------------------------------- open func getNavigationModel() -> NavigationItemModelProtocol? { // TODO: remove legacy. Temporary, convert legacy to navigation model. @@ -224,7 +245,8 @@ import UIKit /// Sets the navigation item for this view controller. open func setNavigationItem() { guard let navigationItemModel = getNavigationModel(), - let navigationController = navigationController else { return } + let navigationController = navigationController + else { return } // We additionally want our left items navigationItem.leftItemsSupplementBackButton = true @@ -238,8 +260,8 @@ import UIKit let viewController = manager ?? self guard let navigationItemModel = getNavigationModel(), let navigationController = viewController.navigationController else { - MVMCoreUISession.sharedGlobal()?.splitViewController?.parent?.setNeedsStatusBarAppearanceUpdate() - return + MVMCoreUISession.sharedGlobal()?.splitViewController?.parent?.setNeedsStatusBarAppearanceUpdate() + return } // Utilize helper function to set the split view and navigation item state. @@ -265,33 +287,35 @@ import UIKit open func showRightPanelForScreenBeforeLaunchApp() -> Bool { return loadObject?.pageJSON?.lenientBoolForKey("showRightPanel") ?? false } - + // Eventually will be moved to separate button in navigation item model open func isOverridingRightButton() -> Bool { - guard let rightPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("rightPanelButtonLink") else { - return false - } + guard let rightPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("rightPanelButtonLink") + else { return false } MVMCoreActionHandler.shared()?.handleAction(with: rightPanelLink, additionalData: nil, delegateObject: delegateObject()) return true } - + // Eventually will be moved to separate button in navigation item model open func isOverridingLeftButton() -> Bool { - guard let leftPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("leftPanelButtonLink") else { - return false - } + guard let leftPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("leftPanelButtonLink") + else { return false } MVMCoreActionHandler.shared()?.handleAction(with: leftPanelLink, additionalData: nil, delegateObject: delegateObject()) return true } - + // Eventually will be moved to Model open func bottomProgress() -> Float? { guard let progressString = loadObject?.pageJSON?.optionalStringForKey(KeyProgressPercent), - let progress = Float(progressString) else { return nil } - return (progress / Float(100)) + let progress = Float(progressString) + else { return nil } + + return progress / Float(100) } - + //-------------------------------------------------- // MARK: - TabBar + //-------------------------------------------------- + open func updateTabBar() { guard MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() == self, let tabModel = pageModel as? TabPageModelProtocol else { return } @@ -301,28 +325,30 @@ import UIKit } MVMCoreUISplitViewController.main()?.updateTabBarShowing(!tabModel.tabBarHidden) } - - // MARK: - View lifecycle + + //-------------------------------------------------- + // MARK: - View Lifecycle + //-------------------------------------------------- /// Called only once in viewDidLoad open func initialLoad() { observeForResponseJSONUpdates() } - + /// Called on screen size update. open func updateViews() { _ = formValidator?.validate() } - + override open func viewDidLoad() { super.viewDidLoad() - + // Do any additional setup after loading the view. MVMCoreLoggingHandler.logDebugMessage(withDelegate: "View Controller Loaded : \(self)") - + // We use our own margins. viewRespectsSystemMinimumLayoutMargins = false - + // Presents from the bottom. modalPresentationStyle = MVMCoreGetterUtility.isOnIPad() ? .formSheet : .overCurrentContext @@ -392,7 +418,7 @@ import UIKit stopObservingForResponseJSONUpdates() MVMCoreLoggingHandler.logDebugMessage(withDelegate: "View Controller Deallocated : \(self)") } - + open override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return MVMCoreGetterUtility.isOnIPad() ? UIInterfaceOrientationMask.all : UIInterfaceOrientationMask.portrait } @@ -401,18 +427,22 @@ import UIKit super.viewWillTransition(to: size, with: coordinator) // Updates the detail view width - coordinator.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) in - }) { (UIViewControllerTransitionCoordinatorContext) in + coordinator.animate(alongsideTransition: { UIViewControllerTransitionCoordinatorContext in + }) { UIViewControllerTransitionCoordinatorContext in self.view.setNeedsLayout() } } - + //-------------------------------------------------- // MARK: - MVMCoreViewManagerViewControllerProtocol + //-------------------------------------------------- + open func viewControllerReady(inManager manager: UIViewController & MVMCoreViewManagerProtocol) { pageShown() } - + //-------------------------------------------------- // MARK: - MVMCoreLoadDelegateProtocol + //-------------------------------------------------- + // TODO: Move this function out of here after architecture cleanup. open func loadFinished(_ loadObject: MVMCoreLoadObject?, loadedViewController: (UIViewController & MVMCoreViewControllerProtocol)?, error: MVMCoreErrorObject?) { @@ -433,8 +463,11 @@ import UIKit }) } } - + + //-------------------------------------------------- // MARK: - MVMCoreActionDelegateProtocol + //-------------------------------------------------- + open func handleOpenPage(for requestParameters: MVMCoreRequestParameters, actionInformation: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?) { formValidator?.addFormParams(requestParameters: requestParameters) requestParameters.parentPageType = loadObject?.pageJSON?.optionalStringForKey("parentPageType") @@ -445,7 +478,10 @@ import UIKit MVMCoreUILoggingHandler.shared()?.defaultLogAction(forController: self, actionInformation: actionInformation, additionalData: additionalData) } + //-------------------------------------------------- // MARK: - MoleculeDelegateProtocol + //-------------------------------------------------- + open func getModuleWithName(_ name: String?) -> [AnyHashable: Any]? { guard let name = name else { return nil } return loadObject?.modulesJSON?.optionalDictionaryForKey(name) @@ -456,6 +492,7 @@ import UIKit let moleculeName = moduleJSON.optionalStringForKey("moleculeName"), let modelType = ModelRegistry.getType(for: moleculeName, with: MoleculeModelProtocol.self) else { return nil } + do { return try modelType.decode(jsonDict: moduleJSON) as? MoleculeModelProtocol } catch { @@ -471,28 +508,37 @@ import UIKit open func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation) {} open func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], animation: UITableView.RowAnimation) {} + //-------------------------------------------------- // MARK: - MVMCoreUIDetailViewProtocol + //-------------------------------------------------- // Reset the navigation state. public func splitViewDidReset() { setNavigationBar() } - // MARK: - UITextFieldDelegate (Check if this is still needed) + //-------------------------------------------------- + // MARK: - UITextFieldDelegate + //-------------------------------------------------- + // To Remove TextFields Bug: Keyboard is not dismissing after reaching textfield max length limit open func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() return true } - + open func textFieldDidBeginEditing(_ textField: UITextField) { selectedField = textField // TODO: Make this into a protocol if UIAccessibility.isVoiceOverRunning { - if let toolBar = textField.inputAccessoryView as? UIToolbar, let _ = toolBar.items?.last, let pickerView = textField.inputView as? UIPickerView { + if let toolBar = textField.inputAccessoryView as? UIToolbar, + let _ = toolBar.items?.last, + let pickerView = textField.inputView as? UIPickerView { + view.accessibilityElements = [pickerView, toolBar] } + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { UIAccessibility.post(notification: .layoutChanged, argument: textField.inputView) } @@ -500,11 +546,13 @@ import UIKit } open func textFieldDidEndEditing(_ textField: UITextField, reason: UITextField.DidEndEditingReason) { + if textField === selectedField { if UIAccessibility.isVoiceOverRunning { view.accessibilityElements = nil UIAccessibility.post(notification: .layoutChanged, argument: textField) } + selectedField = nil } } @@ -513,7 +561,10 @@ import UIKit selectedField?.resignFirstResponder() } - // MARK: - UITextViewDelegate (Check if this is still needed) + //-------------------------------------------------- + // MARK: - UITextViewDelegate + //-------------------------------------------------- + open func textViewDidBeginEditing(_ textView: UITextView) { selectedField = textView } @@ -524,8 +575,11 @@ import UIKit } } + //-------------------------------------------------- // MARK: - Behavior Execution + //-------------------------------------------------- + func executeBehaviors(_ behaviorBlock:(_ behavior:T)->Void) { - pageModel?.behaviors?.compactMap({ $0 as? T }).forEach({ behaviorBlock($0) }) + pageModel?.behaviors?.compactMap({ $0 as? T }).forEach { behaviorBlock($0) } } }