Giter Club home page Giter Club logo

schedulekit's Introduction

ScheduleKit

ScheduleKit

Platforms GitHub release Build Status codecov.io GitHub license Carthage compatible Swift version codebeat badge

ScheduleKit is a powerful graphical event management framework for macOS that provides a great way to display a set of event-like objects in either a day or week based timetable.

ScheduleKit 2.0 has been completely rewritten and its workflow is way different in relation to the one present in previous releases. If you need source compatibility, please keep using the 1.0 release.

Features

  • Easy and intuitive. The SCKViewController class, which works like any other NSViewController, provides automatic event management with conflict handling. Just implement the SCKEventManaging protocol to add a bunch of events!
  • Change aware. The schedule view observes the inserted events' date and duration using KVO, and repositions them whenever a change in these properties is observed.
  • Asynchronous, if you want. SCKViewController supports asynchronously loading events by just changing the value of a property and implementing the appropiate method.
  • Built-in day/week navigation via IBAction connections and zooming capability, either via IBAction connections and/or magnification gestures.
  • Customizable drag & drop for events, with optional delegate methods that allow a more granular control over this feature.
  • Plenty of customization points to allow even more customization, including unavailable time intervals, event coloring and more.

How To Get Started

  • Download ScheduleKit or install it using Carthage.
  • Check out the Documentation for the SCKScheduleViewController class or just read the following section to begin quickly.

First steps

🔶 Working with a Swift target

In a swift target, you may choose between creating a SCKViewController subclass or using the SCKViewController class itself as a child view controller of another view controller of your own. Just choose the approach that feels more confortable to you.

The subclassing approach

  1. Create a new SCKViewController subclass. You can either use it programatically or add it to an Interface Builder storyboard or XIB file. You don't have to insert any SCKView instance nor any NSScrollView, that will be done automatically for you.
  2. If you want to use a week view from the start (defaults is a day view), override the loadView() method to set your preference before the view is set up.
  3. Override viewWillAppear() and:
    1. Set yourself as the controller's event manager.
    2. Set the initial date/time interval for your view.
    3. Optionally set yourself as the view's delegate.
    4. Add a call to either reloadData() if you are using multiple event classes or reloadData(ofConcreteType) for an easier implementation if you're using a single event class.
  4. Implement the SCKEventManaging (or SCKConcreteEventManaging) data source method to provide events to the view. The choice will depend on the reload data method you're using in step 3.4. Check the documentation to learn more.
override func loadView() {
    mode = .week
    super.loadView()
}

override func viewWillAppear() {
    super.viewWillAppear()
    
    self.eventManager = self
    
    let calendar = Calendar.current
    let start = calendar.date(from: calendar.dateComponents([.yearForWeekOfYear,.weekOfYear], from: Date()))!
    let end = calendar.date(byAdding: .weekOfYear, value: 1, to: start)!
    scheduleView.dateInterval = DateInterval(start: start, end: end)
    
	scheduleView.delegate = self
	 
    reloadData(ofConcreteType: MyEventType.self)
}

let allEvents: [MyEventType] = [/* Some cool events */]

func concreteEvents(in dateInterval: DateInterval, 
                    for controller: SCKViewController) -> [MyEventType] {
    let filtered = allEvents.filter {$0.scheduledDate > dateInterval.start && $0.scheduledDate <= dateInterval.end}
    return filtered 
}

The child view controller approach

  1. Add a SCKViewController instance as a child view controller of some other view controller. You can either do it programatically or add it to an Interface Builder storyboard or XIB file. If you do it in IB, be sure to add a blank NSView and configure it as its view, too.
  2. If you want to use a week view from the start (defaults is a day view), make sure you set the mode property on the SCKViewController from your own view controller before the SCKViewController's view loads.
  3. In your controller's viewWillAppear(), do the following:
    1. Set yourself as the controller's event manager.
    2. Set the initial date/time interval for your view.
    3. Optionally set yourself as the view's delegate.
    4. Add a call to either reloadData() if you are using multiple event classes or reloadData(ofConcreteType) for an easier implementation if you're using a single event class.
  4. Implement the SCKEventManaging (or SCKConcreteEventManaging) data source method to provide events to the view. The choice will depend on the reload data method you're using in step 3.4. Check the documentation to learn more.
@IBOutlet weak var scheduleController: SCKViewController!

