From 710adec0379d2570a881701d2d205e14a62e6ed5 Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Mon, 30 Mar 2020 10:58:47 -0400 Subject: [PATCH 01/15] add web view --- MVMCoreUI.xcodeproj/project.pbxproj | 8 + MVMCoreUI/Atomic/Atoms/Views/WebView.swift | 181 ++++++++++++++++++ .../Atomic/Atoms/Views/WebViewModel.swift | 55 ++++++ MVMCoreUI/Atomic/MoleculeObjectMapping.swift | 1 + 4 files changed, 245 insertions(+) create mode 100644 MVMCoreUI/Atomic/Atoms/Views/WebView.swift create mode 100644 MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 03335a4d..9a942945 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -137,6 +137,8 @@ 9432A79F23DB47BA00719041 /* EntryFieldContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9432A79E23DB47BA00719041 /* EntryFieldContainer.swift */; }; 943784F5236B77BB006A1E82 /* GraphView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943784F3236B77BB006A1E82 /* GraphView.swift */; }; 943784F6236B77BB006A1E82 /* GraphViewAnimationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943784F4236B77BB006A1E82 /* GraphViewAnimationHandler.swift */; }; + 943820842432382400B43AF3 /* WebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943820832432382400B43AF3 /* WebView.swift */; }; + 94382086243238D100B43AF3 /* WebViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94382085243238D100B43AF3 /* WebViewModel.swift */; }; 9445890C2385BCE300DE9FD4 /* ProgressBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9445890B2385BCE300DE9FD4 /* ProgressBarModel.swift */; }; 9445890E2385C3F800DE9FD4 /* MultiProgressModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9445890D2385C3F800DE9FD4 /* MultiProgressModel.swift */; }; 9445891F2385D2E900DE9FD4 /* CaretViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9445891E2385D2E900DE9FD4 /* CaretViewModel.swift */; }; @@ -522,6 +524,8 @@ 9432A79E23DB47BA00719041 /* EntryFieldContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EntryFieldContainer.swift; sourceTree = ""; }; 943784F3236B77BB006A1E82 /* GraphView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphView.swift; sourceTree = ""; }; 943784F4236B77BB006A1E82 /* GraphViewAnimationHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphViewAnimationHandler.swift; sourceTree = ""; }; + 943820832432382400B43AF3 /* WebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebView.swift; sourceTree = ""; }; + 94382085243238D100B43AF3 /* WebViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewModel.swift; sourceTree = ""; }; 9445890B2385BCE300DE9FD4 /* ProgressBarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBarModel.swift; sourceTree = ""; }; 9445890D2385C3F800DE9FD4 /* MultiProgressModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiProgressModel.swift; sourceTree = ""; }; 9445891E2385D2E900DE9FD4 /* CaretViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaretViewModel.swift; sourceTree = ""; }; @@ -1526,6 +1530,8 @@ 0AA33B392398524F0067DD0F /* Toggle.swift */, 0AE98BB623FF18E9004C5109 /* ArrowModel.swift */, 0AE98BB423FF18D2004C5109 /* Arrow.swift */, + 94382085243238D100B43AF3 /* WebViewModel.swift */, + 943820832432382400B43AF3 /* WebView.swift */, ); path = Views; sourceTree = ""; @@ -1902,6 +1908,7 @@ 0116A4E5228B19640094F3ED /* RadioButtonSelectionHelper.swift in Sources */, 017BEB48236230DB0024EF95 /* MoleculeViewProtocol.swift in Sources */, D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */, + 94382086243238D100B43AF3 /* WebViewModel.swift in Sources */, D27CD40E2322EEAF00C1DC07 /* TabsTableViewCell.swift in Sources */, D224799B231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift in Sources */, D22D1F1F220343560077CEC0 /* MVMCoreUICheckMarkView.m in Sources */, @@ -1972,6 +1979,7 @@ D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */, D29DF12B21E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m in Sources */, 94C2D9A723872DA90006CF46 /* LabelAttributeColorModel.swift in Sources */, + 943820842432382400B43AF3 /* WebView.swift in Sources */, 0103B84E23D7E33A009C315C /* HeadlineBodyToggleModel.swift in Sources */, D2755D7B23689C7500485468 /* TableViewCell.swift in Sources */, 0A21DB85235E06EF00C160A2 /* MFTextField.m in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift new file mode 100644 index 00000000..64343aa7 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -0,0 +1,181 @@ +// +// MVMCoreUIWebView.swift +// MVMCoreUI +// +// Created by Ryan on 8/29/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit +import WebKit + +@objcMembers open class WebView: View, MVMCoreUIViewConstrainingProtocol { + + public let loadingSpinner = MFLoadingSpinner(frame: .zero) + + var delegateObject: MVMCoreUIDelegateObject? + var webView: WKWebView? + var webViewHeight: NSLayoutConstraint? + var dynamicHeight: Bool = true + + var callHandlerName: String? + + override open func setupView() { + super.setupView() + let webView = createWebView(messageHandler: nil, jsScript: nil) + addSubview(webView) + NSLayoutConstraint.constraintPinSubview(toSuperview: webView) + self.webView = webView + pinSpinnerView() + } + + func createWebView(messageHandler: String?, jsScript:String?) -> WKWebView { + let wkUserController = WKUserContentController() + if let messageHandlerName = messageHandler { + wkUserController.add(self, name: messageHandlerName) + } + //inital-scale is necessary to update display correct scale + let source: String = "var meta = document.createElement('meta');" + + "meta.name = 'viewport';" + + //depends on how much clint driven + "meta.content = 'initial-scale=1.0';" + + "var head = document.getElementsByTagName('head')[0];" + "head.appendChild(meta);"; + let wkScript = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: true) + wkUserController.addUserScript(wkScript) + + //server driven addition script + if let jsScript = jsScript { + let wkScript = WKUserScript(source: jsScript, injectionTime: .atDocumentStart, forMainFrameOnly: true) + wkUserController.addUserScript(wkScript) + } + + let wkConfig = WKWebViewConfiguration() + wkConfig.userContentController = wkUserController + let webView = WKWebView(frame: .zero, configuration: wkConfig) + webView.translatesAutoresizingMaskIntoConstraints = false + webView.uiDelegate = self + webView.navigationDelegate = self + return webView + } + + + // MARK: - MVMCoreUIMoleculeViewProtocol + override open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? WebViewModel else { return } + self.delegateObject = delegateObject + + if model.callHandler != nil || model.jsScript != nil { +/* + webView's configuration property is immutable. + In order to add call handler into webview, need to create a new webview. + callHanlder + */ + webView?.removeFromSuperview() + let webView = createWebView(messageHandler: model.callHandler, jsScript:model.jsScript) + addSubview(webView) + NSLayoutConstraint.constraintPinSubview(toSuperview: webView) + self.webView = webView + } + //init height for loading spinner + webViewHeight = webView?.heightAnchor.constraint(equalToConstant: 50) + webViewHeight?.isActive = true + + if let height = model.height { + webViewHeight?.constant = height + dynamicHeight = false + } + if let url = model.url { + webView?.load(URLRequest(url: url)) + } else if let htmlString = model.htmlString { + webView?.loadHTMLString(htmlString, baseURL: nil) + } + bringSubviewToFront(loadingSpinner) + //TO DO: local path? + } + + func pinSpinnerView() { + addSubview(loadingSpinner) + // Setup spinner. + loadingSpinner.clipsToBounds = true + loadingSpinner.translatesAutoresizingMaskIntoConstraints = false + + loadingSpinner.heightAnchor.constraint(equalToConstant: 50.0).isActive = true + loadingSpinner.widthAnchor.constraint(equalToConstant: 50.0).isActive = true + loadingSpinner.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true + loadingSpinner.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + loadingSpinner.resumeSpinner() + } +} + + +// MARK: - WKUIDelegate +extension WebView : WKUIDelegate { + public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { + // hide loading + loadingSpinner.pause() + + if !dynamicHeight { + return + } + //update webview's heigth when webview is ready + webView.evaluateJavaScript("document.readyState", completionHandler: { [weak self] (result, error) in + if result == nil || error != nil { + return + } + webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { (result, error) in + guard let height = result as? CGFloat else { return } + self?.webViewHeight?.constant = height + self?.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self!) + }) + }) + } + + public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { + // show loading + loadingSpinner.resumeSpinner() + } + public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { + // hide loading + loadingSpinner.pause() + } + +} + + +// MARK: - WKNavigationDelegate +extension WebView : WKNavigationDelegate { + public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { + //validate request url + //all validated link should be open in safari + if (navigationAction.navigationType == .linkActivated), let urlString = navigationAction.request.url?.absoluteString.removingPercentEncoding, !urlString.contains("#") { + MVMCoreActionHandler.shared()?.openURL(inWebView: navigationAction.request.url, actionInformation: nil, additionalData: nil, delegateObject: nil) + decisionHandler(.cancel) + } else { + decisionHandler(.allow) + } + } +} + + +// MARK: - WKScriptMessageHandler +extension WebView: WKScriptMessageHandler { + public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { + if message.name == callHandlerName, let text = message.body as? String { + /* + receiving JavaScript func webkit.messageHandlers.{callHandler}.postMessage(body); + if body is dictionary string + MVMCoreActionHanlder handleAction + */ + if let data = text.data(using: .utf8) { + do { + let actionMap = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: nil, delegateObject: self.delegateObject) + } catch { + // if post message is not dictionary string + print(error.localizedDescription) + } + } + } + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift b/MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift new file mode 100644 index 00000000..bb141b57 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift @@ -0,0 +1,55 @@ +// +// WebviewModel.swift +// MVMCoreUI +// +// Created by Kruthika KP on 09/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers public class WebViewModel: MoleculeModelProtocol { + public static var identifier: String = "webview" + public var moleculeName: String = WebViewModel.identifier + public var backgroundColor: Color? + public var url: URL? + public var htmlString: String? + public var height: CGFloat? + public var jsScript: String? + public var callHandler: String? + public var buttonMap: [String: ButtonModel]? + + private enum CodingKeys: String, CodingKey{ + case moleculeName + case backgroundColor + case url + case htmlString + case height + case buttonMap + case jsScript + case callHandler + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + url = try typeContainer.decodeIfPresent(URL.self, forKey: .url) + htmlString = try typeContainer.decodeIfPresent(String.self, forKey: .htmlString) + height = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .height) + buttonMap = try typeContainer.decodeIfPresent([String: ButtonModel].self, forKey: .buttonMap) + jsScript = try typeContainer.decodeIfPresent(String.self, forKey: .jsScript) + callHandler = try typeContainer.decodeIfPresent(String.self, forKey: .callHandler) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(url, forKey: .url) + try container.encodeIfPresent(htmlString, forKey: .htmlString) + try container.encodeIfPresent(height, forKey: .height) + try container.encodeIfPresent(jsScript, forKey: .jsScript) + try container.encodeIfPresent(callHandler, forKey: .callHandler) + + } +} diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index bfa6db01..ba650ca8 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -80,6 +80,7 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: Arrow.self, viewModelClass: ArrowModel.self) MoleculeObjectMapping.shared()?.register(viewClass: RadioButton.self, viewModelClass: RadioButtonModel.self) MoleculeObjectMapping.shared()?.register(viewClass: RadioButtonLabel.self, viewModelClass: RadioButtonLabelModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: WebView.self, viewModelClass: WebViewModel.self) // Horizontal Combination Molecules MoleculeObjectMapping.shared()?.register(viewClass: StringAndMoleculeView.self, viewModelClass: StringAndMoleculeModel.self) From 16810482796bf8ec5cfff76bc6ae1ee7532d66d1 Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Mon, 30 Mar 2020 12:10:27 -0400 Subject: [PATCH 02/15] remove initial scale jscript --- MVMCoreUI/Atomic/Atoms/Views/WebView.swift | 46 +++++++++++----------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift index 64343aa7..b3016e00 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -19,6 +19,9 @@ import WebKit var dynamicHeight: Bool = true var callHandlerName: String? + var webviewModel: WebViewModel? { + return model as? WebViewModel + } override open func setupView() { super.setupView() @@ -34,14 +37,6 @@ import WebKit if let messageHandlerName = messageHandler { wkUserController.add(self, name: messageHandlerName) } - //inital-scale is necessary to update display correct scale - let source: String = "var meta = document.createElement('meta');" + - "meta.name = 'viewport';" + - //depends on how much clint driven - "meta.content = 'initial-scale=1.0';" + - "var head = document.getElementsByTagName('head')[0];" + "head.appendChild(meta);"; - let wkScript = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: true) - wkUserController.addUserScript(wkScript) //server driven addition script if let jsScript = jsScript { @@ -65,12 +60,11 @@ import WebKit guard let model = model as? WebViewModel else { return } self.delegateObject = delegateObject - if model.callHandler != nil || model.jsScript != nil { + if model.callHandler != nil || model.jsScript != nil { /* webView's configuration property is immutable. In order to add call handler into webview, need to create a new webview. - callHanlder - */ +*/ webView?.removeFromSuperview() let webView = createWebView(messageHandler: model.callHandler, jsScript:model.jsScript) addSubview(webView) @@ -91,7 +85,6 @@ import WebKit webView?.loadHTMLString(htmlString, baseURL: nil) } bringSubviewToFront(loadingSpinner) - //TO DO: local path? } func pinSpinnerView() { @@ -115,20 +108,29 @@ extension WebView : WKUIDelegate { // hide loading loadingSpinner.pause() + //update webview's heigth when webview is ready if !dynamicHeight { return } - //update webview's heigth when webview is ready - webView.evaluateJavaScript("document.readyState", completionHandler: { [weak self] (result, error) in - if result == nil || error != nil { - return - } - webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { (result, error) in - guard let height = result as? CGFloat else { return } - self?.webViewHeight?.constant = height - self?.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self!) + if let _ = webviewModel?.jsScript { +/* evaluateJavaScript only works when webview contains userscrpt. + otherwise, it would return WKErrorDomain Code=4 "A JavaScript exception occurred" + */ + webView.evaluateJavaScript("document.readyState", completionHandler: { [weak self] (result, error) in + if result == nil || error != nil { + return + } + webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { (result, error) in + guard let height = result as? CGFloat else { return } + self?.webViewHeight?.constant = height + self?.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self!) + }) }) - }) + } else { + //get webview's content viewheight when no javescript setup + self.webViewHeight?.constant = webView.scrollView.contentSize.height + self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) + } } public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { From bffd2475dff004940835422a953712ccc8f04999 Mon Sep 17 00:00:00 2001 From: Kruthika KP <> Date: Wed, 1 Apr 2020 12:42:41 +0530 Subject: [PATCH 03/15] Initial Commit of List - Three Column - International Data - Divider --- MVMCoreUI.xcodeproj/project.pbxproj | 8 +++ MVMCoreUI/Atomic/MoleculeObjectMapping.swift | 1 + ...tThreeColumnInternationalDataDivider.swift | 66 +++++++++++++++++++ ...eColumnInternationalDataDividerModel.swift | 52 +++++++++++++++ 4 files changed, 127 insertions(+) create mode 100644 MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift create mode 100644 MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDividerModel.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 03335a4d..142d72ec 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -129,6 +129,8 @@ 8D084AD22410BF7600951227 /* ListOneColumnFullWidthTextBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D084AD12410BF7600951227 /* ListOneColumnFullWidthTextBodyText.swift */; }; 8D24041123E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D24041023E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift */; }; 8D24041523E7FC0B009E23BE /* ListLeftVariableIconWithRightCaretModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D24041423E7FC0B009E23BE /* ListLeftVariableIconWithRightCaretModel.swift */; }; + 8D3BA9BD2433787000D341BA /* ListThreeColumnInternationalDataDividerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D3BA9BC2433787000D341BA /* ListThreeColumnInternationalDataDividerModel.swift */; }; + 8D3BA9BF2433789900D341BA /* ListThreeColumnInternationalDataDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D3BA9BE2433789900D341BA /* ListThreeColumnInternationalDataDivider.swift */; }; 8D448E5524050A46006211BB /* ListOneColumnFullWidthTextAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D448E5424050A46006211BB /* ListOneColumnFullWidthTextAllTextAndLinksModel.swift */; }; 942C372E241149170066E45E /* NHaasGroteskDSStd-75Bd.otf in Resources */ = {isa = PBXBuildFile; fileRef = 942C372C241149170066E45E /* NHaasGroteskDSStd-75Bd.otf */; }; 942C372F241149170066E45E /* NHaasGroteskDSStd-55Rg.otf in Resources */ = {isa = PBXBuildFile; fileRef = 942C372D241149170066E45E /* NHaasGroteskDSStd-55Rg.otf */; }; @@ -513,6 +515,8 @@ 8D084AD12410BF7600951227 /* ListOneColumnFullWidthTextBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextBodyText.swift; sourceTree = ""; }; 8D24041023E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableIconWithRightCaret.swift; sourceTree = ""; }; 8D24041423E7FC0B009E23BE /* ListLeftVariableIconWithRightCaretModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableIconWithRightCaretModel.swift; sourceTree = ""; }; + 8D3BA9BC2433787000D341BA /* ListThreeColumnInternationalDataDividerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnInternationalDataDividerModel.swift; sourceTree = ""; }; + 8D3BA9BE2433789900D341BA /* ListThreeColumnInternationalDataDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnInternationalDataDivider.swift; sourceTree = ""; }; 8D448E5424050A46006211BB /* ListOneColumnFullWidthTextAllTextAndLinksModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextAllTextAndLinksModel.swift; sourceTree = ""; }; 9402C34F23A2CEA3004B974C /* LeftRightLabelModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeftRightLabelModel.swift; sourceTree = ""; }; 942C372C241149170066E45E /* NHaasGroteskDSStd-75Bd.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NHaasGroteskDSStd-75Bd.otf"; sourceTree = ""; }; @@ -1244,6 +1248,8 @@ children = ( 5248BFEB23F12E350059236A /* ListThreeColumnPlanDataDividerModel.swift */, 5248BFEA23F12E350059236A /* ListThreeColumnPlanDataDivider.swift */, + 8D3BA9BC2433787000D341BA /* ListThreeColumnInternationalDataDividerModel.swift */, + 8D3BA9BE2433789900D341BA /* ListThreeColumnInternationalDataDivider.swift */, ); path = ThreeColumn; sourceTree = ""; @@ -1884,6 +1890,7 @@ 8D070BB0241B56530099AC56 /* ListRightVariableTotalDataModel.swift in Sources */, 943784F5236B77BB006A1E82 /* GraphView.swift in Sources */, 31BE15CC23D8924D00452370 /* CheckboxModel.swift in Sources */, + 8D3BA9BF2433789900D341BA /* ListThreeColumnInternationalDataDivider.swift in Sources */, 94C661DA23CCF4FB00D9FE5B /* UIColor+Extension.swift in Sources */, D29DF32121ED0CBA003B2FB9 /* LabelView.m in Sources */, D28A838123CCB0D800DFE4FC /* AccordionListItemModel.swift in Sources */, @@ -2181,6 +2188,7 @@ 01509D912327ECE600EF99AA /* CornerLabels.swift in Sources */, D22D1F1B220341F60077CEC0 /* MVMCoreUICheckBox.m in Sources */, C695A69823C990C200BFB94E /* DoughnutChartView.swift in Sources */, + 8D3BA9BD2433787000D341BA /* ListThreeColumnInternationalDataDividerModel.swift in Sources */, D29DF2CB21E7BFCC003B2FB9 /* MFSizeThreshold.m in Sources */, 011D959F240453A1000E3791 /* RuleAllValueChangedModel.swift in Sources */, 011D95AD2406BB57000E3791 /* FormHolderProtocol.swift in Sources */, diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index bfa6db01..13c61445 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -143,6 +143,7 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: ListOneColumnTextWithWhitespaceDividerShort.self, viewModelClass: ListOneColumnTextWithWhitespaceDividerShortModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListOneColumnTextWithWhitespaceDividerTall.self, viewModelClass: ListOneColumnTextWithWhitespaceDividerTallModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListOneColumnFullWidthTextDividerSubsection.self, viewModelClass: ListOneColumnFullWidthTextDividerSubsectionModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListThreeColumnInternationalDataDivider.self, viewModelClass: ListThreeColumnInternationalDataDividerModel.self) // Designed Headers MoleculeObjectMapping.shared()?.register(viewClass: HeadersH2NoButtonsBodyText.self, viewModelClass: HeadersH2NoButtonsBodyTextModel.self) diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift new file mode 100644 index 00000000..89868806 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift @@ -0,0 +1,66 @@ +// +// ListThreeColumnInternationalDataDivider.swift +// MVMCoreUI +// +// Created by Kruthika KP on 31/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers open class ListThreeColumnInternationalDataDivider: TableViewCell { + + //----------------------------------------------------- + // MARK: - Outlets + //----------------------------------------------------- + let leftLabel = Label.createLabelBoldBodySmall(true) + let centerLabel = Label.createLabelBoldBodySmall(true) + let rightLabel = Label.createLabelBoldBodySmall(true) + var stack: Stack + + public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + stack = Stack.createStack(with: [(view: leftLabel, model: StackItemModel(percent: 33, horizontalAlignment: .leading)), + (view: centerLabel, model: StackItemModel(percent: 27, horizontalAlignment: .leading)), + (view: rightLabel, model: StackItemModel(percent: 40, horizontalAlignment: .center))], + axis: .horizontal) + super.init(style: style, reuseIdentifier: reuseIdentifier) + } + + public required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + //----------------------------------------------------- + // MARK: - MFViewProtocol + //----------------------------------------------------- + open override func setupView() { + super.setupView() + addMolecule(stack) + stack.restack() + } + + //----------------------------------------------------- + // MARK: - ModelMoleculeViewProtocol + //----------------------------------------------------- + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? ListThreeColumnInternationalDataDividerModel else { return } + leftLabel.set(with: model.leftLabel, delegateObject, additionalData) + centerLabel.set(with: model.centerLabel, delegateObject, additionalData) + rightLabel.set(with: model.rightLabel, delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return 121 + } + + //----------------------------------------------------- + // MARK: - MVMCoreUIMoleculeViewProtocol + //----------------------------------------------------- + override open func reset() { + super.reset() + leftLabel.styleBoldBodySmall(true) + centerLabel.styleBoldBodySmall(true) + rightLabel.styleBoldBodySmall(true) + } +} diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDividerModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDividerModel.swift new file mode 100644 index 00000000..51786004 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDividerModel.swift @@ -0,0 +1,52 @@ +// +// ListThreeColumnInternationalDataDividerModel.swift +// MVMCoreUI +// +// Created by Kruthika KP on 31/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +public class ListThreeColumnInternationalDataDividerModel : ListItemModel, MoleculeModelProtocol { + public static var identifier: String = "list3CIntDataDiv" + public var leftLabel: LabelModel + public var centerLabel: LabelModel + public var rightLabel: LabelModel + + public init (leftLabel:LabelModel, centerLabel:LabelModel, rightLabel:LabelModel) { + self.leftLabel = leftLabel + self.centerLabel = centerLabel + self.rightLabel = rightLabel + super.init() + } + + override public func setDefaults() { + super.setDefaults() + style = "tallDivider" + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case leftLabel + case centerLabel + case rightLabel + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + leftLabel = try typeContainer.decode(LabelModel.self, forKey: .leftLabel) + centerLabel = try typeContainer.decode(LabelModel.self, forKey: .centerLabel) + rightLabel = try typeContainer.decode(LabelModel.self, forKey: .rightLabel) + try super.init(from: decoder) + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(leftLabel, forKey: .leftLabel) + try container.encode(centerLabel, forKey: .centerLabel) + try container.encode(rightLabel, forKey: .rightLabel) + } +} From a9ca55c7382f800e4ba62cb285eaf956abdfc9e4 Mon Sep 17 00:00:00 2001 From: Kruthika KP <> Date: Wed, 1 Apr 2020 16:01:00 +0530 Subject: [PATCH 04/15] alignment changes --- .../ListThreeColumnInternationalDataDivider.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift index 89868806..42d3d39f 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift @@ -19,9 +19,9 @@ import Foundation var stack: Stack public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - stack = Stack.createStack(with: [(view: leftLabel, model: StackItemModel(percent: 33, horizontalAlignment: .leading)), - (view: centerLabel, model: StackItemModel(percent: 27, horizontalAlignment: .leading)), - (view: rightLabel, model: StackItemModel(percent: 40, horizontalAlignment: .center))], + stack = Stack.createStack(with: [(view: leftLabel, model: StackItemModel(percent: 30, horizontalAlignment: .leading)), + (view: centerLabel, model: StackItemModel(percent: 40, horizontalAlignment: .leading)), + (view: rightLabel, model: StackItemModel(percent: 30, horizontalAlignment: .center))], axis: .horizontal) super.init(style: style, reuseIdentifier: reuseIdentifier) } From bfe706908f23c960104735a0829632e6fb4527f4 Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Wed, 1 Apr 2020 12:59:54 -0400 Subject: [PATCH 05/15] update web view with default call handler --- MVMCoreUI/Atomic/Atoms/Views/WebView.swift | 47 ++++++++-------------- 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift index b3016e00..c5cc0cf9 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -10,7 +10,9 @@ import UIKit import WebKit @objcMembers open class WebView: View, MVMCoreUIViewConstrainingProtocol { - + + let mvmWebViewMessageHandler = "mvmWebViewMessageHandler" + public let loadingSpinner = MFLoadingSpinner(frame: .zero) var delegateObject: MVMCoreUIDelegateObject? @@ -25,7 +27,7 @@ import WebKit override open func setupView() { super.setupView() - let webView = createWebView(messageHandler: nil, jsScript: nil) + let webView = createWebView(messageHandler: mvmWebViewMessageHandler, jsScript: nil) addSubview(webView) NSLayoutConstraint.constraintPinSubview(toSuperview: webView) self.webView = webView @@ -112,24 +114,16 @@ extension WebView : WKUIDelegate { if !dynamicHeight { return } - if let _ = webviewModel?.jsScript { -/* evaluateJavaScript only works when webview contains userscrpt. - otherwise, it would return WKErrorDomain Code=4 "A JavaScript exception occurred" - */ - webView.evaluateJavaScript("document.readyState", completionHandler: { [weak self] (result, error) in - if result == nil || error != nil { - return - } - webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { (result, error) in - guard let height = result as? CGFloat else { return } - self?.webViewHeight?.constant = height - self?.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self!) - }) + + /* was using "document.readyState" to check the state, while evaluateJavaScript "document.readyState",only works when webview contains userscrpt.otherwise, it would return WKErrorDomain Code=4 "A JavaScript exception occurred". + so webView.isLoading to check load finished state + */ + if !webView.isLoading { + webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { [weak self] (result, error) in + guard let self = self, let height = result as? CGFloat else { return } + self.webViewHeight?.constant = height + self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) }) - } else { - //get webview's content viewheight when no javescript setup - self.webViewHeight?.constant = webView.scrollView.contentSize.height - self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) } } @@ -163,21 +157,14 @@ extension WebView : WKNavigationDelegate { // MARK: - WKScriptMessageHandler extension WebView: WKScriptMessageHandler { public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { - if message.name == callHandlerName, let text = message.body as? String { + if message.name == mvmWebViewMessageHandler, let actionMap = message.body as? [AnyHashable: Any] { /* receiving JavaScript func webkit.messageHandlers.{callHandler}.postMessage(body); - if body is dictionary string + if body is dictionary MVMCoreActionHanlder handleAction */ - if let data = text.data(using: .utf8) { - do { - let actionMap = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: nil, delegateObject: self.delegateObject) - } catch { - // if post message is not dictionary string - print(error.localizedDescription) - } - } + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: nil, delegateObject: self.delegateObject) + } } } From 83e04f0a1202776acf69a73a270bc46885e781e3 Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Wed, 1 Apr 2020 17:16:23 -0400 Subject: [PATCH 06/15] update with message handler string --- MVMCoreUI/Atomic/Atoms/Views/WebView.swift | 61 +++++++++---------- .../Atomic/Atoms/Views/WebViewModel.swift | 13 ++-- 2 files changed, 32 insertions(+), 42 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift index c5cc0cf9..3d870f53 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -27,25 +27,19 @@ import WebKit override open func setupView() { super.setupView() - let webView = createWebView(messageHandler: mvmWebViewMessageHandler, jsScript: nil) + let webView = createWebView(messageHandler: mvmWebViewMessageHandler) addSubview(webView) NSLayoutConstraint.constraintPinSubview(toSuperview: webView) self.webView = webView pinSpinnerView() } - func createWebView(messageHandler: String?, jsScript:String?) -> WKWebView { + func createWebView(messageHandler: String?) -> WKWebView { let wkUserController = WKUserContentController() if let messageHandlerName = messageHandler { wkUserController.add(self, name: messageHandlerName) } - //server driven addition script - if let jsScript = jsScript { - let wkScript = WKUserScript(source: jsScript, injectionTime: .atDocumentStart, forMainFrameOnly: true) - wkUserController.addUserScript(wkScript) - } - let wkConfig = WKWebViewConfiguration() wkConfig.userContentController = wkUserController let webView = WKWebView(frame: .zero, configuration: wkConfig) @@ -59,33 +53,29 @@ import WebKit // MARK: - MVMCoreUIMoleculeViewProtocol override open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { super.set(with: model, delegateObject, additionalData) - guard let model = model as? WebViewModel else { return } self.delegateObject = delegateObject - - if model.callHandler != nil || model.jsScript != nil { -/* - webView's configuration property is immutable. - In order to add call handler into webview, need to create a new webview. -*/ - webView?.removeFromSuperview() - let webView = createWebView(messageHandler: model.callHandler, jsScript:model.jsScript) - addSubview(webView) - NSLayoutConstraint.constraintPinSubview(toSuperview: webView) - self.webView = webView - } //init height for loading spinner - webViewHeight = webView?.heightAnchor.constraint(equalToConstant: 50) + webViewHeight = webView?.heightAnchor.constraint(equalToConstant: 44) webViewHeight?.isActive = true - if let height = model.height { + if let height = webviewModel?.height { webViewHeight?.constant = height dynamicHeight = false } - if let url = model.url { + if let url = webviewModel?.url { webView?.load(URLRequest(url: url)) - } else if let htmlString = model.htmlString { + } else if let htmlString = webviewModel?.htmlString { webView?.loadHTMLString(htmlString, baseURL: nil) } + + if let borderColor = webviewModel?.borderColor?.cgColor { + webView?.layer.borderWidth = 1.0 + webView?.layer.borderColor = borderColor + } else { + webView?.layer.borderWidth = 0.0 + webView?.layer.borderColor = UIColor.clear.cgColor + } + bringSubviewToFront(loadingSpinner) } @@ -95,8 +85,8 @@ import WebKit loadingSpinner.clipsToBounds = true loadingSpinner.translatesAutoresizingMaskIntoConstraints = false - loadingSpinner.heightAnchor.constraint(equalToConstant: 50.0).isActive = true - loadingSpinner.widthAnchor.constraint(equalToConstant: 50.0).isActive = true + loadingSpinner.heightAnchor.constraint(equalToConstant: 44.0).isActive = true + loadingSpinner.widthAnchor.constraint(equalToConstant: 44.0).isActive = true loadingSpinner.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true loadingSpinner.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true loadingSpinner.resumeSpinner() @@ -114,7 +104,6 @@ extension WebView : WKUIDelegate { if !dynamicHeight { return } - /* was using "document.readyState" to check the state, while evaluateJavaScript "document.readyState",only works when webview contains userscrpt.otherwise, it would return WKErrorDomain Code=4 "A JavaScript exception occurred". so webView.isLoading to check load finished state */ @@ -157,14 +146,20 @@ extension WebView : WKNavigationDelegate { // MARK: - WKScriptMessageHandler extension WebView: WKScriptMessageHandler { public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { - if message.name == mvmWebViewMessageHandler, let actionMap = message.body as? [AnyHashable: Any] { + if message.name == mvmWebViewMessageHandler, let text = message.body as? String { /* receiving JavaScript func webkit.messageHandlers.{callHandler}.postMessage(body); - if body is dictionary - MVMCoreActionHanlder handleAction + if body is string, will decode to actionmodel. + use legacy MVMCoreActionHanlder handleAction method for now */ - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: nil, delegateObject: self.delegateObject) - + if let data = text.data(using: .utf8) { + do { + let actionMap = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: nil, delegateObject: self.delegateObject) + } catch { + //actionModel should report error when actionhandler is finshed with actionmodel + } + } } } } diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift b/MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift index bb141b57..d20a5007 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift @@ -15,8 +15,7 @@ import Foundation public var url: URL? public var htmlString: String? public var height: CGFloat? - public var jsScript: String? - public var callHandler: String? + public var borderColor: Color? public var buttonMap: [String: ButtonModel]? private enum CodingKeys: String, CodingKey{ @@ -26,8 +25,7 @@ import Foundation case htmlString case height case buttonMap - case jsScript - case callHandler + case borderColor } required public init(from decoder: Decoder) throws { @@ -37,8 +35,7 @@ import Foundation htmlString = try typeContainer.decodeIfPresent(String.self, forKey: .htmlString) height = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .height) buttonMap = try typeContainer.decodeIfPresent([String: ButtonModel].self, forKey: .buttonMap) - jsScript = try typeContainer.decodeIfPresent(String.self, forKey: .jsScript) - callHandler = try typeContainer.decodeIfPresent(String.self, forKey: .callHandler) + borderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor) } public func encode(to encoder: Encoder) throws { @@ -48,8 +45,6 @@ import Foundation try container.encodeIfPresent(url, forKey: .url) try container.encodeIfPresent(htmlString, forKey: .htmlString) try container.encodeIfPresent(height, forKey: .height) - try container.encodeIfPresent(jsScript, forKey: .jsScript) - try container.encodeIfPresent(callHandler, forKey: .callHandler) - + try container.encodeIfPresent(borderColor, forKey: .borderColor) } } From 3e7a1a3250e2eb52e7ed13a6eba09d29d018cf3f Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Wed, 1 Apr 2020 17:30:06 -0400 Subject: [PATCH 07/15] move some code --- MVMCoreUI/Atomic/Atoms/Views/WebView.swift | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift index 3d870f53..59306739 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -14,7 +14,6 @@ import WebKit let mvmWebViewMessageHandler = "mvmWebViewMessageHandler" public let loadingSpinner = MFLoadingSpinner(frame: .zero) - var delegateObject: MVMCoreUIDelegateObject? var webView: WKWebView? var webViewHeight: NSLayoutConstraint? @@ -27,26 +26,24 @@ import WebKit override open func setupView() { super.setupView() - let webView = createWebView(messageHandler: mvmWebViewMessageHandler) - addSubview(webView) - NSLayoutConstraint.constraintPinSubview(toSuperview: webView) - self.webView = webView + createWebView(messageHandler: mvmWebViewMessageHandler) pinSpinnerView() } - func createWebView(messageHandler: String?) -> WKWebView { + func createWebView(messageHandler: String?) { let wkUserController = WKUserContentController() if let messageHandlerName = messageHandler { wkUserController.add(self, name: messageHandlerName) } - let wkConfig = WKWebViewConfiguration() wkConfig.userContentController = wkUserController let webView = WKWebView(frame: .zero, configuration: wkConfig) webView.translatesAutoresizingMaskIntoConstraints = false webView.uiDelegate = self webView.navigationDelegate = self - return webView + self.webView = webView + addSubview(webView) + NSLayoutConstraint.constraintPinSubview(toSuperview: webView) } From 9851050aca094a2b61ae094ad29f3d070b78f6f0 Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Wed, 1 Apr 2020 17:31:40 -0400 Subject: [PATCH 08/15] move some code --- MVMCoreUI/Atomic/Atoms/Views/WebView.swift | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift index 59306739..6e22d3f5 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -13,16 +13,15 @@ import WebKit let mvmWebViewMessageHandler = "mvmWebViewMessageHandler" - public let loadingSpinner = MFLoadingSpinner(frame: .zero) - var delegateObject: MVMCoreUIDelegateObject? - var webView: WKWebView? - var webViewHeight: NSLayoutConstraint? - var dynamicHeight: Bool = true - - var callHandlerName: String? var webviewModel: WebViewModel? { return model as? WebViewModel } + var webView: WKWebView? + public let loadingSpinner = MFLoadingSpinner(frame: .zero) + var delegateObject: MVMCoreUIDelegateObject? + var webViewHeight: NSLayoutConstraint? + var dynamicHeight: Bool = true + override open func setupView() { super.setupView() From 9cb75817e5f56695e6bf67e4112ae87bb1ae1930 Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Wed, 1 Apr 2020 19:15:49 -0400 Subject: [PATCH 09/15] check previous content --- MVMCoreUI/Atomic/Atoms/Views/WebView.swift | 41 +++++++++++++++++----- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift index 6e22d3f5..72c8c33a 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -27,6 +27,10 @@ import WebKit super.setupView() createWebView(messageHandler: mvmWebViewMessageHandler) pinSpinnerView() + + //init height for loading spinner + webViewHeight = webView?.heightAnchor.constraint(equalToConstant: 44) + webViewHeight?.isActive = true } func createWebView(messageHandler: String?) { @@ -44,24 +48,37 @@ import WebKit addSubview(webView) NSLayoutConstraint.constraintPinSubview(toSuperview: webView) } - // MARK: - MVMCoreUIMoleculeViewProtocol override open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + + //store the previous webview properties + let previousHtmlString = webviewModel?.htmlString + let previousURL = webView?.url + super.set(with: model, delegateObject, additionalData) self.delegateObject = delegateObject - //init height for loading spinner - webViewHeight = webView?.heightAnchor.constraint(equalToConstant: 44) - webViewHeight?.isActive = true if let height = webviewModel?.height { webViewHeight?.constant = height dynamicHeight = false } if let url = webviewModel?.url { - webView?.load(URLRequest(url: url)) + if let previousUrl = previousURL, url == previousUrl { + //dont load the new + } else { + webView?.load(URLRequest(url: url)) + webViewHeight?.constant = 44 + loadingSpinner.resumeSpinner() + } } else if let htmlString = webviewModel?.htmlString { - webView?.loadHTMLString(htmlString, baseURL: nil) + if let previousHTML = previousHtmlString, previousHTML == htmlString { + //dont load the new html since they are the same html string as preivous + } else { + webViewHeight?.constant = 44 + webView?.loadHTMLString(htmlString, baseURL: nil) + loadingSpinner.resumeSpinner() + } } if let borderColor = webviewModel?.borderColor?.cgColor { @@ -105,8 +122,16 @@ extension WebView : WKUIDelegate { */ if !webView.isLoading { webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { [weak self] (result, error) in - guard let self = self, let height = result as? CGFloat else { return } - self.webViewHeight?.constant = height + guard let self = self else { + return + } + if let height = result as? CGFloat { + self.webViewHeight?.constant = height + } else { + //if failed to get height from javascript, using scrollview.contensize's height + let scrollHeight = self.webView?.scrollView.contentSize.height + self.webViewHeight?.constant = scrollHeight ?? 44 + } self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) }) } From 95d305254f937995d67b1bf3faf81910e7be3e71 Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Wed, 1 Apr 2020 19:28:31 -0400 Subject: [PATCH 10/15] add overlay --- MVMCoreUI/Atomic/Atoms/Views/WebView.swift | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift index 72c8c33a..dd8b9776 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -17,6 +17,7 @@ import WebKit return model as? WebViewModel } var webView: WKWebView? + var overLayer = MVMCoreUICommonViewsUtility.commonView() public let loadingSpinner = MFLoadingSpinner(frame: .zero) var delegateObject: MVMCoreUIDelegateObject? var webViewHeight: NSLayoutConstraint? @@ -26,6 +27,7 @@ import WebKit override open func setupView() { super.setupView() createWebView(messageHandler: mvmWebViewMessageHandler) + setupOverLayer() pinSpinnerView() //init height for loading spinner @@ -69,6 +71,7 @@ import WebKit } else { webView?.load(URLRequest(url: url)) webViewHeight?.constant = 44 + overLayer.isHidden = false loadingSpinner.resumeSpinner() } } else if let htmlString = webviewModel?.htmlString { @@ -77,6 +80,7 @@ import WebKit } else { webViewHeight?.constant = 44 webView?.loadHTMLString(htmlString, baseURL: nil) + overLayer.isHidden = false loadingSpinner.resumeSpinner() } } @@ -88,8 +92,6 @@ import WebKit webView?.layer.borderWidth = 0.0 webView?.layer.borderColor = UIColor.clear.cgColor } - - bringSubviewToFront(loadingSpinner) } func pinSpinnerView() { @@ -104,6 +106,12 @@ import WebKit loadingSpinner.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true loadingSpinner.resumeSpinner() } + + func setupOverLayer() { + addSubview(overLayer) + overLayer.backgroundColor = .white + NSLayoutConstraint.constraintPinSubview(toSuperview: overLayer) + } } @@ -111,6 +119,7 @@ import WebKit extension WebView : WKUIDelegate { public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { // hide loading + overLayer.isHidden = true loadingSpinner.pause() //update webview's heigth when webview is ready From 4abfeef11ac426a8209e5c9bff2b1af2b5afa894 Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Wed, 1 Apr 2020 19:32:36 -0400 Subject: [PATCH 11/15] add stop loading, although it not loading --- MVMCoreUI/Atomic/Atoms/Views/WebView.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift index dd8b9776..997f0f96 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -69,6 +69,7 @@ import WebKit if let previousUrl = previousURL, url == previousUrl { //dont load the new } else { + webView?.stopLoading() webView?.load(URLRequest(url: url)) webViewHeight?.constant = 44 overLayer.isHidden = false @@ -78,6 +79,7 @@ import WebKit if let previousHTML = previousHtmlString, previousHTML == htmlString { //dont load the new html since they are the same html string as preivous } else { + webView?.stopLoading() webViewHeight?.constant = 44 webView?.loadHTMLString(htmlString, baseURL: nil) overLayer.isHidden = false From 0edc7a8f00472e07d2c58e1fe02e43fe9f9c4971 Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Wed, 1 Apr 2020 19:51:19 -0400 Subject: [PATCH 12/15] update based on Scott's comment --- MVMCoreUI/Atomic/Atoms/Views/WebView.swift | 8 ++++---- MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift index 997f0f96..4244c330 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -60,8 +60,9 @@ import WebKit super.set(with: model, delegateObject, additionalData) self.delegateObject = delegateObject - + var webViewHeightConstant: CGFloat = 44 if let height = webviewModel?.height { + webViewHeightConstant = height webViewHeight?.constant = height dynamicHeight = false } @@ -71,7 +72,7 @@ import WebKit } else { webView?.stopLoading() webView?.load(URLRequest(url: url)) - webViewHeight?.constant = 44 + webViewHeight?.constant = webViewHeightConstant overLayer.isHidden = false loadingSpinner.resumeSpinner() } @@ -80,7 +81,7 @@ import WebKit //dont load the new html since they are the same html string as preivous } else { webView?.stopLoading() - webViewHeight?.constant = 44 + webViewHeight?.constant = webViewHeightConstant webView?.loadHTMLString(htmlString, baseURL: nil) overLayer.isHidden = false loadingSpinner.resumeSpinner() @@ -106,7 +107,6 @@ import WebKit loadingSpinner.widthAnchor.constraint(equalToConstant: 44.0).isActive = true loadingSpinner.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true loadingSpinner.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true - loadingSpinner.resumeSpinner() } func setupOverLayer() { diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift b/MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift index d20a5007..25142fa5 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebViewModel.swift @@ -16,7 +16,6 @@ import Foundation public var htmlString: String? public var height: CGFloat? public var borderColor: Color? - public var buttonMap: [String: ButtonModel]? private enum CodingKeys: String, CodingKey{ case moleculeName @@ -24,7 +23,6 @@ import Foundation case url case htmlString case height - case buttonMap case borderColor } @@ -33,8 +31,10 @@ import Foundation backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) url = try typeContainer.decodeIfPresent(URL.self, forKey: .url) htmlString = try typeContainer.decodeIfPresent(String.self, forKey: .htmlString) + if url == nil, htmlString == nil { + throw ModelRegistry.Error.decoderErrorModelNotMapped + } height = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .height) - buttonMap = try typeContainer.decodeIfPresent([String: ButtonModel].self, forKey: .buttonMap) borderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor) } From 39e2001533a08dcdf136acebd3baa3ef90579f73 Mon Sep 17 00:00:00 2001 From: "Suresh, Kamlesh" Date: Thu, 2 Apr 2020 10:18:40 -0400 Subject: [PATCH 13/15] fiix --- MVMCoreUI/FormUIHelpers/FormValidator.swift | 3 +++ MVMCoreUI/FormUIHelpers/Rules/Rules/RuleRequiredModel.swift | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/FormUIHelpers/FormValidator.swift b/MVMCoreUI/FormUIHelpers/FormValidator.swift index 7012e64d..592132f5 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidator.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidator.swift @@ -48,6 +48,9 @@ import MVMCore if let validator = delegate?.formValidator { validator.delegate = delegate validator.insert(item) + + // TODO: Temporary hacks, rewrite architecture to support this. + _ = validator.validate() } } diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleRequiredModel.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleRequiredModel.swift index 3900249a..cfff35eb 100644 --- a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleRequiredModel.swift +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleRequiredModel.swift @@ -11,7 +11,7 @@ import Foundation public class RuleRequiredModel: RulesProtocol { - public static var identifier: String = "required" + public static var identifier: String = "allRequired" public var type: String = RuleRequiredModel.identifier public var fields: [String] From 3279ef91fdcd6b4be45b8a4d3b3655e80a2a6939 Mon Sep 17 00:00:00 2001 From: "Xinlei(Ryan) Pan" Date: Thu, 2 Apr 2020 13:40:24 -0400 Subject: [PATCH 14/15] update did fail method --- MVMCoreUI/Atomic/Atoms/Views/WebView.swift | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift index 4244c330..1ce2b123 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -148,12 +148,11 @@ extension WebView : WKUIDelegate { } } - public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { - // show loading - loadingSpinner.resumeSpinner() - } - public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { - // hide loading + + public func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) { + //actually no error handle page show in webview. We can handle the error display view by our self. + //or stop loading by default + overLayer.isHidden = true loadingSpinner.pause() } From bcffe0bbd207eb8326d0c60c1324dc10473d05a1 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 2 Apr 2020 20:28:47 -0400 Subject: [PATCH 15/15] Percent update --- .../ThreeColumn/ListThreeColumnInternationalDataDivider.swift | 4 ++-- .../ListThreeColumnInternationalDataDividerModel.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift index 42d3d39f..7b6af23d 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDivider.swift @@ -20,8 +20,8 @@ import Foundation public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { stack = Stack.createStack(with: [(view: leftLabel, model: StackItemModel(percent: 30, horizontalAlignment: .leading)), - (view: centerLabel, model: StackItemModel(percent: 40, horizontalAlignment: .leading)), - (view: rightLabel, model: StackItemModel(percent: 30, horizontalAlignment: .center))], + (view: centerLabel, model: StackItemModel(percent: 50, horizontalAlignment: .leading)), + (view: rightLabel, model: StackItemModel(percent: 20, horizontalAlignment: .leading))], axis: .horizontal) super.init(style: style, reuseIdentifier: reuseIdentifier) } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDividerModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDividerModel.swift index 51786004..a5f3bcf6 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDividerModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/SectionDividers/ThreeColumn/ListThreeColumnInternationalDataDividerModel.swift @@ -8,13 +8,13 @@ import Foundation -public class ListThreeColumnInternationalDataDividerModel : ListItemModel, MoleculeModelProtocol { +public class ListThreeColumnInternationalDataDividerModel: ListItemModel, MoleculeModelProtocol { public static var identifier: String = "list3CIntDataDiv" public var leftLabel: LabelModel public var centerLabel: LabelModel public var rightLabel: LabelModel - public init (leftLabel:LabelModel, centerLabel:LabelModel, rightLabel:LabelModel) { + public init (leftLabel: LabelModel, centerLabel: LabelModel, rightLabel: LabelModel) { self.leftLabel = leftLabel self.centerLabel = centerLabel self.rightLabel = rightLabel