paint-brush
Kullanıcı Arayüzü Gerilemesi: CALayers'ı kullanarak iOS için Retro İlerleme Çubuğu oluşturmaile@maxkalik
684 okumalar
684 okumalar

Kullanıcı Arayüzü Gerilemesi: CALayers'ı kullanarak iOS için Retro İlerleme Çubuğu oluşturma

ile Max Kalik22m2023/12/17
Read on Terminal Reader

Çok uzun; Okumak

Bu kılavuz, nostaljik bir dokunuş için CALayers'ı kullanarak iOS'ta retro tarzı bir ilerleme çubuğunun oluşturulmasını araştırıyor. Her katmanın tasarlanmasından animasyonların uygulanmasına kadar adım adım süreci kapsıyor ve eski tarz estetiği modern iOS geliştirmeyle birleştirmeye yönelik içgörüler sunuyor. Geliştiriciler ve UI tasarımcıları için ideal olan makale, benzersiz UI bileşenleri için hem teknik derinlik hem de yaratıcı ilham sağlar. Kaynak Kodu: https://github.com/maxkalik/RetroProgressBar
featured image - Kullanıcı Arayüzü Gerilemesi: CALayers'ı kullanarak iOS için Retro İlerleme Çubuğu oluşturma
Max Kalik HackerNoon profile picture
0-item


UI bileşenleri oluşturmayı seviyorum. Bir yapıyı yaptıktan sonra bunları görmek, kullanmak bana bir nevi estetik haz veriyor. Bu aynı zamanda genel olarak MacOS/iOS ekosistemine olan sevgimle de birleşiyor. Son zamanlarda UIKit kullanarak özel bir ilerleme çubuğu oluşturmakla görevlendirildim. Bu çok da önemli değil ama muhtemelen iOS geliştiricilerinin %99'u kariyerlerinin bir noktasında sıfırdan bir tane oluşturmuştur. Benim durumumda, 0'dan 1'e kadar bir değer aralığını temel alan animasyonlara sahip birkaç CALayer oluşturmaktan biraz daha fazlasını yapmak istedim. İlerleme çubuğunun gerekli olduğu birçok senaryoya uygun olacak bir çözüm hedefliyordum. .


Kendimi Apple dünyasındaki kullanıcı arayüzü bileşenlerinin şöyle göründüğü günlere atmak istiyorum:


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


Kullanıcı arayüzü tasarımının evrimini düşünürsek, 2014 yılında iOS 7'nin piyasaya sürülmesiyle Apple dünyasındaki kullanıcı arayüzü öğelerinin düz bir tasarımı benimsemesinden bu yana neredeyse on yıl geçti. Öyleyse, Mac OS ve iOS'un önceki sürümleri için nostaljinin tadını çıkaralım ve yeni tasarımlar oluşturalım. birlikte güzel bir ilerleme çubuğu.


UIKit kullanmaktan hoşlanıyorsanız ancak CALayer'ları nasıl yöneteceğinizden hala emin değilseniz, bu makale sizin için yararlı olacaktır. Burada tipik düzen, animasyon ve çizim zorluklarını ele alacağız ve yol boyunca size yardımcı olacak bilgiler ve çözümler sunacağız.

UIView'dan CALayers API'sine derinlemesine.

İlerleme çubuğu basit görünebilir, ancak ayrıntılara derinlemesine inersek, bunun çok sayıda alt katman gerektirdiğini anlayacaksınız.


Retro İlerleme Çubuğu


Burada CALayers ayrıntılı olarak ele almayacağım. CALayers ne olduğunu ve nasıl çalıştıklarını kısaca anlattığım bu makaleye göz atabilirsiniz: https://hackernoon.com/rolling-numbers-animation-using-only-calayers .


Öyleyse onu küçük katmanlara ayıralım:

  1. Genel arka plan katmanı
  2. Maske katmanı (Animasyona tabi tutulabilir kısım)
  3. İlerleme arka planı katmanı
  4. Parlama Katmanı
  5. Pırıltılı Katman


Arka plan katmanının iki ek alt katmanı vardır:

  1. İç Gölge Katmanı
  2. Kenarlık Degrade Katmanı


Düzgün bir şekilde yapılandıralım:


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


Kısaca şöyle çalışmalıdır: İlerleme Çubuğu 0,0 ile 1,0 arasında bir değere tepki vermelidir. Değer değiştiğinde katmanın genişliğini bu değere göre ayarlamamız gerekir:


 bounds.width * value


