Перед тем, как начать, рассмотрите возможность подписки по этой ссылке, и если вы не читаете ее на TrailingClosure.com, приходите к нам как-нибудь!

Начиная

В приведенном выше примере я показал, как это выглядит, когда Rectangle используется в качестве маски для эффекта растворения. Однако в этом уроке мы собираемся создать ViewModifier, который принимает любую View в качестве формы, которую он использует для создания эффекта растворения. Следите за обновлениями, чтобы увидеть, насколько силен этот эффект на самом деле!

  1. Создайте новый ViewModifier с именем ShapeDissolveModifier. Ниже приведен шаблон для нашей структуры. Определены две переменные. Первый - это наш mask для эффекта растворения. Наш тип Mask определен выше как общий на ShapeDissolveModifier. Второй - это progress анимации растворения. По мере увеличения с 0 до 1 вид будет растворяться.
struct ShapeDissolveModifier<Mask: View>: ViewModifier {
    
    let mask: Mask
    var progress: Double
    
    func body(content: Content) -> some View {
        content
    }
}
  1. Создайте новую функцию buildMask(GeometryProxy, Double) -> some View. Задача этой функции - взять шаблон маски, который мы определили ранее, mask, и создать 100 копий различных opacity. Эти копии объединяются, образуя полную маску, которая со временем растворяется.
func buildMask(geometry: GeometryProxy, progress: Double) -> some View {
    // Create Dissolve Mask here...
}
  1. Внутри функции мы используем GeometryProxy для вычисления размера копий части маски.
func buildMask(geometry: GeometryProxy, progress: Double) -> some View {
    
    let width = geometry.size.width
    let height = geometry.size.height

    let wUnit = width/10.0
    let hUnit = height/10.0

    // resize the mask to 1/10th of the parent view.
    let maskPiece = mask
        .frame(width: wUnit, height: hUnit, alignment: .center)
}
  1. Затем нам нужно создать три закрытия, чтобы мы могли сгенерировать значения x, y и opacity для копий маски.
func buildMask(geometry: GeometryProxy, progress: Double) -> some View {
    
    let width = geometry.size.width
    let height = geometry.size.height

    let wUnit = width/10.0
    let hUnit = height/10.0

    // resize the mask to 1/10th of the parent view.
    let maskPiece = mask
        .frame(width: wUnit, height: hUnit, alignment: .center)
    
    // Calculate X coordinate for a mask copy
    let xCoord = { (x:Int) -> CGFloat in
        wUnit * CGFloat(x)
    }
    
    // Calculate Y coordinate for a mask copy
    let yCoord = { (y:Int) -> CGFloat in
        hUnit * CGFloat(y)
    }
    
    // Calculate a random opacity for a mask copy
    let opacity = { () -> Double in
        return Double.random(in: 0...3) * progress + progress
    }
    
}
  1. Теперь соберем их все вместе. Мы будем использовать ForEach для создания 100 mask частей с соответствующими значениями, используя только что сделанные замыкания.
func buildMask(geometry: GeometryProxy, progress: Double) -> some View {
    
    let width = geometry.size.width
    let height = geometry.size.height

    let wUnit = width/10.0
    let hUnit = height/10.0

    // resize the mask to 1/10th of the parent view.
    let maskPiece = mask
        .frame(width: wUnit, height: hUnit, alignment: .center)
    
    // Calculate X Coordinate for a mask copy
    let xCoord = { (x:Int) -> CGFloat in
        wUnit * CGFloat(x)
    }
    
    // Calculate Y Coordinate for a mask copy
    let yCoord = { (y:Int) -> CGFloat in
        hUnit * CGFloat(y)
    }
    
    // Calculate Random Opacity for a mask copy
    let opacity = { () -> Double in
        return Double.random(in: 0...3) * progress + progress
    }
    
    // Combine all of the mask pieces together
    let fullMask = Group {
        ForEach(0..<100) { x in
            maskPiece
                .offset(x: xCoord(x%10), y: yCoord(x/10))
                .opacity(opacity())
        }
    }

    return fullMask
}

Примеры использования!

А теперь самое интересное. Я собрал несколько разных примеров. В основном я просто играю с ним, но вы сможете увидеть некоторые изящные способы использования.

Это разные эффекты, основанные на простом изменении mask, передаваемого в ShapeDissolveModifier.

Rectangle()
    .foregroundColor(.blue)
    .frame(width: 300, height: 300, alignment: .center)
    .cornerRadius(10)
    .modifier(ShapeDissolveModifier(mask:
        Rectangle()
    ,progress: progress))
    .onAppear {
        withAnimation(Animation.easeInOut(duration: 3.0)) {
            self.progress = 1.0
        }
    }

Rectangle()
    .scaleEffect(0.9)

Используйте это на изображениях

Image("mountains")
    .resizable()
    .scaledToFill()
    .foregroundColor(.blue)
    .frame(width: 300, height: 300, alignment: .center)
    .cornerRadius(10)
    .modifier(ShapeDissolveModifier(mask:
        Rectangle()
    ,progress: progress))
    .onAppear {
        withAnimation(Animation.easeInOut(duration: 3.0)) {
            self.progress = 1.0
        }
    }

Triangle()
    .rotation(Angle(degrees: 90))
    .scaleEffect(3)

Circle()
    .scaleEffect(1.5)

Поддержите будущие учебники, подобные этому!

Пожалуйста, рассмотрите возможность подписки по этой ссылке. Если вы не читаете это на TrailingClosure.com, приходите к нам как-нибудь!

Мы хотим увидеть твои работы! Если вы что-то построили с помощью этого урока, присылайте нам фотографии! Найдите нас в Twitter @TrailingClosure или напишите нам по адресу [email protected]