在 SwiftUI 中向 FSCalendar 添加下一个/上一个按钮
Adding Next/ Prev buttons to FSCalendar in SwiftUI
我一直在研究 FSCalendar,它帮助我构建了自己的自定义日历。
因为它是用 UIKit 编写的,所以我在将它集成到我的 SwiftUI 项目时遇到了一些问题,例如在日历的两侧添加了下一个和上一个按钮。
这是我目前拥有的:
ContentView,我在其中使用 HStack 将按钮添加到日历的两侧
struct ContentView: View {
let myCalendar = MyCalendar()
var body: some View {
HStack(spacing: 5) {
Button(action: {
myCalendar.previousTapped()
}) { Image("back-arrow") }
MyCalendar()
Button(action: {
myCalendar.nextTapped()
}) { Image("next-arrow") }
}
}}
为了集成 FSCalendar 库,MyCalendar 结构是一个 UIViewRepresentable。
这也是我添加两个函数(nextTapped 和 previousTapped)的地方,它们应该在点击按钮时更改显示的月份:
struct MyCalendar: UIViewRepresentable {
let calendar = FSCalendar(frame: CGRect(x: 0, y: 0, width: 320, height: 300))
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> FSCalendar {
calendar.delegate = context.coordinator
calendar.dataSource = context.coordinator
return calendar
}
func updateUIView(_ uiView: FSCalendar, context: Context) {
}
func nextTapped() {
let nextMonth = Calendar.current.date(byAdding: .month, value: 1, to: calendar.currentPage)
calendar.setCurrentPage(nextMonth!, animated: true)
print(calendar.currentPage)
}
func previousTapped() {
let previousMonth = Calendar.current.date(byAdding: .month, value: -1, to: calendar.currentPage)
calendar.setCurrentPage(previousMonth!, animated: true)
print(calendar.currentPage)
}
class Coordinator: NSObject, FSCalendarDelegateAppearance, FSCalendarDataSource, FSCalendarDelegate {
var parent: MyCalendar
init(_ calendar: MyCalendar) {
self.parent = calendar
}
func minimumDate(for calendar: FSCalendar) -> Date {
return Date()
}
func maximumDate(for calendar: FSCalendar) -> Date {
return Date().addingTimeInterval((60 * 60 * 24) * 365)
}
}}
这是它在模拟器中的样子:
如您所见,每当点击下一个或上一个按钮时,我已经设法在终端中打印 currentPage,但 currentPage 在实际日历中没有变化。
我该如何解决这个问题?
因为您正在使用 UIViewRepresentable
协议将 UIView class 与 SwiftUI 绑定。在这里你必须使用 ObservableObject - type of object with a publisher that emit before the object has changed.
您可以检查下面的代码以获得结果输出:(欢迎编辑/改进)
import SwiftUI
import UIKit
import FSCalendar
class CalendarData: ObservableObject{
@Published var selectedDate : Date = Date()
@Published var titleOfMonth : Date = Date()
@Published var crntPage: Date = Date()
}
struct ContentView: View {
@ObservedObject private var calendarData = CalendarData()
var strDateSelected: String {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .none
dateFormatter.locale = Locale.current
return dateFormatter.string(from: calendarData.selectedDate)
}
var strMonthTitle: String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMMM yyyy"
dateFormatter.locale = Locale.current
return dateFormatter.string(from: calendarData.titleOfMonth)
}
var body: some View {
VStack {
HStack(spacing: 100) {
Button(action: {
self.calendarData.crntPage = Calendar.current.date(byAdding: .month, value: -1, to: self.calendarData.crntPage)!
}) { Image(systemName: "arrow.left") }
.frame(width: 35, height: 35, alignment: .leading)
Text(strMonthTitle)
.font(.headline)
Button(action: {
self.calendarData.crntPage = Calendar.current.date(byAdding: .month, value: 1, to: self.calendarData.crntPage)!
}) { Image(systemName: "arrow.right") }
.frame(width: 35, height: 35, alignment: .trailing)
}
CustomCalendar(dateSelected: $calendarData.selectedDate, mnthNm: $calendarData.titleOfMonth, pageCurrent: $calendarData.crntPage)
.padding()
.background(
RoundedRectangle(cornerRadius: 25.0)
.foregroundColor(.white)
.shadow(color: Color.black.opacity(0.2), radius: 10.0, x: 0.0, y: 0.0)
)
.frame(height: 350)
.padding(25)
Text(strDateSelected)
.font(.title)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct CustomCalendar: UIViewRepresentable {
typealias UIViewType = FSCalendar
@Binding var dateSelected: Date
@Binding var mnthNm: Date
@Binding var pageCurrent: Date
var calendar = FSCalendar()
var today: Date{
return Date()
}
func makeUIView(context: Context) -> FSCalendar {
calendar.dataSource = context.coordinator
calendar.delegate = context.coordinator
calendar.appearance.headerMinimumDissolvedAlpha = 0
return calendar
}
func updateUIView(_ uiView: FSCalendar, context: Context) {
uiView.setCurrentPage(pageCurrent, animated: true) // --->> update calendar view when click on left or right button
}
func makeCoordinator() -> CustomCalendar.Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, FSCalendarDelegate, FSCalendarDataSource {
var parent: CustomCalendar
init(_ parent: CustomCalendar) {
self.parent = parent
}
func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
parent.dateSelected = date
}
func calendarCurrentPageDidChange(_ calendar: FSCalendar) {
parent.pageCurrent = calendar.currentPage
parent.mnthNm = calendar.currentPage
}
}
}
输出:
我一直在研究 FSCalendar,它帮助我构建了自己的自定义日历。
因为它是用 UIKit 编写的,所以我在将它集成到我的 SwiftUI 项目时遇到了一些问题,例如在日历的两侧添加了下一个和上一个按钮。
这是我目前拥有的:
ContentView,我在其中使用 HStack 将按钮添加到日历的两侧
struct ContentView: View {
let myCalendar = MyCalendar()
var body: some View {
HStack(spacing: 5) {
Button(action: {
myCalendar.previousTapped()
}) { Image("back-arrow") }
MyCalendar()
Button(action: {
myCalendar.nextTapped()
}) { Image("next-arrow") }
}
}}
为了集成 FSCalendar 库,MyCalendar 结构是一个 UIViewRepresentable。 这也是我添加两个函数(nextTapped 和 previousTapped)的地方,它们应该在点击按钮时更改显示的月份:
struct MyCalendar: UIViewRepresentable {
let calendar = FSCalendar(frame: CGRect(x: 0, y: 0, width: 320, height: 300))
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> FSCalendar {
calendar.delegate = context.coordinator
calendar.dataSource = context.coordinator
return calendar
}
func updateUIView(_ uiView: FSCalendar, context: Context) {
}
func nextTapped() {
let nextMonth = Calendar.current.date(byAdding: .month, value: 1, to: calendar.currentPage)
calendar.setCurrentPage(nextMonth!, animated: true)
print(calendar.currentPage)
}
func previousTapped() {
let previousMonth = Calendar.current.date(byAdding: .month, value: -1, to: calendar.currentPage)
calendar.setCurrentPage(previousMonth!, animated: true)
print(calendar.currentPage)
}
class Coordinator: NSObject, FSCalendarDelegateAppearance, FSCalendarDataSource, FSCalendarDelegate {
var parent: MyCalendar
init(_ calendar: MyCalendar) {
self.parent = calendar
}
func minimumDate(for calendar: FSCalendar) -> Date {
return Date()
}
func maximumDate(for calendar: FSCalendar) -> Date {
return Date().addingTimeInterval((60 * 60 * 24) * 365)
}
}}
这是它在模拟器中的样子:
如您所见,每当点击下一个或上一个按钮时,我已经设法在终端中打印 currentPage,但 currentPage 在实际日历中没有变化。 我该如何解决这个问题?
因为您正在使用 UIViewRepresentable
协议将 UIView class 与 SwiftUI 绑定。在这里你必须使用 ObservableObject - type of object with a publisher that emit before the object has changed.
您可以检查下面的代码以获得结果输出:(欢迎编辑/改进)
import SwiftUI
import UIKit
import FSCalendar
class CalendarData: ObservableObject{
@Published var selectedDate : Date = Date()
@Published var titleOfMonth : Date = Date()
@Published var crntPage: Date = Date()
}
struct ContentView: View {
@ObservedObject private var calendarData = CalendarData()
var strDateSelected: String {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .none
dateFormatter.locale = Locale.current
return dateFormatter.string(from: calendarData.selectedDate)
}
var strMonthTitle: String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMMM yyyy"
dateFormatter.locale = Locale.current
return dateFormatter.string(from: calendarData.titleOfMonth)
}
var body: some View {
VStack {
HStack(spacing: 100) {
Button(action: {
self.calendarData.crntPage = Calendar.current.date(byAdding: .month, value: -1, to: self.calendarData.crntPage)!
}) { Image(systemName: "arrow.left") }
.frame(width: 35, height: 35, alignment: .leading)
Text(strMonthTitle)
.font(.headline)
Button(action: {
self.calendarData.crntPage = Calendar.current.date(byAdding: .month, value: 1, to: self.calendarData.crntPage)!
}) { Image(systemName: "arrow.right") }
.frame(width: 35, height: 35, alignment: .trailing)
}
CustomCalendar(dateSelected: $calendarData.selectedDate, mnthNm: $calendarData.titleOfMonth, pageCurrent: $calendarData.crntPage)
.padding()
.background(
RoundedRectangle(cornerRadius: 25.0)
.foregroundColor(.white)
.shadow(color: Color.black.opacity(0.2), radius: 10.0, x: 0.0, y: 0.0)
)
.frame(height: 350)
.padding(25)
Text(strDateSelected)
.font(.title)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct CustomCalendar: UIViewRepresentable {
typealias UIViewType = FSCalendar
@Binding var dateSelected: Date
@Binding var mnthNm: Date
@Binding var pageCurrent: Date
var calendar = FSCalendar()
var today: Date{
return Date()
}
func makeUIView(context: Context) -> FSCalendar {
calendar.dataSource = context.coordinator
calendar.delegate = context.coordinator
calendar.appearance.headerMinimumDissolvedAlpha = 0
return calendar
}
func updateUIView(_ uiView: FSCalendar, context: Context) {
uiView.setCurrentPage(pageCurrent, animated: true) // --->> update calendar view when click on left or right button
}
func makeCoordinator() -> CustomCalendar.Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, FSCalendarDelegate, FSCalendarDataSource {
var parent: CustomCalendar
init(_ parent: CustomCalendar) {
self.parent = parent
}
func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
parent.dateSelected = date
}
func calendarCurrentPageDidChange(_ calendar: FSCalendar) {
parent.pageCurrent = calendar.currentPage
parent.mnthNm = calendar.currentPage
}
}
}
输出: