В этой подборке статей поговорим о том, как раскрасить SF Symbols по своему усмотрению, обсудим тему блокировки свайпа сверху вниз при работе с модальным окном и узнаем что нового привнес SwiftUI 3.0 при работе с Alert и ActionSheet.
SwiftUI 3.0. Изменение цветов и прозрачности SF Symbols
Теперь для изображений из библиотеки SF Symbols можно задавать свои цвета. Делается это при помощи атрибута foregroundColor(). Более того при помощи .renderingMode(.original) эти изображения можно делать многоцветными. А для более точной передачи оттенков отдельных частей изображения можно использовать иерархический вариант или использовать полностью настраиваемую палитру.
Иерархический рендеринг использует прозрачность для создания разных оттенков, чтобы обеспечить дополнительную глубину и четкость:
Image(systemName: "theatermasks")
.symbolRenderingMode(.hierarchical)
.font(.system(size: 144))
Иерархический рендеринг работает в сочетании с цветом самого изображения, поэтому его можно использовать в сочетании с модификатором foregroundColor:
Image(systemName: "theatermasks")
.symbolRenderingMode(.hierarchical)
.foregroundColor(.blue)
.font(.system(size: 144))
Параметр .palette в сочетании с модификатором foregroundStyle() дает еще больше возможностей, позволяя использовать более одного цвета для изображения:
Image(systemName: "shareplay")
.symbolRenderingMode(.palette)
.foregroundStyle(.blue, .black)
.font(.system(size: 144))
Распределение цветов по определенным элементам изображения зависит от количества слоёв конкретного изображения. Разные изображения состоят из разного количества слоёв и в зависимости от этого цвета могут распределяться по разному. Для символов содержащих три элемента достаточно добавить еще один цвет:
Image(systemName: "person.3.sequence.fill")
.symbolRenderingMode(.palette)
.foregroundStyle(.blue, .green, .red)
.font(.system(size: 144))
Цвета можно смешивать, создавая градиенты градиенты и более сложные оттенки:
та можно смешивать, создавая градиенты градиенты и более сложные оттенки:
Image(systemName: "person.3.sequence.fill")
.symbolRenderingMode(.palette)
.foregroundStyle(
.linearGradient(colors: [.red, .black], startPoint: .top, endPoint: .bottomTrailing),
.linearGradient(colors: [.green, .black], startPoint: .top, endPoint: .bottomTrailing),
.linearGradient(colors: [.blue, .black], startPoint: .top, endPoint: .bottomTrailing)
)
.font(.system(size: 144))
SwiftUI 3.0. Блокирование свайпа сверху вниз при закрытии модального экрана
Новый модификатор interactiveDismissDisabled() в SwiftUI, позволяет блокировать закрытие модального окна свайпом сверху вниз:
struct ExampleSheet: View {
@Environment(\.presentationMode) var presentationMode
var body: some View {
VStack {
Text("Sheet view")
Button("Dismiss", action: close)
}
.interactiveDismissDisabled()
}
func close() {
presentationMode.wrappedValue.dismiss()
}
}
struct ContentView: View {
@State private var showingSheet = false
var body: some View {
Button("Show Sheet") {
showingSheet.toggle()
}
.sheet(isPresented: $showingSheet, content: ExampleSheet.init)
}
}
Использование модификатора interactiveDismissDisabled() в таком виде просто блокирует закрытие окна свайпом сверху вниз:
Бывают такие случаи, когда смахивание окна должно быть доступно только при выполнении определенных условий. Для этого к модификатору можно привязать логическое свойство:
struct ExampleSheet: View {
@Environment(\.presentationMode) var presentationMode
@State private var termsAccepted = false
var body: some View {
VStack {
Text("Terms and conditions")
.font(.title)
Text("Lots of legalese here.")
Toggle("Accept", isOn: $termsAccepted)
}
.padding()
.interactiveDismissDisabled(!termsAccepted)
}
func close() {
presentationMode.wrappedValue.dismiss()
}
}
struct ContentView: View {
@State private var showingSheet = false
var body: some View {
Button("Show Sheet") {
showingSheet.toggle()
}
.sheet(isPresented: $showingSheet, content: ExampleSheet.init)
}
}
SwiftUI 3.0. Alert in SwiftUI
С версии iOS 15 при работе с алертом используются новые стили кнопок, которые так же доступны с этой же версии ОС:
struct ContentView: View {
@State private var showingAlert = false
var body: some View {
Button("Show Alert") {
showingAlert = true
}
.alert("Important message", isPresented: $showingAlert) {
Button("OK", role: .cancel) { }
}
}
}
Здесь мы прикрепили к предупреждению кнопку "ОК", которой назначили роль .cancel.
К алерту можно прикрепить любое количество кнопок и если ни одна из них не будет иметь роль .cancel, то помимо заданных вами кнопок будет автоматически добавлена и кнопка Cancel с соответствующей ролью.
struct ContentView: View {
@State private var showingAlert = false
var body: some View {
Button("Show Alert") {
showingAlert = true
}
.alert("Important message", isPresented: $showingAlert) {
Button("First") { }
Button("Second") { }
Button("Third") { }
}
}
}
Поскольку это новые кнопки SwiftUI, то им можно назначить любые роли, например .destructive, чтобы заголовки кнопок были красного цвета:
Если необходимо сделать поддержку iOS 14 и 13, то придется использовать структуру Alert, которая выглядит следующим образом:
Alert(
title: Text("Important message"),
message: Text("Wear sunscreen"),
dismissButton: .default(Text("Got it!"))
)
Структура определяет заголовок и сообщение, как в UIAlertController, а затем добавляет кнопку отмены со стилем по умолчанию и текстом «Got it!»:
struct ContentView: View {
@State private var showingAlert = false
var body: some View {
Button("Show Alert") {
showingAlert = true
}
.alert(isPresented: $showingAlert) {
Alert(title: Text("Important message"), message: Text("Wear sunscreen"), dismissButton: .default(Text("Got it!")))
}
}
}
Есть еще один способ создания алертов, связанный с привязкой к опциональному стейт свойству, тип которого удовлетворяет протоколу Identifiable.
У этого подхода есть два преимущества:
- Через связанное с алертом стейт свойство можно передавать любые данные, для дальнейшего их отображения в алерте.
- SwiftUI автоматически извлекает опциональное значение когда то становится доступным, поэтому нет необходимости в том, чтобы самостоятельно его извлекать.
struct TVShow: Identifiable {
var id: String { name }
let name: String
}
struct ContentView: View {
@State private var selectedShow: TVShow?
var body: some View {
VStack(spacing: 20) {
Text("What is your favorite TV show?")
.font(.headline)
Button("Select Ted Lasso") {
selectedShow = TVShow(name: "Ted Lasso")
}
Button("Select Bridgerton") {
selectedShow = TVShow(name: "Bridgerton")
}
}
.alert(item: $selectedShow) { show in
Alert(title: Text(show.name), message: Text("Great choice!"), dismissButton: .cancel())
}
}
}
SwiftUI 3.0. Action sheet in SwiftUI
Начиная с версии iOS 15 для работы с меню пользовательских действий (action sheet) используется модификатор confirmationDialog(). Для более ранних версий ОС используется тип данных ActionSheet. Мы рассмотрим оба способа работы с этим элементом интерфейса.
Для создания меню пользовательских действий при помощи confirmDialog(), необходимо задать заголовок меню и определить должен ли он отображаться. Само меню нужно связать с логическим свойством от значения которого будет зависеть должно оно отображаться или нет:
struct ContentView: View {
@State private var showingOptions = false
@State private var selection = "None"
var body: some View {
VStack {
Text(selection)
Button("Confirm paint color") {
showingOptions = true
}
.confirmationDialog("Select a color", isPresented: $showingOptions, titleVisibility: .visible) {
Button("Red") {
selection = "Red"
}
Button("Green") {
selection = "Green"
}
Button("Blue") {
selection = "Blue"
}
}
}
}
}
Если не задать параметр titleVisibility, то SwiftUI будет автоматически определять стоит ли его отображать в зависимости от контекста.
Кнопкам в меню пользовательских действий можно определить роли. Если для кнопки определить роль .destructive то её название будет красным.
Поскольку этот новый API более гибкий по сравнению с ActionSheet, то мы можем оптимизировать логику работы с кнопками, выполнив в цикле ForEach:
struct ContentView: View {
@State private var showingOptions = false
@State private var selection = "None"
var body: some View {
VStack {
Text(selection)
Button("Confirm paint color") {
showingOptions = true
}
.confirmationDialog("Select a color", isPresented: $showingOptions, titleVisibility: .visible) {
ForEach(["Red", "Green", "Blue"], id: \.self) { color in
Button(color) {
selection = color
}
}
}
}
}
}
Если вам нужно настроить таргетинг на работу с iOS 14 или ниже, то для отображения меню пользовательских действий придется использовать тип ActionSheet:
struct ContentView: View {
@State private var showingOptions = false
@State private var selection = "None"
var body: some View {
VStack {
Text(selection)
Button("Show Options") {
showingOptions = true
}
.actionSheet(isPresented: $showingOptions) {
ActionSheet(
title: Text("Select a color"),
buttons: [
.default(Text("Red")) {
selection = "Red"
},
.default(Text("Green")) {
selection = "Green"
},
.default(Text("Blue")) {
selection = "Blue"
},
]
)
}
}
}
}
В статье использованы материалы из источников:
- https://www.hackingwithswift.com/quick-start/swiftui/how-to-get-custom-colors-and-transparency-with-sf-symbols
- https://www.hackingwithswift.com/quick-start/swiftui/how-to-prevent-a-sheet-from-being-dismissed-with-a-swipe
- https://www.hackingwithswift.com/quick-start/swiftui/how-to-show-an-alert
- https://www.hackingwithswift.com/quick-start/swiftui/how-to-show-an-action-sheet
- https://www.hackingwithswift.com/quick-start/swiftui/how-to-show-an-action-sheet