formatitgn and commenting
This commit is contained in:
parent
f9ce6e9f0f
commit
ecba5863ff
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<MVMCoreErrorObject>) -> 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<T>(_ behaviorBlock:(_ behavior:T)->Void) {
|
||||
pageModel?.behaviors?.compactMap({ $0 as? T }).forEach({ behaviorBlock($0) })
|
||||
pageModel?.behaviors?.compactMap({ $0 as? T }).forEach { behaviorBlock($0) }
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user