Туториалы
05 декабря 2022
Туториалы
05 декабря 2022
Создание custom layout в SwiftUI. Кэширование.

В предыдущем посте мы рассказали об основах нового протокола Layout. Сегодня я собираюсь продолжить серию постов, посвященной созданию многократно используемых custom layouts, рассказав про кэширование информации layout и настройку производительности.

SwiftUI вызывает функции вашего custom layout множество раз на протяжении всего жизненного цикла для определения различных вариантов размеров в процессе компоновки. Он кэширует несколько объектов автоматически, но вы также можете реализовать свой собственный вариант кэширования, если вам нужно улучшить производительность layout.

Протокол Layout имеет связанный тип Cache, который по умолчанию имеет значение Void. Однако вы можете определить любой тип, который вам нужен, и реализовать собственное поведение кэширования. Самый простой способ — определить вложенный тип с именем Cache внутри вашего пользовательского типа макета.

struct FlowLayout: Layout {
    struct Cache {
        var sizes: [CGSize] = []
    }
}

 

Протокол Layout содержит функцию makeCache, которую мы можем реализовать для предоставления экземпляра кэша и выполнения некоторых начальных вычислений для хранения при изменении layout.

struct FlowLayout: Layout {
    struct Cache {
        var sizes: [CGSize] = []
    }
    
    func makeCache(subviews: Subviews) -> Cache {
        let sizes = subviews.map { $0.sizeThatFits(.unspecified) }
        return Cache(sizes: sizes)
    }
}

 

Протокол Layout также содержит функцию updateCache, которую мы можем использовать для обновления нашего кэша. Фреймворк SwiftUI вызывает эту функцию, как только изменяются дочерние элементы макета. Вы можете игнорировать функцию updateCache. В этом случае SwiftUI вызывает функцию makeCache и создает кэш с нуля.

struct FlowLayout: Layout {
    struct Cache {
        var sizes: [CGSize] = []
    }
    
    func makeCache(subviews: Subviews) -> Cache {
        let sizes = subviews.map { $0.sizeThatFits(.unspecified) }
        return Cache(sizes: sizes)
    }
    
    func updateCache(_ cache: inout Cache, subviews: Subviews) {
        cache.sizes = subviews.map { $0.sizeThatFits(.unspecified) }
    }
}

 

Как вы можете видеть в приведенном выше примере, функция updateCache предоставляет экземпляр кэша в качестве входного параметра, что позволяет нам изменять его во время вызова.

Имейте в виду, что обе функции makeCache и updateCache предоставляют вам только экземпляр типа Subviews - прокси в списке дочерних элементов. Он не предоставляет вам предлагаемый размер и границы прямоугольника, что означает, что мы не можем вычислить точное положение представлений.
Вместо этого протокол Layout позволяет нам изменять наш кэш в функциях sizeThatFits и placeSubviews, где у нас хранится предлагаемый размер.

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 position = CGPoint(
                x: lineX + cache.sizes[index].width / 2,
                y: lineY + cache.sizes[index].height / 2
            )
            
            // you can populate cache
            // with additional information here
            
            lineHeight = max(lineHeight, cache.sizes[index].height)
            lineX += cache.sizes[index].width
            
            subviews[index].place(
                at: position,
                anchor: .center,
                proposal: ProposedViewSize(cache.sizes[index])
            )
        }
    }
}

 

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

Протокол Layout предоставляет нам все необходимые API для создания эффективных пользовательских layout. Мы продолжим изучать огромный набор API-интерфейсов, которые SwiftUI дает нам для создания многоразовых и гибких layout. 

 

Оригинал статьи


Оцените статью
0
0
0
0
0

Чтобы добавить комментарий, авторизуйтесь
Войти
Безумова Виола
Пишет и переводит статьи для SwiftBook