feature/stack_appearance_layout #9

Merged
ivan.smolin merged 3 commits from feature/stack_appearance_layout into master 2023-06-26 19:51:16 +03:00
Member

Более полная поддержка коллекций и stack'ов для быстрой вёрстки вместе с поддержкой WrapperViewLayout во всех контейнерах.

Пример экрана на ~150 строк кода

@MainActor
struct StationInfoTableBuilder {
    typealias LabelRow = DefaultConfigurableLabel.InTableRow
    typealias TraitsRow = TraitsCollectionView.InCollectionTableCell.TableRow
    typealias AddressRow = DefaultConfigurableLabel.InSeparatableRow
    typealias RefuelRow = DefaultConfigurableStatefulButton.InTableRow
    typealias ScheduleRow = BaseStackView<DefaultConfigurableLabel.InContainerView>.InTableRow

    static func buildMainSection(dataSource: FuelingStationInfoViewPresenter) -> TableSection {
        TableSection(onlyRows: [
            LabelRow(item: dataSource.station.name)
                .with(appearance: Self.titleAppearance),
            TraitsRow(item: dataSource.traits)
                .with(appearance: Self.traitsAppearance),
            AddressRow(item: dataSource.station.address)
                .with(appearance: Self.addressAppearance)
                .with(separators: .top(Self.separatorAppearance)),
            buildRefuelRow()
        ])
    }

    static func buildWorkHoursSection(dataSource: FuelingStationInfoViewPresenter) -> TableSection {
        if dataSource.schedule.isEmpty {
            return TableSection.emptySection()
        } else {
            return TableSection(onlyRows: [
                LabelRow(item: "Режим работы")
                    .with(appearance: Self.workingHoursAppearance),
                ScheduleRow(item: dataSource.schedule)
                    .with(appearance: Self.scheduleAppearance)
            ])
        }
    }

    static func buildRefuelRow() -> RefuelRow {
        let refuelImage = Asset.Station.fuelingStationRefuelDefault.image

        let renderer = UIGraphicsImageRenderer(size: refuelImage.size)

        let disabledImage = renderer.image {
            guard let cgImage = refuelImage.cgImage else {
                return
            }

            TemplateDrawingOperation(image: cgImage,
                                     imageSize: refuelImage.size,
                                     color: Asset.Station.refuelDisabledColor.color.cgColor)
            .apply(in: $0.cgContext)
        }

        return RefuelRow(item: [
            .normal: .init(title: "Заправиться",
                           image: refuelImage),
            .disabled: .init(title: "Заправиться",
                             image: disabledImage)
        ])
        .with(appearance: Self.refuelButtonAppearance)
    }
}

private extension StationInfoTableBuilder {
    static var titleAppearance: LabelRow.Appearance {
        .make {
            $0.subviewAppearance {
                $0.textAttributes = .stationTitle
                $0.layout.insets = UIEdgeInsets(top: 8, left: 16, bottom: 2, right: 16)
            }
        }
    }

    static var traitsAppearance: TraitsRow.Appearance {
        .make {
            $0.subviewAppearance.layout.insets = UIEdgeInsets(top: 6, left: 16, bottom: 14, right: 16)
        }
    }

    static var addressAppearance: AddressRow.Appearance {
        .make {
            $0.subviewAppearance {
                $0.textAttributes = .stationAddress
                $0.layout.insets = .horizontal(16).vertical(top: 12)
            }
        }
    }

    static var refuelButtonAppearance: RefuelRow.Appearance {
        .make {
            $0.subviewAppearance {
                if let normalAppearance = $0.stateAppearance[.normal] {
                    normalAppearance.backgroundColor = Asset.Common.mainRedColor.color.withAlphaComponent(0.1)
                    normalAppearance.textAttributes = .smallActionButtonDefault
                    normalAppearance.titleInsets = .horizontal(left: 8)
                }

                if let disabledAppearance = $0.stateAppearance[.disabled] {
                    disabledAppearance.backgroundColor = Asset.Common.disabledLightGrayColor.color.withAlphaComponent(0.3)

                    disabledAppearance.textAttributes = .smallActionButtonDisabled
                    disabledAppearance.titleInsets = .horizontal(left: 8)
                }

                $0.layout {
                    $0.insets = .horizontal(16).vertical(top: 16, bottom: 0)
                    $0.size = .fixedHeight(48)
                }
            }
        }
    }

    static var separatorAppearance: SeparatorAppearance {
        .make {
            $0.layout.insets = .horizontal(16)
            $0.border.color = .black.withAlphaComponent(0.1)
        }
    }

    static var workingHoursAppearance: LabelRow.Appearance {
        .make {
            $0.subviewAppearance {
                $0.textAttributes = .stationAddress
                $0.layout.insets = .horizontal(16).vertical(top: 0)
            }
        }
    }

    static var scheduleAppearance: ScheduleRow.Appearance {
        .make {
            $0.subviewAppearance {
                $0.layout {
                    $0.insets = .horizontal(16).vertical(top: 8, bottom: 24)
                    $0.spacing = 8
                    $0.alignment = .leading
                }

                $0.arrangedSubviewsAppearance {
                    $0.subviewAppearance {
                        $0.textAttributes = .stationSchedule
                        $0.layout.insets = UIEdgeInsets(top: 6, left: 12, bottom: 8, right: 12)
                    }

                    $0.backgroundColor = Asset.Common.lightGrayColor.color
                    $0.border.cornerRadius = 4
                    $0.border.roundedCorners = .allCorners
                }
            }
        }
    }
}
Более полная поддержка коллекций и stack'ов для быстрой вёрстки вместе с поддержкой WrapperViewLayout во всех контейнерах. Пример экрана на ~150 строк кода <img width="439" src="https://github.com/TouchInstinct/LeadKit/assets/6436245/5977da91-1f56-4ccb-868d-f790983d278d"> ```swift @MainActor struct StationInfoTableBuilder { typealias LabelRow = DefaultConfigurableLabel.InTableRow typealias TraitsRow = TraitsCollectionView.InCollectionTableCell.TableRow typealias AddressRow = DefaultConfigurableLabel.InSeparatableRow typealias RefuelRow = DefaultConfigurableStatefulButton.InTableRow typealias ScheduleRow = BaseStackView<DefaultConfigurableLabel.InContainerView>.InTableRow static func buildMainSection(dataSource: FuelingStationInfoViewPresenter) -> TableSection { TableSection(onlyRows: [ LabelRow(item: dataSource.station.name) .with(appearance: Self.titleAppearance), TraitsRow(item: dataSource.traits) .with(appearance: Self.traitsAppearance), AddressRow(item: dataSource.station.address) .with(appearance: Self.addressAppearance) .with(separators: .top(Self.separatorAppearance)), buildRefuelRow() ]) } static func buildWorkHoursSection(dataSource: FuelingStationInfoViewPresenter) -> TableSection { if dataSource.schedule.isEmpty { return TableSection.emptySection() } else { return TableSection(onlyRows: [ LabelRow(item: "Режим работы") .with(appearance: Self.workingHoursAppearance), ScheduleRow(item: dataSource.schedule) .with(appearance: Self.scheduleAppearance) ]) } } static func buildRefuelRow() -> RefuelRow { let refuelImage = Asset.Station.fuelingStationRefuelDefault.image let renderer = UIGraphicsImageRenderer(size: refuelImage.size) let disabledImage = renderer.image { guard let cgImage = refuelImage.cgImage else { return } TemplateDrawingOperation(image: cgImage, imageSize: refuelImage.size, color: Asset.Station.refuelDisabledColor.color.cgColor) .apply(in: $0.cgContext) } return RefuelRow(item: [ .normal: .init(title: "Заправиться", image: refuelImage), .disabled: .init(title: "Заправиться", image: disabledImage) ]) .with(appearance: Self.refuelButtonAppearance) } } private extension StationInfoTableBuilder { static var titleAppearance: LabelRow.Appearance { .make { $0.subviewAppearance { $0.textAttributes = .stationTitle $0.layout.insets = UIEdgeInsets(top: 8, left: 16, bottom: 2, right: 16) } } } static var traitsAppearance: TraitsRow.Appearance { .make { $0.subviewAppearance.layout.insets = UIEdgeInsets(top: 6, left: 16, bottom: 14, right: 16) } } static var addressAppearance: AddressRow.Appearance { .make { $0.subviewAppearance { $0.textAttributes = .stationAddress $0.layout.insets = .horizontal(16).vertical(top: 12) } } } static var refuelButtonAppearance: RefuelRow.Appearance { .make { $0.subviewAppearance { if let normalAppearance = $0.stateAppearance[.normal] { normalAppearance.backgroundColor = Asset.Common.mainRedColor.color.withAlphaComponent(0.1) normalAppearance.textAttributes = .smallActionButtonDefault normalAppearance.titleInsets = .horizontal(left: 8) } if let disabledAppearance = $0.stateAppearance[.disabled] { disabledAppearance.backgroundColor = Asset.Common.disabledLightGrayColor.color.withAlphaComponent(0.3) disabledAppearance.textAttributes = .smallActionButtonDisabled disabledAppearance.titleInsets = .horizontal(left: 8) } $0.layout { $0.insets = .horizontal(16).vertical(top: 16, bottom: 0) $0.size = .fixedHeight(48) } } } } static var separatorAppearance: SeparatorAppearance { .make { $0.layout.insets = .horizontal(16) $0.border.color = .black.withAlphaComponent(0.1) } } static var workingHoursAppearance: LabelRow.Appearance { .make { $0.subviewAppearance { $0.textAttributes = .stationAddress $0.layout.insets = .horizontal(16).vertical(top: 0) } } } static var scheduleAppearance: ScheduleRow.Appearance { .make { $0.subviewAppearance { $0.layout { $0.insets = .horizontal(16).vertical(top: 8, bottom: 24) $0.spacing = 8 $0.alignment = .leading } $0.arrangedSubviewsAppearance { $0.subviewAppearance { $0.textAttributes = .stationSchedule $0.layout.insets = UIEdgeInsets(top: 6, left: 12, bottom: 8, right: 12) } $0.backgroundColor = Asset.Common.lightGrayColor.color $0.border.cornerRadius = 4 $0.border.roundedCorners = .allCorners } } } } } ```
ivan.smolin added 2 commits 2023-06-17 02:03:44 +03:00
e8b026302e BaseStackView with configurable items appearance
CollectionTableViewCell self-sizing
ViewAppearance.WrappedViewLayout support for all WrappedViewHolders
ViewCallbacks support for all BaseInitializeableViews
nikita.semenov reviewed 2023-06-25 23:17:47 +03:00
@ -0,0 +57,4 @@
}
}
func update(from layout: some WrappedViewLayout) {
Member

Кажется будет логичнее, если update(from:) будет находится выше update(insets:size:centerOffset:)

Кажется будет логичнее, если `update(from:)` будет находится выше `update(insets:size:centerOffset:)`
Author
Member

Я не против, но не понимаю почему? Видимо для меня не логичнее)

Я не против, но не понимаю почему? Видимо для меня не логичнее)
Author
Member

Я вообще хотел этот метод вынести в extension потому как он просто добавляет удобства, но не добавляет новой функциональности. Из-за этого он и оказался ниже.
Предлагаю добавить mark и на этом закрыть вопрос

Я вообще хотел этот метод вынести в extension потому как он просто добавляет удобства, но не добавляет новой функциональности. Из-за этого он и оказался ниже. Предлагаю добавить mark и на этом закрыть вопрос
nikita.semenov marked this conversation as resolved
nikita.semenov reviewed 2023-06-25 23:19:18 +03:00
@ -31,3 +34,3 @@
public var contentInsets: UIEdgeInsets = .zero {
didSet {
contentEdgeConstraints?.update(from: contentInsets)
update(subviewCpnstraints: subviewContraints)
Member

Опечатка: subviewCpnstraints -> subviewConstraints

Опечатка: subviewCpnstraints -> subviewConstraints
nikita.semenov marked this conversation as resolved
nikita.semenov reviewed 2023-06-25 23:20:25 +03:00
@ -31,3 +32,3 @@
public var contentInsets: UIEdgeInsets = .zero {
didSet {
contentEdgeConstraints?.update(from: contentInsets)
update(subviewCpnstraints: subviewContraints)
Member

И тута subviewCpnstraints

И тута subviewCpnstraints
nikita.semenov marked this conversation as resolved
nikita.semenov reviewed 2023-06-25 23:23:23 +03:00
@ -28,0 +30,4 @@
public extension ConfigurableView where Self: UIView {
init(viewModel: ViewModelType) {
self.init()
Member

пустая строка после self.init()?

пустая строка после self.init()?
nikita.semenov marked this conversation as resolved
nikita.semenov reviewed 2023-06-25 23:27:44 +03:00
@ -0,0 +30,4 @@
open func onDidInitialize() {}
open func onDidLayoutSubviews() {}
open func withView(_ unwrappedViewClosure: (View) -> Void) {
Member

Думаешь withView - наиболее удачное имя для метода? Кмк в контексте при его использовании не совсем понятно, что это за функция. Предложил бы переименовать в withStrongView или что-то подобное

Думаешь `withView` - наиболее удачное имя для метода? Кмк в контексте при его использовании не совсем понятно, что это за функция. Предложил бы переименовать в `withStrongView` или что-то подобное
nikita.semenov marked this conversation as resolved
nikita.semenov approved these changes 2023-06-25 23:37:13 +03:00
ivan.smolin added 1 commit 2023-06-26 14:07:02 +03:00
ivan.smolin merged commit 77559babdb into master 2023-06-26 19:51:16 +03:00
ivan.smolin deleted branch feature/stack_appearance_layout 2023-06-26 19:51:17 +03:00
Sign in to join this conversation.
No reviewers
No Label
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: TouchInstinct/LeadKit#9
No description provided.