Согласно этой статье, должно быть просто создать пользовательскую привязку с разными типами для привязки и отображения. Это мой нерабочий пример:
import SwiftUI
struct BindingButton: View {
@Binding var title: String
var onTap: () -> Void
var body: some View {
Button {
onTap()
} label: {
Text(title)
}
}
}
struct CustomBinding: View {
@State var date: Date
@State var int: Int
lazy var descriptiveDate = Binding(
get: { //Escaping closure captures mutating 'self' parameter
return self.date.description
},
set: { _ in }
)
lazy var descriptiveInt = Binding(
get: { //Escaping closure captures mutating 'self' parameter
return String(self.int)
},
set: { _ in }
)
var body: some View {
VStack {
// Cannot find '$descriptiveDate' in scope
BindingButton(title: $descriptiveDate) {
date = Date()
}
// Cannot find '$descriptiveInt' in scope
BindingButton(title: $descriptiveInt) {
int += 2
}
}
}
}
Я хотел бы связать Date и Int, но использовать строку для отображения. Возможно ли это без двойной привязки? Я имею в виду, что я хотел бы избежать создания первой привязки для Date и Int, а затем .onChange
обновить другую привязку для String и String. Должен ли я делать это так, или, может быть, есть более элегантный способ?
Binding
по определению является двусторонним соединением. lazy
подразумевает, что код запускается только один раз. Исходя из этого и пустого set
ваши descriptiveDate
и descriptiveInt
не должны быть Binding
просто get
для String
struct CustomBinding: View {
@State var date: Date
@State var int: Int
var descriptiveDate: String {
date.description
}
var descriptiveInt : String{
String(self.int)
}
var body: some View {
VStack {
BindingButton(title: descriptiveDate) {
date = Date()
}
BindingButton(title: descriptiveInt) {
int += 2
}
}
}
}
struct BindingButton: View {
let title: String
var onTap: () -> Void
var body: some View {
Button {
onTap()
} label: {
Text(title)
}
}
}
lazy
не работает с SwiftUI
View
s, когда @State
вызывает перерисовку, вам нужно пересчитать body
и описательные переменные.
Спасибо за ваши усилия ;) Еще один вопрос. Когда я изменю дату с Button
при нажатии, изменится ли название этой кнопки так же, как это? Что приведет к обновлению этого заголовка?
@BartłomiejSemańczyk немедленно, установка даты вызовет перерисовку
Хорошо, так что для меня это лучшее решение, я думаю.
Полностью согласен с предыдущим ответом, что в данном случае привязка не нужна. Но чтобы ответить на другую часть вопроса: «Возможно ли иметь взаимозависимые @State
и @Binding
без двойной привязки?»
Ну, во-первых, даже самым примитивным способом не нужна кастомная привязка. Вместо этого вы можете использовать 2 переменные состояния:
@State var descriptiveDate: String
@State var descriptiveInt: String
и используйте либо didSet
:
@State var date: Date {
didSet {
descriptiveDate = date.description
}
}
@State var int: Int {
didSet {
descriptiveInt = String(int)
}
}
@State var descriptiveDate: String
@State var descriptiveInt: String
Или реализовать onChange
для date
и int
состояния:
struct CustomBinding: View {
@State var date: Date
@State var int: Int
@State var descriptiveDate: String
@State var descriptiveInt: String
var body: some View {
VStack {
BindingButton(title: $descriptiveDate) {
date = Date()
}
.onChange(of: date) { newDate in
descriptiveDate = newDate.description
}
BindingButton(title: $descriptiveInt) {
int += 2
}
.onChange(of: int) { newInt in
descriptiveInt = String(int)
}
}
}
}
Но это все еще немного избыточно, так как у вас будет дублирование @State
для каждой переменной. Поэтому, чтобы избежать этой пользовательской привязки, полезно:
BindingButton(title: Binding(
get: { int.description },
set: { int = Int($0)! }
)) {
int += 2
}
То есть: вам не нужно сохранять дополнительное состояние для каждого состояния, но при этом есть возможность изменить родительское значение из дочернего. В этом конкретном примере это не имеет особого смысла, но в некоторых случаях имеет смысл.
Так что мы избежали "двойной" переменной - не совсем. Поскольку у вас есть 2 части информации, которые могут меняться по некоторым правилам, но независимо друг от друга, вам нужно 2 чего-то. Но пользовательская привязка — это только одна из возможностей.
О, это довольно красиво, я не знал, что могу добавить didSet
к свойству @State
.
Зачем вам нужна привязка к заголовку кнопки? Привязка необходима только тогда, когда вам нужно изменить состояние дочернего компонента. Ваш
BindingButton
ничего в нем не меняет.