在 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
        }
    }
}

输出: