如何使用 SwiftUI 和 Combine 检测日期选择器的值变化?
How to detect a value change of a Datepicker using SwiftUI and Combine?
在使用 SwiftUI 和 Combine 时,您如何检测 Datepicker 值的变化?
每当移动日期选择器轮时,我都需要调用一个方法来更新文本和滑块。
我一直在寻找特定的方法来识别值的变化(使用 UIKit 可以将一个动作与一个事件相关联),但显然我没有在文档中找到任何有用的东西(我试过 onTapGesture方法,但这不是我想要的,因为它会强制用户点击选择器来更新其他视图,而我希望在用户移动滚轮时自动更新)。
import SwiftUI
struct ContentView: View {
private var calendar = Calendar.current
@State private var date = Date()
@State private var weekOfYear = Double(Calendar.current.component(.weekOfYear, from: Date()) )
@State private var lastWeekOfThisYear = 53.0
@State private var weekDay: String = { () -> String in
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE"
let weekDay = dateFormatter.string(from: Date())
return weekDay
}()
var body: some View {
VStack {
// Date Picker
DatePicker(selection: $date, displayedComponents: .date, label:{ Text("Please enter a date") }
)
.labelsHidden()
.datePickerStyle(WheelDatePickerStyle())
.onTapGesture {
self.updateWeekAndDayFromDate()
}
// Week number and day
Text("Week \(Int(weekOfYear.rounded()))")
Text("\(weekDay)")
// Slider
Slider(value: $weekOfYear, in: 1...lastWeekOfThisYear, onEditingChanged: { _ in
self.updateDateFromWeek()
})
}
}
func updateWeekAndDayFromDate() {
// To do
}
func updateDateFromWeek() {
// To do
}
func setToday() {
// To do
}
func getWeekDay(_ date: Date) -> String {
//To do
}
}
我想这可以使用 Combine(observableobject、published、sink 等)来解决,但我还没有使用 Combine 的经验,因此我想寻求一些帮助...有什么想法吗? :)
非常感谢!
请在下面找到一种可能的方法(稍微修改一下您的代码)。这是一个让源状态透明以激活其中的依赖的想法。希望能有所帮助。
这是演示
这是代码
struct TestDatePicker: View {
private static func weekOfYear(for date: Date) -> Double {
Double(Calendar.current.component(.weekOfYear, from: date))
}
private static func weekDay(for date: Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE"
let weekDay = dateFormatter.string(from: date)
return weekDay
}
@State private var date: Date
@State private var weekOfYear: Double
@State private var weekDay: String
@State private var lastWeekOfThisYear = 53.0
private var dateProxy:Binding<Date> {
Binding<Date>(get: {self.date }, set: {
self.date = [=10=]
self.updateWeekAndDayFromDate()
})
}
init() {
let now = Date()
self._date = State<Date>(initialValue: now)
self._weekOfYear = State<Double>(initialValue: Self.weekOfYear(for: now))
self._weekDay = State<String>(initialValue: Self.weekDay(for: now))
}
var body: some View {
VStack {
// Date Picker
DatePicker(selection: dateProxy, displayedComponents: .date, label:{ Text("Please enter a date") }
)
.labelsHidden()
.datePickerStyle(WheelDatePickerStyle())
// Week number and day
Text("Week \(Int(weekOfYear.rounded()))")
Text("\(weekDay)")
// Slider
Slider(value: $weekOfYear, in: 1...lastWeekOfThisYear, onEditingChanged: { _ in
self.updateDateFromWeek()
})
}
}
func updateWeekAndDayFromDate() {
self.weekOfYear = Self.weekOfYear(for: self.date)
self.weekDay = Self.weekDay(for: self.date)
}
func updateDateFromWeek() {
// To do
}
func setToday() {
// To do
}
func getWeekDay(_ date: Date) -> String {
""
}
}
struct TestDatePicker_Previews: PreviewProvider {
static var previews: some View {
TestDatePicker()
}
}
一种使用 ObservableObject 的可能方法
class TestPikerModel: ObservableObject {
@Published var expireDate: Date = Date()
}
struct TestPikerView: View {
@ObservedObject var testPikerModel = TestPikerModel()
@State private var weekOfYear = Double(Calendar.current.component(.weekOfYear, from: Date()) )
var body: some View {
VStack {
Text("\(Int(weekOfYear))")
DatePicker("", selection: $testPikerModel.expireDate, displayedComponents: .date)
.datePickerStyle(CompactDatePickerStyle())
.clipped()
.labelsHidden()
}
.onReceive(testPikerModel.$expireDate) { date in
weekOfYear = Double(Calendar.current.component(.weekOfYear, from: date))
updateWeekAndDayFromDate()
}
}
func updateWeekAndDayFromDate() {
print("updateWeekAndDayFromDate performed")
}
}
struct TestPikerView_Previews: PreviewProvider {
static var previews: some View {
TestPikerView()
}
}
另一种方法是,每当日期更改时调用 viewModel 中的观察者
class TestPikerModel: ObservableObject {
@Published var expireDate: Date = Date()
init() {
setupObserver()
}
private func setupObserver() {
$expireDate
.sink { date in
// do some networking call
}
.store(in: &cancellables)
}
}
struct TestPikerView: View {
@ObservedObject var testPikerModel = TestPikerModel()
@State private var weekOfYear = Double(Calendar.current.component(.weekOfYear, from: Date()) )
var body: some View {
VStack {
Text("\(Int(weekOfYear))")
DatePicker("", selection: $testPikerModel.expireDate, displayedComponents: .date)
.datePickerStyle(CompactDatePickerStyle())
.clipped()
.labelsHidden()
}
}
}
更简单的方法是在 DatePicker 上使用 .onChange:
DatePicker(
"Datum",
selection: $date,
displayedComponents: [.date]
).onChange(of: date, perform: { value in
// Do what you want with "date", like array.timeStamp = date
});
在使用 SwiftUI 和 Combine 时,您如何检测 Datepicker 值的变化? 每当移动日期选择器轮时,我都需要调用一个方法来更新文本和滑块。
我一直在寻找特定的方法来识别值的变化(使用 UIKit 可以将一个动作与一个事件相关联),但显然我没有在文档中找到任何有用的东西(我试过 onTapGesture方法,但这不是我想要的,因为它会强制用户点击选择器来更新其他视图,而我希望在用户移动滚轮时自动更新)。
import SwiftUI
struct ContentView: View {
private var calendar = Calendar.current
@State private var date = Date()
@State private var weekOfYear = Double(Calendar.current.component(.weekOfYear, from: Date()) )
@State private var lastWeekOfThisYear = 53.0
@State private var weekDay: String = { () -> String in
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE"
let weekDay = dateFormatter.string(from: Date())
return weekDay
}()
var body: some View {
VStack {
// Date Picker
DatePicker(selection: $date, displayedComponents: .date, label:{ Text("Please enter a date") }
)
.labelsHidden()
.datePickerStyle(WheelDatePickerStyle())
.onTapGesture {
self.updateWeekAndDayFromDate()
}
// Week number and day
Text("Week \(Int(weekOfYear.rounded()))")
Text("\(weekDay)")
// Slider
Slider(value: $weekOfYear, in: 1...lastWeekOfThisYear, onEditingChanged: { _ in
self.updateDateFromWeek()
})
}
}
func updateWeekAndDayFromDate() {
// To do
}
func updateDateFromWeek() {
// To do
}
func setToday() {
// To do
}
func getWeekDay(_ date: Date) -> String {
//To do
}
}
我想这可以使用 Combine(observableobject、published、sink 等)来解决,但我还没有使用 Combine 的经验,因此我想寻求一些帮助...有什么想法吗? :)
非常感谢!
请在下面找到一种可能的方法(稍微修改一下您的代码)。这是一个让源状态透明以激活其中的依赖的想法。希望能有所帮助。
这是演示
这是代码
struct TestDatePicker: View {
private static func weekOfYear(for date: Date) -> Double {
Double(Calendar.current.component(.weekOfYear, from: date))
}
private static func weekDay(for date: Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE"
let weekDay = dateFormatter.string(from: date)
return weekDay
}
@State private var date: Date
@State private var weekOfYear: Double
@State private var weekDay: String
@State private var lastWeekOfThisYear = 53.0
private var dateProxy:Binding<Date> {
Binding<Date>(get: {self.date }, set: {
self.date = [=10=]
self.updateWeekAndDayFromDate()
})
}
init() {
let now = Date()
self._date = State<Date>(initialValue: now)
self._weekOfYear = State<Double>(initialValue: Self.weekOfYear(for: now))
self._weekDay = State<String>(initialValue: Self.weekDay(for: now))
}
var body: some View {
VStack {
// Date Picker
DatePicker(selection: dateProxy, displayedComponents: .date, label:{ Text("Please enter a date") }
)
.labelsHidden()
.datePickerStyle(WheelDatePickerStyle())
// Week number and day
Text("Week \(Int(weekOfYear.rounded()))")
Text("\(weekDay)")
// Slider
Slider(value: $weekOfYear, in: 1...lastWeekOfThisYear, onEditingChanged: { _ in
self.updateDateFromWeek()
})
}
}
func updateWeekAndDayFromDate() {
self.weekOfYear = Self.weekOfYear(for: self.date)
self.weekDay = Self.weekDay(for: self.date)
}
func updateDateFromWeek() {
// To do
}
func setToday() {
// To do
}
func getWeekDay(_ date: Date) -> String {
""
}
}
struct TestDatePicker_Previews: PreviewProvider {
static var previews: some View {
TestDatePicker()
}
}
一种使用 ObservableObject 的可能方法
class TestPikerModel: ObservableObject {
@Published var expireDate: Date = Date()
}
struct TestPikerView: View {
@ObservedObject var testPikerModel = TestPikerModel()
@State private var weekOfYear = Double(Calendar.current.component(.weekOfYear, from: Date()) )
var body: some View {
VStack {
Text("\(Int(weekOfYear))")
DatePicker("", selection: $testPikerModel.expireDate, displayedComponents: .date)
.datePickerStyle(CompactDatePickerStyle())
.clipped()
.labelsHidden()
}
.onReceive(testPikerModel.$expireDate) { date in
weekOfYear = Double(Calendar.current.component(.weekOfYear, from: date))
updateWeekAndDayFromDate()
}
}
func updateWeekAndDayFromDate() {
print("updateWeekAndDayFromDate performed")
}
}
struct TestPikerView_Previews: PreviewProvider {
static var previews: some View {
TestPikerView()
}
}
另一种方法是,每当日期更改时调用 viewModel 中的观察者
class TestPikerModel: ObservableObject {
@Published var expireDate: Date = Date()
init() {
setupObserver()
}
private func setupObserver() {
$expireDate
.sink { date in
// do some networking call
}
.store(in: &cancellables)
}
}
struct TestPikerView: View {
@ObservedObject var testPikerModel = TestPikerModel()
@State private var weekOfYear = Double(Calendar.current.component(.weekOfYear, from: Date()) )
var body: some View {
VStack {
Text("\(Int(weekOfYear))")
DatePicker("", selection: $testPikerModel.expireDate, displayedComponents: .date)
.datePickerStyle(CompactDatePickerStyle())
.clipped()
.labelsHidden()
}
}
}
更简单的方法是在 DatePicker 上使用 .onChange:
DatePicker(
"Datum",
selection: $date,
displayedComponents: [.date]
).onChange(of: date, perform: { value in
// Do what you want with "date", like array.timeStamp = date
});