Bu basit hesaplamayı kullanarak CATransaction kullanarak genişliği değiştirebiliriz. Ancak bunu yapmadan önce değer değiştiğinde hangi katmanların değişmesi gerektiğini anlamamız gerekiyor. Yapıyı yeniden gözden geçirelim ve değer değişikliklerine yanıt vermesi gereken katmanları belirleyelim.


 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]


Görünüşe göre, değere dayalı olarak animasyonlu genişliğe ihtiyaç duyan katmanların benimseyebileceği bir protokol geliştirmemiz gerekiyor.

ProgressAnimable protokolü

Protokolde yalnızca iki yönteme ihtiyacımız var: katmanın genişliğini animasyonla ve animasyon olmadan değiştirin.


 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)?) }


Uygulama:


 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() } }


Her iki yöntem de CATransaction kullanır. İlk yöntem tuhaf görünebilir çünkü animasyon olmadan yalnızca bir değeri değiştirmemiz gerekiyor. Peki CATransaction neden gereklidir?


Temel bir CALayer oluşturmayı ve herhangi bir animasyon olmadan genişliğini veya yüksekliğini değiştirmeyi deneyin. Bir yapıyı çalıştırdığınızda CALayer bir animasyonla değiştiğini fark edeceksiniz. Bu nasıl mümkün olabilir? Çekirdek Animasyonda, katman özelliklerinde (sınır, konum, opaklık vb. gibi) yapılan değişiklikler genellikle o özellikle ilişkili varsayılan eyleme göre canlandırılır. Bu varsayılan davranışın amacı, her özellik değişikliği için açık animasyonlara ihtiyaç duymadan yumuşak görsel geçişler sağlamaktır.


Bu, CALayer eylemleri açıkça devre dışı bırakmamız gerektiği anlamına gelir. CATransaction.setDisableActions(true) ayarını yaparak, katman genişliğinin herhangi bir ara animasyonlu geçiş olmadan anında yeni değere güncellenmesini sağlarsınız.


İkinci yöntem, ProgressAnimatable protokolüne uygun herhangi bir katmanın genişliğini canlandırmak için esnek ve yeniden kullanılabilir bir yol sunar. Animasyonun süresi ve ilerleme hızı üzerinde kontrol sağlar ve tamamlama eylemi için bir seçenek içerir. Bu, onu UIKit çerçevesi içindeki çeşitli animasyon ihtiyaçları için çok uygun hale getirir.


Bu protokole uyacak adaylarımız üç katman olacaktır: Animasyonlu Maske Katmanı, Parlama Katmanı ve Parıltılı Katman. Devam edelim ve uygulayalım.

İlerleme Maskesi Katmanı

Artık protokolümüze uyma zamanı geldi ve bu kurulumdaki en önemli katman genel CAShapeLayer olacak. Bu maskeye neden ihtiyacımız var? Neden sadece animasyonlu genişliğe sahip bir CALayer olamıyor? Bir iOS Mühendisi olarak, özellikle de ön uç geliştirmeye odaklanan biri olarak, genellikle bileşenleriniz için potansiyel kullanım durumlarını tahmin etmeniz gerekir. Benim örneğimde bir İlerleme Arka Plan Katmanı var. Bu animasyonlu bir CALayer değil ama ya CAReplicatorLayer gibi bir şeyle animasyonlu olsaydı? Performans konusunda endişeleriniz varsa, muhtemelen böyle bir katmanın bir kez oluşturulması ve ardından animasyonunun gerçekleştirilmesi gerektiğini düşünürsünüz. Bu yüzden bu maske son derece faydalı olabilir. Çeşitli animasyon senaryoları için gereken esnekliği sunarken aynı zamanda verimli görüntü oluşturmaya olanak tanır.


 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") } }


Bu satıra bir göz atalım:


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


Bu çizgi İlerleme Çubuğumuz için çok önemlidir. Varsayılan olarak, CALayers ortalanmıştır ve anchorPoint genel olarak varsayıldığı gibi sol kenarın ortasında değil, katmanın merkezinde bulunur. anchorPoint , katmandaki tüm dönüşümlerin gerçekleştiği noktadır. İlerleme çubuğu olarak kullanılan bir katman için bu ayar, genellikle katmanın genişliği değiştiğinde, anchorPoint itibaren her iki yönde de eşit şekilde genişleyeceği anlamına gelir. Ancak, anchorPoint sol kenarın ortasına ayarlarsak, katman yalnızca sağa doğru genişlerken sol kenar yerinde sabit kalır. Bu davranış, ilerleme çubuğu için gereklidir ve çubuğun büyümesinin her iki taraftan ziyade bir taraftan görünmesini sağlar.


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


