Пользовательская анимация загрузки для ваших приложений

Обзор

В этом руководстве мы будем создавать мерцающую анимацию, которую можно использовать в качестве представления-заполнителя на время, когда ваше приложение загружает данные через API или какой-либо фоновый процесс.

Этот тип загрузки был введен Facebook и теперь часто встречается в таких приложениях, как Youtube и Instagram.

Если вы хотите, чтобы ваше приложение выделялось, это новая норма.

Что мы строим?

Пользовательское представление анимации, которое может действовать как заполнитель до тех пор, пока API не вернет ответ.

Наш конечный продукт будет выглядеть примерно так:

Начиная

Загрузите финальный проект отсюда.

Давайте посмотрим на проект. Для создания эффекта мерцания будем работать с GradientLayer и CABasicAnimation.

Расширьте UIView и добавьте следующий код:

extension UIView {
func applyShimmerEffect() {
//1
let light = backgroundColor?.withAlphaComponent(0.5).cgColor ?? UIColor(red: 0, green: 0, blue: 0, alpha: 0.1).cgColor
let dark = UIColor.clear.cgColor
let shimmerGradientLayer = CAGradientLayer()
shimmerGradientLayer.colors = [light, dark]
shimmerGradientLayer.frame = CGRect(
x: -bounds.width,
y: 0,
width: 50 * bounds.size.width,
height: bounds.size.height
)
shimmerGradientLayer.startPoint = CGPoint(x: 0.0, y: 0)
shimmerGradientLayer.endPoint = CGPoint(x: 1.0, y: 0)
shimmerGradientLayer.locations = [0.0, 0.1]
layer.mask = shimmerGradientLayer
//2
let animation = CABasicAnimation(keyPath: "locations")
animation.fromValue = [0.0, 0.1]
animation.toValue = [0.1, 0.2]
animation.duration = 0.7
animation.repeatCount = Float.infinity
animation.autoreverses = true
shimmerGradientLayer.add(animation, forKey: "shimmer")
}
}

//1 Мы начнем с создания двух цветов для нашего градиентного слоя. Первый - это Light, который имеет цвет обзора с альфа-каналом 0.5, второй - это цвет clear.

Создайте слой с градиентом, добавьте к нему указанные выше цвета. Установите рамку градиента на что-то большее и большей ширины и поместите в позицию -bounds.width. Это сделано для создания эффекта импульса при анимации слоя.

Наш градиент должен анимироваться вдоль xaxis, поэтому точки start и end даны соответственно.

location свойство градиента можно анимировать. Мы изменим свойство location для создания анимации.

//2 Создайте CABasicAnimation экземпляр. Мы предоставим экземпляру toValue и fromValue, toValue должно быть свойством местоположения, установленным нашим слоем градиента, а fromValue может соответствовать нашей анимации.

Мы устанавливаем счетчик повторов на infinity, поскольку наш мотив - показывать анимацию до тех пор, пока API не вернет ответ. Также, чтобы сделать анимацию плавной и иметь импульсный эффект, мы установим autoreverses = true.

Это все, что нам нужно для создания эффекта мерцания. Следующий шаг. Создание s карточек химмера.

Просмотр карты:

Нам понадобится протокол Shimmerable для создания мерцающих карт, добавьте следующий код -

protocol Shimmerable: UIView {
var shimmerViews: [UIView] { get }
}
extension Shimmerable {
func loadShimmer() {
shimmerViews.forEach { $0.applyShimmerEffect() }
}

Наше представление карты будет соответствовать этому протоколу и предоставит ему представления, к которым нужно применить мерцание.

Примечание. Наша мерцающая карточка - это то, что будет отображаться во время вызова API. Это заполнитель, который будет отображаться до тех пор, пока не будут получены данные.

Давайте создадим карту мерцающего вида, добавим следующий класс, и мы шаг за шагом рассмотрим его.

final class ShimmerView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
backgroundColor = .clear
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//1
private let titleView: UIView = {
let view = UIView()
view.layer.cornerRadius = 5
view.width(160)
view.height(25)
view.backgroundColor = .lightGray
return view
}()
private let descriptionView: UIView = {
let view = UIView()
view.layer.cornerRadius = 5
view.width(UIScreen.main.bounds.width * 0.80)
view.height(150)
view.backgroundColor = UIColor.red.withAlphaComponent(0.5)
return view
}()
}
//2
extension ShimmerView {
func commonInit() {
setupUI()
setConstraints()
}
func setupUI() {
addSubview(titleView)
addSubview(descriptionView)
}
func setConstraints() {
titleView.leadingToSuperview(offset: 15)
titleView.topToSuperview(offset: 15)
descriptionView.leadingToSuperview(offset: 15)
titleView.bottomToTop(of: descriptionView, offset: -12)
}
}
//3
extension ShimmerView: Shimmerable {
  public var shimmerViews: [UIView] {
    return [titleView, descriptionView]
   }
}

//1 Мы создали класс ShimmerView, который является подклассом UIView. Мы добавили два вида, title и description, к которым мы будем применять эффект мерцания.

//2 это расширение просто добавляет представление к superView и выравнивает их одно под другим.

//3 Теперь мы будем соответствовать протоколу Shimmerable и предоставим представления, в которые мы будем добавлять эффект мерцания, в нашем случае title и description.

Давайте добавим это представление и свяжем его с нашим ViewController. Откройте ViewController.swift и добавьте следующий код в viewDidLoad метод—

override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(shimmerView)
shimmerView.topToSuperview(offset: 20, usingSafeArea: true)
shimmerView.width(UIScreen.main.bounds.width)
shimmerView.centerXToSuperview()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.shimmerView.loadShimmer()
   }
}

Строй и беги! Также давайте посмотрим, как это выглядит в темном режиме.

Потрясающие !! Теперь у нас есть заполнитель для нашего контента, и мы избавились от нашего традиционного стиля загрузки. Знаете ли вы, что SwiftUI имеет заполнитель in-build для своих свойств? Что ж, это хорошая тема для другого урока по анимации.