Вопрос или проблема
У меня есть 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
можно воспользоваться следующим решением. Сначала необходимо создать сам список и элементы, как указано в вашем коде, но с некоторыми изменениями, чтобы убрать проблему с игнорированием закругленных углов.
-
Создание структуры списка. Основная структура остается без изменений, за исключением добавления прозрачных фонов для элементов свайпа.
-
Корректный настройка
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
для применения закругленных углов.
Это должно помочь вам добиться желаемого визуального результата аналогичного представленным примерам из других приложений. Самый главный момент в том, чтобы сделать фон элементов свайпа таким же, как и у основного элемента списка, чтобы сохранять единообразие и обеспечить эффект закругления.