Yukarıdaki kod parçasında, İlerleme Çubuğunun iki ana bölümünü başlatıyoruz: ProgressMaskLayer ve ProgressBackgroundLayer . Şimdi ikincisine bir göz atalım.


 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 }) } }


Gördüğünüz gibi üç renkten oluşan bir CAGradientLayer . Bu uygulama, enjekte edilen rengi dikey bir degradeye dönüştürerek ilerleme çubuğunun görünürlüğünü artırır ve ona daha hacimli bir görünüm kazandırır.


Gradyan

Bunu mümkün kılmak için CGColor eklentisinde iki yöntem hazırladım. Eğlenmek için CGColor tekrar UIColor dönüştürmeyi değil, yalnızca CGColor alanında oynamayı seçtim:


 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 } }


Gördüğünüz gibi çok karmaşık değil. Sadece colorSpace , components ( red , green , blue ve alpha ) ve model almamız gerekiyor. Bu, değere bağlı olarak yeni bir renk hesaplayabileceğimiz anlamına gelir: daha koyu veya daha açık.


Parlama Katmanı

İlerleme Çubuğumuzu daha 3 boyutlu hale getirmeye doğru ilerleyelim. Şimdi Glare adını vereceğimiz başka bir katman eklememiz gerekiyor.


Bu basit bir uygulamadır ancak ProgressAnimatable protokolüne uygun olmalıdır.


 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") } } 


parlama ile


İşleri basitleştirmek için burada arka plan renginin yalnızca 0,3 alfa ile beyaz olmasına ihtiyacımız var.


Küçük Güzel Parıltılı (Parıldayan Katman)

Bazılarının, sürüm 6'dan önceki MacOS X veya iOS'un bunun gibi parıldayan efektlere sahip kullanıcı arayüzü bileşenlerine sahip olmadığını söyleyebileceğini biliyorum. Ama dediğim gibi gerçek bir hikayeden esinlenilmiş bir film gibi bu. Bu parıltı, 3D efektini vurgular ve onu daha görünür hale getirir.


 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") } } }


Burada, CAAnimationGroup birleştirilmiş iki animasyona (Opaklık ve Konum) ihtiyacımız var. Ayrıca CAAnimationGroup kullanmak, animasyon oturumları arasında bir duraklama ayarlamamıza olanak tanır. Bu özellik oldukça kullanışlıdır çünkü istemciler de bu özelliği kontrol etmek isteyebilir.


parıldayan


Resimde görebileceğiniz gibi bu daha fazla 3D efekt ekliyor ancak bu yeterli değil. İlerleyelim.

Arka Plan Katmanı

Animasyonlu İlerleme Çizgisinin altında yer alan statik arka plan katmanımızla çalışmamız gerekiyor. Hatırlarsanız orada iki alt katmanımız daha var: İç Gölge ve Kenar Gradyanı.


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


Bu iki katmanın, muhtemelen kullanıcı arayüzü eğilimleri nedeniyle, birkaç yıl önce arama sorgularında oldukça popüler olduğunu söyleyebilirim. Gelin kendi versiyonlarımızı oluşturalım ve gelecek nesiller için bu yazımızda ölümsüzleştirelim :)

İç Gölge Katmanı

Sınıf, kullanıcı arayüzü öğelerine derinlik katmanın görsel olarak çekici bir yolu olabilecek iç gölge efektine sahip bir katman oluşturmak üzere tasarlanmıştır.


 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 } }


Bir CAShapeLayer ihtiyacımız var çünkü shadowPath ayarlamamız gerekiyor. Bunun için insetBy frombounds kullanarak katman sınırlarının biraz dışına uzanan gölge için bir yol oluşturmamız gerekiyor. Ve sonra başka bir UIBezierPath - katmanın sınırlarının tersi olan bir kesme yolu.


İç gölge


Resimde zaten neredeyse orada. Ancak bizi MacOS X Leopard'a geri getirecek son katmana ihtiyacımız var.

Kenarlık Degrade Katmanı

