Skip to content

Commit

Permalink
Merge pull request #26 from naneun/eddy-20-2
Browse files Browse the repository at this point in the history
[iOS] 커스텀 `CalendarPicker`를 사용해 현재 월부터의 캘린더를 띄울 수 있음
  • Loading branch information
BumgeunSong committed May 31, 2022
2 parents 744337b + b2e711f commit aa31feb
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 88 deletions.
20 changes: 12 additions & 8 deletions iOS/Targets/Cherrybnb/Sources/Common/Calendar+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,24 @@
import Foundation

extension Calendar {
func getNumberOfDaysInMonth(for basedate: Date) -> Int? {
return range(of: .day, in: .month, for: basedate)?.count
func getNumberOfDaysInMonth(for baseDate: Date) -> Int {
// month가 day보다 더 큰 component이므로 range()에서 nil이 return 되지 않음을 확신할 수 있음.
return range(of: .day, in: .month, for: baseDate)?.count ?? 0
}

func getFirstDayOfMonth(for basedate: Date) -> Date? {
return date(from: dateComponents([.year, .month], from: basedate))
func getFirstDayOfMonth(for baseDate: Date) -> Date {
// baseDateComponents는 반드시 존재하는 일자의 구성 요소이므로 nil이 return 되지 않음을 확신할 수 있음.
let baseDateComponents = dateComponents([.year, .month], from: baseDate)
return date(from: baseDateComponents) ?? baseDate
}

func getNextMonth(for basedate: Date, offset: Int) -> Date? {
return date(byAdding: .month, value: offset, to: basedate)
func getFirstDayOfMonthAfter(for baseDate: Date, offsetBy: Int) -> Date {
// 첫번째 날짜의 다음 달은 반드시 존재하므로 nil이 return 되지 않음을 확신할 수 있음.
return date(byAdding: .month, value: offsetBy, to: getFirstDayOfMonth(for: baseDate)) ?? baseDate
}

func getNextDay(for basedate: Date, offset: Int) -> Date? {
return date(byAdding: .day, value: offset, to: basedate)
func getNextDay(for baseDate: Date, offsetBy: Int) -> Date? {
return date(byAdding: .day, value: offsetBy, to: baseDate)
}

func getWeekDay(of date: Date) -> Int {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class CalendarPickerViewController: UIViewController {

collectionView.delegate = self
collectionView.dataSource = self

collectionView.isScrollEnabled = true
return collectionView
}()
Expand All @@ -35,12 +35,15 @@ class CalendarPickerViewController: UIViewController {
var didSelectDate: ((Date) -> Void)?
var didSelectDataRange: ((Range<Date>) -> Void)?

init(basedate: Date, numOfMonths: Int, didDateSelect: ((Date) -> Void)?, didDataRangeSelect: ((Range<Date>) -> Void)?) throws {
self.calendarPicker = try CalendarPicker(basedate: basedate, numOfMonths: numOfMonths)
init(baseDate: Date, numOfMonths: Int,
didDateSelect: ((Date) -> Void)? = nil,
didDataRangeSelect: ((Range<Date>) -> Void)? = nil) {
self.calendarPicker = CalendarPicker(baseDate: baseDate, numOfMonths: numOfMonths)

self.didSelectDate = didDateSelect
self.didSelectDataRange = didDataRangeSelect
super.init(nibName: nil, bundle: nil)

}

required init?(coder: NSCoder) {
Expand All @@ -52,7 +55,7 @@ class CalendarPickerViewController: UIViewController {
setSubviews()
setLayout()
}

private func setSubviews() {
view.addSubview(collectionView)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,51 +9,36 @@
import Foundation

struct CalendarPicker {

private var months: [Month]

init(basedate: Date, numOfMonths: Int) throws {
// BaseDate를 기준으로 Month 모델을 생성해서 저장.

let firstMonth = try Month(basedate: basedate)

guard let firstDayOfMonth = CalendarPicker.KRCalendar.getFirstDayOfMonth(for: basedate) else {
throw CalendarPickerError.metadataGeneration
}

let afterMonths: [Month] = try (1..<numOfMonths).map { offset in


guard let firstDayOfMonthAfter = CalendarPicker.KRCalendar.getNextMonth(for: firstDayOfMonth, offset: offset) else {
throw CalendarPickerError.offsetMonthGeneration
}

return try Month(basedate: firstDayOfMonthAfter)

init(baseDate: Date, numOfMonths: Int) {
let firstMonth = Month(baseDate: baseDate)

let afterMonths: [Month] = (1..<numOfMonths).map { offset in
let firstDayOfMonthAfter = Calendar.current.getFirstDayOfMonthAfter(for: baseDate, offsetBy: offset)
return Month(baseDate: firstDayOfMonthAfter)
}

self.months = [firstMonth] + afterMonths
}

// 전체 Month 섹션의 갯수를 리턴함.

var monthCount: Int {
return months.count
}

// 특정 Month 섹션을 모두 리턴함.

func getMonth(monthSection: Int) -> Month {
return months[monthSection]
}

// 특정 먼스 섹션의 전체 Day 갯수를 return함.

func dayCount(monthSection: Int) -> Int {
return months[monthSection].days.count
}

// 특정 먼스 섹션의 특정 Day를 리턴함.

func getDay(monthSection: Int, dayItem: Int) -> Day {
return months[monthSection].days[dayItem]
}

func select(monthSection: Int, dayItem: Int) {
// 특정 데이트를 select함.
// 그러면 해당 특정 데이트에 해당하는 Day 모델을 업데이트함.
Expand All @@ -70,7 +55,6 @@ extension CalendarPicker {
}()
}


enum CalendarPickerError: Error {
case metadataGeneration
case offsetMonthGeneration
Expand Down
44 changes: 19 additions & 25 deletions iOS/Targets/Cherrybnb/Sources/Search_Date/Model/Month.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,45 +10,39 @@ import Foundation

extension CalendarPicker {
struct Month {
var numberOfNonEmptyDays: Int
var numberOfDays: Int
let firstDay: Date
let firstDayWeekday: Int
let days: [Day]

init(basedate: Date) throws {
guard let numberOfDaysInMonth = CalendarPicker.KRCalendar.getNumberOfDaysInMonth(for: basedate),
let firstDayOfMonth = CalendarPicker.KRCalendar.getFirstDayOfMonth(for: basedate) else {
throw CalendarPickerError.metadataGeneration
}

let firstDayWeekday = CalendarPicker.KRCalendar.getWeekDay(of: firstDayOfMonth)
init(baseDate: Date) {
let numberOfDaysInMonth = Calendar.current.getNumberOfDaysInMonth(for: baseDate)
let firstDayOfMonth = Calendar.current.getFirstDayOfMonth(for: baseDate)
let firstDayWeekday = Calendar.current.getWeekDay(of: firstDayOfMonth)

self.numberOfNonEmptyDays = numberOfDaysInMonth
self.numberOfDays = numberOfDaysInMonth
self.firstDay = firstDayOfMonth
self.firstDayWeekday = firstDayWeekday

let emptyDays = (1..<firstDayWeekday).map { _ in
return Day(date: nil, isSelected: false, isPast: nil)

let daysOfLastMonth: [Day] = (1..<firstDayWeekday).reversed().map { offset in
let date = Calendar.current.getNextDay(for: firstDayOfMonth, offsetBy: -offset) ?? firstDayOfMonth
return Day(date: date, isSelected: false, isPast: true, isHidden: true)
}

let days: [Day] = (0..<numberOfNonEmptyDays).map { offset in
let date = CalendarPicker.KRCalendar.getNextDay(for: firstDayOfMonth, offset: offset) ?? firstDayOfMonth
let isPast = date < basedate
return Day(date: date, isSelected: false, isPast: isPast)
let days: [Day] = (0..<numberOfDays).map { offset in
let date = Calendar.current.getNextDay(for: firstDayOfMonth, offsetBy: offset) ?? firstDayOfMonth
let isPast = date < Calendar.current.startOfDay(for: baseDate)
return Day(date: date, isSelected: false, isPast: isPast, isHidden: false)
}

self.days = emptyDays + days
self.days = daysOfLastMonth + days
}
}

struct Day {
// Day가 나타내는 시점(일)
let date: Date?

// Calendar에서 선택되었는지 여부
let isSelected: Bool?

// 현재 이전 날짜인지 여부
let isPast: Bool?
let date: Date
let isSelected: Bool
let isPast: Bool
let isHidden: Bool
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,29 @@ class CalendarPickerViewCell: UICollectionViewCell {

func setDay(_ day: CalendarPicker.Day) {
self.day = day
guard let date = day.date, let isPast = day.isPast else { return }

let dateString = dateFormatter.string(from: date)
guard !day.isHidden else { return }

if isPast {
numberLabel.attributedText = strikethrough(dateString)
} else {
numberLabel.text = dateString
}
let dateString = dateFormatter.string(from: day.date)

numberLabel.attributedText = day.isPast ? strikethrough(dateString) : normal(dateString)
}

private func strikethrough(_ string: String) -> NSAttributedString {
let attributes: [NSAttributedString.Key: Any] = [.strikethroughStyle: NSUnderlineStyle.single.rawValue, .strikethroughColor: UIColor.systemGray, .foregroundColor: UIColor.systemGray]
let attributes: [NSAttributedString.Key: Any] = [
.strikethroughStyle: NSUnderlineStyle.single.rawValue,
.strikethroughColor: UIColor.systemGray,
.foregroundColor: UIColor.systemGray]

return NSAttributedString(string: string, attributes: attributes)

}

private func normal(_ string: String) -> NSAttributedString {
let attributes: [NSAttributedString.Key: Any] = [
.font: UIFont.systemFont(ofSize: 18, weight: .medium),
.strikethroughStyle: "nil",
.foregroundColor: UIColor.black]

return NSAttributedString(string: string, attributes: attributes)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ import UIKit

class LocationCell: UICollectionViewCell {
static let reuseIdentifier = String(describing: LocationCell.self)

lazy var imageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.image = UIImage(systemName: "mappin.and.ellipse")
imageView.layer.cornerRadius = 10
return imageView
}()

lazy var titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .systemFont(ofSize: 17)
return label
}()

override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(imageView)
Expand All @@ -36,16 +36,16 @@ class LocationCell: UICollectionViewCell {
required init?(coder: NSCoder) {
super.init(coder: coder)
}
private func setLayout(){

private func setLayout() {
NSLayoutConstraint.activate([
imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
imageView.topAnchor.constraint(equalTo: contentView.topAnchor),
imageView.widthAnchor.constraint(equalToConstant: 64),
imageView.heightAnchor.constraint(equalToConstant: 64),
imageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
])

NSLayoutConstraint.activate([
titleLabel.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 16),
titleLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
import UIKit

class PlaceCell: UICollectionViewCell {

static let reuseIdentifier = String(describing: PlaceCell.self)

lazy var imageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
Expand All @@ -20,15 +20,15 @@ class PlaceCell: UICollectionViewCell {
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()

lazy var nameLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .systemFont(ofSize: 17)
label.text = "서울"
return label
}()

lazy var distanceLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
Expand Down Expand Up @@ -58,7 +58,7 @@ class PlaceCell: UICollectionViewCell {
imageView.heightAnchor.constraint(equalToConstant: 64),
imageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
])

NSLayoutConstraint.activate([
nameLabel.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 16),
nameLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
Expand Down
5 changes: 3 additions & 2 deletions iOS/Targets/Cherrybnb/Sources/Support/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
let storyboard = UIStoryboard(name: "Main", bundle: .main)
let viewController = storyboard.instantiateInitialViewController()
// let storyboard = UIStoryboard(name: "Main", bundle: .main)
// let viewController = storyboard.instantiateInitialViewController()

let viewController = CalendarPickerViewController(baseDate: Date(), numOfMonths: 12)
window?.rootViewController = viewController
window?.makeKeyAndVisible()

Expand Down
Loading

0 comments on commit aa31feb

Please sign in to comment.