paint-brush
UI 후퇴: CALayer를 사용하여 iOS용 레트로 진행률 표시줄 만들기~에 의해@maxkalik
684 판독값
684 판독값

UI 후퇴: CALayer를 사용하여 iOS용 레트로 진행률 표시줄 만들기

~에 의해 Max Kalik22m2023/12/17
Read on Terminal Reader

너무 오래; 읽다

이 가이드에서는 향수를 불러일으키는 CALayers를 사용하여 iOS에서 복고풍 스타일의 진행률 표시줄을 만드는 방법을 살펴봅니다. 각 레이어 디자인부터 애니메이션 구현까지 단계별 프로세스를 다루며, 전통적인 미학과 최신 iOS 개발을 결합하는 방법에 대한 통찰력을 제공합니다. 개발자와 UI 디자이너에게 이상적인 이 기사는 고유한 UI 구성 요소에 대한 기술적 깊이와 창의적인 영감을 모두 제공합니다. 소스 코드: https://github.com/maxkalik/RetroProgressBar
featured image - UI 후퇴: CALayer를 사용하여 iOS용 레트로 진행률 표시줄 만들기
Max Kalik HackerNoon profile picture
0-item


저는 UI 구성 요소를 만드는 것을 좋아합니다. 빌드를 만든 후 보고 사용하는 것은 나에게 일종의 미학적 즐거움을 줍니다. 이는 또한 MacOS/iOS 생태계 전체에 대한 나의 사랑과 결합됩니다. 최근에 저는 UIKit 사용하여 사용자 정의 진행률 표시줄을 만드는 임무를 받았습니다. 큰 문제는 아니지만 아마도 99%의 iOS 개발자가 경력 중 어느 시점에 처음부터 새로 만들었습니다. 제 경우에는 0에서 1 사이의 값 범위를 기반으로 하는 애니메이션으로 몇 개의 CALayer 를 만드는 것 이상의 작업을 하고 싶었습니다. 진행률 표시줄이 필요한 많은 시나리오에 적합한 솔루션을 목표로 삼았습니다. .


저는 Apple 세계의 UI 구성요소가 다음과 같았던 시절로 돌아가고 싶습니다.


출처: https://www.smashingmagazine.com/2010/08/free-wireframing-kits-ui-design-kits-pdfs-and-resources/


UI 디자인의 발전을 되돌아보면, 2014년 iOS 7이 출시되면서 애플 세계의 UI 요소가 플랫 디자인을 채택한 지 거의 10년이 되었습니다. 그러니 이전 버전의 Mac OS와 iOS에 대한 향수에 빠져 아름다운 진행률 표시줄이 함께 표시됩니다.


UIKit 즐겨 사용하지만 CALayer를 조작하는 방법에 대해 여전히 확신이 없다면 이 기사가 도움이 될 것입니다. 여기에서는 일반적인 레이아웃, 애니메이션 및 그리기 문제를 살펴보고 그 과정에서 도움이 되는 통찰력과 솔루션을 제공합니다.

UIView에서 CALayer API까지 심층적으로 살펴보세요.

진행률 표시줄은 간단해 보일 수 있지만 세부 사항을 자세히 살펴보면 많은 하위 레이어가 필요하다는 것을 이해할 수 있습니다.


레트로 진행률 표시줄


여기서는 CALayers 에 대해 자세히 다루지 않겠습니다. CALayers 무엇이고 어떻게 작동하는지 간략하게 설명한 이 기사를 확인하실 수 있습니다: https://hackernoon.com/rolling-numbers-animation-using-only-calayers .


이제 작은 레이어로 나누어 보겠습니다.

  1. 일반 배경 레이어
  2. 마스크 레이어(애니메이션 가능한 부분)
  3. 진행 배경 레이어
  4. 글레어 레이어
  5. 쉬머링 레이어


배경 레이어에는 두 개의 추가 하위 레이어가 있습니다.

  1. 내부 그림자 레이어
  2. 테두리 그라데이션 레이어


올바르게 구조화해 보겠습니다.


 RetroProgressBar [UIView] |--General background [CALayer] |--Inner Shadow [CALayer] |--Border Gradient [CALayer] |--Progress Background Layer [CALayer] |--Animated Mask Layer [CALayer] |--Glare [CALayer] |--Shimering [CALayer]


간략하게 작동 방식은 다음과 같습니다. 진행률 표시줄은 0.0에서 1.0 사이의 값에 반응해야 합니다. 값이 변경되면 이 값을 기준으로 레이어 너비를 조정해야 합니다.


 bounds.width * value


