From 6e22fe4f53e6016ed5c9ec74466a44fccfd7cd27 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Fri, 15 Mar 2019 17:20:27 -0400 Subject: [PATCH 01/12] warning fix --- MVMCoreUI.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 856b15bb..06014f98 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -946,6 +946,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -1010,6 +1011,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; From 57b99d66582e9c65511f50a234694159c1bb04a0 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Mon, 18 Mar 2019 08:53:33 -0400 Subject: [PATCH 02/12] Name short --- MVMCoreUI.xcodeproj/project.pbxproj | 16 ++++++++-------- ...leStackView.swift => MoleculeStackView.swift} | 4 ++-- ...HeaderView.swift => StandardHeaderView.swift} | 4 ++-- .../MVMCoreUIMoleculeMappingObject.m | 4 ++-- .../MoleculeStackCenteredTemplate.swift | 2 +- MVMCoreUI/Templates/MoleculeStackTemplate.swift | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) rename MVMCoreUI/Molecules/{MVMCoreUIMoleculeStackView.swift => MoleculeStackView.swift} (97%) rename MVMCoreUI/Molecules/{MVMCoreUIHeaderView.swift => StandardHeaderView.swift} (98%) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 06014f98..19113130 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -150,10 +150,10 @@ D2A514582211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = D2A514562211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; D2A514592211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D2A514572211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m */; }; D2A5145D2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D2A5145C2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D2A5145F2211DDC100345BFB /* MVMCoreUIMoleculeStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A5145E2211DDC100345BFB /* MVMCoreUIMoleculeStackView.swift */; }; + D2A5145F2211DDC100345BFB /* MoleculeStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A5145E2211DDC100345BFB /* MoleculeStackView.swift */; }; D2A5146122121FBF00345BFB /* MoleculeStackTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A5146022121FBF00345BFB /* MoleculeStackTemplate.swift */; }; D2A514632213643100345BFB /* MoleculeStackCenteredTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A514622213643100345BFB /* MoleculeStackCenteredTemplate.swift */; }; - D2A514672213885800345BFB /* MVMCoreUIHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A514662213885800345BFB /* MVMCoreUIHeaderView.swift */; }; + D2A514672213885800345BFB /* StandardHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A514662213885800345BFB /* StandardHeaderView.swift */; }; D2A5146B2214905000345BFB /* ThreeLayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A5146A2214905000345BFB /* ThreeLayerViewController.swift */; }; D2C5001821F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */; }; @@ -309,10 +309,10 @@ D2A514562211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIMoleculeMappingObject.h; sourceTree = ""; }; D2A514572211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIMoleculeMappingObject.m; sourceTree = ""; }; D2A5145C2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIMoleculeViewProtocol.h; sourceTree = ""; }; - D2A5145E2211DDC100345BFB /* MVMCoreUIMoleculeStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUIMoleculeStackView.swift; sourceTree = ""; }; + D2A5145E2211DDC100345BFB /* MoleculeStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeStackView.swift; sourceTree = ""; }; D2A5146022121FBF00345BFB /* MoleculeStackTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeStackTemplate.swift; sourceTree = ""; }; D2A514622213643100345BFB /* MoleculeStackCenteredTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeStackCenteredTemplate.swift; sourceTree = ""; }; - D2A514662213885800345BFB /* MVMCoreUIHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUIHeaderView.swift; sourceTree = ""; }; + D2A514662213885800345BFB /* StandardHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardHeaderView.swift; sourceTree = ""; }; D2A5146A2214905000345BFB /* ThreeLayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerViewController.swift; sourceTree = ""; }; D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIViewControllerMappingObject.h; sourceTree = ""; }; D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIViewControllerMappingObject.m; sourceTree = ""; }; @@ -421,9 +421,9 @@ D29770C621F7C4AE00B2F0D0 /* TopLabelsView.m */, D29770F621F7C73800B2F0D0 /* PrimaryButtonView.h */, D29770F721F7C73800B2F0D0 /* PrimaryButtonView.m */, - D2A514662213885800345BFB /* MVMCoreUIHeaderView.swift */, + D2A514662213885800345BFB /* StandardHeaderView.swift */, D2A5145C2211D22A00345BFB /* MVMCoreUIMoleculeViewProtocol.h */, - D2A5145E2211DDC100345BFB /* MVMCoreUIMoleculeStackView.swift */, + D2A5145E2211DDC100345BFB /* MoleculeStackView.swift */, ); path = Molecules; sourceTree = ""; @@ -869,14 +869,14 @@ D29DF2CF21E7C104003B2FB9 /* MFLoadingViewController.m in Sources */, D22D1F572204CE5D0077CEC0 /* MVMCoreUIStackableViewController.m in Sources */, 01DF567021FA5AB300CC099B /* MVMCoreUITextFieldListFormViewController.swift in Sources */, - D2A5145F2211DDC100345BFB /* MVMCoreUIMoleculeStackView.swift in Sources */, + D2A5145F2211DDC100345BFB /* MoleculeStackView.swift in Sources */, D29DF27621E79E81003B2FB9 /* MVMCoreUILoggingHandler.m in Sources */, D29DF24D21E6A177003B2FB9 /* MFTextField.m in Sources */, D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */, D29DF12B21E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m in Sources */, D29DF25421E6A177003B2FB9 /* MFMdnTextField.m in Sources */, D29DF26521E6A9D9003B2FB9 /* MFTransparentGIFView.m in Sources */, - D2A514672213885800345BFB /* MVMCoreUIHeaderView.swift in Sources */, + D2A514672213885800345BFB /* StandardHeaderView.swift in Sources */, D29DF13021E6851E003B2FB9 /* MVMCoreUITopAlertShortView.m in Sources */, D28B4F8B21FF967C00712C7A /* MVMCoreUIObject.m in Sources */, D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */, diff --git a/MVMCoreUI/Molecules/MVMCoreUIMoleculeStackView.swift b/MVMCoreUI/Molecules/MoleculeStackView.swift similarity index 97% rename from MVMCoreUI/Molecules/MVMCoreUIMoleculeStackView.swift rename to MVMCoreUI/Molecules/MoleculeStackView.swift index e8f6cc26..a9ea7d8e 100644 --- a/MVMCoreUI/Molecules/MVMCoreUIMoleculeStackView.swift +++ b/MVMCoreUI/Molecules/MoleculeStackView.swift @@ -1,5 +1,5 @@ // -// MVMCoreUIMoleculeStackView.swift +// MoleculeStackView.swift // MVMCoreUI // // Created by Scott Pfeil on 2/11/19. @@ -8,7 +8,7 @@ import UIKit -public class MVMCoreUIMoleculeStackView: MFView { +public class MoleculeStackView: MFView { var spacingBlock: ((Any) -> UIEdgeInsets)? var moleculesArray: [UIView]? diff --git a/MVMCoreUI/Molecules/MVMCoreUIHeaderView.swift b/MVMCoreUI/Molecules/StandardHeaderView.swift similarity index 98% rename from MVMCoreUI/Molecules/MVMCoreUIHeaderView.swift rename to MVMCoreUI/Molecules/StandardHeaderView.swift index a1a0ffba..47ff92ef 100644 --- a/MVMCoreUI/Molecules/MVMCoreUIHeaderView.swift +++ b/MVMCoreUI/Molecules/StandardHeaderView.swift @@ -1,5 +1,5 @@ // -// MVMCoreUIHeaderView.swift +// StandardHeaderView.swift // MVMCoreUI // // Created by Scott Pfeil on 2/12/19. @@ -8,7 +8,7 @@ import UIKit -public class MVMCoreUIHeaderView: ViewConstrainingView { +public class StandardHeaderView: ViewConstrainingView { let headlineLabel = MFLabel.commonLabelH2(true) let messageLabel = MFLabel.commonLabelB2(true) var separatorView: SeparatorView? diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index a18be0a4..a8584ab2 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -21,8 +21,8 @@ static NSMutableDictionary *mapping; dispatch_once(&onceToken, ^{ mapping = [@{ - @"standardHeader": MVMCoreUIHeaderView.class, - @"moleculeStack": MVMCoreUIMoleculeStackView.class, + @"standardHeader": StandardHeaderView.class, + @"moleculeStack": MoleculeStackView.class, @"twoButtonView": PrimaryButtonView.class } mutableCopy]; }); diff --git a/MVMCoreUI/Templates/MoleculeStackCenteredTemplate.swift b/MVMCoreUI/Templates/MoleculeStackCenteredTemplate.swift index 2a81afaa..7f7411a3 100644 --- a/MVMCoreUI/Templates/MoleculeStackCenteredTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeStackCenteredTemplate.swift @@ -12,7 +12,7 @@ public class MoleculeStackCenteredTemplate: ThreeLayerViewController { public override func viewForMiddle() -> UIView? { let molecule = loadObject?.pageJSON?.optionalDictionaryForKey("moleculeStack") - let moleculeStack = MVMCoreUIMoleculeStackView(withJSON: molecule, delegate: self, additionalData: nil) + let moleculeStack = MoleculeStackView(withJSON: molecule, delegate: self, additionalData: nil) return moleculeStack } diff --git a/MVMCoreUI/Templates/MoleculeStackTemplate.swift b/MVMCoreUI/Templates/MoleculeStackTemplate.swift index b2349d70..2910782d 100644 --- a/MVMCoreUI/Templates/MoleculeStackTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeStackTemplate.swift @@ -26,7 +26,7 @@ public class MoleculeStackTemplate: ThreeLayerViewController { guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("moleculeStack") else { return nil } - return MVMCoreUIMoleculeStackView(withJSON: moleculeJSON, delegate: self, additionalData: nil) + return MoleculeStackView(withJSON: moleculeJSON, delegate: self, additionalData: nil) } override public func viewForBottom() -> UIView? { From 33510a648447d8a1ee7dd35ce059f551ad425017 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 19 Mar 2019 13:11:02 -0400 Subject: [PATCH 03/12] Loadimageview to swift --- MVMCoreUI.xcodeproj/project.pbxproj | 24 +- MVMCoreUI/Atoms/Views/MFLoadImageView.h | 76 ----- MVMCoreUI/Atoms/Views/MFLoadImageView.m | 308 ------------------ MVMCoreUI/Atoms/Views/MFLoadImageView.swift | 283 ++++++++++++++++ MVMCoreUI/Atoms/Views/MFTransparentGIFView.h | 33 -- MVMCoreUI/Atoms/Views/MFTransparentGIFView.m | 74 ----- .../MFScrollingViewController.m | 4 +- MVMCoreUI/BaseControllers/MFViewController.m | 2 +- MVMCoreUI/MVMCoreUI.h | 2 - .../TopAlert/MVMCoreUITopAlertMainView.m | 4 +- 10 files changed, 296 insertions(+), 514 deletions(-) delete mode 100644 MVMCoreUI/Atoms/Views/MFLoadImageView.h delete mode 100644 MVMCoreUI/Atoms/Views/MFLoadImageView.m create mode 100644 MVMCoreUI/Atoms/Views/MFLoadImageView.swift delete mode 100644 MVMCoreUI/Atoms/Views/MFTransparentGIFView.h delete mode 100644 MVMCoreUI/Atoms/Views/MFTransparentGIFView.m diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index a6a4447b..a222046d 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -20,6 +20,8 @@ D22D1F562204CE5D0077CEC0 /* MVMCoreUIStackableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = D22D1F542204CE5D0077CEC0 /* MVMCoreUIStackableViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; D22D1F572204CE5D0077CEC0 /* MVMCoreUIStackableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D22D1F552204CE5D0077CEC0 /* MVMCoreUIStackableViewController.m */; }; D274CA332236A78900B01B62 /* StandardFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D274CA322236A78900B01B62 /* StandardFooterView.swift */; }; + D282AAB4223FDDAE00C46919 /* MFLoadImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D282AAB3223FDDAE00C46919 /* MFLoadImageView.swift */; }; + D282AABA224131D100C46919 /* MFTransparentGIFView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D282AAB9224131D100C46919 /* MFTransparentGIFView.swift */; }; D28B4F8A21FF967C00712C7A /* MVMCoreUIObject.h in Headers */ = {isa = PBXBuildFile; fileRef = D28B4F8821FF967C00712C7A /* MVMCoreUIObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; D28B4F8B21FF967C00712C7A /* MVMCoreUIObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D28B4F8921FF967C00712C7A /* MVMCoreUIObject.m */; }; D29770C821F7C4AE00B2F0D0 /* TopLabelsView.m in Sources */ = {isa = PBXBuildFile; fileRef = D29770C621F7C4AE00B2F0D0 /* TopLabelsView.m */; }; @@ -80,10 +82,6 @@ D29DF25921E6A22D003B2FB9 /* MFButtonProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF25821E6A22D003B2FB9 /* MFButtonProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29DF25C21E6A2B6003B2FB9 /* DashLine.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF25A21E6A2B6003B2FB9 /* DashLine.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29DF25D21E6A2B6003B2FB9 /* DashLine.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF25B21E6A2B6003B2FB9 /* DashLine.m */; }; - D29DF26021E6A985003B2FB9 /* MFLoadImageView.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF25E21E6A985003B2FB9 /* MFLoadImageView.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D29DF26121E6A985003B2FB9 /* MFLoadImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF25F21E6A985003B2FB9 /* MFLoadImageView.m */; }; - D29DF26421E6A9D9003B2FB9 /* MFTransparentGIFView.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF26221E6A9D9003B2FB9 /* MFTransparentGIFView.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D29DF26521E6A9D9003B2FB9 /* MFTransparentGIFView.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF26321E6A9D9003B2FB9 /* MFTransparentGIFView.m */; }; D29DF26C21E6AA0B003B2FB9 /* FLAnimatedImage.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF26821E6AA0B003B2FB9 /* FLAnimatedImage.m */; }; D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF26921E6AA0B003B2FB9 /* FLAnimatedImageView.m */; }; D29DF26E21E6AA0B003B2FB9 /* FLAnimatedImage.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF26A21E6AA0B003B2FB9 /* FLAnimatedImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -176,6 +174,8 @@ D22D1F542204CE5D0077CEC0 /* MVMCoreUIStackableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIStackableViewController.h; sourceTree = ""; }; D22D1F552204CE5D0077CEC0 /* MVMCoreUIStackableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIStackableViewController.m; sourceTree = ""; }; D274CA322236A78900B01B62 /* StandardFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardFooterView.swift; sourceTree = ""; }; + D282AAB3223FDDAE00C46919 /* MFLoadImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MFLoadImageView.swift; sourceTree = ""; }; + D282AAB9224131D100C46919 /* MFTransparentGIFView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MFTransparentGIFView.swift; sourceTree = ""; }; D28B4F8821FF967C00712C7A /* MVMCoreUIObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIObject.h; sourceTree = ""; }; D28B4F8921FF967C00712C7A /* MVMCoreUIObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIObject.m; sourceTree = ""; }; D29770C621F7C4AE00B2F0D0 /* TopLabelsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TopLabelsView.m; sourceTree = ""; }; @@ -246,10 +246,6 @@ D29DF25821E6A22D003B2FB9 /* MFButtonProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MFButtonProtocol.h; sourceTree = ""; }; D29DF25A21E6A2B6003B2FB9 /* DashLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DashLine.h; sourceTree = ""; }; D29DF25B21E6A2B6003B2FB9 /* DashLine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DashLine.m; sourceTree = ""; }; - D29DF25E21E6A985003B2FB9 /* MFLoadImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MFLoadImageView.h; sourceTree = ""; }; - D29DF25F21E6A985003B2FB9 /* MFLoadImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFLoadImageView.m; sourceTree = ""; }; - D29DF26221E6A9D9003B2FB9 /* MFTransparentGIFView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MFTransparentGIFView.h; sourceTree = ""; }; - D29DF26321E6A9D9003B2FB9 /* MFTransparentGIFView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFTransparentGIFView.m; sourceTree = ""; }; D29DF26821E6AA0B003B2FB9 /* FLAnimatedImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLAnimatedImage.m; sourceTree = ""; }; D29DF26921E6AA0B003B2FB9 /* FLAnimatedImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLAnimatedImageView.m; sourceTree = ""; }; D29DF26A21E6AA0B003B2FB9 /* FLAnimatedImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLAnimatedImage.h; sourceTree = ""; }; @@ -556,10 +552,8 @@ D2C5001C21F8EE66001DA659 /* LabelWithInternalButton.m */, D29DF28721E7AC2B003B2FB9 /* ViewConstrainingView.h */, D29DF28821E7AC2B003B2FB9 /* ViewConstrainingView.m */, - D29DF26221E6A9D9003B2FB9 /* MFTransparentGIFView.h */, - D29DF26321E6A9D9003B2FB9 /* MFTransparentGIFView.m */, - D29DF25E21E6A985003B2FB9 /* MFLoadImageView.h */, - D29DF25F21E6A985003B2FB9 /* MFLoadImageView.m */, + D282AAB9224131D100C46919 /* MFTransparentGIFView.swift */, + D282AAB3223FDDAE00C46919 /* MFLoadImageView.swift */, D29DF2AD21E7B3A4003B2FB9 /* MFTextView.h */, D29DF2AB21E7B3A4003B2FB9 /* MFTextView.m */, D29DF2AC21E7B3A4003B2FB9 /* MFTextView.xib */, @@ -704,7 +698,6 @@ buildActionMask = 2147483647; files = ( D29DF18021E69E49003B2FB9 /* MFView.h in Headers */, - D29DF26421E6A9D9003B2FB9 /* MFTransparentGIFView.h in Headers */, D29DF27921E7A533003B2FB9 /* MVMCoreUISession.h in Headers */, D29DF25C21E6A2B6003B2FB9 /* DashLine.h in Headers */, D206997721FB8A0B00CAE0DE /* MVMCoreUINavigationController.h in Headers */, @@ -765,7 +758,6 @@ D29DF17421E69E1F003B2FB9 /* MFCustomButton.h in Headers */, D29DF29721E7ADB8003B2FB9 /* MFScrollingViewController.h in Headers */, D29DF26F21E6AA0B003B2FB9 /* FLAnimatedImageView.h in Headers */, - D29DF26021E6A985003B2FB9 /* MFLoadImageView.h in Headers */, D29DF2A121E7AF4E003B2FB9 /* MVMCoreUIUtility.h in Headers */, D29DF17621E69E1F003B2FB9 /* PrimaryButton.h in Headers */, D29DF2C821E7BFC1003B2FB9 /* MFSizeObject.h in Headers */, @@ -860,6 +852,7 @@ D29770F221F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsTableViewController.m in Sources */, D29DF29621E7ADB8003B2FB9 /* StackableViewController.m in Sources */, D22D1F1F220343560077CEC0 /* MVMCoreUICheckMarkView.m in Sources */, + D282AAB4223FDDAE00C46919 /* MFLoadImageView.swift in Sources */, D29DF11721E6805F003B2FB9 /* UIColor+MFConvenience.m in Sources */, D29DF25321E6A177003B2FB9 /* MFDigitTextField.m in Sources */, D29770F921F7C73800B2F0D0 /* PrimaryButtonView.m in Sources */, @@ -878,7 +871,7 @@ D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */, D29DF12B21E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m in Sources */, D29DF25421E6A177003B2FB9 /* MFMdnTextField.m in Sources */, - D29DF26521E6A9D9003B2FB9 /* MFTransparentGIFView.m in Sources */, + D282AABA224131D100C46919 /* MFTransparentGIFView.swift in Sources */, D2A514672213885800345BFB /* StandardHeaderView.swift in Sources */, D29DF13021E6851E003B2FB9 /* MVMCoreUITopAlertShortView.m in Sources */, D28B4F8B21FF967C00712C7A /* MVMCoreUIObject.m in Sources */, @@ -888,7 +881,6 @@ D29DF18121E69E50003B2FB9 /* MFView.m in Sources */, D29DF18321E69E54003B2FB9 /* SeparatorView.m in Sources */, D29DF17A21E69E1F003B2FB9 /* MFCustomButton.m in Sources */, - D29DF26121E6A985003B2FB9 /* MFLoadImageView.m in Sources */, D274CA332236A78900B01B62 /* StandardFooterView.swift in Sources */, D29DF2BF21E7BEA4003B2FB9 /* MVMCoreUITabBarPageControlViewController.m in Sources */, D29DF28321E7AB24003B2FB9 /* MVMCoreUICommonViewsUtility.m in Sources */, diff --git a/MVMCoreUI/Atoms/Views/MFLoadImageView.h b/MVMCoreUI/Atoms/Views/MFLoadImageView.h deleted file mode 100644 index 895da071..00000000 --- a/MVMCoreUI/Atoms/Views/MFLoadImageView.h +++ /dev/null @@ -1,76 +0,0 @@ -// -// MFLoadImageView.h -// mobilefirst -// -// Created by Scott Pfeil on 5/26/16. -// Copyright © 2016 Verizon Wireless. All rights reserved. -// -// Shows a loading indicator while the image is downloading and then replaces loading indicator with image after - -#import -#import -@import MVMCore.MVMCoreCache; - -@class MFLoadingSpinner; - -@interface MFLoadImageView : UIView - -@property (nonnull, strong, nonatomic) MFLoadingSpinner *loadingSpinner; -@property (nonnull, strong, nonatomic) MFTransparentGIFView *imageView; - -@property (nullable, weak, nonatomic) NSLayoutConstraint *leftPin; -@property (nullable, weak, nonatomic) NSLayoutConstraint *rightPin; -@property (nullable, weak, nonatomic) NSLayoutConstraint *topPin; -@property (nullable, weak, nonatomic) NSLayoutConstraint *centerX; -@property (nullable, weak, nonatomic) NSLayoutConstraint *centerY; -@property (nullable, weak, nonatomic) NSLayoutConstraint *bottomPin; - -@property (nullable, weak, nonatomic) NSLayoutConstraint *widthConstraint; -@property (nullable, weak, nonatomic) NSLayoutConstraint *heightConstraint; - -// If true, will add size constraints once the image is downloaded. This will help remove white space for aspect fit issues. -@property (nonatomic) BOOL addSizeConstraintsForAspectRatio; - -// image loaded in the center. -- (nonnull instancetype)initWithCenteredImage; - -// Use to pin edges for the image. Shouldn't pin both left and right or top and bottom because then the image will stretch. -- (nonnull instancetype)initWithPinnedEdges:(UIRectEdge)edge; - -// Returns the default setter block to be used -- (nonnull MVMCoreGetImageBlock)defaultCompletionBlock; - -// Allows to pin edges after initialization -- (void)pinEdges:(UIRectEdge)edge; - -// Helper for if we need to load a new image. Returns true if: there is no current image, imageName and the current loadingImageName are not the same, width and the current width are not the same, we are using a fallback image. -- (BOOL)shouldLoadImageWithName:(nullable NSString *)imageName width:(CGFloat)width; - -// Loads an image with the name. This function will use the scene 7 MFCache function to download from server or from local -// @param imageName The string for the image, url or local name -// @param format The scene 7 format of the image -// @param width The scene7 width. -// @param height The scene7 height. -// @param customFallbackImage if nil, the default fallback for MF is used. -// @param completionHandler can be used to define a custom completion handler. Useful when loading images in a collection view. Load a UIImage when receiving image, load a gif when receiving imageData. -// See MFCache for more information of load image function -- (void)loadImageWithName:(nullable NSString *)imageName; -- (void)loadImageWithName:(nullable NSString *)imageName width:(nullable NSNumber *)width; -- (void)loadImageWithName:(nullable NSString *)imageName height:(nullable NSNumber *)height; -- (void)loadImageWithName:(nullable NSString *)imageName width:(nullable NSNumber *)width height:(nullable NSNumber *)height; -- (void)loadImageWithName:(nullable NSString *)imageName format:(nullable NSString *)format width:(nullable NSNumber *)width height:(nullable NSNumber *)height; -- (void)loadImageWithName:(nullable NSString *)imageName width:(nullable NSNumber *)width height:(nullable NSNumber *)height customFallbackImage:(nullable NSString *)customFallbackImage; -- (void)loadImageWithName:(nullable NSString *)imageName width:(nullable NSNumber *)width height:(nullable NSNumber *)height completionHandler:(nonnull MVMCoreGetImageBlock)completionHandler; -- (void)loadImageWithName:(nullable NSString *)imageName format:(nullable NSString *)format width:(nullable NSNumber *)width height:(nullable NSNumber *)height customFallbackImage:(nullable NSString *)customFallbackImage completionHandler:(nonnull MVMCoreGetImageBlock)completionHandler; -- (void)loadImageWithName:(nullable NSString *)imageName exceptImageType:(nullable NSString *)format width:(nullable NSNumber *)width height:(nullable NSNumber *)height customFallbackImage:(nullable NSString *)customFallbackImage; - -// Loads a cropped image with the name. This function will use the scene 7 MFCache function to download from server or from local -// @param imageName The string for the image, url or local name -// @param width The scene7 width. -// @param height The scene7 height. -// @param cropRect The crop rectangle requested -// @param customFallbackImage if nil, the default fallback for MF is used. -// See MFCache for more information of load image function -- (void)loadCroppedImageWithName:(nullable NSString *)imageName width:(nullable NSNumber *)width height:(nullable NSNumber *)height cropRect:(CGRect)cropRect flipImage:(BOOL)flipImage customFallbackImage:(nullable NSString *)customFallbackImage; - -@end diff --git a/MVMCoreUI/Atoms/Views/MFLoadImageView.m b/MVMCoreUI/Atoms/Views/MFLoadImageView.m deleted file mode 100644 index 2a0572da..00000000 --- a/MVMCoreUI/Atoms/Views/MFLoadImageView.m +++ /dev/null @@ -1,308 +0,0 @@ -// -// MFLoadImageView.m -// mobilefirst -// -// Created by Scott Pfeil on 5/26/16. -// Copyright © 2016 Verizon Wireless. All rights reserved. -// - -#import "MFLoadImageView.h" -#import "MFLoadingSpinner.h" -#import "NSLayoutConstraint+MFConvenience.h" -#import "MVMCoreUIUtility.h" -@import MVMCore.NSDictionary_MFConvenience; -@import MVMCore.MVMCoreDispatchUtility; -@import MVMCore.MVMCoreGetterUtility; -@import MVMCore.MVMCoreCache; - -@interface MFLoadImageView () - -@property (nonnull, strong, nonatomic) NSLayoutConstraint *loadingSpinnerHeight; -@property (nonatomic) CGFloat height; -@property (strong, nonatomic) NSString *loadingImageName; -@property (nonatomic) CGFloat width; -@property (nonatomic) BOOL isFallbackImage; - -@end - -@implementation MFLoadImageView - -- (instancetype)initWithCoder:(NSCoder *)aDecoder { - if (self = [super initWithCoder:aDecoder]) { - [self setupView:UIRectEdgeNone]; - } - return self; -} - -- (nonnull instancetype)initWithCenteredImage { - return [self initWithPinnedEdges:UIRectEdgeNone]; -} - -- (nonnull instancetype)initWithPinnedEdges:(UIRectEdge)edge { - if (self = [super init]) { - [self setupView:edge]; - } - return self; -} - -- (void)setupView:(UIRectEdge)edge { - - if (!self.imageView) { - self.clipsToBounds = YES; - self.translatesAutoresizingMaskIntoConstraints = NO; - [self setContentHuggingPriority:950 forAxis:UILayoutConstraintAxisHorizontal]; - [self setContentHuggingPriority:950 forAxis:UILayoutConstraintAxisVertical]; - - MFTransparentGIFView *imageView = [[MFTransparentGIFView alloc] initWithFrame:CGRectZero]; - imageView.translatesAutoresizingMaskIntoConstraints = NO; - [self addSubview:imageView]; - self.imageView = imageView; - if (edge == UIRectEdgeAll) { - [imageView setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal]; - [imageView setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisVertical]; - } else { - [imageView setContentHuggingPriority:900 forAxis:UILayoutConstraintAxisHorizontal]; - [imageView setContentHuggingPriority:900 forAxis:UILayoutConstraintAxisVertical]; - } - - [self pinEdges:edge]; - - MFLoadingSpinner *loadingSpinner = [[MFLoadingSpinner alloc] initWithFrame:CGRectZero]; - loadingSpinner.clipsToBounds = YES; - loadingSpinner.translatesAutoresizingMaskIntoConstraints = NO; - [self addSubview:loadingSpinner]; - self.loadingSpinner = loadingSpinner; - [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0@500-[loadingSpinner]-0@500-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(loadingSpinner)]]; - [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0@500-[loadingSpinner]-0@500-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(loadingSpinner)]]; - [NSLayoutConstraint constraintWithItem:loadingSpinner attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0].active = YES; - [NSLayoutConstraint constraintWithItem:loadingSpinner attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0].active = YES; - - self.loadingSpinnerHeight = [[loadingSpinner pinWidthAndHeight] objectForKey:ConstraintHeight ofType:[NSLayoutConstraint class]]; - self.height = self.loadingSpinnerHeight.constant; - self.loadingSpinnerHeight.constant = 0; - self.loadingSpinnerHeight.active = YES; - [loadingSpinner pauseSpinner]; - } -} - -- (void)pinEdges:(UIRectEdge)edge { - - if (self.topPin) { - [self removeConstraint:self.topPin]; - } - NSLayoutRelation relation; - if (edge & UIRectEdgeTop) { - relation = NSLayoutRelationEqual; - } else { - relation = NSLayoutRelationGreaterThanOrEqual; - } - NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.imageView attribute:NSLayoutAttributeTop relatedBy:relation toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]; - constraint.active = YES; - self.topPin = constraint; - - if (self.bottomPin) { - [self removeConstraint:self.bottomPin]; - } - if (edge & UIRectEdgeBottom) { - relation = NSLayoutRelationEqual; - } else { - relation = NSLayoutRelationGreaterThanOrEqual; - } - constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeBottom relatedBy:relation toItem:self.imageView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0]; - constraint.active = YES; - self.bottomPin = constraint; - - if (self.leftPin) { - [self removeConstraint:self.leftPin]; - } - if (edge & UIRectEdgeLeft) { - relation = NSLayoutRelationEqual; - } else { - relation = NSLayoutRelationGreaterThanOrEqual; - } - constraint = [NSLayoutConstraint constraintWithItem:self.imageView attribute:NSLayoutAttributeLeft relatedBy:relation toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0]; - constraint.active = YES; - self.leftPin = constraint; - - if (self.rightPin) { - [self removeConstraint:self.rightPin]; - } - if (edge & UIRectEdgeRight) { - relation = NSLayoutRelationEqual; - } else { - relation = NSLayoutRelationGreaterThanOrEqual; - } - constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:relation toItem:self.imageView attribute:NSLayoutAttributeRight multiplier:1.0 constant:0]; - constraint.active = YES; - self.rightPin = constraint; - - if (self.centerY) { - [self removeConstraint:self.centerY]; - } - if ((edge & (UIRectEdgeTop | UIRectEdgeBottom)) == 0) { - constraint = [NSLayoutConstraint constraintWithItem:self.imageView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]; - constraint.active = YES; - self.centerY = constraint; - } - - if (self.centerX) { - [self removeConstraint:self.centerX]; - } - if ((edge & (UIRectEdgeLeft | UIRectEdgeRight)) == 0) { - constraint = [NSLayoutConstraint constraintWithItem:self.imageView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]; - constraint.active = YES; - self.centerX = constraint; - } -} - -- (nonnull MVMCoreGetImageBlock)defaultCompletionBlock { - return ^(UIImage * _Nullable image, NSData * _Nullable gifData, BOOL isFallBackImage) { - [MVMCoreDispatchUtility performBlockOnMainThread:^{ - if (image && [image isKindOfClass:[UIImage class]]) { - [self.imageView setImage:image]; - [self layoutIfNeeded]; - } else if (gifData) { - [self.imageView loadGifWithData:gifData]; - [self layoutIfNeeded]; - } - }]; - }; -} - -- (BOOL)shouldLoadImageWithName:(nullable NSString *)imageName width:(CGFloat)width { - return (!self.imageView.image && !self.imageView.animatedImage) || ![imageName isEqualToString:self.loadingImageName] || !fequal(width,self.width) || self.isFallbackImage; -} - -- (void)loadImageWithName:(nullable NSString *)imageName { - [self loadImageWithName:imageName format:nil width:nil height:nil customFallbackImage:nil completionHandler:[self defaultCompletionBlock]]; -} - -- (void)loadImageWithName:(nullable NSString *)imageName width:(nullable NSNumber *)width { - [self loadImageWithName:imageName format:nil width:width height:nil customFallbackImage:nil completionHandler:[self defaultCompletionBlock]]; -} - -- (void)loadImageWithName:(nullable NSString *)imageName height:(nullable NSNumber *)height { - [self loadImageWithName:imageName format:nil width:nil height:height customFallbackImage:nil completionHandler:[self defaultCompletionBlock]]; -} - -- (void)loadImageWithName:(nullable NSString *)imageName width:(nullable NSNumber *)width height:(nullable NSNumber *)height { - [self loadImageWithName:imageName format:nil width:width height:height customFallbackImage:nil completionHandler:[self defaultCompletionBlock]]; -} - -- (void)loadImageWithName:(nullable NSString *)imageName format:(nullable NSString *)format width:(nullable NSNumber *)width height:(nullable NSNumber *)height { - [self loadImageWithName:imageName format:format width:width height:height customFallbackImage:nil completionHandler:[self defaultCompletionBlock]]; -} - -- (void)loadImageWithName:(nullable NSString *)imageName width:(nullable NSNumber *)width height:(nullable NSNumber *)height customFallbackImage:(nullable NSString *)customFallbackImage { - [self loadImageWithName:imageName format:nil width:width height:height customFallbackImage:customFallbackImage completionHandler:[self defaultCompletionBlock]]; -} - -- (void)loadImageWithName:(nullable NSString *)imageName width:(nullable NSNumber *)width height:(nullable NSNumber *)height completionHandler:(nonnull MVMCoreGetImageBlock)completionHandler { - [self loadImageWithName:imageName format:nil width:width height:height customFallbackImage:nil completionHandler:completionHandler]; -} - -- (void)loadImageWithName:(nullable NSString *)imageName exceptImageType:(nullable NSString *)format width:(nullable NSNumber *)width height:(nullable NSNumber *)height customFallbackImage:(nullable NSString *)customFallbackImage { - [self loadImageWithName:imageName format:format width:width height:height customFallbackImage:customFallbackImage completionHandler:[self defaultCompletionBlock]]; -} - -- (void)addConstraintsForWidth:(nullable NSNumber *)width height:(nullable NSNumber *)height size:(CGSize)size { - self.widthConstraint.active = NO; - self.heightConstraint.active = NO; - if (self.addSizeConstraintsForAspectRatio) { - if (width && height) { - NSLayoutConstraint *constraint = [self.imageView.heightAnchor constraintEqualToConstant:height.floatValue]; - constraint.active = YES; - self.heightConstraint = constraint; - constraint = [self.imageView.widthAnchor constraintEqualToConstant:width.floatValue]; - constraint.active = YES; - self.widthConstraint = constraint; - } else if (width) { - NSLayoutConstraint *constraint = [self.imageView.widthAnchor constraintEqualToConstant:width.floatValue]; - constraint.active = YES; - self.widthConstraint = constraint; - constraint = [self.imageView.heightAnchor constraintEqualToAnchor:self.imageView.widthAnchor multiplier:size.height/size.width]; - constraint.active = YES; - self.heightConstraint = constraint; - } else if (height) { - NSLayoutConstraint *constraint = [self.imageView.heightAnchor constraintEqualToConstant:height.floatValue]; - constraint.active = YES; - self.heightConstraint = constraint; - constraint = [self.imageView.widthAnchor constraintEqualToAnchor:self.imageView.heightAnchor multiplier:size.width/size.height]; - constraint.active = YES; - self.widthConstraint = constraint; - } - self.widthConstraint.priority = 900; - self.heightConstraint.priority = 900; - [self.imageView setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal]; - [self.imageView setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisVertical]; - } -} - -- (void)loadImageWithName:(nullable NSString *)imageName format:(nullable NSString *)format width:(nullable NSNumber *)width height:(nullable NSNumber *)height customFallbackImage:(nullable NSString *)customFallbackImage completionHandler:(nonnull MVMCoreGetImageBlock)completionHandler { - __weak typeof(self) weakSelf = self; - [MVMCoreDispatchUtility performBlockOnMainThread:^{ - self.loadingImageName = imageName; - self.width = [width floatValue]; - - [self.loadingSpinner resumeSpinnerAfterDelay]; - self.loadingSpinnerHeight.constant = self.height; - - void (^finishedLoadBlock)(UIImage * _Nullable, NSData * _Nullable, BOOL) = ^(UIImage * _Nullable image, NSData * _Nullable imageData, BOOL isFallBackImage) { - [MVMCoreDispatchUtility performBlockOnMainThread:^{ - // Makes sure the last requested image is the one we are loading. - if ([weakSelf.loadingImageName isEqualToString:imageName]) { - weakSelf.isFallbackImage = isFallBackImage; - weakSelf.loadingSpinnerHeight.constant = 0; - [weakSelf.loadingSpinner pauseSpinner]; - [weakSelf addConstraintsForWidth:width height:height size:image.size]; - completionHandler(image, imageData, isFallBackImage); - } - }]; - }; - - NSString *fallBackImageName = [MVMCoreUIUtility localizedImageName:@"fallback"]; - if ([[format lowercaseString] containsString:@"gif"]) { - - // Gifs aren't supported by default and need special handling - [[MVMCoreCache sharedCache] getGif:imageName useWidth:(width ? YES : NO) widthForS7:[width floatValue] useHeight:(height ? YES : NO) heightForS7:[height floatValue] format:format localFallbackImageName:(customFallbackImage ?: fallBackImageName) completionHandler:^(UIImage * _Nullable image, NSData * _Nullable imageData, BOOL isFallBackImage) { - finishedLoadBlock(image, imageData, isFallBackImage); - }]; - } else { - [[MVMCoreCache sharedCache] getImage:imageName useWidth:(width ? YES : NO) widthForS7:[width floatValue] useHeight:(height ? YES : NO) heightForS7:[height floatValue] format:format localFallbackImageName:(customFallbackImage ?: fallBackImageName) completionHandler:^(UIImage * _Nullable image, NSData * _Nullable imageData, BOOL isFallBackImage) { - finishedLoadBlock(image, nil, isFallBackImage); - }]; - } - }]; -} - -- (void)loadCroppedImageWithName:(nullable NSString *)imageName width:(nullable NSNumber *)width height:(nullable NSNumber *)height cropRect:(CGRect)cropRect flipImage:(BOOL)flipImage customFallbackImage:(nullable NSString *)customFallbackImage { - [MVMCoreDispatchUtility performBlockOnMainThread:^{ - self.loadingImageName = imageName; - [self.loadingSpinner resumeSpinnerAfterDelay]; - self.loadingSpinnerHeight.constant = self.height; - - __weak typeof(self) weakSelf = self; - [[MVMCoreCache sharedCache] getCroppedImage:imageName useWidth:(width ? YES : NO) widthForS7:[width floatValue] useHeight:(height ? YES : NO) heightForS7:[height floatValue] finalRect:cropRect flipImage:flipImage localFallbackImageName:(customFallbackImage ?: @"fallback") completionHandler:^(UIImage * _Nullable image, NSData * _Nullable imageData, BOOL isFallBackImage) { - [MVMCoreDispatchUtility performBlockOnMainThread:^{ - if (image && [image isKindOfClass:[UIImage class]] && ([weakSelf.loadingImageName isEqualToString:imageName])) { - weakSelf.loadingSpinnerHeight.constant = 0; - [weakSelf.loadingSpinner pauseSpinner]; - if (flipImage) { - [weakSelf.imageView setImage:[UIImage imageWithCGImage:image.CGImage - scale:image.scale - orientation:UIImageOrientationUpMirrored]]; - } else { - [weakSelf.imageView setImage:image]; - } - } - [weakSelf layoutIfNeeded]; - }]; - }]; - }]; -} - -- (void)setFrame:(CGRect)frame { - [super setFrame:frame]; -} - -@end diff --git a/MVMCoreUI/Atoms/Views/MFLoadImageView.swift b/MVMCoreUI/Atoms/Views/MFLoadImageView.swift new file mode 100644 index 00000000..46a21f78 --- /dev/null +++ b/MVMCoreUI/Atoms/Views/MFLoadImageView.swift @@ -0,0 +1,283 @@ +// +// LoadImageView.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 3/18/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + +@objcMembers public class MFLoadImageView: ViewConstrainingView { + @objc public let loadingSpinner = MFLoadingSpinner(frame: .zero) + @objc public let imageView = MFTransparentGIFView(frame: .zero) + @objc public var addSizeConstraintsForAspectRatio = false + var centerX: NSLayoutConstraint? + var centerY: NSLayoutConstraint? + var widthConstraint: NSLayoutConstraint? + var heightConstraint: NSLayoutConstraint? + var loadingSpinnerHeightConstraint: NSLayoutConstraint? + + // For keeping track of current state. + private var edges: UIRectEdge? + private var spinnerHeight: CGFloat? + private var width: CGFloat? + private var loadingImageName: String? + private var isFallbackImage: Bool = false + + public init(pinnedEdges edge: UIRectEdge) { + edges = edge + super.init(frame: .zero) + } + + @objc public init() { + edges = UIRectEdge(rawValue: 0) + super.init(frame: .zero) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + @objc public func pinEdges(_ edge: UIRectEdge) { + edges = edge + if edge == UIRectEdge.all { + imageView.setContentHuggingPriority(UILayoutPriority.defaultLow, for: NSLayoutConstraint.Axis.horizontal) + imageView.setContentHuggingPriority(UILayoutPriority.defaultLow, for: NSLayoutConstraint.Axis.vertical) + } else { + imageView.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: NSLayoutConstraint.Axis.horizontal) + imageView.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: NSLayoutConstraint.Axis.vertical) + } + + if let topPin = topPin { + removeConstraint(topPin) + } + if edge.contains(UIRectEdge.top) { + topPin = imageView.topAnchor.constraint(equalTo: topAnchor) + } else { + topPin = imageView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor) + } + topPin?.isActive = true + + if let bottomPin = bottomPin { + removeConstraint(bottomPin) + } + if edge.contains(UIRectEdge.bottom) { + bottomPin = bottomAnchor.constraint(equalTo: imageView.bottomAnchor) + } else { + bottomPin = bottomAnchor.constraint(greaterThanOrEqualTo: imageView.bottomAnchor) + } + bottomPin?.isActive = true + + if let leftPin = leftPin { + removeConstraint(leftPin) + } + if edge.contains(UIRectEdge.left) { + leftPin = imageView.leftAnchor.constraint(equalTo: leftAnchor) + } else { + leftPin = imageView.leftAnchor.constraint(greaterThanOrEqualTo: leftAnchor) + } + leftPin?.isActive = true + + if let rightPin = rightPin { + removeConstraint(rightPin) + } + if edge.contains(UIRectEdge.right) { + rightPin = rightAnchor.constraint(equalTo: imageView.rightAnchor) + } else { + rightPin = rightAnchor.constraint(greaterThanOrEqualTo: imageView.rightAnchor) + } + rightPin?.isActive = true + + // If neither the top or the bottom are pinned, center it. + if let centerY = centerY { + removeConstraint(centerY) + } + if !edge.contains(UIRectEdge.top) && !edge.contains(UIRectEdge.bottom) { + centerY = imageView.centerYAnchor.constraint(equalTo: centerYAnchor) + centerY?.isActive = true + } + + // If neither the left or the right are pinned, center it. + if let centerX = centerX { + removeConstraint(centerX) + } + if !edge.contains(UIRectEdge.left) && !edge.contains(UIRectEdge.right) { + centerX = imageView.centerXAnchor.constraint(equalTo: centerXAnchor) + centerX?.isActive = true + } + } + + override public func setupView() { + super.setupView() + guard subviews.count == 0 else { + return + } + clipsToBounds = true + setContentHuggingPriority(UILayoutPriority(rawValue: 950), for: NSLayoutConstraint.Axis.horizontal) + setContentHuggingPriority(UILayoutPriority(rawValue: 950), for: NSLayoutConstraint.Axis.vertical) + + // Setup image. + imageView.translatesAutoresizingMaskIntoConstraints = false; + addSubview(imageView) + + // Setup edges constraints + if edges == nil { + edges = UIRectEdge(rawValue: 0) + } + pinEdges(edges!) + + // Setup spinner. + loadingSpinner.clipsToBounds = true + loadingSpinner.translatesAutoresizingMaskIntoConstraints = false + addSubview(loadingSpinner) + NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0@500-[loadingSpinner]-0@500-|", metrics: nil, views: ["loadingSpinner" : loadingSpinner])) + NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0@500-[loadingSpinner]-0@500-|", metrics: nil, views: ["loadingSpinner" : loadingSpinner])) + loadingSpinner.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true + loadingSpinner.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + if let constraint = loadingSpinner.pinWidthAndHeight()?[ConstraintHeight] as! NSLayoutConstraint? { + loadingSpinnerHeightConstraint = constraint + spinnerHeight = constraint.constant + loadingSpinnerHeightConstraint?.constant = 0 + loadingSpinnerHeightConstraint?.isActive = true + loadingSpinner.pause() + } + } + + @objc public func defaultCompletionBlock() -> MVMCoreGetImageBlock { + return {image,gifData,_ in MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in + if let image = image { + self?.imageView.image = image + self?.layoutIfNeeded() + } else if let gifData = gifData { + self?.imageView.loadGifWithData(gifData) + self?.layoutIfNeeded() + } + })} + } + + @objc public func shouldLoadImageWithName(_ imageName: String?, width: CGFloat) -> Bool { + // We should load a new image if there is no current image, the image names are different, the width is different, or we are using a fallback image. + guard let currentWidth = self.width else { + return true + } + return (imageView.image == nil && imageView.animatedImage == nil) || imageName != loadingImageName || !MVMCoreGetterUtility.cgfequal(width, currentWidth) || self.isFallbackImage + } + + // Constrains the image view to be the size provided. Used to size it to the image to fix aspect fit defect. + func addConstraints(width: NSNumber?, height: NSNumber?, size: CGSize?) { + widthConstraint?.isActive = false + heightConstraint?.isActive = false + guard addSizeConstraintsForAspectRatio else { + return + } + + if let width = width, let height = height { + heightConstraint = imageView.heightAnchor.constraint(equalToConstant: height.cgfloat()) + widthConstraint = imageView.widthAnchor.constraint(equalToConstant: width.cgfloat()) + } else if let width = width, let size = size { + widthConstraint = imageView.widthAnchor.constraint(equalToConstant: width.cgfloat()) + heightConstraint = imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: size.height/size.width) + } else if let height = height, let size = size { + heightConstraint = imageView.heightAnchor.constraint(equalToConstant: height.cgfloat()) + widthConstraint = imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor, multiplier: size.width/size.height) + } + widthConstraint?.priority = UILayoutPriority(rawValue: 900) + heightConstraint?.priority = UILayoutPriority(rawValue: 900) + heightConstraint?.isActive = true + widthConstraint?.isActive = true + imageView.setContentHuggingPriority(UILayoutPriority.defaultLow, for: NSLayoutConstraint.Axis.horizontal) + imageView.setContentHuggingPriority(UILayoutPriority.defaultLow, for: NSLayoutConstraint.Axis.vertical) + } + + // MARK: - load functions + @objc public func loadImage(withName imageName: String?, format: String?, width: NSNumber?, height: NSNumber?, customFallbackImage: String?, completionHandler: @escaping MVMCoreGetImageBlock) { + MVMCoreDispatchUtility.performBlock(onMainThread: { [unowned self] in + self.loadingImageName = imageName + if let width = width { + self.width = width.cgfloat() + } + self.loadingSpinner.resumeSpinnerAfterDelay() + if let height = self.spinnerHeight { + self.loadingSpinnerHeightConstraint?.constant = height + } + + let finishedLoadingBlock: MVMCoreGetImageBlock = {[weak self] (image, data, isFallbackImage) in MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in + guard let loadingImageName = self?.loadingImageName, loadingImageName == imageName else { + return + } + self?.isFallbackImage = isFallbackImage + self?.loadingSpinnerHeightConstraint?.constant = 0 + self?.loadingSpinner.pause() + self?.addConstraints(width: width, height: height, size: image?.size) + completionHandler(image,data,isFallbackImage) + })} + + let fallbackImageName = customFallbackImage ?? MVMCoreUIUtility.localizedImageName("fallback") + if let format = format, format.lowercased().contains("gif") { + // Gifs aren't supported by default and need special handling + MVMCoreCache.shared()?.getGif(imageName, useWidth: width != nil, widthForS7: width?.intValue ?? 0, useHeight: height != nil, heightForS7: height?.intValue ?? 0, format: format, localFallbackImageName: fallbackImageName, completionHandler: finishedLoadingBlock) + } else { + MVMCoreCache.shared()?.getImage(imageName, useWidth: width != nil, widthForS7: width?.intValue ?? 0, useHeight: height != nil, heightForS7: height?.intValue ?? 0, localFallbackImageName: fallbackImageName, completionHandler: finishedLoadingBlock) + } + }) + } + + @objc public func loadCroppedImage(withName imageName: + String?, width: NSNumber?, height: NSNumber?, cropRect: CGRect, flipImage: Bool, customFallbackImage: String?) { + MVMCoreDispatchUtility.performBlock(onMainThread: { [unowned self] in + self.loadingImageName = imageName + self.loadingSpinner.resumeSpinnerAfterDelay() + if let height = self.spinnerHeight { + self.loadingSpinnerHeightConstraint?.constant = height + } + MVMCoreCache.shared()?.getCroppedImage(imageName, useWidth: width != nil, widthForS7: width?.intValue ?? 0, useHeight: height != nil, heightForS7: height?.intValue ?? 0, finalRect: cropRect, flipImage: flipImage, localFallbackImageName: customFallbackImage ?? MVMCoreUIUtility.localizedImageName("fallback"), completionHandler: { [weak self] (image, data, isFallBackImage) in + MVMCoreDispatchUtility.performBlock(onMainThread: { + guard let image = image, let loadingImageName = self?.loadingImageName, loadingImageName == imageName else { + return + } + self?.loadingSpinnerHeightConstraint?.constant = 0 + self?.loadingSpinner.pause() + if flipImage, let cgImage = image.cgImage { + self?.imageView.image = UIImage(cgImage: cgImage, scale: image.scale, orientation: UIImage.Orientation.upMirrored) + } else { + self?.imageView.image = image + } + self?.layoutIfNeeded() + }) + }) + }) + } + + @objc public func loadImage(withName imageName: String?) { + loadImage(withName: imageName, format: nil, width: nil, height: nil, customFallbackImage: nil, completionHandler: defaultCompletionBlock()) + } + + @objc public func loadImage(withName imageName: String?, width: NSNumber?) { + loadImage(withName: imageName, format: nil, width: width, height: nil, customFallbackImage: nil, completionHandler: defaultCompletionBlock()) + } + + @objc public func loadImage(withName imageName: String?, height: NSNumber?) { + loadImage(withName: imageName, format: nil, width: nil, height: height, customFallbackImage: nil, completionHandler: defaultCompletionBlock()) + } + + @objc public func loadImage(withName imageName: String?, width: NSNumber?, height: NSNumber?) { + loadImage(withName: imageName, format: nil, width: width, height: height, customFallbackImage: nil, completionHandler: defaultCompletionBlock()) + } + + @objc public func loadImage(withName imageName: String?, format: String?, width: NSNumber?, height: NSNumber?) { + loadImage(withName: imageName, format: format, width: width, height: height, customFallbackImage: nil, completionHandler: defaultCompletionBlock()) + } + + @objc public func loadImage(withName imageName: String?, width: NSNumber?, height: NSNumber?, customFallbackImage: String?) { + loadImage(withName: imageName, format: nil, width: width, height: height, customFallbackImage: customFallbackImage, completionHandler: defaultCompletionBlock()) + } + + @objc public func loadImage(withName imageName: String?, width: NSNumber?, height: NSNumber?, completionHandler: @escaping MVMCoreGetImageBlock) { + loadImage(withName: imageName, format: nil, width: width, height: height, customFallbackImage: nil, completionHandler: completionHandler) + } + + @objc public func loadImage(withName imageName: String?, format: String?, width: NSNumber?, height: NSNumber?, customFallbackImage: String?) { + loadImage(withName: imageName, format: format, width: width, height: height, customFallbackImage: customFallbackImage, completionHandler: defaultCompletionBlock()) + } +} diff --git a/MVMCoreUI/Atoms/Views/MFTransparentGIFView.h b/MVMCoreUI/Atoms/Views/MFTransparentGIFView.h deleted file mode 100644 index 38037395..00000000 --- a/MVMCoreUI/Atoms/Views/MFTransparentGIFView.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// MFTransparentGIFView.h -// mobilefirst -// -// Created by Wesolowski, Brendan on 3/16/16. -// Copyright © 2016 Verizon Wireless. All rights reserved. -// - -#import - -#import - -@interface MFTransparentGIFView : FLAnimatedImageView - -/** Creates the GIF display view with the passed in frame. - frame: frame to set the view to. - ImageName: name of the .gif to load. Should not contain the extension. - StartImmediately: should the gif immeidately begin playing. If YES, it will start. If NO, call [performAnimations] to start it. - Duration: how long the animation takes to loop. Pass a negative value to use the default. - LoopCompletionBlock: a block called whenever the gif finishes a loop. - animatedImage : set as nil when use this view in reusable cell - */ --(nullable instancetype)initWithFrame:(CGRect)frame ImageName:(nonnull NSString *)imageName StartImmediately:(BOOL)startImmediately Duration:(NSTimeInterval)duration LoopCompletionBlock:(void (^ __nullable)(NSUInteger loopCountRemaining))loopCompletionBlock; - --(void)loadImage:(nonnull NSString *)imageName StartImmediately:(BOOL)startImmediately Duration:(NSTimeInterval)duration LoopCompletionBlock:(void (^ __nullable)(NSUInteger))loopCompletionBlock; - --(void)loadGifWithData:(nonnull NSData *)imageData; - --(void)setImageData:(nonnull NSData *)imageData; - --(void)performAnimations; - -@end diff --git a/MVMCoreUI/Atoms/Views/MFTransparentGIFView.m b/MVMCoreUI/Atoms/Views/MFTransparentGIFView.m deleted file mode 100644 index 8c05e1f1..00000000 --- a/MVMCoreUI/Atoms/Views/MFTransparentGIFView.m +++ /dev/null @@ -1,74 +0,0 @@ -// -// MFTransparentGIFView.m -// mobilefirst -// -// Created by Wesolowski, Brendan on 3/16/16. -// Copyright © 2016 Verizon Wireless. All rights reserved. -// - -#import "MFTransparentGIFView.h" -#import "FLAnimatedImage.h" -#import "MVMCoreUIUtility.h" -@import MVMCore.MFFreebeeHandler; -@import MVMCore.MVMCoreConstants; - -@interface MFTransparentGIFView () - -@property (strong, nullable, nonatomic) NSData *imageData; - -@end - -@implementation MFTransparentGIFView - - - --(instancetype)initWithFrame:(CGRect)frame ImageName:(NSString *)imageName StartImmediately:(BOOL)startImmediately Duration:(NSTimeInterval)duration LoopCompletionBlock:(void (^)(NSUInteger))loopCompletionBlock { - if(self = [super initWithFrame:frame]) { - [self loadImage:imageName StartImmediately:startImmediately Duration:duration LoopCompletionBlock:loopCompletionBlock]; - } - return self; -} - --(void)loadImage:(NSString *)imageName StartImmediately:(BOOL)startImmediately Duration:(NSTimeInterval)duration LoopCompletionBlock:(void (^)(NSUInteger))loopCompletionBlock { - self.contentMode = UIViewContentModeScaleAspectFill; - self.clipsToBounds = YES; - - if(duration >= 0.0) { - self.animationDuration = duration; - } - self.loopCompletionBlock = loopCompletionBlock; - self.backgroundColor = [UIColor clearColor]; - - NSURL *url; - if([imageName containsString:@"http"]) { - url = [[NSURL alloc] initWithString:imageName]; - } else { - url = [[MVMCoreUIUtility bundleForMVMCoreUI] URLForResource:imageName withExtension:@"gif"]; - } - self.imageData = [[MFFreebeeHandler sharedHandler] freebee_dataWithContentsOfURL:url]; - - self.runLoopMode = NSRunLoopCommonModes; - - if(startImmediately) { - [self performAnimations]; - } -} - --(void)loadGifWithData:(nonnull NSData *)imageData { - - self.contentMode = UIViewContentModeScaleAspectFill; - self.clipsToBounds = YES; - self.backgroundColor = [UIColor clearColor]; - self.imageData = imageData; - self.runLoopMode = NSRunLoopCommonModes; - [self performAnimations]; -} - --(void)setImageData:(NSData *)imageData { - _imageData = imageData; -} - --(void) performAnimations{ - self.animatedImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:self.imageData optimalFrameCacheSize:250 predrawingEnabled:YES]; -} -@end diff --git a/MVMCoreUI/BaseControllers/MFScrollingViewController.m b/MVMCoreUI/BaseControllers/MFScrollingViewController.m index efbf3b9a..f6e2c4d3 100644 --- a/MVMCoreUI/BaseControllers/MFScrollingViewController.m +++ b/MVMCoreUI/BaseControllers/MFScrollingViewController.m @@ -13,10 +13,10 @@ @import MVMCore.NSDictionary_MFConvenience; @import MVMCore.MVMCoreJSONConstants; #import "NSLayoutConstraint+MFConvenience.h" -#import "MFTransparentGIFView.h" #import "MVMCoreUIUtility.h" #import "MVMCoreUIConstants.h" #import "MVMCoreUISession.h" +#import @interface MFScrollingViewController () @@ -299,7 +299,7 @@ static NSTimeInterval const HandScrollAnimationTiming = 7.f; if (!self.gifView) { - MFTransparentGIFView *gifView = [[MFTransparentGIFView alloc] initWithFrame:CGRectZero ImageName:KeyHandScroll StartImmediately:YES Duration:-1 LoopCompletionBlock:nil]; + MFTransparentGIFView *gifView = [[MFTransparentGIFView alloc] initWithFrame:CGRectZero imageName:KeyHandScroll startImmediately:YES duration:-1 loopCompletionBlock:nil]; gifView.translatesAutoresizingMaskIntoConstraints = NO; [self.view addSubview:gifView]; gifView.contentMode = UIViewContentModeScaleAspectFit; diff --git a/MVMCoreUI/BaseControllers/MFViewController.m b/MVMCoreUI/BaseControllers/MFViewController.m index 6bcd4984..1349721d 100644 --- a/MVMCoreUI/BaseControllers/MFViewController.m +++ b/MVMCoreUI/BaseControllers/MFViewController.m @@ -21,13 +21,13 @@ @import MVMCore.NSArray_MFConvenience; @import MVMCore.MVMCoreGetterUtility; @import MVMCore.MVMCoreConstants; +@import MVMCore.MVMCoreCache; #import "NSLayoutConstraint+MFConvenience.h" #import "UIColor+MFConvenience.h" #import "MVMCoreUICommonViewsUtility.h" #import "MFStyler.h" #import "MVMCoreUISplitViewController.h" #import "MVMCoreUITabBarPageControlViewController.h" -#import "MFLoadImageView.h" #import "MFFonts.h" #import #import "MVMCoreUIUtility.h" diff --git a/MVMCoreUI/MVMCoreUI.h b/MVMCoreUI/MVMCoreUI.h index f1fdd0b9..3a8029c4 100644 --- a/MVMCoreUI/MVMCoreUI.h +++ b/MVMCoreUI/MVMCoreUI.h @@ -76,8 +76,6 @@ FOUNDATION_EXPORT const unsigned char MVMCoreUIVersionString[]; #import #import #import -#import -#import #import #import #import diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m index d05132f4..8583cc2e 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m @@ -15,7 +15,7 @@ #import #import "UIColor+MFConvenience.h" #import -#import "MFLoadImageView.h" +#import #import #import "MVMCoreUICommonViewsUtility.h" #import "MVMCoreUITopAlertView.h" @@ -155,7 +155,7 @@ } if (imageURL) { - MFLoadImageView *imageView = [[MFLoadImageView alloc] initWithCenteredImage]; + MFLoadImageView *imageView = [[MFLoadImageView alloc] init]; imageView.translatesAutoresizingMaskIntoConstraints = NO; [imageView setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal]; [self addSubview:imageView]; From 9b6df99126b2d0531ae07c8c0aa16fc7f93dc904 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 19 Mar 2019 13:12:09 -0400 Subject: [PATCH 04/12] gif view to swift --- .../Atoms/Views/MFTransparentGIFView.swift | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift diff --git a/MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift b/MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift new file mode 100644 index 00000000..3556dcb4 --- /dev/null +++ b/MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift @@ -0,0 +1,67 @@ +// +// MFTransparentGifView.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 3/19/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + +@objcMembers public class MFTransparentGifView: FLAnimatedImageView { + var imageData: Data? + + /** Creates the GIF display view with the passed in frame. + frame: frame to set the view to. + ImageName: name of the .gif to load. Should not contain the extension. + StartImmediately: should the gif immeidately begin playing. If YES, it will start. If NO, call [performAnimations] to start it. + Duration: how long the animation takes to loop. Pass a negative value to use the default. + LoopCompletionBlock: a block called whenever the gif finishes a loop. + animatedImage : set as nil when use this view in reusable cell + */ + init(withFrame frame: CGRect, imageName: String, startImmediately: Bool, duration: TimeInterval, loopCompletionBlock: ((UInt) -> Void)?) { + super.init(frame: frame) + loadImage(imageName: imageName, startImmediately: startImmediately, duration: duration, loopCompletionBlock: loopCompletionBlock) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + func loadImage(imageName: String, startImmediately: Bool, duration: TimeInterval, loopCompletionBlock: ((UInt) -> Void)?) { + contentMode = UIView.ContentMode.scaleAspectFill + clipsToBounds = true + + if duration >= 0 { + animationDuration = duration + } + self.loopCompletionBlock = loopCompletionBlock + backgroundColor = .clear + + var url: URL? + if imageName.contains("http") { + url = URL(string: imageName) + } else { + url = MVMCoreUIUtility.bundleForMVMCoreUI()?.url(forResource: imageName, withExtension: "gif") + } + imageData = MFFreebeeHandler.shared()?.freebee_data(withContentsOf: url) + + runLoopMode = RunLoop.Mode.common.rawValue + if startImmediately { + performAnimations() + } + } + + func loadGifWithData(imageData: Data) { + contentMode = UIView.ContentMode.scaleAspectFill + clipsToBounds = true + backgroundColor = .clear + self.imageData = imageData + runLoopMode = RunLoop.Mode.common.rawValue + performAnimations() + } + + func performAnimations() { + animatedImage = FLAnimatedImage(animatedGIFData: imageData, optimalFrameCacheSize: 250, predrawingEnabled: true) + } +} From bef105decd16e7e055ea5870f6017483518dbfa2 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 19 Mar 2019 13:13:18 -0400 Subject: [PATCH 05/12] gif view to swift --- MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift b/MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift index 3556dcb4..f6f26204 100644 --- a/MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift +++ b/MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift @@ -8,7 +8,7 @@ import UIKit -@objcMembers public class MFTransparentGifView: FLAnimatedImageView { +@objcMembers public class MFTransparentGIFView: FLAnimatedImageView { var imageData: Data? /** Creates the GIF display view with the passed in frame. @@ -19,16 +19,20 @@ import UIKit LoopCompletionBlock: a block called whenever the gif finishes a loop. animatedImage : set as nil when use this view in reusable cell */ - init(withFrame frame: CGRect, imageName: String, startImmediately: Bool, duration: TimeInterval, loopCompletionBlock: ((UInt) -> Void)?) { + @objc public init(withFrame frame: CGRect, imageName: String, startImmediately: Bool, duration: TimeInterval, loopCompletionBlock: ((UInt) -> Void)?) { + super.init(frame: frame) + loadImage(imageName, startImmediately: startImmediately, duration: duration, loopCompletionBlock: loopCompletionBlock) + } + + public override init(frame: CGRect) { super.init(frame: frame) - loadImage(imageName: imageName, startImmediately: startImmediately, duration: duration, loopCompletionBlock: loopCompletionBlock) } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - func loadImage(imageName: String, startImmediately: Bool, duration: TimeInterval, loopCompletionBlock: ((UInt) -> Void)?) { + func loadImage(_ imageName: String, startImmediately: Bool, duration: TimeInterval, loopCompletionBlock: ((UInt) -> Void)?) { contentMode = UIView.ContentMode.scaleAspectFill clipsToBounds = true @@ -52,7 +56,7 @@ import UIKit } } - func loadGifWithData(imageData: Data) { + public func loadGifWithData(_ imageData: Data) { contentMode = UIView.ContentMode.scaleAspectFill clipsToBounds = true backgroundColor = .clear @@ -61,7 +65,7 @@ import UIKit performAnimations() } - func performAnimations() { + public func performAnimations() { animatedImage = FLAnimatedImage(animatedGIFData: imageData, optimalFrameCacheSize: 250, predrawingEnabled: true) } } From 74ab14f5f962e42cf2f5a8f00bf3ae0d5cf004dc Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 19 Mar 2019 13:52:30 -0400 Subject: [PATCH 06/12] cleaning --- MVMCoreUI/Atoms/Views/MFLoadImageView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/MFLoadImageView.swift b/MVMCoreUI/Atoms/Views/MFLoadImageView.swift index 46a21f78..d94813a6 100644 --- a/MVMCoreUI/Atoms/Views/MFLoadImageView.swift +++ b/MVMCoreUI/Atoms/Views/MFLoadImageView.swift @@ -156,7 +156,7 @@ import UIKit })} } - @objc public func shouldLoadImageWithName(_ imageName: String?, width: CGFloat) -> Bool { + @objc public func shouldLoadImage(withName imageName: String?, width: CGFloat) -> Bool { // We should load a new image if there is no current image, the image names are different, the width is different, or we are using a fallback image. guard let currentWidth = self.width else { return true From 0a8d76bb7bcd39d56cb1712acb2fcd625c73baab Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 19 Mar 2019 13:53:16 -0400 Subject: [PATCH 07/12] clean --- MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift b/MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift index f6f26204..1db220fc 100644 --- a/MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift +++ b/MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift @@ -32,7 +32,7 @@ import UIKit super.init(coder: aDecoder) } - func loadImage(_ imageName: String, startImmediately: Bool, duration: TimeInterval, loopCompletionBlock: ((UInt) -> Void)?) { + public func loadImage(_ imageName: String, startImmediately: Bool, duration: TimeInterval, loopCompletionBlock: ((UInt) -> Void)?) { contentMode = UIView.ContentMode.scaleAspectFill clipsToBounds = true From 680f8fc4aafcb26b7f5694fb5cfafd181e3b0cc5 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 19 Mar 2019 14:02:29 -0400 Subject: [PATCH 08/12] comment --- MVMCoreUI/Atoms/Views/MFLoadImageView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/MVMCoreUI/Atoms/Views/MFLoadImageView.swift b/MVMCoreUI/Atoms/Views/MFLoadImageView.swift index d94813a6..fad344b4 100644 --- a/MVMCoreUI/Atoms/Views/MFLoadImageView.swift +++ b/MVMCoreUI/Atoms/Views/MFLoadImageView.swift @@ -30,6 +30,7 @@ import UIKit super.init(frame: .zero) } + // The default is an image that is centered with no edges pinned. So it will take the size of the content and fill as needed. @objc public init() { edges = UIRectEdge(rawValue: 0) super.init(frame: .zero) From 3564d993e17cfbaf1fd61a67efbe3fd9b18de529 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Tue, 19 Mar 2019 17:08:26 -0400 Subject: [PATCH 09/12] image molecule protocol --- MVMCoreUI/Atoms/Views/MFLoadImageView.swift | 22 +++++++++++++++++++++ MVMCoreUI/Atoms/Views/MFView.h | 2 ++ MVMCoreUI/Atoms/Views/MFView.m | 1 + 3 files changed, 25 insertions(+) diff --git a/MVMCoreUI/Atoms/Views/MFLoadImageView.swift b/MVMCoreUI/Atoms/Views/MFLoadImageView.swift index fad344b4..9c135325 100644 --- a/MVMCoreUI/Atoms/Views/MFLoadImageView.swift +++ b/MVMCoreUI/Atoms/Views/MFLoadImageView.swift @@ -191,6 +191,28 @@ import UIKit imageView.setContentHuggingPriority(UILayoutPriority.defaultLow, for: NSLayoutConstraint.Axis.vertical) } + public override func updateView(_ size: CGFloat) { + super.updateView(size) + let width = size.rounded() + if let imageName = json?.optionalStringForKey("image"), shouldLoadImage(withName: imageName, width: width) { + imageView.image = nil + imageView.animatedImage = nil + loadImage(withName: imageName, format: json?.optionalStringForKey("imageFormat"), width: NSNumber(value: Double(width)), height: nil) + loadImage(withName: imageName, format: json?.optionalStringForKey("imageFormat"), width: NSNumber(value: Double(width)), height: nil, customFallbackImage: json?.optionalStringForKey("fallbackImage")) + } + } + + // MARK: - MVMCoreUIMoleculeViewProtocol functions + public override func setWithJSON(_ json: [AnyHashable : Any]?, delegate: NSObject?, additionalData: [AnyHashable : Any]?) { + super.setWithJSON(json, delegate: delegate, additionalData: additionalData) + if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) { + backgroundColor = UIColor.mfGet(forHex: backgroundColorString) + } + if let accessibilityString = json?.optionalStringForKey("accessibilityText") { + imageView.accessibilityLabel = accessibilityString + } + } + // MARK: - load functions @objc public func loadImage(withName imageName: String?, format: String?, width: NSNumber?, height: NSNumber?, customFallbackImage: String?, completionHandler: @escaping MVMCoreGetImageBlock) { MVMCoreDispatchUtility.performBlock(onMainThread: { [unowned self] in diff --git a/MVMCoreUI/Atoms/Views/MFView.h b/MVMCoreUI/Atoms/Views/MFView.h index eb76b858..5e32ff0a 100644 --- a/MVMCoreUI/Atoms/Views/MFView.h +++ b/MVMCoreUI/Atoms/Views/MFView.h @@ -12,6 +12,8 @@ @interface MFView : UIView +@property (nullable, nonatomic, strong) NSDictionary *json; + // Called in the initialization functions. Can setup ui here. - (void)setupView; diff --git a/MVMCoreUI/Atoms/Views/MFView.m b/MVMCoreUI/Atoms/Views/MFView.m index 40df33a2..b2047c45 100644 --- a/MVMCoreUI/Atoms/Views/MFView.m +++ b/MVMCoreUI/Atoms/Views/MFView.m @@ -44,6 +44,7 @@ #pragma mark - MVMCoreUIMoleculeViewProtocol - (void)setWithJSON:(NSDictionary *)json delegate:(NSObject *)delegate additionalData:(nullable NSDictionary *)additionalData { + self.json = json; } @end From 3040629fcbea5f6e53b0e7fcb5d76e7a5c60b4c1 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 20 Mar 2019 10:07:50 -0400 Subject: [PATCH 10/12] objc clean --- MVMCoreUI/Atoms/Views/MFLoadImageView.swift | 56 +++++++++++---------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/MFLoadImageView.swift b/MVMCoreUI/Atoms/Views/MFLoadImageView.swift index 9c135325..f707253b 100644 --- a/MVMCoreUI/Atoms/Views/MFLoadImageView.swift +++ b/MVMCoreUI/Atoms/Views/MFLoadImageView.swift @@ -8,10 +8,10 @@ import UIKit -@objcMembers public class MFLoadImageView: ViewConstrainingView { - @objc public let loadingSpinner = MFLoadingSpinner(frame: .zero) - @objc public let imageView = MFTransparentGIFView(frame: .zero) - @objc public var addSizeConstraintsForAspectRatio = false +@objcMembers open class MFLoadImageView: ViewConstrainingView { + public let loadingSpinner = MFLoadingSpinner(frame: .zero) + public let imageView = MFTransparentGIFView(frame: .zero) + public var addSizeConstraintsForAspectRatio = false var centerX: NSLayoutConstraint? var centerY: NSLayoutConstraint? var widthConstraint: NSLayoutConstraint? @@ -19,11 +19,11 @@ import UIKit var loadingSpinnerHeightConstraint: NSLayoutConstraint? // For keeping track of current state. - private var edges: UIRectEdge? - private var spinnerHeight: CGFloat? - private var width: CGFloat? - private var loadingImageName: String? - private var isFallbackImage: Bool = false + var edges: UIRectEdge? + var spinnerHeight: CGFloat? + var width: CGFloat? + var loadingImageName: String? + var isFallbackImage: Bool = false public init(pinnedEdges edge: UIRectEdge) { edges = edge @@ -31,16 +31,16 @@ import UIKit } // The default is an image that is centered with no edges pinned. So it will take the size of the content and fill as needed. - @objc public init() { + public init() { edges = UIRectEdge(rawValue: 0) super.init(frame: .zero) } - required init?(coder aDecoder: NSCoder) { + required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - @objc public func pinEdges(_ edge: UIRectEdge) { + public func pinEdges(_ edge: UIRectEdge) { edges = edge if edge == UIRectEdge.all { imageView.setContentHuggingPriority(UILayoutPriority.defaultLow, for: NSLayoutConstraint.Axis.horizontal) @@ -109,7 +109,7 @@ import UIKit } } - override public func setupView() { + override open func setupView() { super.setupView() guard subviews.count == 0 else { return @@ -145,7 +145,7 @@ import UIKit } } - @objc public func defaultCompletionBlock() -> MVMCoreGetImageBlock { + open func defaultCompletionBlock() -> MVMCoreGetImageBlock { return {image,gifData,_ in MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in if let image = image { self?.imageView.image = image @@ -157,7 +157,7 @@ import UIKit })} } - @objc public func shouldLoadImage(withName imageName: String?, width: CGFloat) -> Bool { + open func shouldLoadImage(withName imageName: String?, width: CGFloat) -> Bool { // We should load a new image if there is no current image, the image names are different, the width is different, or we are using a fallback image. guard let currentWidth = self.width else { return true @@ -191,7 +191,7 @@ import UIKit imageView.setContentHuggingPriority(UILayoutPriority.defaultLow, for: NSLayoutConstraint.Axis.vertical) } - public override func updateView(_ size: CGFloat) { + open override func updateView(_ size: CGFloat) { super.updateView(size) let width = size.rounded() if let imageName = json?.optionalStringForKey("image"), shouldLoadImage(withName: imageName, width: width) { @@ -203,18 +203,20 @@ import UIKit } // MARK: - MVMCoreUIMoleculeViewProtocol functions - public override func setWithJSON(_ json: [AnyHashable : Any]?, delegate: NSObject?, additionalData: [AnyHashable : Any]?) { + open override func setWithJSON(_ json: [AnyHashable : Any]?, delegate: NSObject?, additionalData: [AnyHashable : Any]?) { super.setWithJSON(json, delegate: delegate, additionalData: additionalData) if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) { backgroundColor = UIColor.mfGet(forHex: backgroundColorString) } if let accessibilityString = json?.optionalStringForKey("accessibilityText") { imageView.accessibilityLabel = accessibilityString + imageView.accessibilityTraits = .staticText + imageView.isAccessibilityElement = true } } // MARK: - load functions - @objc public func loadImage(withName imageName: String?, format: String?, width: NSNumber?, height: NSNumber?, customFallbackImage: String?, completionHandler: @escaping MVMCoreGetImageBlock) { + public func loadImage(withName imageName: String?, format: String?, width: NSNumber?, height: NSNumber?, customFallbackImage: String?, completionHandler: @escaping MVMCoreGetImageBlock) { MVMCoreDispatchUtility.performBlock(onMainThread: { [unowned self] in self.loadingImageName = imageName if let width = width { @@ -246,7 +248,7 @@ import UIKit }) } - @objc public func loadCroppedImage(withName imageName: + public func loadCroppedImage(withName imageName: String?, width: NSNumber?, height: NSNumber?, cropRect: CGRect, flipImage: Bool, customFallbackImage: String?) { MVMCoreDispatchUtility.performBlock(onMainThread: { [unowned self] in self.loadingImageName = imageName @@ -272,35 +274,35 @@ import UIKit }) } - @objc public func loadImage(withName imageName: String?) { + public func loadImage(withName imageName: String?) { loadImage(withName: imageName, format: nil, width: nil, height: nil, customFallbackImage: nil, completionHandler: defaultCompletionBlock()) } - @objc public func loadImage(withName imageName: String?, width: NSNumber?) { + public func loadImage(withName imageName: String?, width: NSNumber?) { loadImage(withName: imageName, format: nil, width: width, height: nil, customFallbackImage: nil, completionHandler: defaultCompletionBlock()) } - @objc public func loadImage(withName imageName: String?, height: NSNumber?) { + public func loadImage(withName imageName: String?, height: NSNumber?) { loadImage(withName: imageName, format: nil, width: nil, height: height, customFallbackImage: nil, completionHandler: defaultCompletionBlock()) } - @objc public func loadImage(withName imageName: String?, width: NSNumber?, height: NSNumber?) { + public func loadImage(withName imageName: String?, width: NSNumber?, height: NSNumber?) { loadImage(withName: imageName, format: nil, width: width, height: height, customFallbackImage: nil, completionHandler: defaultCompletionBlock()) } - @objc public func loadImage(withName imageName: String?, format: String?, width: NSNumber?, height: NSNumber?) { + public func loadImage(withName imageName: String?, format: String?, width: NSNumber?, height: NSNumber?) { loadImage(withName: imageName, format: format, width: width, height: height, customFallbackImage: nil, completionHandler: defaultCompletionBlock()) } - @objc public func loadImage(withName imageName: String?, width: NSNumber?, height: NSNumber?, customFallbackImage: String?) { + public func loadImage(withName imageName: String?, width: NSNumber?, height: NSNumber?, customFallbackImage: String?) { loadImage(withName: imageName, format: nil, width: width, height: height, customFallbackImage: customFallbackImage, completionHandler: defaultCompletionBlock()) } - @objc public func loadImage(withName imageName: String?, width: NSNumber?, height: NSNumber?, completionHandler: @escaping MVMCoreGetImageBlock) { + public func loadImage(withName imageName: String?, width: NSNumber?, height: NSNumber?, completionHandler: @escaping MVMCoreGetImageBlock) { loadImage(withName: imageName, format: nil, width: width, height: height, customFallbackImage: nil, completionHandler: completionHandler) } - @objc public func loadImage(withName imageName: String?, format: String?, width: NSNumber?, height: NSNumber?, customFallbackImage: String?) { + public func loadImage(withName imageName: String?, format: String?, width: NSNumber?, height: NSNumber?, customFallbackImage: String?) { loadImage(withName: imageName, format: format, width: width, height: height, customFallbackImage: customFallbackImage, completionHandler: defaultCompletionBlock()) } } From a8145fd3c036d6e5aabb1ef617e8ca7368914099 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 20 Mar 2019 10:09:56 -0400 Subject: [PATCH 11/12] objc clean --- MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift b/MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift index 1db220fc..0c220de5 100644 --- a/MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift +++ b/MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift @@ -19,7 +19,7 @@ import UIKit LoopCompletionBlock: a block called whenever the gif finishes a loop. animatedImage : set as nil when use this view in reusable cell */ - @objc public init(withFrame frame: CGRect, imageName: String, startImmediately: Bool, duration: TimeInterval, loopCompletionBlock: ((UInt) -> Void)?) { + public init(withFrame frame: CGRect, imageName: String, startImmediately: Bool, duration: TimeInterval, loopCompletionBlock: ((UInt) -> Void)?) { super.init(frame: frame) loadImage(imageName, startImmediately: startImmediately, duration: duration, loopCompletionBlock: loopCompletionBlock) } From c411ec83eed72480ec2106a9ec25384057fe6976 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 20 Mar 2019 14:42:20 -0400 Subject: [PATCH 12/12] Button defaulting --- MVMCoreUI/Atoms/Buttons/PrimaryButton.h | 4 + MVMCoreUI/Atoms/Buttons/PrimaryButton.m | 171 ++++++++++++++---------- MVMCoreUI/Molecules/PrimaryButtonView.h | 7 +- MVMCoreUI/Molecules/PrimaryButtonView.m | 57 ++++---- 4 files changed, 128 insertions(+), 111 deletions(-) diff --git a/MVMCoreUI/Atoms/Buttons/PrimaryButton.h b/MVMCoreUI/Atoms/Buttons/PrimaryButton.h index ba1c06d2..182a527b 100644 --- a/MVMCoreUI/Atoms/Buttons/PrimaryButton.h +++ b/MVMCoreUI/Atoms/Buttons/PrimaryButton.h @@ -75,6 +75,10 @@ static CGFloat const PrimaryButtonSmallHeight = 30.0; #pragma mark - Setting +// The new defaults. +- (void)setAsStandardCustom; +- (void)setAsSecondaryCustom; + // For setting after creation. - (void)setAsSmallButton:(BOOL)smallButton enabled:(BOOL)enabled bordered:(BOOL)bordered; diff --git a/MVMCoreUI/Atoms/Buttons/PrimaryButton.m b/MVMCoreUI/Atoms/Buttons/PrimaryButton.m index 5300d774..960e29e7 100644 --- a/MVMCoreUI/Atoms/Buttons/PrimaryButton.m +++ b/MVMCoreUI/Atoms/Buttons/PrimaryButton.m @@ -34,6 +34,56 @@ @implementation PrimaryButton +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + if (self = [super initWithCoder:aDecoder]) { + [self pinHeight]; + [self setAsRed]; + [self setAsSmallButton:NO]; + } + return self; +} + +- (CGSize)intrinsicContentSize { + CGSize size = [super intrinsicContentSize]; + CGSize newSize = CGSizeMake(size.width + (self.smallButton ? [[self innerPaddingSmallButton] getValueBasedOnSize:self.sizeForSizing]*2 : [[self innerPadding] getValueBasedOnSize:self.sizeForSizing]*2), size.height); + if (self.tinyButton) { + newSize = CGSizeMake(size.width + [[self innerPaddingTinyButton] getValueBasedOnSize:self.sizeForSizing]*2 , size.height); + } + if (self.smallButton) { + CGFloat minimumWidth = [[self minimumWidthSmallButton] getValueBasedOnSize:self.sizeForSizing]; + if (newSize.width > minimumWidth) { + return newSize; + } else { + return CGSizeMake(minimumWidth, size.height); + } + } else if (self.tinyButton) { + CGFloat minimumWidth = [[self minimumWidthTinyButton] getValueBasedOnSize:self.sizeForSizing]; + if (newSize.width > minimumWidth) { + return newSize; + } else { + return CGSizeMake(minimumWidth, size.height); + } + } else { + CGFloat minimumWidth = [[self minimumWidth] getValueBasedOnSize:self.sizeForSizing]; + if (newSize.width > minimumWidth) { + return newSize; + } else { + return CGSizeMake(minimumWidth, size.height); + } + } +} + +- (void)pinHeight { + + // Adds the height constraint. + if (!self.height) { + NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:[[self buttonHeight] getValueBasedOnSize:self.sizeForSizing]]; + self.height = height; + self.heightConstraint = height; + height.active = YES; + } +} + #pragma mark - Sizing - (MFSizeObject *)innerPadding { @@ -470,6 +520,15 @@ #pragma mark - Creation ++ (instancetype)getButton { + PrimaryButton *button = [PrimaryButton buttonWithType:UIButtonTypeCustom]; + button.translatesAutoresizingMaskIntoConstraints = NO; + button.sizeForSizing = [MVMCoreUISplitViewController getApplicationViewWidth]; + [button pinHeight]; + button.skipHighlighted = NO; + return button; +} + + (instancetype)primaryButton:(BOOL)enabled { PrimaryButton *button = [self getButton]; button.primaryButtonType = PrimaryButtonTypeBlack; @@ -561,86 +620,56 @@ return button; } -#pragma mark - For Subclassing +#pragma mark - Molecule protocol + +- (void)setAsStandardCustom { + // Default to standard look. + self.primaryButtonType = PrimaryButtonTypeCustom; + self.fillColor = [UIColor blackColor]; + self.textColor = [UIColor whiteColor]; + self.borderColor = nil; + _bordered = false; +} + +- (void)setAsSecondaryCustom { + // Default to standard look. + self.primaryButtonType = PrimaryButtonTypeCustom; + self.fillColor = nil; + self.textColor = nil; + self.borderColor = [UIColor blackColor]; + _bordered = true; +} + +- (void)setAsMolecule { + [self setAsStandardCustom]; +} - (void)setWithJSON:(NSDictionary *)json delegate:(NSObject *)delegate additionalData:(nullable NSDictionary *)additionalData { self.primaryButtonType = PrimaryButtonTypeCustom; NSString *color = [json string:@"fillColor"]; - self.fillColor = (color ? [UIColor mfGetColorForHex:color] : nil); - color = [json string:KeyTextColor]; - self.textColor = (color ? [UIColor mfGetColorForHex:color] : nil); - color = [json string:@"borderColor"]; - self.borderColor = (color ? [UIColor mfGetColorForHex:color] : nil); + if (color) { + self.fillColor = [UIColor mfGetColorForHex:color]; + } + if ((color = [json string:KeyTextColor])) { + self.textColor = [UIColor mfGetColorForHex:color]; + } + if ((color = [json string:@"borderColor"])) { + self.borderColor = [UIColor mfGetColorForHex:color]; + } _bordered = self.borderColor != nil; - color = [json string:@"disabledFillColor"]; - self.disabledFillColor = (color ? [UIColor mfGetColorForHex:color] : nil); - color = [json string:@"disabledTextColor"]; - self.disabledTextColor = (color ? [UIColor mfGetColorForHex:color] : nil); - color = [json string:@"disabledBorderColor"]; - self.disabledBorderColor = (color ? [UIColor mfGetColorForHex:color] : nil); + if ((color = [json string:@"disabledFillColor"])) { + self.disabledFillColor = [UIColor mfGetColorForHex:color]; + } + if ((color = [json string:@"disabledTextColor"])) { + self.disabledTextColor = [UIColor mfGetColorForHex:color]; + } + if ((color = [json string:@"disabledBorderColor"])) { + self.disabledBorderColor = [UIColor mfGetColorForHex:color]; + } [self setAsSmallButton:[json boolForKey:@"small"]]; [self setWithActionMap:json actionDelegate:([delegate conformsToProtocol:@protocol(MVMCoreActionDelegateProtocol)] ? (NSObject *)delegate : nil) additionalData:additionalData buttonDelegate:([delegate conformsToProtocol:@protocol(ButtonDelegateProtocol)] ? (id )delegate : nil)]; } -- (instancetype)initWithCoder:(NSCoder *)aDecoder { - if (self = [super initWithCoder:aDecoder]) { - [self pinHeight]; - [self setAsRed]; - [self setAsSmallButton:NO]; - } - return self; -} - -- (CGSize)intrinsicContentSize { - CGSize size = [super intrinsicContentSize]; - CGSize newSize = CGSizeMake(size.width + (self.smallButton ? [[self innerPaddingSmallButton] getValueBasedOnSize:self.sizeForSizing]*2 : [[self innerPadding] getValueBasedOnSize:self.sizeForSizing]*2), size.height); - if (self.tinyButton) { - newSize = CGSizeMake(size.width + [[self innerPaddingTinyButton] getValueBasedOnSize:self.sizeForSizing]*2 , size.height); - } - if (self.smallButton) { - CGFloat minimumWidth = [[self minimumWidthSmallButton] getValueBasedOnSize:self.sizeForSizing]; - if (newSize.width > minimumWidth) { - return newSize; - } else { - return CGSizeMake(minimumWidth, size.height); - } - } else if (self.tinyButton) { - CGFloat minimumWidth = [[self minimumWidthTinyButton] getValueBasedOnSize:self.sizeForSizing]; - if (newSize.width > minimumWidth) { - return newSize; - } else { - return CGSizeMake(minimumWidth, size.height); - } - } else { - CGFloat minimumWidth = [[self minimumWidth] getValueBasedOnSize:self.sizeForSizing]; - if (newSize.width > minimumWidth) { - return newSize; - } else { - return CGSizeMake(minimumWidth, size.height); - } - } -} - -+ (instancetype)getButton { - PrimaryButton *button = [PrimaryButton buttonWithType:UIButtonTypeCustom]; - button.translatesAutoresizingMaskIntoConstraints = NO; - button.sizeForSizing = [MVMCoreUISplitViewController getApplicationViewWidth]; - [button pinHeight]; - button.skipHighlighted = NO; - return button; -} - -- (void)pinHeight { - - // Adds the height constraint. - if (!self.height) { - NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:[[self buttonHeight] getValueBasedOnSize:self.sizeForSizing]]; - self.height = height; - self.heightConstraint = height; - height.active = YES; - } -} - #pragma mark - Handling Validations - (void)setEnabledByValidity { diff --git a/MVMCoreUI/Molecules/PrimaryButtonView.h b/MVMCoreUI/Molecules/PrimaryButtonView.h index 7140a0d3..dbfd0c20 100644 --- a/MVMCoreUI/Molecules/PrimaryButtonView.h +++ b/MVMCoreUI/Molecules/PrimaryButtonView.h @@ -24,14 +24,9 @@ // Inits with two buttons. - (nonnull instancetype)initWithTwoButtons; -// Inits with whatever is in the passed in button map. (could be 0, 1, or 2 buttons) +// Legacy: Sets up with whatever is in the passed in button map. (could be 0, 1, or 2 buttons) - (nonnull instancetype)initButtonSmall:(BOOL)small buttonMap:(nullable NSDictionary *)buttonMap actionDelegate:(nullable NSObject *)actionDelegate additionalData:(nullable NSDictionary *)additionalData buttonDelegate:(nullable id )buttonDelegate; - (nonnull instancetype)initWithPrimaryButtonMap:(nullable NSDictionary *)primaryButtonMap secondaryButtonMap:(nullable NSDictionary *)secondaryButtonMap actionDelegate:(nullable NSObject *)actionDelegate additionalData:(nullable NSDictionary *)additionalData buttonDelegate:(nullable id )buttonDelegate; - -// Sets up with whatever is in the passed in. (could be 0, 1, or 2 buttons) -- (void)setupWithFirstButtonJSON:(nullable NSDictionary *)firstButtonJSON secondButtonJSON:(nullable NSDictionary *)secondButtonJSON additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)actionDelegate buttonDelegate:(nullable id )buttonDelegate; - -// Legacy: Sets up with whatever is in the passed in button map. (could be 0, 1, or 2 buttons) - (void)setupWithButtonMap:(nullable NSDictionary *)buttonMap actionDelegate:(nullable NSObject *)actionDelegate additionalData:(nullable NSDictionary *)additionalData buttonDelegate:(nullable id )buttonDelegate; - (void)setupWithPrimaryButtonMap:(nullable NSDictionary *)primaryButtonMap secondaryButtonMap:(nullable NSDictionary *)secondaryButtonMap actionDelegate:(nullable NSObject *)actionDelegate additionalData:(nullable NSDictionary *)additionalData buttonDelegate:(nullable id )buttonDelegate; diff --git a/MVMCoreUI/Molecules/PrimaryButtonView.m b/MVMCoreUI/Molecules/PrimaryButtonView.m index 16ce03ed..a479efab 100644 --- a/MVMCoreUI/Molecules/PrimaryButtonView.m +++ b/MVMCoreUI/Molecules/PrimaryButtonView.m @@ -42,7 +42,13 @@ if (backgroundColorString) { self.backgroundColor = [UIColor mfGetColorForHex:backgroundColorString]; } - [self setupWithFirstButtonJSON:[json dict:@"firstButton"] secondButtonJSON:[json dict:@"secondButton"] additionalData:nil actionDelegate:([delegate conformsToProtocol:@protocol(MVMCoreActionDelegateProtocol)] ? (NSObject *)delegate : nil) buttonDelegate:([delegate conformsToProtocol:@protocol(ButtonDelegateProtocol)] ? (id )delegate : nil)]; + NSDictionary *primaryButtonMap = [json dict:@"primaryButton"]; + NSDictionary *secondaryButtonMap = [json dict:@"secondaryButton"]; + [self setupUIWithPrimaryButtonMap:primaryButtonMap secondaryButtonMap:secondaryButtonMap]; + [self.primaryButton setAsStandardCustom]; + [self.secondaryButton setAsSecondaryCustom]; + [self.primaryButton setWithJSON:primaryButtonMap delegate:delegate additionalData:additionalData]; + [self.secondaryButton setWithJSON:secondaryButtonMap delegate:delegate additionalData:additionalData]; } #pragma mark - Inits @@ -89,6 +95,8 @@ return self; } +#pragma mark - Legacy Setup + - (nonnull instancetype)initButtonSmall:(BOOL)small buttonMap:(nullable NSDictionary *)buttonMap actionDelegate:(nullable NSObject *)actionDelegate additionalData:(nullable NSDictionary *)additionalData buttonDelegate:(nullable id )buttonDelegate { if (self = [self init]) { [self setupWithButtonMap:buttonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate]; @@ -105,24 +113,29 @@ return self; } -#pragma mark - Setup - - (void)setupWithButtonMap:(nullable NSDictionary *)buttonMap actionDelegate:(nullable NSObject *)actionDelegate additionalData:(nullable NSDictionary *)additionalData buttonDelegate:(nullable id )buttonDelegate { - NSDictionary *secondaryButtonMap = [buttonMap dict:KeySecondaryButton]; NSDictionary *primaryButtonMap = [buttonMap dict:KeyPrimaryButton]; [self setupWithPrimaryButtonMap:primaryButtonMap secondaryButtonMap:secondaryButtonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate]; } -- (void)setupWithFirstButtonJSON:(nullable NSDictionary *)firstButtonJSON secondButtonJSON:(nullable NSDictionary *)secondButtonJSON additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)actionDelegate buttonDelegate:(nullable id )buttonDelegate { - [self setupWithPrimaryButtonMap:secondButtonJSON secondaryButtonMap:firstButtonJSON additionalData:additionalData actionDelegate:actionDelegate buttonDelegate:buttonDelegate legacyJSON:NO]; -} - - (void)setupWithPrimaryButtonMap:(nullable NSDictionary *)primaryButtonMap secondaryButtonMap:(nullable NSDictionary *)secondaryButtonMap actionDelegate:(nullable NSObject *)actionDelegate additionalData:(nullable NSDictionary *)additionalData buttonDelegate:(nullable id )buttonDelegate { - [self setupWithPrimaryButtonMap:primaryButtonMap secondaryButtonMap:secondaryButtonMap additionalData:additionalData actionDelegate:actionDelegate buttonDelegate:buttonDelegate legacyJSON:YES]; + [self setupUIWithPrimaryButtonMap:primaryButtonMap secondaryButtonMap:secondaryButtonMap]; + if (self.primaryButton && self.secondaryButton) { + [self.primaryButton setWithActionMap:primaryButtonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate]; + [self.secondaryButton setWithActionMap:secondaryButtonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate]; + } else if (self.primaryButton) { + [self.primaryButton setWithActionMap:primaryButtonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate]; + self.primaryButton.bordered = NO; + } else { + [self.primaryButton setWithActionMap:secondaryButtonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate]; + self.primaryButton.bordered = YES; + } } -- (void)setupWithPrimaryButtonMap:(nullable NSDictionary *)primaryButtonMap secondaryButtonMap:(nullable NSDictionary *)secondaryButtonMap additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)actionDelegate buttonDelegate:(nullable id )buttonDelegate legacyJSON:(BOOL)legacyJSON { +#pragma mark - Setup + +- (void)setupUIWithPrimaryButtonMap:(nullable NSDictionary *)primaryButtonMap secondaryButtonMap:(nullable NSDictionary *)secondaryButtonMap { if (primaryButtonMap && secondaryButtonMap) { self.height.active = NO; @@ -132,13 +145,6 @@ self.twoButtonView = nil; [self setupWithTwoButtons]; } - if (legacyJSON) { - [self.primaryButton setWithActionMap:primaryButtonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate]; - [self.secondaryButton setWithActionMap:secondaryButtonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate]; - } else { - [self.primaryButton setWithJSON:primaryButtonMap delegate:actionDelegate additionalData:additionalData]; - [self.secondaryButton setWithJSON:secondaryButtonMap delegate:actionDelegate additionalData:additionalData]; - } } else if (primaryButtonMap || secondaryButtonMap) { self.height.active = NO; @@ -149,23 +155,6 @@ self.secondaryButton = nil; [self setupWithSingleButton]; } - [self alignCenter]; - - if (legacyJSON) { - if (primaryButtonMap) { - - // Only primary button - [self.primaryButton setWithActionMap:primaryButtonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate]; - self.primaryButton.bordered = NO; - } else { - - // Only secondary button - [self.primaryButton setWithActionMap:secondaryButtonMap actionDelegate:actionDelegate additionalData:additionalData buttonDelegate:buttonDelegate]; - self.primaryButton.bordered = YES; - } - } else { - [self.primaryButton setWithJSON:primaryButtonMap delegate:actionDelegate additionalData:additionalData]; - } } else { [self removeSubviews]; if (!self.height) {