Bir katmanın etrafında degrade kenarlık oluşturmak için tasarlanmış CAGradientLayer bir alt sınıfıdır. Kenarlık efektini elde etmek için CAShapeLayer maske olarak kullanır. shapeLayer kontur rengiyle (başlangıçta siyah) ve dolgu rengi olmadan yapılandırılmıştır ve BorderGradientLayer için maske olarak kullanılır. Bu kurulum, degradenin yalnızca shapeLayer konturunun olduğu yerde görünmesini sağlayarak etkili bir degrade kenarlığı oluşturur.


 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 başlatıcısı, degrade için bir borderWidth ve bir UIColor dizisini kabul eder. Degrade renklerini ayarlar, kenarlık genişliğini shapeLayer uygular ve degradeyi kenarlık alanıyla sınırlamak için shapeLayer maske olarak kullanır.


layoutSublayers yöntemi, shapeLayer yolunun katmanın sınırları ve köşe yarıçapıyla eşleşecek şekilde güncellenmesini sağlayarak kenarlığın katmana doğru şekilde uymasını sağlar. setBorderWidth yöntemi, başlatma sonrasında kenarlığın genişliğinin dinamik olarak ayarlanmasına olanak tanır.


Sınır ile


Çok daha iyi. Tamam. Artık görsellik yapmayı bırakalım ve mantık üzerinde çalışalım.

Hep birlikte

Artık tüm bu katmanları birleştirip hayata geçirmenin zamanı geldi.


 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 }


Genişliği değişmesi gereken tüm katmanları animasyonla nasıl ele alacağımıza bir göz atalım:


 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?() } }


Öncelikle bir değerden genişlik hazırlamamız gerekiyor. Değerin 0'dan küçük ve 1'den büyük olması gerekiyor. Ayrıca olası kenar genişliğini de hesaba katmamız gerekiyor.


 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 }


Doğru 3D efekti için parlama ve parıltılı katmanlar sağ ve sol taraftan biraz dolgulu olmalıdır:


 newWidth - WIDTH_ANIMATABLE_INSET


Son olarak yöntem, üç katmanın animasyonlarını senkronize etmek için bir animasyon grubu ( DispatchGroup ) kullanır: shimmeringLayer , progressMaskLayer ve glareLayer . Her katmanın genişliği, belirtilen süre boyunca ve belirtilen animasyon eğrisiyle hesaplanan genişliğe göre canlandırılır ( shimmeringLayer ve glareLayer için WIDTH_ANIMATABLE_INSET ile tasarıma uyacak şekilde ayarlanır).


Animasyonlar DispatchGroup kullanılarak koordine edilir, böylece tüm animasyonların aynı anda başlaması ve tamamlama kapanışının yalnızca tüm animasyonlar bittikten sonra çağrılması sağlanır. Bu yöntem, ilerleme çubuğunun değerini, çeşitli dekoratif katmanları boyunca düzgün, senkronize bir animasyonla güncellemek için görsel olarak çekici bir yol sağlar.

Kaynak kodu

En son sürüm Swift Paketi ve Pod olarak mevcuttur. Yani buradan alabilirsiniz Retro Progress Bar deposu . Bu arada, katkılarınızı bekliyoruz!

Kullanım

RetroProgressBar'ın bir örneğini oluşturun. Herhangi bir UIView'da yaptığınız gibi bunu görünüm hiyerarşinize ekleyebilirsiniz.


 let progressBar = RetroProgressBar()


Özelleştirme:


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


Değeri animasyonla değiştirmek için:


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


Animasyonsuz:


 progressBar.setValue(0.5)

Son düşünceler

Bu eski tarz ilerleme çubuğunu yeniden yaratmak, çok özlediğim estetiğe nostaljik bir dalış oldu. UIKit ve CALayer ile çalışmak bana geliştiriciler olarak araçlarımızın çok yönlülüğünü ve gücünü hatırlattı. Bu proje sadece teknik uygulamayla ilgili değildi; eski tasarımların eskimeyen cazibesi de dahil olmak üzere hâlâ her şeyi üretebildiğimizi kanıtlayan, sevilen bir tasarım çağına geri dönüş yolculuğuydu.


Bu deneyim, kullanıcı arayüzü tasarımında sadeliğin ve nostaljinin güzel bir şekilde bir arada var olabileceğini, geçmiş ile günümüz arasında köprü kurabileceğini hatırlattı.


Mutlu kodlama!