override func viewWillAppear() {
    super.viewWillAppear()
    
    scheduleController.eventManager = self
    
    let calendar = Calendar.current
    let start = calendar.date(from: calendar.dateComponents([.yearForWeekOfYear,.weekOfYear], from: Date()))!
    let end = calendar.date(byAdding: .weekOfYear, value: 1, to: start)!
    scheduleController.scheduleView.dateInterval = DateInterval(start: start, end: end)
    
	scheduleController.scheduleView.delegate = self
	 
    scheduleController.reloadData()
}

let meetings: [Meeting] = [/* Some cool events */]
let otherEvents: [OtherEvent] = [/* Some cool events */]

func events(in dateInterval: DateInterval, 
            for controller: SCKViewController) -> [SCKEvent] {
    let filteredMeetings = meetings.filter {$0.scheduledDate > dateInterval.start && $0.scheduledDate <= dateInterval.end}
    let filteredOther = otherEvents.filter {$0.scheduledDate > dateInterval.start && $0.scheduledDate <= dateInterval.end}
    return filteredMeetings + filteredOther
}

🔷 Working with an Objective-C target

ScheduleKit is written Swift but you can use it in your Objective-C targets taking a few considerations into account:

  • You cannot use the subclassing approach, since Swift classes cannot be subclassed in Objective-C. Sorry for that :(
  • You can't set the SCKViewController's event manager using the eventManager property. Please use the -setObjCDelegate: method instead.
  • The SCKConcreteEventManaging protocol uses Swift generics and is not available.
- (void)viewWillAppear {
    [super viewWillAppear];
    
    [_scheduleController setObjCDelegate:self];
    [_scheduleController.scheduleView setDelegate:self];
    [self addChildViewController:_scheduleController];
    
    NSCalendar *cal = [NSCalendar currentCalendar];
    NSDate *today = [NSDate date];
    NSDate *dayStart = [cal dateBySettingHour:0 minute:0 second:0 ofDate:today options:0];
    NSDate *dayEnd = [cal dateBySettingHour:23 minute:59 second:59 ofDate:today options:0];
    NSDateInterval *interval = [[NSDateInterval alloc] initWithStartDate:dayStart endDate:dayEnd];
    [_scheduleController.scheduleView setDateInterval:interval];

    [_scheduleController reloadData];
}

@property (strong) IBOutlet SCKViewController * scheduleController;

Installation with Carthage

Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.

You can install Carthage with Homebrew using the following command:

$ brew update
$ brew install carthage

To integrate ScheduleKit into your Xcode project using Carthage, specify it in your cartfile:

github "gservera/ScheduleKit" "master"

Run carthage update on your project's directory to build the framework and drag the built ScheduleKit.framework into your Xcode project.

Ideas for the future

  • Ability to create an event by clicking and dragging on a region of the view (thus, setting the corresponding start and end date values), as suggested by @ronnyek

Requirements

  • Xcode: 10.0 or greater.
  • Deployment target: macOS 10.12

Unit Tests

ScheduleKit includes a suite of unit tests within the ScheduleKitTests subdirectory. These tests can be run simply be executed the test action on the platform framework you would like to test.

☕️ Author

Guillem Servera, https://gservera.com

License

ScheduleKit is released under the MIT license. See LICENSE for details.

schedulekit's People

Contributors

bryant1410 avatar gservera avatar josh- avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

schedulekit's Issues

Event properties not being updated after unlocking

In the current implementation all the event holder objects get locked (thus, they stop observing changes from their represented object) when a layout process takes place, in order to prevent unexpected changes while calculating event conflicts; and they are unlocked as soon as the layout process finishes. This seems to be a good approach when handling event objects that might change unexpectedly (eg. events fetched from a remote database)

However, despite the objects don't stay locked for more than a few miliseconds, their observed properties could actually change during this short period of time and in that case, we would not be aware of the new values and so, the event could become misplaced or even not removed from screen if necessary.

POSSIBLE FIX: Instead of stopping KVO functionality when locking events, we coud keep observing these properties and delay change propagation until the -unlock method is called. Adding a changedWhileLocked flag could also be an interesting workaround. In that case, we should eventually trigger a new relayout process in order to display the new values.

MonthView and select region for new appointment

Questions, not so much bugs...

I'm just asking... is there currently a monthview that I can use, (maybe gridview?)

Also, what about the ability to highly an empty region of the schedule component to be able to create a new appointment with the specfied start and end times?

Crash at assert(unsortedConflicts.count > 0)

Thank you for the framework, it's amazing!!
I am getting crash when trying to change event's date to the next week. Looks like commenting out the assert statement helps to fix it.
//assert(unsortedConflicts.count > 0)

assertion failed: file …Frameworks/ScheduleKit/ScheduleKit/SCKViewController.swift, line 373
Current stack trace:
0 libswiftCore.dylib 0x0000000100bdccc0 swift_reportError + 132
1 libswiftCore.dylib 0x0000000100bf9f50 _swift_stdlib_reportFatalErrorInFile + 112
2 libswiftCore.dylib 0x0000000100ba8370 partial apply for (_assertionFailed(StaticString, String, StaticString, UInt, flags : UInt32) -> Never).(closure #1).(closure #1).(closure #1) + 99
3 libswiftCore.dylib 0x00000001009f00a0 specialized specialized StaticString.withUTF8Buffer ((UnsafeBufferPointer) -> A) -> A + 355
4 libswiftCore.dylib 0x0000000100ba82b0 partial apply for (_assertionFailed(StaticString, StaticString, StaticString, UInt, flags : UInt32) -> Never).(closure #1).(closure #1) + 144
5 libswiftCore.dylib 0x00000001009f05b0 specialized specialized String._withUnsafeBufferPointerToUTF8 ((UnsafeBufferPointer) throws -> A) throws -> A + 124
6 libswiftCore.dylib 0x0000000100b4caf0 partial apply for (_assertionFailed(StaticString, String, StaticString, UInt, flags : UInt32) -> Never).(closure #1) + 185
7 libswiftCore.dylib 0x00000001009f00a0 specialized specialized StaticString.withUTF8Buffer ((UnsafeBufferPointer) -> A) -> A + 355
8 libswiftCore.dylib 0x00000001009efe80 _assertionFailed(StaticString, String, StaticString, UInt, flags : UInt32) -> Never + 144
9 ScheduleKit 0x000000010054f3f0 SCKViewController.resolvedConflicts(for : SCKEventHolder) -> [SCKEventHolder] + 833
10 ScheduleKit 0x000000010051f890 SCKGridView.invalidateLayout(for : SCKEventView) -> () + 383
11 ScheduleKit 0x0000000100539880 SCKView.invalidateLayout(for : [SCKEventView], animated : Bool) -> () + 2391
12 ScheduleKit 0x000000010053ada0 SCKView.invalidateLayoutForAllEventViews(animated : Bool) -> () + 71
13 ScheduleKit 0x000000010054d1e0 SCKViewController.parseEvents([SCKEvent]) -> () + 8546
14 ScheduleKit 0x000000010054bb60 SCKViewController.parseData(in : [SCKEvent], from : SCKEventRequest) -> () + 1040
15 ScheduleKit 0x0000000100552800 protocol witness for AsynchronousRequestParsing.parseData(in : [SCKEvent], from : SCKEventRequest) -> () in conformance SCKViewController + 57
16 ScheduleKit 0x0000000100545870 SCKEventRequest.complete(with : [SCKEvent]) -> () + 498
17 ScheduleKit 0x0000000100546590 SCKConcreteEventRequest.complete<A where ...> (with : [A1]) -> () + 122
18 MyApp 0x00000001001652f0 WeekViewController.(scheduleController(SCKViewController, didMakeConcreteEventRequest : SCKConcreteEventRequest) -> ()).(closure #1).(closure #1) + 125
19 MyApp 0x0000000100004ac0 thunk + 39
20 libdispatch.dylib 0x000000010127c6d9 _dispatch_call_block_and_release + 12
21 libdispatch.dylib 0x0000000101272f54 _dispatch_client_callout + 8
22 libdispatch.dylib 0x0000000101281a09 _dispatch_main_queue_callback_4CF + 362
23 CoreFoundation 0x00007fff9c0f0520 CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE + 9
24 CoreFoundation 0x00007fff9c0b0bd0 __CFRunLoopRun + 2205
25 CoreFoundation 0x00007fff9c0b07d0 CFRunLoopRunSpecific + 420
26 HIToolbox 0x00007fff9b63ca3c RunCurrentEventLoopInMode + 240
27 HIToolbox 0x00007fff9b63c7b1 ReceiveNextEventCommon + 432
28 HIToolbox 0x00007fff9b63c74f _BlockUntilNextEventMatchingListInModeWithFilter + 71
29 AppKit 0x00007fff99be2718 _DPSNextEvent + 1120
30 AppKit 0x00007fff9a35c77a -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 2789
31 AppKit 0x00007fff99bd715b -[NSApplication run] + 926
32 AppKit 0x00007fff99ba193f NSApplicationMain + 1237
33 MyApp 0x000000010001ca60 main + 84
34 libdyld.dylib 0x00007fffb1613254 start + 1

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.