Два года назад я написал пост, посвященный созданию кастомного bottom sheet в SwiftUI. Сегодня же необходимости писать его вручную с нуля больше нет, по крайней мере, если вам не нужно какое-нибудь супер-уникальное поведение. SwiftUI теперь предоставляет новый API для отображения bottom sheet всего в несколько строк кода. В этом посте мы рассмотрим новый API, позволяющий нам отображать различные вариации bottom sheet.
Новый API SwiftUI для отображения bottom sheet достаточно прост в использовании. Все, что нам нужно сделать, это прикрепить модификатор представления presentationDetents
к содержимому модификатора представления sheet.
struct ContentView: View {
@State private var sheetShown = false
@State private var query = ""
var body: some View {
Button("Display bottom sheet") {
sheetShown = true
}
.sheet(isPresented: $sheetShown) {
NavigationStack {
Text("You query: \(query)")
.searchable(text: $query)
.navigationTitle("Search")
}
.presentationDetents([.medium])
}
}
}
Чтобы узнать, как создать bottom sheet в SwiftUI с нуля, читайте мой пост “Создание Bottom Sheet в SwiftUI”.
Как вы можете видеть в приведенном выше примере, мы используем модификатор представления presentationDetents
для отображения sheet в качестве bottom sheet. Мы также устанавливаем размер bottom sheet на medium. В этом случае она займет половину экрана. Вы можете указать в модификаторе представления presentationDetent
массив допустимых размеров, тем самым позволяя пользователю изменять размер sheet, перетаскивая его.
struct ContentView: View {
@State private var sheetShown = false
@State private var query = ""
var body: some View {
Button("Display bottom sheet") {
sheetShown = true
}
.sheet(isPresented: $sheetShown) {
NavigationStack {
Text("You query: \(query)")
.searchable(text: $query)
.navigationTitle("Search")
}
.presentationDetents([.medium, .large])
}
}
}
Новый bottom sheet API очень гибкий и позволяет настраивать высоту sheet как вам нужно, используя следующие параметры.
struct ContentView: View {
@State private var sheetShown = false
@State private var query = ""
var body: some View {
Button("Display bottom sheet") {
sheetShown = true
}
.sheet(isPresented: $sheetShown) {
NavigationStack {
Text("You query: \(query)")
.searchable(text: $query)
.navigationTitle("Search")
}
.presentationDetents(
[
.fraction(0.2),
.height(300),
.fraction(0.5),
.height(600)
]
)
}
}
}
Как вы можете видеть в приведенном выше примере, мы используем функцию fraction
, чтобы установить высоту sheet на определенную часть доступного пространства. Вы также можете использовать функцию height
, чтобы установить высоту фиксированного размера. Вы также можете смешивать различные варианты для установки высоты bottom sheet, чтобы добиться нужного вам внешнего вида.
В зависимости от текущей среды нам может потребоваться создать супер-индивидуальную логику изменения размера. Например, я хотел бы отобразить bottom sheet, заняв 80% доступного пространства на iPad и в полную высоту на iPhone. Мы можем реализовать эту кастомную презентационную логику и повторно использовать ее на нескольких экранах.
struct MyCustomDetent: CustomPresentationDetent {
static func height(in context: Context) -> CGFloat? {
if context.verticalSizeClass == .regular {
return context.maxDetentValue * 0.8
} else {
return context.maxDetentValue
}
}
}
Во-первых, мы должны создать тип, соответствующий протоколу CustomPresentationDetent
. Во-вторых, мы должны реализовать единственную необходимую функцию с названием height, которая принимает context в качестве параметра. Параметр context содержит все значения среды SwiftUI, и вы можете использовать его, чтобы определить, какая высота вам нужна в конкретной среде. В приведенном выше примере, чтобы определить высоту sheet, я использую verticalSizeClass
.
struct ContentView: View {
@State private var sheetShown = false
@State private var query = ""
var body: some View {
Button("Display bottom sheet") {
sheetShown = true
}
.sheet(isPresented: $sheetShown) {
NavigationStack {
Text("You query: \(query)")
.searchable(text: $query)
.navigationTitle("Search")
}
.presentationDetents([.medium, .custom(MyCustomDetent.self)])
}
}
}
Изменить размер bottom sheet можно не только с помощью перетаскивания, но и программным способом. В этом случае вы должны передать @Binding
выбранному размеру из допустимых.
struct ContentView: View {
@State private var sheetShown = false
@State private var sheetSize: PresentationDetent = .medium
var body: some View {
VStack {
Picker("Size", selection: $sheetSize) {
Text("Medium").tag(PresentationDetent.medium)
Text("Large").tag(PresentationDetent.large)
}
Button("Display bottom sheet") {
sheetShown = true
}
.sheet(isPresented: $sheetShown) {
NavigationStack {
Text("Sheet content")
}
.presentationDetents([.medium, .large], selection: $sheetSize)
}
}
}
}
Последнее, что мы можем настроить, - это индикатор перетаскивания bottom sheet. Мы можем сделать его всегда видимым, скрытым или разрешить решать, должен ли он отображаться, SwiftUI.
struct ContentView: View {
@State private var sheetShown = false
@State private var query = ""
var body: some View {
Button("Display bottom sheet") {
sheetShown = true
}
.sheet(isPresented: $sheetShown) {
NavigationStack {
Text("You query: \(query)")
.searchable(text: $query)
.navigationTitle("Search")
}
.presentationDetents([.medium, .large])
.presentationDragIndicator(.automatic)
}
}
}
Сегодня мы узнали, как использовать новый bottom sheet API в SwiftUI. Мне нравится уровень кастомизации, который он обеспечивает. Надеюсь, вам понравился этот пост, не стесняйтесь подписываться на меня в Твиттере и задавать вопросы, связанные с этой темой.
Всех желающих приглашаем на открытый урок «Делаем сетевой слой в приложении iOS». На уроке рассмотрим, как и с помощью каких решений написать сетевой слой приложения iOS и как делать сетевые запросы. Регистрация открыта по ссылке.
evaganov
К сожалению всё это работает только начиная с iOS 16. Если нужна поддержка более старых версий, приходится всё так же писать вручную.