SwiftUI ViewModifier для настраиваемого просмотра
Есть ли способ создать модификатор для обновления
@State private var
в изменяемом виде?
У меня есть настраиваемое представление, которое возвращает либо
Text
с "динамическим" цветом фона ИЛИ
Circle
с "динамическим" цветом переднего плана.
struct ChildView: View {
var theText = ""
@State private var color = Color(.purple)
var body: some View {
HStack {
if theText.isEmpty { // If there's no theText, a Circle is created
Circle()
.foregroundColor(color)
.frame(width: 100, height: 100)
} else { // If theText is provided, a Text is created
Text(theText)
.padding()
.background(RoundedRectangle(cornerRadius: 25.0)
.foregroundColor(color))
.foregroundColor(.white)
}
}
}
}
Я повторно использую это представление в разных частях своего приложения. Как видите, единственный параметр, который мне нужно указать, - это
theText
. Итак, возможные способы создания этого ChildView следующие:
struct SomeParentView: View {
var body: some View {
VStack(spacing: 20) {
ChildView() // <- Will create a circle
ChildView(theText: "Hello world!") // <- Will create a text with background
}
}
}
Пока ничего особенного. Теперь мне нужно создать (возможно) модификатор или что-то подобное, чтобы в родительских представлениях я мог изменить значение этого
@State private var color
из
.red
на другой цвет, если мне нужна дополнительная настройка этого ChildView. Пример того, чего я пытаюсь достичь:
struct SomeOtherParentView: View {
var body: some View {
HStack(spacing: 20) {
ChildView()
ChildView(theText: "Hello world!")
.someModifierOrTheLike(color: Color.green) // <- what I think I need
}
}
}
Я знаю, что могу просто удалить
private
ключевое слово из этого
var
и пройти
color
как параметр в конструкторе (например:
ChildView(theText: "Hello World", color: .green)
), но я не думаю, что это способ решить эту проблему, потому что, если мне понадобится дополнительная настройка дочернего представления, я бы получил очень большой конструктор.
Итак, есть идеи о том, как достичь того, что я ищу? Надеюсь, я объяснил:) Спасибо!!!
3 ответа
Это ваше представление, а модификаторы - это просто функции, которые генерируют другое, измененное представление, так что... вот несколько возможных простых способов добиться того, чего вы хотите.
Протестировано с Xcode 12 / iOS 14
struct ChildView: View {
var theText = ""
@State private var color = Color(.purple)
var body: some View {
HStack {
if theText.isEmpty { // If there's no theText, a Circle is created
Circle()
.foregroundColor(color)
.frame(width: 100, height: 100)
} else { // If theText is provided, a Text is created
Text(theText)
.padding()
.background(RoundedRectangle(cornerRadius: 25.0)
.foregroundColor(color))
.foregroundColor(.white)
}
}
}
// simply modify self, as self is just a value
public func someModifierOrTheLike(color: Color) -> some View {
var view = self
view._color = State(initialValue: color)
return view.id(UUID())
}
}
Использование настраиваемого модификатора ViewModifier действительно является способом помочь предоставить пользователям более простой интерфейс, но общая идея того, как передать параметры настройки в представление (кроме использования
init
), осуществляется через переменные среды с
.environment
.
struct MyColorKey: EnvironmentKey {
static var defaultValue: Color = .black
}
extension EnvironmentValues {
var myColor: Color {
get { self[MyColorKey] }
set { self[MyColorKey] = newValue }
}
}
Тогда вы можете положиться на это в своем представлении:
struct ChildView: View {
@Environment(\.myColor) var color: Color
var body: some View {
Circle()
.foregroundColor(color)
.frame(width: 100, height: 100)
}
}
И использование будет:
ChildView()
.environment(\.myColor, .blue)
Вы можете сделать его немного лучше, используя модификатор вида:
struct MyColorModifier: ViewModifier {
var color: Color
func body(content: Content) -> some View {
content
.environment(\.myColor, color)
}
}
extension ChildView {
func myColor(_ color: Color) {
self.modifier(MyColorModifier(color: color)
}
}
ChildView()
.myColor(.blue)
Конечно, если у вас есть несколько параметров настройки или если это слишком низкий уровень для пользователя, вы можете создать ViewModifier, который предоставляет их подмножество, или создать тип, который инкапсулирует стиль, как SwiftUI делает с .buttonStyle(_:)
Вот как вы можете связать методы на основе ответа Аспери:
struct ChildView: View {
@State private var foregroundColor = Color.red
@State private var backgroundColor = Color.blue
var body: some View {
Text("Hello World")
.foregroundColor(foregroundColor)
.background(backgroundColor)
}
func foreground(color: Color) -> ChildView {
var view = self
view._foregroundColor = State(initialValue: color)
return view
}
func background(color: Color) -> ChildView {
var view = self
view._backgroundColor = State(initialValue: color)
return view
}
}
struct ParentView: View {
var body: some View {
ChildView()
.foreground(color: .yellow)
.background(color: .green)
.id(UUID())
}
}