我可以在 SwiftUI 应用程序中使用 View Controller (CalendarKit) 吗?
Posted
技术标签:
【中文标题】我可以在 SwiftUI 应用程序中使用 View Controller (CalendarKit) 吗?【英文标题】:Can I use View Controller (CalendarKit) in SwiftUI application? 【发布时间】:2020-10-19 17:54:38 【问题描述】:我想在我的项目中使用 CalendarKit github here
它是使用 UIKit 编写的,但我的项目使用的是 SwiftUI。我可以在 SwiftUI 中使用CustomCalendarExampleController
吗? (也许通过UIViewControllerRepresentable
或其他方式?)
CustomCalendarExampleController -
class CustomCalendarExampleController: DayViewController, DatePickerControllerDelegate
var data = [["Breakfast at Tiffany's",
"New York, 5th avenue"],
["Workout",
"Tufteparken"],
["Meeting with Alex",
"Home",
"Oslo, Tjuvholmen"],
["Beach Volleyball",
"Ipanema Beach",
"Rio De Janeiro"],
["WWDC",
"Moscone West Convention Center",
"747 Howard St"],
["Google I/O",
"Shoreline Amphitheatre",
"One Amphitheatre Parkway"],
["✈️️ to Svalbard ❄️️❄️️❄️️❤️️",
"Oslo Gardermoen"],
["???????? Developing CalendarKit",
"???? Worldwide"],
["Software Development Lecture",
"Mikpoli MB310",
"Craig Federighi"],
]
var generatedEvents = [EventDescriptor]()
var alreadyGeneratedSet = Set<Date>()
var colors = [UIColor.blue,
UIColor.yellow,
UIColor.green,
UIColor.red]
lazy var customCalendar: Calendar =
let customNSCalendar = NSCalendar(identifier: NSCalendar.Identifier.gregorian)!
customNSCalendar.timeZone = TimeZone(abbreviation: "CEST")!
let calendar = customNSCalendar as Calendar
return calendar
()
override func loadView()
calendar = customCalendar
dayView = DayView(calendar: calendar)
view = dayView
override func viewDidLoad()
super.viewDidLoad()
title = "CalendarKit Demo"
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Change Date",
style: .plain,
target: self,
action: #selector(presentDatePicker))
navigationController?.navigationBar.isTranslucent = false
dayView.autoScrollToFirstEvent = true
reloadData()
@objc func presentDatePicker()
let picker = DatePickerController()
// let calendar = dayView.calendar
// picker.calendar = calendar
// picker.date = dayView.state!.selectedDate
picker.datePicker.timeZone = TimeZone(secondsFromGMT: 0)!
picker.delegate = self
let navC = UINavigationController(rootViewController: picker)
navigationController?.present(navC, animated: true, completion: nil)
func datePicker(controller: DatePickerController, didSelect date: Date?)
if let date = date
var utcCalendar = Calendar(identifier: .gregorian)
utcCalendar.timeZone = TimeZone(secondsFromGMT: 0)!
let offsetDate = dateOnly(date: date, calendar: dayView.calendar)
print(offsetDate)
dayView.state?.move(to: offsetDate)
controller.dismiss(animated: true, completion: nil)
func dateOnly(date: Date, calendar: Calendar) -> Date
let yearComponent = calendar.component(.year, from: date)
let monthComponent = calendar.component(.month, from: date)
let dayComponent = calendar.component(.day, from: date)
let zone = calendar.timeZone
let newComponents = DateComponents(timeZone: zone,
year: yearComponent,
month: monthComponent,
day: dayComponent)
let returnValue = calendar.date(from: newComponents)
// let returnValue = calendar.date(bySettingHour: 0, minute: 0, second: 0, of: date)
return returnValue!
// MARK: EventDataSource
override func eventsForDate(_ date: Date) -> [EventDescriptor]
if !alreadyGeneratedSet.contains(date)
alreadyGeneratedSet.insert(date)
generatedEvents.append(contentsOf: generateEventsForDate(date))
return generatedEvents
private func generateEventsForDate(_ date: Date) -> [EventDescriptor]
var workingDate = date.add(TimeChunk.dateComponents(hours: Int(arc4random_uniform(10) + 5)))
var events = [Event]()
for i in 0...4
let event = Event()
let duration = Int(arc4random_uniform(160) + 60)
let datePeriod = TimePeriod(beginning: workingDate,
chunk: TimeChunk.dateComponents(minutes: duration))
event.startDate = datePeriod.beginning!
event.endDate = datePeriod.end!
var info = data[Int(arc4random_uniform(UInt32(data.count)))]
let timezone = dayView.calendar.timeZone
print(timezone)
info.append(datePeriod.beginning!.format(with: "dd.MM.YYYY", timeZone: timezone))
info.append("\(datePeriod.beginning!.format(with: "HH:mm", timeZone: timezone)) - \(datePeriod.end!.format(with: "HH:mm", timeZone: timezone))")
event.text = info.reduce("", $0 + $1 + "\n")
event.color = colors[Int(arc4random_uniform(UInt32(colors.count)))]
event.isAllDay = Int(arc4random_uniform(2)) % 2 == 0
// Event styles are updated independently from CalendarStyle
// hence the need to specify exact colors in case of Dark style
if #available(ios 12.0, *)
if traitCollection.userInterfaceStyle == .dark
event.textColor = textColorForEventInDarkTheme(baseColor: event.color)
event.backgroundColor = event.color.withAlphaComponent(0.6)
events.append(event)
let nextOffset = Int(arc4random_uniform(250) + 40)
workingDate = workingDate.add(TimeChunk.dateComponents(minutes: nextOffset))
event.userInfo = String(i)
print("Events for \(date)")
return events
private func textColorForEventInDarkTheme(baseColor: UIColor) -> UIColor
var h: CGFloat = 0, s: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
baseColor.getHue(&h, saturation: &s, brightness: &b, alpha: &a)
return UIColor(hue: h, saturation: s * 0.3, brightness: b, alpha: a)
// MARK: DayViewDelegate
private var createdEvent: EventDescriptor?
override func dayViewDidSelectEventView(_ eventView: EventView)
guard let descriptor = eventView.descriptor as? Event else
return
print("Event has been selected: \(descriptor) \(String(describing: descriptor.userInfo))")
override func dayViewDidLongPressEventView(_ eventView: EventView)
guard let descriptor = eventView.descriptor as? Event else
return
endEventEditing()
print("Event has been longPressed: \(descriptor) \(String(describing: descriptor.userInfo))")
beginEditing(event: descriptor, animated: true)
print(Date())
override func dayView(dayView: DayView, didTapTimelineAt date: Date)
endEventEditing()
print("Did Tap at date: \(date)")
override func dayViewDidBeginDragging(dayView: DayView)
print("DayView did begin dragging")
override func dayView(dayView: DayView, willMoveTo date: Date)
print("DayView = \(dayView) will move to: \(date)")
override func dayView(dayView: DayView, didMoveTo date: Date)
print("DayView = \(dayView) did move to: \(date)")
override func dayView(dayView: DayView, didLongPressTimelineAt date: Date)
print("Did long press timeline at date \(date)")
// Cancel editing current event and start creating a new one
endEventEditing()
let event = generateEventNearDate(date)
print("Creating a new event")
create(event: event, animated: true)
createdEvent = event
private func generateEventNearDate(_ date: Date) -> EventDescriptor
let duration = Int(arc4random_uniform(160) + 60)
let startDate = date.subtract(TimeChunk.dateComponents(minutes: Int(CGFloat(duration) / 2)))
let event = Event()
let datePeriod = TimePeriod(beginning: startDate,
chunk: TimeChunk.dateComponents(minutes: duration))
event.startDate = datePeriod.beginning!
event.endDate = datePeriod.end!
var info = data[Int(arc4random_uniform(UInt32(data.count)))]
let timezone = dayView.calendar.timeZone
info.append(datePeriod.beginning!.format(with: "dd.MM.YYYY", timeZone: timezone))
info.append("\(datePeriod.beginning!.format(with: "HH:mm", timeZone: timezone)) - \(datePeriod.end!.format(with: "HH:mm", timeZone: timezone))")
event.text = info.reduce("", $0 + $1 + "\n")
event.color = colors[Int(arc4random_uniform(UInt32(colors.count)))]
event.editedEvent = event
// Event styles are updated independently from CalendarStyle
// hence the need to specify exact colors in case of Dark style
if #available(iOS 12.0, *)
if traitCollection.userInterfaceStyle == .dark
event.textColor = textColorForEventInDarkTheme(baseColor: event.color)
event.backgroundColor = event.color.withAlphaComponent(0.6)
return event
override func dayView(dayView: DayView, didUpdate event: EventDescriptor)
print("did finish editing \(event)")
print("new startDate: \(event.startDate) new endDate: \(event.endDate)")
if let _ = event.editedEvent
event.commitEditing()
if let createdEvent = createdEvent
createdEvent.editedEvent = nil
generatedEvents.append(createdEvent)
self.createdEvent = nil
endEventEditing()
reloadData()
DayViewController -
#if os(iOS)
import UIKit
import DateToolsSwift
open class DayViewController: UIViewController, EventDataSource, DayViewDelegate
public lazy var dayView: DayView = DayView()
public var dataSource: EventDataSource?
get
return dayView.dataSource
set(value)
dayView.dataSource = value
public var delegate: DayViewDelegate?
get
return dayView.delegate
set(value)
dayView.delegate = value
public var calendar = Calendar.autoupdatingCurrent
didSet
dayView.calendar = calendar
open override func loadView()
view = dayView
override open func viewDidLoad()
super.viewDidLoad()
edgesForExtendedLayout = []
view.tintColor = SystemColors.systemRed
dataSource = self
delegate = self
dayView.reloadData()
let sizeClass = traitCollection.horizontalSizeClass
configureDayViewLayoutForHorizontalSizeClass(sizeClass)
open override func viewDidAppear(_ animated: Bool)
super.viewDidAppear(animated)
dayView.scrollToFirstEventIfNeeded()
open override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator)
super.willTransition(to: newCollection, with: coordinator)
configureDayViewLayoutForHorizontalSizeClass(newCollection.horizontalSizeClass)
open func configureDayViewLayoutForHorizontalSizeClass(_ sizeClass: UIUserInterfaceSizeClass)
dayView.transitionToHorizontalSizeClass(sizeClass)
// MARK: - CalendarKit API
open func move(to date: Date)
dayView.move(to: date)
open func reloadData()
dayView.reloadData()
open func updateStyle(_ newStyle: CalendarStyle)
dayView.updateStyle(newStyle)
open func eventsForDate(_ date: Date) -> [EventDescriptor]
return [Event]()
// MARK: - DayViewDelegate
open func dayViewDidSelectEventView(_ eventView: EventView)
open func dayViewDidLongPressEventView(_ eventView: EventView)
open func dayView(dayView: DayView, didTapTimelineAt date: Date)
open func dayViewDidBeginDragging(dayView: DayView)
open func dayView(dayView: DayView, willMoveTo date: Date)
open func dayView(dayView: DayView, didMoveTo date: Date)
open func dayView(dayView: DayView, didLongPressTimelineAt date: Date)
open func dayView(dayView: DayView, didUpdate event: EventDescriptor)
// MARK: - Editing
open func create(event: EventDescriptor, animated: Bool = false)
dayView.create(event: event, animated: animated)
open func beginEditing(event: EventDescriptor, animated: Bool = false)
dayView.beginEditing(event: event, animated: animated)
open func endEventEditing()
dayView.endEventEditing()
#endif
DatePickerControllerDelegate
没什么好玩的。
感谢您的回复!
【问题讨论】:
是的,你可以把它包装在 UIViewControllerRepresentable 中 【参考方案1】:大声笑我不知道为什么它以前不起作用,但如果你正在寻找这样的东西
struct CustomController: UIViewControllerRepresentable
func updateUIViewController(_ uiViewController: UIViewController, context: Context)
func makeUIViewController(context: Context) -> UIViewController
let dayViewController = CustomCalendarExampleController()
return dayViewController
【讨论】:
这是目前使用 CalendarKit 的正确方法。在 UIKit 对应部分中编写代码,然后桥接到 SwiftUI以上是关于我可以在 SwiftUI 应用程序中使用 View Controller (CalendarKit) 吗?的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI - 解释 View 中数据刷新时 EnvironmentObject 与 ObservableObject 行为的差异
如何在 SwiftUI 中创建一个获取 View 并返回自定义结果的函数?