Свайп-экшены в SwiftUI List – Как сделать их с радиусом углов?

Вопрос или проблема

У меня есть Swift UI List с plain стилем, как на следующем примере:


struct CustomList: View {
    var body: some View {
        List {
            ForEach(state.sections) { section in
                inboxSection(section: section)
            }
        }
        .listStyle(.plain)
    }

    private func inboxSection(section: Section) -> some View {
        Section(header: sectionHeader(for: section)) {
            ForEach(section.elements) { element in
                ItemRow(element: element)
                    .swipeActions(edge: .leading, allowsFullSwipe: false) {
                        CustomButton()
                    }
                    .listRowInsets(EdgeInsets(top: 4, leading: 0, bottom: 4, trailing: 0))
                    .listRowSeparator(.hidden)
                    .roundedCornerWithBorder(
                        borderColor: .Light.Border.defaultColor,
                        radius: .BorderRadius.lg,
                        corners: .allCorners
                    )
            }
        }
    }

    @ViewBuilder
    private func sectionHeader(for section: ViewState.Section) -> some View {
        Text(section.headerTitle)
    }
}

struct RoundedCorner: Shape {
    var radius: CGFloat = .infinity
    var corners: UIRectCorner = .allCorners

    func path(in rect: CGRect) -> Path {
        let path = UIBezierPath(
            roundedRect: rect,
            byRoundingCorners: corners,
            cornerRadii: CGSize(width: radius, height: radius)
        )
        return Path(path.cgPath)
    }
}

public extension View {
    func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
        clipShape(RoundedCorner(radius: radius, corners: corners))
    }

    func roundedCornerWithBorder(
        lineWidth: CGFloat = 1,
        borderColor: Color,
        radius: CGFloat,
        corners: UIRectCorner
    ) -> some View {
        clipShape(RoundedCorner(radius: radius, corners: corners))
            .overlay(RoundedCorner(radius: radius, corners: corners)
                .stroke(borderColor, lineWidth: lineWidth))
    }
}


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

вставьте описание изображения здесь

Как вы видите, строки с .swipeActions не принимают во внимание радиус углов строк списка. Я попробовал практически всё без особого успеха. Кто-нибудь знает, как это реализовать в Swift UI и достижимо ли это в принципе? Я могу привести другие примеры более удачного результата с приложением погоды:

вставьте описание изображения здесь

Или этот скриншот, который также был бы еще лучше:

вставьте описание изображения здесь

Важная заметка:
Другие стили списков, такие как .insetGrouped, имеют лучшие действия свайпа, но тогда они слишком изменят макет, а проблема всё равно останется, учитывая требования, которые я имею к своим спискам. (например, заголовок секции будет с заглавными буквами и другим стилем.)

Ответ или решение

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

  1. Создание структуры списка. Основная структура остается без изменений, за исключением добавления прозрачных фонов для элементов свайпа.

  2. Корректный настройка swipeActions. При добавлении свайп-экшенов, важно убедиться, что элементы, которые находятся в состоянии свайпа, также имеют прозрачный фон, что позволит достичь эффекта закругления углов.

Вот пример кода с внесенными изменениями:

struct CustomList: View {
    var body: some View {
        List {
            ForEach(state.sections) { section in
                inboxSection(section: section)
            }
        }
        .listStyle(.plain)
        .padding(0) // Убедитесь, что нет лишнего отступа
    }

    private func inboxSection(section: Section) -> some View {
        Section(header: sectionHeader(for: section)) {
            ForEach(section.elements) { element in
                ItemRow(element: element)
                    .background(Color.white) // Прозрачный фон
                    .clipShape(RoundedCorner(radius: 10, corners: .allCorners)) // Закругление углов
                    .swipeActions(edge: .leading, allowsFullSwipe: false) {
                        CustomButton()
                            .background(Color.white) // Прозрачный фон для кнопки
                            .clipShape(RoundedCorner(radius: 10, corners: .allCorners)) // Закругление углов для кнопки
                    }
                    .listRowInsets(EdgeInsets(top: 4, leading: 0, bottom: 4, trailing: 0))
                    .listRowSeparator(.hidden)
            }
        }
    }

    @ViewBuilder
    private func sectionHeader(for section: ViewState.Section) -> some View {
        Text(section.headerTitle)
    }
}

struct RoundedCorner: Shape {
    var radius: CGFloat = .infinity
    var corners: UIRectCorner = .allCorners

    func path(in rect: CGRect) -> Path {
        let path = UIBezierPath(
            roundedRect: rect,
            byRoundingCorners: corners,
            cornerRadii: CGSize(width: radius, height: radius)
        )
        return Path(path.cgPath)
    }
}

public extension View {
    func roundedCornerWithBorder(
        lineWidth: CGFloat = 1,
        borderColor: Color,
        radius: CGFloat,
        corners: UIRectCorner
    ) -> some View {
        clipShape(RoundedCorner(radius: radius, corners: corners))
            .overlay(RoundedCorner(radius: radius, corners: corners)
                .stroke(borderColor, lineWidth: lineWidth))
    }
}

Основные изменения:

  • Добавлен прозрачный фон для каждой строки списка и для элементов в swipeActions, чтобы они имели одинаковую закругленную форму.
  • Использован clipShape для применения закругленных углов.

Это должно помочь вам добиться желаемого визуального результата аналогичного представленным примерам из других приложений. Самый главный момент в том, чтобы сделать фон элементов свайпа таким же, как и у основного элемента списка, чтобы сохранять единообразие и обеспечить эффект закругления.

Оцените материал
Добавить комментарий

Капча загружается...