Code Monkey home page Code Monkey logo

infinitecalendar's Introduction

InfiniteCalendar

Google Calendar-like infinite scrollable calendar for SwiftUI.

Twitter Swift Version SPM compatible License

InfiniteCalendar is infinite scrollable calendar for iOS written in Swift.

UI/UX design inspired by GoogleCalendar. Implementation inspired by JZCalendarWeekView.



Features

  • Infinite scroll
  • Multiple scroll type
  • Custamazable UI
  • Support long tap gesture actions
  • Support autoScroll for drag
  • Support handling multiple actions
  • Support vibrate feedback like GoogleCalendar

Requirements

  • Swift 5.1
  • iOS 13.0 or later

Installation

Swift Package Manager

InfiniteCalendar is available through Swift Package Manager.

Add it to an existing Xcode project as a package dependency:

  1. From the File menu, select Add Packages…
  2. Enter "https://github.com/shohe/InfiniteCalendar.git" into the package repository URL text field

Documentation

1. Initialization

You need to define View, ViewModel and CollectionViewCell for display cell on Calendar:

  1. Create View complianted for CellableView protocol:
struct EventCellView: CellableView {
    typealias VM = ViewModel

    // MARK: ViewModel
    struct ViewModel: ICEventable { ... }


    // MARK: View
    var viewModel: ViewModel
    
    init(_ viewModel: ViewModel) {
        self.viewModel = viewModel
    }
    
    var body: some View {
        ...
    }
}
  1. Implement ViewModel complianted for ICEventable protocol:
struct ViewModel: ICEventable { 
    private(set) var id: String = UUID().uuidString
    var text: String

    var startDate: Date
    var endDate: Date?
    var intraStartDate: Date
    var intraEndDate: Date
    var editState: EditState?
    var isAllDay: Bool
    
    
    init(text: String, start: Date, end: Date?, isAllDay: Bool = false, editState: EditState? = nil) {
        ...
    }
    
    // ! Make sure copy current object, otherwise view won't display properly when SwiftUI View is updated.
    func copy() -> EventCellView.ViewModel {
        var copy = ViewModel(text: text, start: startDate, end: endDate, isAllDay: isAllDay, editState: editState)
        ...
        return copy
    }
    
    static func create(from eventable: EventCellView.ViewModel?, state: EditState?) -> EventCellView.ViewModel {
        if var model = eventable?.copy() {
            model.editState = state
            return model
        }
        return ViewModel(text: "New", start: Date(), end: nil, editState: state)
    }
}
  1. Create CollectionViewCell complianted for ViewHostingCell protocol:
final class EventCell: ViewHostingCell<EventCellView> {
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
}
  1. Use InfiniteCalendar with CustomViews:
struct ContentView: View {
    @State var events: [EventCellView.VM] = []
    @State var didTapToday: Bool = false
    @ObservedObject var settings: ICViewSettings = ICViewSettings(
        numOfDays: 3, 
        initDate: Date(), 
        scrollType: .sectionScroll, 
        moveTimeMinInterval: 15, 
        timeRange: (1, 23), 
        withVibration: true
    )

    var body: some View {
        InfiniteCalendar<EventCellView, EventCell, ICViewSettings>(events: $events, settings: settings, didTapToday: $didTapToday)
    }
}

2. Handling

.onCurrentDateChanged

This method will be called when changed currrent date displayed on calendar. The date can be get is the leftest date on current display.

ex.) Display 3 column dates, 4/1 | 4/2 | 4/3 -> 4/1 can be obtained.

InfiniteCalendar<EventCellView, EventCell, Settings>(events: $events, settings: settings, didTapToday: $didTapToday)
.onCurrentDateChanged { date in
    currentDate = date
}

.onItemSelected

This method will be called when item was tapped.

InfiniteCalendar<EventCellView, EventCell, Settings>(events: $events, settings: settings, didTapToday: $didTapToday)
.onItemSelected { item in
    selectedItem = item
}

.onEventAdded

This method will be called when item was created by long tap with drag gesture.

InfiniteCalendar<EventCellView, EventCell, Settings>(events: $events, settings: settings, didTapToday: $didTapToday)
.onEventAdded { item in
    events.append(item)
}

.onEventMoved

This method will be called when item was created by long tap gesture on exist item.

InfiniteCalendar<EventCellView, EventCell, Settings>(events: $events, settings: settings, didTapToday: $didTapToday)
.onEventMoved { item in
    if let index = events.firstIndex(where: {$0.id == item.id}) {
        events[index] = item
    }
}

.onEventCanceled

This method will be called when canceled gesture event by some accident or issues.

InfiniteCalendar<EventCellView, EventCell, Settings>(events: $events, settings: settings, didTapToday: $didTapToday)
.onEventCanceled { item in
    print("Canceled some event gesture for \(item.id).")
}

3. Settings

If you want to customize Settings, create SubClass of ICViewSettings.

numOfDays

Number of dispaly dates on a screen

