diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index bae49737..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 */, @@ -950,6 +942,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"; @@ -1014,6 +1007,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"; 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/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 05444f03..00000000 --- a/MVMCoreUI/Atoms/Views/MFLoadImageView.m +++ /dev/null @@ -1,312 +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.priority = 900; - constraint.active = YES; - self.heightConstraint = constraint; - constraint = [self.imageView.widthAnchor constraintEqualToConstant:width.floatValue]; - constraint.priority = 900; - constraint.active = YES; - self.widthConstraint = constraint; - } else if (width) { - NSLayoutConstraint *constraint = [self.imageView.widthAnchor constraintEqualToConstant:width.floatValue]; - constraint.priority = 900; - constraint.active = YES; - self.widthConstraint = constraint; - constraint = [self.imageView.heightAnchor constraintEqualToAnchor:self.imageView.widthAnchor multiplier:size.height/size.width]; - constraint.priority = 900; - constraint.active = YES; - self.heightConstraint = constraint; - } else if (height) { - NSLayoutConstraint *constraint = [self.imageView.heightAnchor constraintEqualToConstant:height.floatValue]; - constraint.priority = 900; - constraint.active = YES; - self.heightConstraint = constraint; - constraint = [self.imageView.widthAnchor constraintEqualToAnchor:self.imageView.heightAnchor multiplier:size.width/size.height]; - constraint.priority = 900; - constraint.active = YES; - self.widthConstraint = constraint; - } - [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..f707253b --- /dev/null +++ b/MVMCoreUI/Atoms/Views/MFLoadImageView.swift @@ -0,0 +1,308 @@ +// +// LoadImageView.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 3/18/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import UIKit + +@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? + var heightConstraint: NSLayoutConstraint? + var loadingSpinnerHeightConstraint: NSLayoutConstraint? + + // For keeping track of current state. + var edges: UIRectEdge? + var spinnerHeight: CGFloat? + var width: CGFloat? + var loadingImageName: String? + var isFallbackImage: Bool = false + + public init(pinnedEdges edge: UIRectEdge) { + edges = edge + 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. + public init() { + edges = UIRectEdge(rawValue: 0) + super.init(frame: .zero) + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + 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 open 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() + } + } + + open 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() + } + })} + } + + 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 + } + 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) + } + + open 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 + 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 + 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) + } + }) + } + + 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() + }) + }) + }) + } + + public func loadImage(withName imageName: String?) { + loadImage(withName: imageName, format: nil, width: nil, height: nil, customFallbackImage: nil, completionHandler: defaultCompletionBlock()) + } + + public func loadImage(withName imageName: String?, width: NSNumber?) { + loadImage(withName: imageName, format: nil, width: width, height: nil, customFallbackImage: nil, completionHandler: defaultCompletionBlock()) + } + + public func loadImage(withName imageName: String?, height: NSNumber?) { + loadImage(withName: imageName, format: nil, width: nil, height: height, customFallbackImage: nil, completionHandler: defaultCompletionBlock()) + } + + public func loadImage(withName imageName: String?, width: NSNumber?, height: NSNumber?) { + loadImage(withName: imageName, format: nil, width: width, height: height, customFallbackImage: nil, completionHandler: defaultCompletionBlock()) + } + + 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()) + } + + 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()) + } + + 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) + } + + 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/Atoms/Views/MFTransparentGIFView.swift b/MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift new file mode 100644 index 00000000..0c220de5 --- /dev/null +++ b/MVMCoreUI/Atoms/Views/MFTransparentGIFView.swift @@ -0,0 +1,71 @@ +// +// 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 + */ + 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) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + public 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() + } + } + + public func loadGifWithData(_ imageData: Data) { + contentMode = UIView.ContentMode.scaleAspectFill + clipsToBounds = true + backgroundColor = .clear + self.imageData = imageData + runLoopMode = RunLoop.Mode.common.rawValue + performAnimations() + } + + public func performAnimations() { + animatedImage = FLAnimatedImage(animatedGIFData: imageData, optimalFrameCacheSize: 250, predrawingEnabled: true) + } +} 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 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/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) { 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];