이 간단한 계산을 사용하여 CATransaction 사용하여 너비를 수정할 수 있습니다. 하지만 이를 수행하기 전에 값이 변경될 때 어떤 레이어가 변경되어야 하는지 이해해야 합니다. 구조를 다시 살펴보고 값 변화에 반응해야 하는 레이어를 식별해 보겠습니다.


 RetroProgressBar [UIView] |--General background [CALayer] |--Inner Shadow [CALayer] |--Border Gradient [CALayer] |--Progress Background Layer [CALayer] |--Animated Mask Layer [CALayer] : [Animated Width] |--Glare [CALayer] : [Animated Width] |--Shimering [CALayer] : [Animated Width]


값에 따라 애니메이션 너비가 필요한 레이어에서 채택할 수 있는 프로토콜을 개발해야 할 것으로 보입니다.

ProgressAnimatable 프로토콜

프로토콜에서는 애니메이션을 사용하거나 사용하지 않고 레이어 너비를 변경하는 두 가지 방법만 필요합니다.


 protocol ProgressAnimatable: CALayer { /// Sets the layer's width instantly to the specified value. func setToWidth(_ width: CGFloat) /// Animates the layer's width to the specified value. func animateToWidth(_ width: CGFloat, duration: TimeInterval, animationType: CAMediaTimingFunctionName, completion: (() -> Void)?) }


구현:


 extension ProgressAnimatable { func setToWidth(_ width: CGFloat) { guard width >= 0 else { return } removeAllAnimations() CATransaction.begin() CATransaction.setDisableActions(true) self.bounds.size.width = width CATransaction.commit() } func animateToWidth(_ width: CGFloat, duration: TimeInterval, animationType: CAMediaTimingFunctionName = .easeInEaseOut, completion: (() -> Void)? = nil) { guard width >= 0, width != self.bounds.width else { completion?() return } let animation = CABasicAnimation(keyPath: "bounds.size.width") animation.fromValue = bounds.width animation.toValue = width animation.duration = duration animation.fillMode = .forwards animation.isRemovedOnCompletion = false animation.timingFunction = CAMediaTimingFunction(name: animationType) CATransaction.begin() CATransaction.setCompletionBlock { self.bounds.size.width = width completion?() } self.add(animation, forKey: "widthChange") CATransaction.commit() } }


두 방법 모두 CATransaction 사용합니다. 첫 번째 방법은 애니메이션 없이 값만 변경하면 되기 때문에 이상해 보일 수 있습니다. 그렇다면 CATransaction 왜 필요한가요?


기본 CALayer 만들고 애니메이션 없이 너비나 높이를 변경해 보세요. 빌드를 실행하면 CALayer 애니메이션으로 변경되는 것을 확인할 수 있습니다. 이것이 어떻게 가능한지? Core Animation에서 레이어 속성(예: 경계, 위치, 불투명도 등)에 대한 변경 사항은 일반적으로 해당 속성과 관련된 기본 동작을 기반으로 애니메이션화됩니다. 이 기본 동작은 모든 속성 변경에 대해 명시적인 애니메이션이 필요 없이 부드러운 시각적 전환을 제공하기 위한 것입니다.


이는 CALayer 에서 작업을 명시적으로 비활성화해야 함을 의미합니다. CATransaction.setDisableActions(true) 를 설정하면 중간 애니메이션 전환 없이 레이어 너비가 즉시 새 값으로 업데이트됩니다.


두 번째 방법은 ProgressAnimatable 프로토콜을 준수하는 모든 레이어의 너비에 애니메이션을 적용하는 유연하고 재사용 가능한 방법을 제공합니다. 애니메이션의 지속 시간과 속도를 제어하고 완료 작업에 대한 옵션을 포함합니다. 이는 UIKit 프레임워크 내의 다양한 애니메이션 요구에 매우 적합합니다.


이 프로토콜을 준수할 후보는 Animated Mask Layer, Glare Layer 및 Shimmering Layer의 세 가지 레이어입니다. 계속해서 구현해 보겠습니다.

진행마스크레이어

이제 프로토콜을 준수해야 할 때이며 이 설정에서 가장 중요한 레이어는 일반 CAShapeLayer 입니다. 이 마스크가 왜 필요한가요? 왜 애니메이션 폭을 가진 CALayer 가 될 수 없나요? iOS 엔지니어 , 특히 프런트 엔드 개발에 중점을 두는 엔지니어로서 구성 요소의 잠재적인 사용 사례를 예상해야 하는 경우가 많습니다. 제 경우에는 Progress Background Layer가 있습니다. 이것은 애니메이션 CALayer 아니지만 CAReplicatorLayer 와 같은 것으로 애니메이션된다면 어떨까요? 성능이 염려된다면 해당 레이어를 한 번 렌더링한 다음 해당 애니메이션을 수행하는 것을 고려할 수 있습니다. 이것이 바로 이 마스크가 매우 유용할 수 있는 이유입니다. 다양한 애니메이션 시나리오에 필요한 유연성을 제공하면서 효율적인 렌더링이 가능합니다.


 final class ProgressMaskLayer: CAShapeLayer, ProgressAnimatable { override init() { super.init() backgroundColor = UIColor.black.cgColor anchorPoint = CGPoint(x: 0, y: 0.5) } override init(layer: Any) { super.init(layer: layer) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }


다음 줄을 살펴보겠습니다.


 anchorPoint = CGPoint(x: 0, y: 0.5)


이 줄은 진행률 표시줄에 매우 중요합니다. 기본적으로 CALayers 중앙에 위치하며, anchorPoint 일반적으로 가정되는 왼쪽 가장자리의 중앙이 아닌 레이어 중앙에 위치합니다. anchorPoint 레이어의 모든 변형이 발생하는 지점입니다. 진행률 표시줄로 사용되는 레이어의 경우 이 설정은 일반적으로 레이어의 너비가 변경될 때 anchorPoint 에서 양방향으로 동일하게 확장됨을 의미합니다. 그러나 anchorPoint 왼쪽 가장자리의 중앙으로 조정하면 레이어는 오른쪽으로만 확장되고 왼쪽 가장자리는 제자리에 고정됩니다. 이 동작은 진행률 표시줄에 필수적이므로 표시줄의 증가가 양쪽이 아닌 한쪽에서 나타나도록 합니다.


 private let progressMaskLayer = ProgressMaskLayer() private let progressBackgroundLayer = ProgressBackgroundLayer() public init() { super.init(frame: .zero) layer.addSublayer(progressMaskLayer) layer.addSublayer(progressBackgroundLayer) progressBackgroundLayer.mask = progressMaskLayer }


위의 코드 조각에서는 진행률 표시줄의 두 가지 주요 부분인 ProgressMaskLayerProgressBackgroundLayer 초기화합니다. 두 번째를 살펴 보겠습니다.


 final class ProgressBackgroundLayer: CAGradientLayer { override init() { super.init() locations = [0, 0.5, 1] } override init(layer: Any) { super.init(layer: layer) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func layoutSublayers() { super.layoutSublayers() colors = [ backgroundColor?.lightened(by: 0.3), backgroundColor, backgroundColor?.darkened(by: 0.5) ].compactMap({ $0 }) } }


보시다시피 세 가지 색상을 갖춘 CAGradientLayer 입니다. 이 구현은 주입된 색상을 수직 그라데이션으로 변환하여 진행률 표시줄의 가시성을 향상시키고 더 볼륨감 있는 모양을 제공합니다.


구배

이를 가능하게 하기 위해 CGColor 확장에 두 가지 메서드를 준비했습니다. 재미삼아 CGColor 다시 UIColor 로 변환하지 않고 CGColor 영역 내에서만 재생하기로 결정했습니다.


 extension CGColor { func darkened(by value: CGFloat) -> CGColor { guard let colorSpace = self.colorSpace, let components = self.components, colorSpace.model == .rgb, components.count >= 3 else { return self } let multiplier = 1 - min(max(value, 0), 1) let red = components[0] * multiplier let green = components[1] * multiplier let blue = components[2] * multiplier let alpha = components.count > 3 ? components[3] : 1.0 return CGColor( colorSpace: colorSpace, components: [red, green, blue, alpha] ) ?? self } func lightened(by value: CGFloat) -> CGColor { guard let colorSpace = self.colorSpace, let components = self.components, colorSpace.model == .rgb, components.count >= 3 else { return self } let red = min(components[0] + value, 1.0) let green = min(components[1] + value, 1.0) let blue = min(components[2] + value, 1.0) let alpha = components.count > 3 ? components[3] : 1.0 return CGColor( colorSpace: colorSpace, components: [red, green, blue, alpha] ) ?? self } }


보시다시피, 그다지 복잡하지 않습니다. colorSpace , components ( red , green , bluealpha ) 및 model 만 가져오면 됩니다. 이는 값을 기반으로 더 어둡거나 밝은 새로운 색상을 계산할 수 있음을 의미합니다.


글레어 레이어

진행률 표시줄을 좀 더 3D처럼 만들어 보겠습니다. 이제 Glare라고 하는 또 다른 레이어를 추가해야 합니다.


이는 간단한 구현이지만 ProgressAnimatable 프로토콜을 준수해야 합니다.


 final class ProgressGlareLayer: CALayer, ProgressAnimatable { override init() { super.init() backgroundColor = UIColor.white.withAlphaComponent(0.3).cgColor anchorPoint = CGPoint(x: 0, y: 0.5) } override init(layer: Any) { super.init(layer: layer) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } 


눈부심 있음


작업을 단순하게 유지하기 위해 여기서는 알파가 0.3인 흰색 배경색만 필요합니다.


리틀 나이스 쉬머링 (쉬머링 레이어)

버전 6 이전의 MacOS X나 iOS에는 이와 같은 반짝이는 효과가 있는 UI 구성 요소가 없었다고 말하는 사람도 있을 것입니다. 하지만 아까 말했듯이 이 영화는 실화를 바탕으로 한 영화 같아요. 이 반짝임은 3D 효과를 강조하고 더욱 눈에 띄게 만듭니다.


 final class ShimmeringLayer: CAGradientLayer, ProgressAnimatable { let glowColor: UIColor = UIColor.white private func shimmerAnimation() -> CABasicAnimation { let animation = CABasicAnimation(keyPath: "locations") animation.fromValue = [-1.0, -0.5, 0.0] animation.toValue = [1.0, 1.5, 2.0] animation.duration = 1.5 animation.repeatCount = Float.infinity return animation } private func opacityAnimation() -> CABasicAnimation { let animation = CABasicAnimation(keyPath: "opacity") animation.fromValue = 1.0 animation.toValue = 0.0 animation.duration = 0.1 // Quick transition to transparent animation.beginTime = shimmerAnimation().duration animation.fillMode = .forwards animation.isRemovedOnCompletion = false return animation } private func animationGroup() -> CAAnimationGroup { let pauseDuration = 3.0 // Duration of pause between shimmering let shimmerAnimation = shimmerAnimation() let opacityAnimation = opacityAnimation() let animationGroup = CAAnimationGroup() animationGroup.animations = [shimmerAnimation, opacityAnimation] animationGroup.duration = shimmerAnimation.duration + pauseDuration animationGroup.repeatCount = Float.infinity return animationGroup } override init() { super.init() setupLayer() } override init(layer: Any) { super.init(layer: layer) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func layoutSublayers() { super.layoutSublayers() startAnimation() } private func setupLayer() { let lightColor = UIColor.white.withAlphaComponent(0.7).cgColor let darkColor = UIColor.white.withAlphaComponent(0).cgColor colors = [ darkColor, lightColor, darkColor ] anchorPoint = CGPoint(x: 0, y: 0.5) locations = [0.0, 0.5, 1.0] startPoint = CGPoint(x: 0.0, y: 0.5) endPoint = CGPoint(x: 1.0, y: 0.5) shadowColor = glowColor.cgColor shadowRadius = 5.0 shadowOpacity = 1 shadowOffset = .zero } private func startAnimation() { if animation(forKey: "shimmerEffect") == nil { let animationGroup = animationGroup() add(animationGroup, forKey: "shimmerEffect") } } }


여기서는 CAAnimationGroup 으로 결합된 두 개의 애니메이션(불투명도 및 위치)이 필요합니다. 또한 CAAnimationGroup 사용하면 애니메이션 세션 사이에 일시 중지를 조정할 수 있습니다. 클라이언트도 이 속성을 제어할 수 있기 때문에 이 기능은 매우 유용합니다.


반짝반짝 빛나면서


그림에서 볼 수 있듯이 3D 효과가 더 많이 추가되지만 충분하지는 않습니다. 앞으로 나아가자.

배경 레이어

애니메이션 진행 라인 아래에 있는 정적 배경 레이어로 작업해야 합니다. 기억하시면 내부 그림자와 테두리 그라데이션이라는 두 개의 추가 하위 레이어가 있습니다.


 |--General background [CALayer] |--Inner Shadow [CALayer] |--Border Gradient [CALayer]


이 두 레이어는 몇 년 전 검색어에서 상당히 인기가 있었는데, 아마도 UI 트렌드 때문일 것입니다. 우리만의 버전을 만들어 미래 세대를 위해 이 글에서 영원히 간직해 봅시다 :)

내부 그림자 레이어

이 클래스는 UI 요소에 깊이를 추가하는 시각적으로 매력적인 방법이 될 수 있는 내부 그림자 효과가 있는 레이어를 생성하도록 설계되었습니다.


 final class InnerShadowLayer: CAShapeLayer { override init() { super.init() masksToBounds = true shadowRadius = 3 shadowColor = UIColor.black.cgColor shadowOffset = CGSize(width: 0.0, height: 1.0) shadowOpacity = 0.5 } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func layoutSublayers() { super.layoutSublayers() // Creates a path for the shadow that extends slightly outside the bounds of the layer. let shadowPath = UIBezierPath(roundedRect: bounds.insetBy(dx: -5, dy: -5), cornerRadius: cornerRadius) // Creates a cutout path that is the inverse of the layer's bounds. let cutoutPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).reversing() shadowPath.append(cutoutPath) self.shadowPath = shadowPath.cgPath } }


shadowPath 설정해야 하므로 CAShapeLayer 가 필요합니다. 이를 위해 경계에서 insetBy 사용하여 레이어 경계 밖으로 약간 확장되는 그림자 경로를 만들어야 합니다. 그런 다음 또 다른 UIBezierPath — 레이어 경계의 반대인 컷아웃 경로입니다.


내부 그림자


그림에서는 이미 거의 다 왔습니다. 하지만 우리는 MacOS X Leopard로 돌아갈 마지막 레이어가 필요합니다.

테두리 그라데이션 레이어

레이어 주위에 그라데이션 테두리를 만들도록 설계된 CAGradientLayer 의 하위 클래스입니다. 테두리 효과를 얻기 위해 CAShapeLayer 마스크로 사용합니다. shapeLayer 획 색상(초기에는 검은색)으로 구성되고 채우기 색상은 없으며 BorderGradientLayer 의 마스크로 사용됩니다. 이 설정을 사용하면 shapeLayer 에 스트로크가 있는 위치에만 그라디언트가 표시되어 효과적으로 그라디언트 테두리를 만들 수 있습니다.


 final class BorderGradientLayer: CAGradientLayer { let shapeLayer: CAShapeLayer = { let layer = CAShapeLayer() layer.strokeColor = UIColor.black.cgColor // Temporary color layer.fillColor = nil return layer }() init(borderWidth: CGFloat, colors: [UIColor]) { super.init() self.mask = shapeLayer self.colors = colors.map { $0.cgColor } self.setBorderWidth(borderWidth) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func layoutSublayers() { super.layoutSublayers() shapeLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).cgPath } func setBorderWidth(_ borderWidth: CGFloat) { self.shapeLayer.lineWidth = borderWidth } }


BorderGradientLayer 의 초기화 프로그램은 그라디언트에 대해 borderWidthUIColor 배열을 허용합니다. 그라디언트 색상을 설정하고, shapeLayer 에 테두리 너비를 적용하고, shapeLayer 마스크로 사용하여 그라디언트를 테두리 영역으로 제한합니다.


layoutSublayers 메소드는 레이어의 경계 및 모서리 반경과 일치하도록 shapeLayer 의 경로가 업데이트되어 테두리가 레이어에 올바르게 맞는지 확인합니다. setBorderWidth 메소드를 사용하면 초기화 후 테두리 너비를 동적으로 조정할 수 있습니다.


테두리 있음


훨씬 나아 졌어. 좋아요. 시각적인 작업은 그만하고 논리 작업을 해보자.

모두 함께

이제 이 모든 레이어를 결합하여 생명을 불어넣을 차례입니다.


 private lazy var backgroundLayer = BackgroundLayer( borderWidth: borderWidth, colors: borderColors ) private let glareLayer = ProgressGlareLayer() private let shimmeringLayer = ShimmeringLayer() private let progressMaskLayer = ProgressMaskLayer() private let progressBackgroundLayer = ProgressBackgroundLayer() public init() { super.init(frame: .zero) layer.backgroundColor = UIColor.white.cgColor layer.masksToBounds = true layer.addSublayer(progressMaskLayer) layer.addSublayer(progressBackgroundLayer) layer.insertSublayer(backgroundLayer, at: 0) progressBackgroundLayer.addSublayer(shimmeringLayer) progressBackgroundLayer.addSublayer(glareLayer) progressBackgroundLayer.mask = progressMaskLayer }


애니메이션으로 너비를 변경해야 하는 모든 레이어를 어떻게 처리할지 살펴보겠습니다.


 public func setValueWithAnimation(_ value: Double, duration: TimeInterval = 1.0, animationType: CAMediaTimingFunctionName = .easeInEaseOut, completion: (() -> Void)? = nil) { let newWidth = calculateProgressWidth(value) let animationGroup = DispatchGroup() animationGroup.enter() shimmeringLayer.animateToWidth( newWidth - WIDTH_ANIMATABLE_INSET, duration: duration, animationType: animationType, completion: animationGroup.leave ) animationGroup.enter() progressMaskLayer.animateToWidth( newWidth, duration: duration, animationType: animationType, completion: animationGroup.leave ) animationGroup.enter() glareLayer.animateToWidth( newWidth - WIDTH_ANIMATABLE_INSET, duration: duration, animationType: animationType, completion: animationGroup.leave ) animationGroup.notify(queue: .main) { completion?() } }


먼저 값에서 너비를 준비해야 합니다. 값은 0보다 작고 1보다 커야 합니다. 또한 가능한 테두리 너비도 고려해야 합니다.


 fileprivate func normalizeValue(_ value: Double) -> Double { max(min(value, 1), 0) } private func calculateProgressWidth(_ value: Double) -> CGFloat { let normalizedValue = normalizeValue(value) let width = bounds.width * normalizedValue - borderWidth return width }


올바른 3D 효과를 얻으려면 눈부심 및 반짝이는 레이어에 오른쪽과 왼쪽에 약간의 패딩을 추가해야 합니다.


 newWidth - WIDTH_ANIMATABLE_INSET


마지막으로 이 메서드는 애니메이션 그룹( DispatchGroup )을 사용하여 shimmeringLayer , progressMaskLayerglareLayer 세 레이어의 애니메이션을 동기화합니다. 각 레이어의 너비는 지정된 기간 동안 지정된 애니메이션 곡선을 사용하여 계산된 너비( shimmeringLayerglareLayer 디자인에 맞게 WIDTH_ANIMATABLE_INSET 으로 조정됨)로 애니메이션 처리됩니다.


애니메이션은 DispatchGroup 사용하여 조정되어 모든 애니메이션이 동시에 시작되고 모든 애니메이션이 완료된 후에만 완료 클로저가 호출되도록 합니다. 이 방법은 다양한 장식 레이어에서 부드럽고 동기화된 애니메이션으로 진행률 표시줄의 값을 업데이트하는 시각적으로 매력적인 방법을 제공합니다.

소스 코드

최신 버전은 Swift 패키지 및 Pod로 존재합니다. 따라서 여기 Retro Progress Bar 저장소 에서 가져올 수 있습니다. 하지만 기여는 환영합니다!

용법

RetroProgressBar의 인스턴스를 만듭니다. UIView와 마찬가지로 뷰 계층 구조에 추가할 수 있습니다.


 let progressBar = RetroProgressBar()


사용자 정의:


 progressBar.progressColor = UIColor.systemBlue progressBar.cornerRadius = 5.0 progressBar.borderWidth = 2.0 progressBar.borderColors = [UIColor.white, UIColor.gray]


애니메이션으로 값을 변경하려면:


 progressBar.setValueWithAnimation(0.75, duration: 1.0, animationType: .easeInEaseOut) { print("Animation Completed") }


애니메이션이 없는 경우:


 progressBar.setValue(0.5)

마지막 생각들

이 구식 진행률 표시줄을 재현하는 것은 제가 깊이 그리워하는 미학에 대한 향수를 불러일으키는 일이었습니다. UIKit 및 CALayer 사용하면서 개발자로서 우리 도구의 다양성과 강력함을 상기하게 되었습니다. 이 프로젝트는 단지 기술적인 구현에 관한 것이 아니었습니다. 그것은 사랑받는 디자인 시대로의 여행이었고, 오래된 디자인의 시대를 초월한 매력을 포함하여 우리가 여전히 무엇이든 만들 수 있다는 것을 증명했습니다.


이 경험은 UI 디자인 에서 단순함과 향수가 과거와 현재를 연결하면서 아름답게 공존할 수 있다는 것을 뼈저리게 일깨워주었습니다.


즐거운 코딩하세요!