Sample: numOfDays = 1, numOfDays = 3, numOfDays = 7

datePosition

The display position of Date only for One-day layout.

Sample: datePosition = .top, datePosition = .left

initDate

The display date for lounch app

scrollType

There is two kinds of scroll type, Section and Page.

SectionType will deside scroll amount by scroll velocity. On the other hand PageType is always scroll to next / prev page with scroll gesture.

moveTimeMinInterval

Interval minutes time for drag gesture. Default value is 15. Which means when item was created/moved time will move every 15 minutes

timeRange

The display time on time header as a label. Default value is (1, 23). Which means display 1:00 ~ 23:00.

withVibrateFeedback

If vibration is needed during dragging gesture. Default value is true. Vibration feedback is almost same as GoogleCalendar.


4. Custom UI components

You can customize each components on the bellow.

  • SupplementaryCell (Use as SupplementaryCell in CollectionView)
    • TimeHeader
    • DateHeader
    • DateHeaderCorner
    • AllDayHeader
    • AllDayHeaderCorner
    • Timeline
  • DecorationCell (Use as DecorationCell in CollectionView)
    • TimeHeaderBackground
    • DateHeaderBackground
    • AllDayHeaderBackground

Associatedtypes

Component class is define as typealias to customize.

//* When you customize, set two of classes to custom class you created.
// TimeHeader 
associatedtype TimeHeaderView: ICTimeHeaderView
associatedtype TimeHeader: ICTimeHeader<TimeHeaderView>

// DateHeader
associatedtype DateHeaderView: ICDateHeaderView
associatedtype DateHeader: ICDateHeader<DateHeaderView>

// DateHeaderCorner
associatedtype DateCornerView: ICDateCornerView
associatedtype DateCorner: ICDateCorner<DateCornerView>

// AllDayHeader
associatedtype AllDayHeaderView: ICAllDayHeaderView
associatedtype AllDayHeader: ICAllDayHeader<AllDayHeaderView>

// AllDayHeaderCorner
associatedtype AllDayCornerView: ICAllDayCornerView
associatedtype AllDayCorner: ICAllDayCorner<AllDayCornerView>

// Timeline
associatedtype TimelineView: ICTimelineView
associatedtype Timeline: ICTimeline<TimelineView>

// TimeHeaderBackground
associatedtype TimeHeaderBackgroundView: ICTimeHeaderBackgroundView
associatedtype TimeHeaderBackground: ICTimeHeaderBackground<TimeHeaderBackgroundView>

// DateHeaderBackground
associatedtype DateHeaderBackgroundView: ICDateHeaderBackgroundView
associatedtype DateHeaderBackground: ICDateHeaderBackground<DateHeaderBackgroundView>

// AllDayHeaderBackground
associatedtype AllDayHeaderBackgroundView: ICAllDayHeaderBackgroundView
associatedtype AllDayHeaderBackground: ICAllDayHeaderBackground<AllDayHeaderBackgroundView>

Sample custom component (DateHeader)

All you need is 4 steps.

  1. Create CustomView and Cell for wrap the View
  2. Create CustomSetting class
  3. Set the typealiases of each classes to View and Cell you created
  4. Set the CustomSetting class to InfiniteCalendar

e.g. Customize DateHeader component.
1. Create CustomView and Cell for wrap the View
// DateHeader should be inherited `ICDateHeader`.
class CustomDateHeader: ICDateHeader<CustomDateHeaderView> {}

// CustomView should be inherited `ICDateHeaderView`.
struct CustomDateHeaderView: ICDateHeaderView {
    public typealias Item = ICDateHeaderItem

    var item: Item
    
    public init(_ item: Item) {
        self.item = item
    }
    
    public var body: some View {
        ...
    }
}
2. Create CustomSetting class

Create SubClass inherited ICSettings.

class CustomSettings: ICSettings {
    @Published public var numOfDays: Int = 1
    @Published public var initDate: Date = Date()
    @Published public var scrollType: ScrollType = .pageScroll
    @Published public var moveTimeMinInterval: Int = 15
    @Published public var timeRange: (startTime: Int, endTime: Int) = (1, 23)
    @Published public var withVibrateFeedback: Bool = true
    
    required public init() {}
    
    ...
}
3. Set the typealiases of each classes to View and Cell you created
class CustomSettings: ICSettings {
    typealias DateHeaderView = CustomDateHeaderView
    typealias DateHeader = CustomDateHeader
    ...
}
4. Set the CustomSetting class to InfiniteCalendar
...

@State var events: [EventCellView.VM] = SampleData().events
@State var didTapToday: Bool = false
@ObservedObject var settings: CustomSettings = CustomSettings()

var body: some View {
    InfiniteCalendar<EventCellView, EventCell, CustomSettings>(events: $events, settings: settings, didTapToday: $didTapToday)
}

...

License

InfiniteCalendar is available under the MIT license. See the LICENSE file for more info.

infinitecalendar's People

Contributors

shohe avatar max1hale avatar

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.