В последние несколько недель мы рассмотрели многие аспекты создания пользовательских слоев с использованием нового Layout протокола в Swift UI, однако нам еще многое предстоит обсудить. На этой неделе мы узнаем, как использовать протокол Layout Value Key для передачи параметров пользовательских слоев при создании представлений в пользовательском слое.

В предыдущих постах мы создали FlowLayout слой, используя новый Layout протокол в SwiftUI. Давайте продолжим наше работу над FlowLayout, добавив в него еще одну функцию. Допустим, мы хотим настроить точку привязки при размещении представлений в слое. В первом представлении может использоваться верхняя точка, а во втором — нижняя.

Если вы хотите узнать больше об основах протокола Layout, пожалуйста ознакомьтесь с отдельным постом на эту тему «Building custom layout in SwiftUI. Basics».

Swift UI предоставляет нам протокол LayoutValueKey, позволяющий нам заводить кастомные параметры для слоя. Мы можем использовать этот тип, чтобы закрепить любое нужное нам значение для вида внутри слоя, а также иметь возможность извлекать это значение в цикле слоя позже при необходимости.

Для начала, мы должны определить тип, соответствующий протоколу LayoutValueKey.

struct UnitPointKey: LayoutValueKey {
    static var defaultValue: UnitPoint = .center
}

Создать пользовательский параметр слоя довольно просто. Единственное, что нам нужно сделать, это указать значение по умолчанию для параметра. Во‑вторых, мы должны создать расширение для View, чтобы упростить передачу пользовательских параметров слоя.

extension View {
    func anchor(_ anchor: UnitPoint) -> some View {
        layoutValue(key: UnitPointKey.self, value: anchor)
    }
}

Как вы можете видеть в примере выше, мы используем модификатор представления layoutValue, чтобы закрепить конкретное значение к определенному типу, соответствующему протоколу LayoutValueKey. Мы можем использовать модификатор представления layoutValue без создания расширения для типа представления, но расширение предоставляет гораздо более приятный и чистый API.

Text("!!!")
    .font(.title3)
    .layoutValue(key: UnitPointKey.self, value: .top)
    
Text("!!!")
    .font(.title3)
    .anchor(.top)

Теперь мы можем определить FlowLayout с набором представлений и передать пользовательскую точку привязки для каждого представления внутри слоя. Если мы не определим ни какого значения в параметре пользовательского слоя, SwiftUI использует значение по умолчанию, которое мы предоставим.

struct ContentView: View {
    var body: some View {
        FlowLayout {
            Text("Hello")
                .font(.largeTitle)
                .anchor(.bottom)
            Text("World")
                .font(.title)
                .anchor(.top)
            Text("!!!")
                .font(.title3)
        }
    }
}

Последний шаг — использовать параметр пользовательского слоя при размещении или изменении размера слоя. Мы можем получить доступ к пользовательским параметрам слоя, используя subscript в прокси‑типе Subview.

struct FlowLayout: Layout {
//  ....
    func placeSubviews(
        in bounds: CGRect,
        proposal: ProposedViewSize,
        subviews: Subviews,
        cache: inout Cache
    ) {
        var lineX = bounds.minX
        var lineY = bounds.minY
        var lineHeight: CGFloat = 0
        
        for index in subviews.indices {
            if lineX + cache.sizes[index].width > (proposal.width ?? 0) {
                lineY += lineHeight
                lineHeight = 0
                lineX = bounds.minX
            }
            
            let anchor = subviews[index][UnitPointKey.self]
            let position = CGPoint(
                x: lineX + cache.sizes[index].width / 2,
                y: lineY + cache.sizes[index].height / 2
            )
            
            lineHeight = max(lineHeight, cache.sizes[index].height)
            lineX += cache.sizes[index].width
            
            subviews[index].place(
                at: position,
                anchor: anchor,
                proposal: ProposedViewSize(cache.sizes[index])
            )
        }
    }
}

Как вы можете видеть в примере выше, мы используем subscript в прокси‑типе Subview для извлечения значения типа UnitPointKey. Наконец, мы используем это значение для обеспечения точки привязки при размещении представлений в слое.

Пользовательские параметры слоя позволяют нам очень легко создавать супернастраиваемые и повторно используемые слои в SwiftUI. Я надеюсь, вам понравился этот пост. Не стесняйтесь подписываться на меня в Twitter и задавать вопросы. Спасибо за чтение!

Комментарии (0)