From c03e04069abe60053ded75da63cb58134ed4a0b9 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 3 Jan 2024 14:04:30 -0600 Subject: [PATCH] refactored loader Signed-off-by: Matt Bruce --- MVMCoreUI.xcodeproj/project.pbxproj | 10 +- .../Atomic/Atoms/Views/LoadImageView.swift | 6 +- MVMCoreUI/Atomic/Atoms/Views/WebView.swift | 4 +- .../Legacy/Views/MFLoadingSpinner+VDS.swift | 42 ++++++ MVMCoreUI/Legacy/Views/MFLoadingSpinner.h | 11 -- MVMCoreUI/Legacy/Views/MFLoadingSpinner.m | 138 ++---------------- 6 files changed, 68 insertions(+), 143 deletions(-) create mode 100644 MVMCoreUI/Legacy/Views/MFLoadingSpinner+VDS.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 8619ce46..09c333cb 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -298,8 +298,8 @@ AFA4935729EE3DCC001A9663 /* AlertDelegateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFA4935629EE3DCC001A9663 /* AlertDelegateProtocol.swift */; }; AFE4A1D127DFB5EE00C458D0 /* VDSColorTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = AFE4A1D027DFB5EE00C458D0 /* VDSColorTokens.xcframework */; }; AFE4A1D627DFBB6F00C458D0 /* UINavigationController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFE4A1D527DFBB6F00C458D0 /* UINavigationController+Extension.swift */; }; - B4CC8FBF29DF34730005D28B /* BadgeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4CC8FBE29DF34730005D28B /* BadgeModel.swift */; }; B4CC8FBD29DF34680005D28B /* Badge.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4CC8FBC29DF34680005D28B /* Badge.swift */; }; + B4CC8FBF29DF34730005D28B /* BadgeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4CC8FBE29DF34730005D28B /* BadgeModel.swift */; }; BB105859248DEFF70069D008 /* UICollectionViewLeftAlignedLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB105858248DEFF60069D008 /* UICollectionViewLeftAlignedLayout.swift */; }; BB1D17E0244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1D17DF244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift */; }; BB1D17E2244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1D17E1244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift */; }; @@ -576,8 +576,8 @@ EA5124FF2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5124FE2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift */; }; EA7E67742758310500ABF773 /* EnableFormFieldEffectModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7E67732758310500ABF773 /* EnableFormFieldEffectModel.swift */; }; EA7E67762758365300ABF773 /* UIUpdatableModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7E67752758365300ABF773 /* UIUpdatableModelProtocol.swift */; }; - EA985C402970939A00F2FF2E /* TileletModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA985C3F2970939A00F2FF2E /* TileletModel.swift */; }; EA985C3E2970938F00F2FF2E /* Tilelet.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA985C3D2970938F00F2FF2E /* Tilelet.swift */; }; + EA985C402970939A00F2FF2E /* TileletModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA985C3F2970939A00F2FF2E /* TileletModel.swift */; }; EA985C602970A3F000F2FF2E /* VDS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA985C5F2970A3F000F2FF2E /* VDS.framework */; }; EA985C642970A40E00F2FF2E /* VDSTypographyTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA985C632970A40E00F2FF2E /* VDSTypographyTokens.xcframework */; }; EA985C852981AA9C00F2FF2E /* VDS-Enums+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA985C842981AA9C00F2FF2E /* VDS-Enums+Codable.swift */; }; @@ -587,6 +587,7 @@ EAA0CFAF275E7D8000D65EB0 /* FormFieldEffectProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA0CFAE275E7D8000D65EB0 /* FormFieldEffectProtocol.swift */; }; EAA0CFB1275E823A00D65EB0 /* HideFormFieldEffectModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA0CFB0275E823A00D65EB0 /* HideFormFieldEffectModel.swift */; }; EAA0CFB3275E831E00D65EB0 /* DisableFormFieldEffectModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA0CFB2275E831E00D65EB0 /* DisableFormFieldEffectModel.swift */; }; + EAA482CE2B45F2F300978105 /* MFLoadingSpinner+VDS.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA482CD2B45F2F300978105 /* MFLoadingSpinner+VDS.swift */; }; EAA78020290081320057DFDF /* VDSMoleculeViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA7801F290081320057DFDF /* VDSMoleculeViewProtocol.swift */; }; EAB14BC127D935F00012AB2C /* RuleCompareModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB14BC027D935F00012AB2C /* RuleCompareModelProtocol.swift */; }; EAB14BC327D9378D0012AB2C /* RuleAnyModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB14BC227D9378D0012AB2C /* RuleAnyModelProtocol.swift */; }; @@ -890,8 +891,8 @@ AFA4935629EE3DCC001A9663 /* AlertDelegateProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertDelegateProtocol.swift; sourceTree = ""; }; AFE4A1D027DFB5EE00C458D0 /* VDSColorTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSColorTokens.xcframework; path = ../SharedFrameworks/VDSColorTokens.xcframework; sourceTree = ""; }; AFE4A1D527DFBB6F00C458D0 /* UINavigationController+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationController+Extension.swift"; sourceTree = ""; }; - B4CC8FBE29DF34730005D28B /* BadgeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeModel.swift; sourceTree = ""; }; B4CC8FBC29DF34680005D28B /* Badge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Badge.swift; sourceTree = ""; }; + B4CC8FBE29DF34730005D28B /* BadgeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeModel.swift; sourceTree = ""; }; BB105858248DEFF60069D008 /* UICollectionViewLeftAlignedLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionViewLeftAlignedLayout.swift; sourceTree = ""; }; BB1D17DF244EAA30001D2002 /* ListDeviceComplexButtonMediumModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexButtonMediumModel.swift; sourceTree = ""; }; BB1D17E1244EAA46001D2002 /* ListDeviceComplexButtonMedium.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexButtonMedium.swift; sourceTree = ""; }; @@ -1180,6 +1181,7 @@ EAA0CFAE275E7D8000D65EB0 /* FormFieldEffectProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormFieldEffectProtocol.swift; sourceTree = ""; }; EAA0CFB0275E823A00D65EB0 /* HideFormFieldEffectModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HideFormFieldEffectModel.swift; sourceTree = ""; }; EAA0CFB2275E831E00D65EB0 /* DisableFormFieldEffectModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableFormFieldEffectModel.swift; sourceTree = ""; }; + EAA482CD2B45F2F300978105 /* MFLoadingSpinner+VDS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MFLoadingSpinner+VDS.swift"; sourceTree = ""; }; EAA7801F290081320057DFDF /* VDSMoleculeViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VDSMoleculeViewProtocol.swift; sourceTree = ""; }; EAB14BC027D935F00012AB2C /* RuleCompareModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleCompareModelProtocol.swift; sourceTree = ""; }; EAB14BC227D9378D0012AB2C /* RuleAnyModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleAnyModelProtocol.swift; sourceTree = ""; }; @@ -1635,6 +1637,7 @@ D20492A324329A2800A5EED6 /* MVMCoreUIPagingProtocol.h */, D29DF2B121E7B76C003B2FB9 /* MFLoadingSpinner.h */, D29DF2B221E7B76D003B2FB9 /* MFLoadingSpinner.m */, + EAA482CD2B45F2F300978105 /* MFLoadingSpinner+VDS.swift */, D29DF25821E6A22D003B2FB9 /* MFButtonProtocol.h */, D29DF16B21E69E1F003B2FB9 /* ButtonDelegateProtocol.h */, ); @@ -2689,6 +2692,7 @@ 0A6682A42434DB8D00AD3CA1 /* ListLeftVariableRadioButtonBodyTextModel.swift in Sources */, AA2AD116244EE46800BBFFE3 /* ListDeviceComplexLinkMedium.swift in Sources */, AA7F32AD246C0F8C00C965BA /* ListLeftVariableRadioButtonAllTextAndLinks.swift in Sources */, + EAA482CE2B45F2F300978105 /* MFLoadingSpinner+VDS.swift in Sources */, D272F5F92473163100BD1A8F /* BarButtonItem.swift in Sources */, D2D2FCF3252B72CF0033EAAA /* MoleculeSectionFooter.swift in Sources */, 0A9D09202433796500D2E6C0 /* BarsIndicatorView.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift b/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift index 780f8829..e7601015 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/LoadImageView.swift @@ -117,7 +117,7 @@ spinnerHeight = constraint.constant loadingSpinnerHeightConstraint?.constant = 0 loadingSpinnerHeightConstraint?.isActive = true - loadingSpinner.pause() + loadingSpinner.pauseSpinner() } } @@ -326,7 +326,7 @@ guard let self = self, let loadingImageName = self.currentImageName, loadingImageName == imageName else { return } self.isFallbackImage = isFallbackImage - self.loadingSpinner.pause() + self.loadingSpinner.pauseSpinner() let layoutWillChange = self.shouldNotifyDelegateOnUpdate ? self.layoutWillChange(width: self.currentImageWidth, height: self.currentImageHeight, size: image?.size) : false self.addConstraints(width: width, height: height, size: image?.size) self.loadingSpinnerHeightConstraint?.constant = 0 @@ -359,7 +359,7 @@ return } self?.loadingSpinnerHeightConstraint?.constant = 0 - self?.loadingSpinner.pause() + self?.loadingSpinner.pauseSpinner() if flipImage, let cgImage = image.cgImage { self?.imageView.image = UIImage(cgImage: cgImage, scale: image.scale, orientation: UIImage.Orientation.upMirrored) } else { diff --git a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift index d3a91920..8e2923be 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WebView.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WebView.swift @@ -122,7 +122,7 @@ extension WebView : WKUIDelegate { public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { // hide loading overLayer.isHidden = true - loadingSpinner.pause() + loadingSpinner.pauseSpinner() //update webview's heigth when webview is ready if !dynamicHeight { @@ -159,7 +159,7 @@ extension WebView : WKUIDelegate { //actually no error handle page show in webview. We can handle the error display view by our self. //or stop loading by default overLayer.isHidden = true - loadingSpinner.pause() + loadingSpinner.pauseSpinner() } } diff --git a/MVMCoreUI/Legacy/Views/MFLoadingSpinner+VDS.swift b/MVMCoreUI/Legacy/Views/MFLoadingSpinner+VDS.swift new file mode 100644 index 00000000..f058699c --- /dev/null +++ b/MVMCoreUI/Legacy/Views/MFLoadingSpinner+VDS.swift @@ -0,0 +1,42 @@ +// +// MFLoadingSpinner+VDS.swift +// MVMCoreUI +// +// Created by Matt Bruce on 1/3/24. +// Copyright © 2024 Verizon Wireless. All rights reserved. +// + +import Foundation +import VDS + +extension MFLoadingSpinner { + var loader: Loader? { + subviews.first as? Loader + } + + @objc(setUpCircle:) + open func setUpCircle(strokeColor: UIColor?) { + if let strokeColor { + loader?.surface = strokeColor.isDark() ? .light : .dark + } + } + + @objc open func pauseSpinner() { + loader?.isActive = false + } + + @objc open func resumeSpinner() { + loader?.isActive = true + } + + @objc open func resumeSpinnerAfterDelay() { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in + self?.loader?.isActive = true + } + } + + @objc open func pinWidthAndHeight() -> NSDictionary? { + guard let size = loader?.size else { return nil } + return NSLayoutConstraint.constraintPinView(self, heightConstraint: true, heightConstant: CGFloat(size), widthConstraint: true, widthConstant: CGFloat(size)) as NSDictionary? + } +} diff --git a/MVMCoreUI/Legacy/Views/MFLoadingSpinner.h b/MVMCoreUI/Legacy/Views/MFLoadingSpinner.h index 6a44f743..6e4bb55c 100644 --- a/MVMCoreUI/Legacy/Views/MFLoadingSpinner.h +++ b/MVMCoreUI/Legacy/Views/MFLoadingSpinner.h @@ -12,17 +12,6 @@ -(void)setUpCircle; --(void)setUpCircle:(nullable UIColor *)strokeColor; - -(void)changeColor:(nullable UIColor *)strokeColor; -- (void)pauseSpinner; - -- (void)resumeSpinner; - -// Starts the spinner after a slight delay. -- (void)resumeSpinnerAfterDelay; - -- (nullable NSDictionary *)pinWidthAndHeight; - @end diff --git a/MVMCoreUI/Legacy/Views/MFLoadingSpinner.m b/MVMCoreUI/Legacy/Views/MFLoadingSpinner.m index 7b354dca..07a254d6 100644 --- a/MVMCoreUI/Legacy/Views/MFLoadingSpinner.m +++ b/MVMCoreUI/Legacy/Views/MFLoadingSpinner.m @@ -7,143 +7,33 @@ // #import "MFLoadingSpinner.h" -#import "UIColor+MFConvenience.h" #import "NSLayoutConstraint+MFConvenience.h" +#import +#import @interface MFLoadingSpinner () - -@property (strong, nonatomic) CAShapeLayer *myCircle; -@property (strong, nonatomic) CADisplayLink *myDisplay; -@property (weak, nonatomic) dispatch_block_t resumeBlock; - -@property (nonatomic) double prevFrame; - -@property (nonatomic) BOOL isFast; - +@property (strong, nonatomic) VDSLoader *loader; @end @implementation MFLoadingSpinner - - - -const float radius = 19; -const float lineWidth = 3.0; -const float slowSpeed = 0.5; -const float fastSpeed = 2.0; -const float startSpeed = 1.0; -const float fastDistance = .45; -const float slowDistance = 0.1; - - --(void)finalize { - [self.myDisplay invalidate]; - self.myDisplay = nil; +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + self.loader = [[VDSLoader alloc] init]; + [self addSubview: self.loader]; + [NSLayoutConstraint pinViewToSuperview:self.loader useMargins:false]; + } + return self; } -(void)setUpCircle { - [self setUpCircle:[UIColor blackColor]]; -} - --(void)setUpCircle:(UIColor *)strokeColor { - if(self.myCircle) - { - return; - } - - CAShapeLayer *circle = [CAShapeLayer layer]; - circle.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(radius + lineWidth/2, radius + lineWidth/2) radius:radius startAngle:-M_PI_2 endAngle:3.5*M_PI clockwise:YES].CGPath; - circle.lineWidth = lineWidth; - circle.fillColor = [UIColor clearColor].CGColor; - circle.strokeColor = strokeColor.CGColor; - circle.lineCap = kCALineCapButt; - circle.strokeStart = 0; - circle.strokeEnd = 0+.05; - [self.layer addSublayer:circle]; - self.myCircle = circle; - - self.isFast = YES; - - NSMutableDictionary *newActions = [[NSMutableDictionary alloc] initWithObjectsAndKeys:[NSNull null], @"strokeStart", - [NSNull null], @"strokeEnd", - [NSNull null], @"strokeColor", - nil]; - circle.actions = newActions; - - self.myDisplay = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateSpinner)]; - [self.myDisplay addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; - self.myDisplay.frameInterval = 2; - self.prevFrame = CACurrentMediaTime(); + [self setUpCircle:[UIColor blackColor]]; } -(void)changeColor:(UIColor *)strokeColor { - self.myCircle.strokeColor = strokeColor.CGColor; -} - --(void)updateSpinner { - double currentTime = CACurrentMediaTime(); - double renderTime = currentTime - self.prevFrame; - self.prevFrame = currentTime; - - if(self.myCircle.strokeStart > 0.5 && self.myCircle.strokeEnd > 0.5) { - self.myCircle.strokeStart -= 0.5; - self.myCircle.strokeEnd -= 0.5; - } - - float distanceToStart = self.myCircle.strokeEnd - self.myCircle.strokeStart; - if(distanceToStart < slowDistance && !self.isFast) { - self.isFast = YES; - } - else if(distanceToStart > fastDistance && self.isFast) { - self.isFast = NO; - } - self.myCircle.strokeEnd += (self.isFast ? fastSpeed : slowSpeed) * renderTime; - self.myCircle.strokeStart+= startSpeed * renderTime; - -} - -- (void)pauseSpinner { - if (self.resumeBlock) { - // Cancel the current resume block if it hasn't run. dispatch our pause into the same queue incase the resume block is already running. - dispatch_block_cancel(self.resumeBlock); - self.resumeBlock = nil; - __weak typeof(self) weakSelf = self; - dispatch_async(dispatch_get_main_queue(), ^{ - weakSelf.myDisplay.paused = YES; - weakSelf.hidden = YES; - }); - } else { - self.myDisplay.paused = YES; - self.hidden = YES; - } -} - -- (void)resumeSpinner { - self.hidden = NO; - if (!self.myCircle) { - [self setUpCircle]; - return; - } - - self.myDisplay.paused = NO; - self.prevFrame = CACurrentMediaTime(); -} - -- (void)resumeSpinnerAfterDelay { - if (!self.resumeBlock) { - __weak typeof(self) weakSelf = self; - dispatch_block_t resume = dispatch_block_create(0, ^{ - [weakSelf resumeSpinner]; - weakSelf.resumeBlock = nil; - }); - self.resumeBlock = resume; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), resume); - } -} - -- (nullable NSDictionary *)pinWidthAndHeight { - CGFloat diameter = radius*2 + lineWidth; - return [NSLayoutConstraint constraintPinView:self heightConstraint:YES heightConstant:diameter widthConstraint:YES widthConstant:diameter]; + [self setUpCircle: strokeColor]; } @end