'*** -[__NSSingleObjectArrayI objectAtIndex:]:索引 1 超出范围 [0 .. 0]'
'*** -[__NSSingleObjectArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
从swift3升级到swift4后,xcode9升级到xcode10.1,点击一个按钮应用程序停止工作(在swift3和xcode9中工作正常)
并给出警告:
reason: '*** -[__NSSingleObjectArrayI objectAtIndex:]:
index 1 beyond bounds [0 .. 0]'
和警告:
libc++abi.dylib: terminating with uncaught exception of type NSException
我用谷歌搜索并阅读了这些答案:
'NSRangeException', reason: '* -[__NSArrayM objectAtIndex:]: index 2 beyond bounds [0 .. 1]'
iOS error: [__NSArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]
https://github.com/invertase/react-native-firebase/issues/313
但是我无法理解答案以及导致问题的原因。
我也尝试将我的按钮重新连接到代码,但 working.I 不需要更精确的错误在哪里以及如何修复。
我得到了什么:
在点击按钮之前,记录器已经给出:
Failed to set (placeholderSpacing) user defined inspected property on
(SkyFloatingLabelTextField.SkyFloatingLabelTextField):
[<SkyFloatingLabelTextField.SkyFloatingLabelTextField 0x10684f200>
setValue:forUndefinedKey:]: this class is not key value coding-
compliant for the key placeholderSpacing.
点击按钮后,应用程序停止工作,我没有收到任何错误(红色),但在调试器中:
*** Terminating app due to uncaught exception 'NSRangeException',
reason: '*** -[__NSSingleObjectArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
*** First throw call stack:
(0x1..330ec4 .... 0x1d..7abb4)
libc++abi.dylib: terminating with uncaught exception of type NSException
代码在 AppDelegate.swift 的这一行停止 运行:
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
调试器中的一些其他信息显示 XCGLogger.swift:
这一行中的问题
/// Option: a closure to execute whenever a logging method is called without a log message
open var noMessageClosure: () -> Any? = { return "" }
试过答案后:
我得到了一些与 XCGLogger 相关的 return 日志。请参阅所附图片。
这是我点击的按钮:
这是按钮的代码 activity:
@IBAction func btnCheckInTapped(_ sender: UIButton) {
if let vc = UIStoryboard.bookingPeriodVC() as? BookingPeriodVC{
vc.delegate = self
vc.modalTransitionStyle = .crossDissolve
vc.modalPresentationStyle = .overCurrentContext
tabBarController?.present(vc, animated: true, completion: nil)
}
}
故事板名称:EAN 场景
BookingPeriodVC.swift代码:
import UIKit
import FSCalendar
import Device
@objc protocol SkyCalendarDelegate: class {
@objc optional func didSelectDates(dates:[String])
@objc optional func didSelectDateAndTime(dateTime: [String])
}
class BookingPeriodVC: UIViewController {
fileprivate let currentCalendar = Calendar.current
@objc weak var delegate: SkyCalendarDelegate?
fileprivate var startSelectingDate: Bool = false {
didSet {
if calendar.getUserSelectedDate.count >= 2 {
startDateValueLbl.text = calendar.getUserSelectedDate[0]
endDateValueLbl.text = calendar.getUserSelectedDate[1]
} else {
if let firstDate = calendar.getUserSelectedDate.first {
startDateValueLbl.text = firstDate
}else {
startDateValueLbl.text = ""
}
endDateValueLbl.text = ""
}
updateConstraintForDateLabels()
}
}
fileprivate var currentSelectedDates:[Date] = [] // Temp var
fileprivate var lastSelectedDate: Date = Date()
@IBOutlet weak var containerView: UIView!
@IBOutlet weak var startDateValueLbl: UILabel!
@IBOutlet weak var endDateValueLbl: UILabel!
@IBOutlet weak var calendarContainerView: UIView!
@IBOutlet weak var previousBtn: UIButton!
@IBOutlet weak var nextBtn: UIButton!
@IBOutlet weak var calendarHeight: NSLayoutConstraint! {
didSet {
calendarHeight.constant = Device.size() == .screen3_5Inch ? 250 : 280
}
}
@IBOutlet weak var startDateTopConstraint: NSLayoutConstraint!
@IBOutlet weak var endDateTopConstraint: NSLayoutConstraint!
@objc lazy var calendar: FSCalendar = {
let calender = FSCalendar()
calender.translatesAutoresizingMaskIntoConstraints = false
calender.scope = .month
calender.delegate = self
calender.dataSource = self
calender.swipeToChooseGesture.isEnabled = true
calender.allowsMultipleSelection = true
// First Row - DEC 2017
calender.appearance.headerTitleFont = FontBook.Bold.of(size: 14)
calender.appearance.headerTitleColor = Color.Tuna.instance()
calender.appearance.headerMinimumDissolvedAlpha = 0
calender.appearance.headerDateFormat = "MMMM yyyy"
// Second Row
calender.appearance.weekdayTextColor = Color.heather.instance()
calender.appearance.weekdayFont = FontBook.Bold.of(size: 9)
// day text color
calender.appearance.titleDefaultColor = Color.Tuna.instance()
calender.appearance.titleFont = FontBook.Bold.of(size: 14)
// Uppercase text for first and second row
calender.appearance.caseOptions = [.headerUsesUpperCase,.weekdayUsesUpperCase]
calender.today = nil
calender.register(SkyCalendarCell.self, forCellReuseIdentifier: "cell")
calender.headerHeight = 50
//calender.pagingEnabled = true
calender.scrollEnabled = false
calender.hero.isEnabled = true
//calender.heroID = "calendar"
calender.backgroundColor = .clear
calender.subviews[1].backgroundColor = .white
return calender
}()
override func viewDidLoad() {
super.viewDidLoad()
commonInit()
}
@objc func commonInit() {
setupCalendar()
startDateValueLbl.text = ""
endDateValueLbl.text = ""
}
@IBAction func didPressDoneBtn(_ sender: UIButton) {
if calendar.getUserSelectedDate.count >= 2 {
delegate?.didSelectDates?(dates: calendar.getUserSelectedDate)
dismiss(animated: true, completion: nil)
return
}
print("Select start and end date")
}
@IBAction func didPressPrevNextCalendarBtn(_ sender : UIButton) {
let tag = sender.tag
doTimeTravel(tag)
}
@IBAction func didPressBackBtn(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
@IBAction func didPressClearBtn(_ sender: UIButton) {
if calendar.selectedDates.count > 0 {
calendar.selectedDates.forEach { calendar.deselect([=19=]) }
configureVisibleCells()
startSelectingDate = false
didSetDayScope = false
calendar.reloadData()
}
}
// is called when user tap on previous / next button of calendar
@objc func doTimeTravel(_ tag: Int){
calendar.scrollEnabled = true
tag == 0 ? setPreviousAndNextForCurrentCalendarScope(value: -1) : setPreviousAndNextForCurrentCalendarScope(value: 1)
configureVisibleCells()
disablePanOnCalendar()
}
private func setupCalendar() {
calendarContainerView.addSubview(calendar)
calendar.topAnchor.constraint(equalTo: calendarContainerView.topAnchor).isActive = true
calendar.leftAnchor.constraint(equalTo: calendarContainerView.leftAnchor).isActive = true
calendar.rightAnchor.constraint(equalTo: calendarContainerView.rightAnchor).isActive = true
calendar.bottomAnchor.constraint(equalTo: calendarContainerView.bottomAnchor).isActive = true
previousBtn.leftAnchor.constraint(equalTo: calendar.leftAnchor, constant: 25).isActive = true
previousBtn.centerYAnchor.constraint(equalTo: calendar.calendarHeaderView.centerYAnchor).isActive = true
nextBtn.rightAnchor.constraint(equalTo: calendar.rightAnchor, constant: -25).isActive = true
nextBtn.centerYAnchor.constraint(equalTo: calendar.calendarHeaderView.centerYAnchor).isActive = true
calendarContainerView.bringSubview(toFront: nextBtn)
calendarContainerView.bringSubview(toFront: previousBtn)
}
@objc func updateConstraintForDateLabels() {
startDateTopConstraint.constant = 10
endDateTopConstraint.constant = 10
startDateValueLbl.isHidden = false
endDateValueLbl.isHidden = false
UIView.animate(withDuration: 0.4) {
self.view.layoutIfNeeded()
}
}
@objc var didSetDayScope = false
}
extension BookingPeriodVC: FSCalendarDataSource {
func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell {
let cell = calendar.dequeueReusableCell(withIdentifier: "cell", for: date, at: position)
return cell
}
func calendar(_ calendar: FSCalendar, willDisplay cell: FSCalendarCell, for date: Date, at position: FSCalendarMonthPosition) {
self.configure(cell: cell, for: date, at: position)
}
func minimumDate(for calendar: FSCalendar) -> Date {
if startSelectingDate {
return lastSelectedDate
}
return Date()
}
func maximumDate(for calendar: FSCalendar) -> Date {
let cldr = Calendar.current
if startSelectingDate {
return cldr.date(byAdding: .day, value: 28, to: calendar.selectedDate!) ?? Date()
}
return cldr.date(byAdding: .day, value: 499, to: Date()) ?? Date()
}
func calendar(_ calendar: FSCalendar, shouldSelect date: Date, at monthPosition: FSCalendarMonthPosition) -> Bool {
return monthPosition == .current
}
func calendar(_ calendar: FSCalendar, shouldDeselect date: Date, at monthPosition: FSCalendarMonthPosition) -> Bool {
return monthPosition == .current
}
}
/*
* Responsible For drawing circle for selected dates
*/
extension BookingPeriodVC {
@objc func configureVisibleCells() {
calendar.visibleCells().forEach { (cell) in
let date = calendar.date(for: cell)
let position = calendar.monthPosition(for: cell)
self.configure(cell: cell, for: date!, at: position)
}
}
// marking cell when user select or drag on day
@objc func configure(cell: FSCalendarCell, for date: Date, at position: FSCalendarMonthPosition) {
let skyCell = cell as! SkyCalendarCell
if position == .current {
var selectionType = SelectionType.none
if calendar.selectedDates.contains(date) {
let previousDate = self.currentCalendar.date(byAdding: .day, value: -1, to: date)!
let nextDate = self.currentCalendar.date(byAdding: .day, value: 1, to: date)!
if calendar.selectedDates.contains(date) {
if calendar.selectedDates.contains(previousDate) && calendar.selectedDates.contains(nextDate) {
selectionType = .middle
}
else if calendar.selectedDates.contains(previousDate) && calendar.selectedDates.contains(date) {
selectionType = .rightBorder
}
else if calendar.selectedDates.contains(nextDate) {
selectionType = .leftBorder
}
else {
selectionType = .single
}
}
}else {
selectionType = .none
}
if selectionType == .none {
skyCell.selectionLayer.isHidden = true
return
}
skyCell.selectionLayer.isHidden = false
skyCell.selectionType = selectionType
} else {
skyCell.selectionLayer.isHidden = true
}
}
}
extension BookingPeriodVC: FSCalendarDelegate {
func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
self.configureVisibleCells()
if calendar.selectedDates.count > 1 {
selectAllDateBetween(start: lastSelectedDate, end: date)
}
lastSelectedDate = date
startSelectingDate = true
if startSelectingDate && !didSetDayScope {
calendar.reloadData()
didSetDayScope = true
}
}
func calendar(_ calendar: FSCalendar, didDeselect date: Date) {
deselectDateStartingFrom(date: date, selectedDates: calendar.selectedDates)
lastSelectedDate = currentCalendar.date(byAdding: .day, value: -1, to: date) ?? date
configureVisibleCells()
startSelectingDate = false
if calendar.selectedDates.count < 1 {
didSetDayScope = false
calendar.reloadData()
}
}
func calendarCurrentPageDidChange(_ calendar: FSCalendar) {
let selectedcomponents = self.currentCalendar.dateComponents([.month], from: calendar.currentPage)
let currentComponents = self.currentCalendar.dateComponents([.month], from: Date())
guard let selectedMonth = selectedcomponents.month, let currentMonth = currentComponents.month else { return }
if calendar.currentPage > Date() {
previousBtn.isEnabled = true
}else{
if calendar.scope == .week {
if currentMonth == selectedMonth {
previousBtn.isEnabled = true
}else{
previousBtn.isEnabled = false
}
}else{
previousBtn.isEnabled = false
}
}
}
}
/*
* Responsible for auto selecting/deselecting dates which is between start and end dates
*/
extension BookingPeriodVC {
@objc func changeCalendarScope(scope: FSCalendarScope ) {
self.calendar.setScope(scope, animated: true)
}
@objc func deselectDateStartingFrom(date: Date,selectedDates: [Date]) {
let dates = selectedDates.sorted { [=19=].compare() == .orderedAscending }
dates.forEach {
if [=19=] > date {
calendar.deselect([=19=])
}
}
}
fileprivate func selectAllDateBetween(start: Date, end: Date) {
getMiddleDays(start: start, end: end).forEach {
calendar.select([=19=])
configureVisibleCells() // make it out of this scope
}
}
fileprivate func getMiddleDays(start: Date, end: Date) -> [Date] {
var fromDate = start
let toDate = end
var middleDates: [Date] = []
if fromDate < toDate {
while fromDate < toDate {
if let nextDay = currentCalendar.date(byAdding: .day, value: 1, to: fromDate){
middleDates.append(nextDay)
fromDate = nextDay
}
}
}else {
while fromDate > toDate {
if let previousDay = currentCalendar.date(byAdding: .day, value: -1, to: fromDate){
middleDates.append(previousDay)
fromDate = previousDay
}
}
}
return middleDates
}
fileprivate func disablePanOnCalendar(){
DispatchQueue.main.asyncAfter(deadline: .now()+0.5) {
self.calendar.scrollEnabled = false
}
}
fileprivate func setPreviousAndNextForCurrentCalendarScope(value: Int) {
if calendar.scope == .month {
let date = currentCalendar.date(byAdding: .month, value: value, to:calendar.currentPage)!
calendar.setCurrentPage(date, animated: true)
}else {
let date = currentCalendar.date(byAdding: .weekOfMonth, value: value, to:calendar.currentPage)!
calendar.setCurrentPage(date, animated: true)
}
}
}
extension FSCalendar {
@objc var getUserSelectedDate: [String] {
var userSelectedDates:[Date] = []
if selectedDates.count >= 2 {
userSelectedDates = selectedDates.sorted { [=19=].compare() == .orderedAscending }
guard let firstDate = userSelectedDates.first, let lastDate = userSelectedDates.last else {
return []
}
return [firstDate,lastDate].map{ DateFormatter.getDateFor(type: .ddMMMyyyyE, date: [=19=]) }
}
return selectedDates.map{ DateFormatter.getDateFor(type: .ddMMMyyyyE, date: [=19=]) }
}
}
enum DateFormatType: String {
case dmy = "d MMM yyyy" // 5 Dec 2017
case hmi = "h:mm a" // 12:20 AM
case ddm = "E, dd MMM" // Tue 02 Dec
case hm = "HH,mm" // 03:02
case m = "M,y" // 1
case ddMMMyyyyE = "dd MMM yyyy (E)"
case yyyyMMdd = "yyyy-MM-dd"
}
extension DateFormatter {
static func getDateFor(type: DateFormatType, date: Date) -> String {
let formatter = DateFormatter()
switch type {
case .dmy:
formatter.dateFormat = type.rawValue
case .hmi:
formatter.dateFormat = type.rawValue
case .ddm:
formatter.dateFormat = type.rawValue
case .hm:
formatter.dateFormat = type.rawValue
case .m:
formatter.dateFormat = type.rawValue
case .ddMMMyyyyE:
formatter.dateFormat = type.rawValue
case .yyyyMMdd:
formatter.dateFormat = type.rawValue
}
return formatter.string(from: date)
}
}
奇怪的是我没有收到红色错误,日志消息让我很困惑。我是 iOS 的新手,请帮助我并准确说明我应该在什么地方解决这个问题。非常感谢。
编辑:
最终解决方案:
改变
calender.subviews[1].backgroundColor = .white
至
calender.subviews.count >= 2 {
calender.subviews[1].backgroundColor = .white
}
let vc: BookingPeriodVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ViewController") as BookingPeriodVC
vc.delegate = self
vc.modalTransitionStyle = .crossDissolve
vc.modalPresentationStyle = .overCurrentContext
self.presentViewController(vc, animated: false, completion: nil)
喜欢这样...
你的问题在startSelectingDate
首先像这样
实例化你的 viewController
let vc: BookingPeriodVC = UIStoryboard(name: "EAN Scene", bundle: nil).instantiateViewControllerWithIdentifier("identifier for your controller") as BookingPeriodVC
if let vc = UIStoryboard.bookingPeriodVC() as? BookingPeriodVC{
vc.delegate = self
vc.modalTransitionStyle = .crossDissolve
vc.modalPresentationStyle = .overCurrentContext
tabBarController?.present(vc, animated: true, completion: nil)
}
其次 calender.subviews[1].backgroundColor = .white
检查是否
calender.subviews.count >= 2 {
calender.subviews[1].backgroundColor = .white
}
第三,你试图在下面的行中访问 selectedDates 的第一个元素会让你崩溃,因为你的 calendar.getUserSelectedDate
数组是空的
当你发起 viewController
if let firstDate = calendar.getUserSelectedDate.first
你必须像
这样打勾
if let calenderDates = calendar.getUserSelectedDate{
if calenderDates.count>0{
firstDate = calendar.getUserSelectedDate.first
}
}
从swift3升级到swift4后,xcode9升级到xcode10.1,点击一个按钮应用程序停止工作(在swift3和xcode9中工作正常) 并给出警告:
reason: '*** -[__NSSingleObjectArrayI objectAtIndex:]:
index 1 beyond bounds [0 .. 0]'
和警告:
libc++abi.dylib: terminating with uncaught exception of type NSException
我用谷歌搜索并阅读了这些答案:
'NSRangeException', reason: '* -[__NSArrayM objectAtIndex:]: index 2 beyond bounds [0 .. 1]'
iOS error: [__NSArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]
https://github.com/invertase/react-native-firebase/issues/313
但是我无法理解答案以及导致问题的原因。 我也尝试将我的按钮重新连接到代码,但 working.I 不需要更精确的错误在哪里以及如何修复。
我得到了什么: 在点击按钮之前,记录器已经给出:
Failed to set (placeholderSpacing) user defined inspected property on
(SkyFloatingLabelTextField.SkyFloatingLabelTextField):
[<SkyFloatingLabelTextField.SkyFloatingLabelTextField 0x10684f200>
setValue:forUndefinedKey:]: this class is not key value coding-
compliant for the key placeholderSpacing.
点击按钮后,应用程序停止工作,我没有收到任何错误(红色),但在调试器中:
*** Terminating app due to uncaught exception 'NSRangeException',
reason: '*** -[__NSSingleObjectArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
*** First throw call stack:
(0x1..330ec4 .... 0x1d..7abb4)
libc++abi.dylib: terminating with uncaught exception of type NSException
代码在 AppDelegate.swift 的这一行停止 运行:
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
调试器中的一些其他信息显示 XCGLogger.swift:
这一行中的问题/// Option: a closure to execute whenever a logging method is called without a log message
open var noMessageClosure: () -> Any? = { return "" }
试过答案后:
我得到了一些与 XCGLogger 相关的 return 日志。请参阅所附图片。
这是我点击的按钮:
这是按钮的代码 activity:
@IBAction func btnCheckInTapped(_ sender: UIButton) {
if let vc = UIStoryboard.bookingPeriodVC() as? BookingPeriodVC{
vc.delegate = self
vc.modalTransitionStyle = .crossDissolve
vc.modalPresentationStyle = .overCurrentContext
tabBarController?.present(vc, animated: true, completion: nil)
}
}
故事板名称:EAN 场景
BookingPeriodVC.swift代码:
import UIKit
import FSCalendar
import Device
@objc protocol SkyCalendarDelegate: class {
@objc optional func didSelectDates(dates:[String])
@objc optional func didSelectDateAndTime(dateTime: [String])
}
class BookingPeriodVC: UIViewController {
fileprivate let currentCalendar = Calendar.current
@objc weak var delegate: SkyCalendarDelegate?
fileprivate var startSelectingDate: Bool = false {
didSet {
if calendar.getUserSelectedDate.count >= 2 {
startDateValueLbl.text = calendar.getUserSelectedDate[0]
endDateValueLbl.text = calendar.getUserSelectedDate[1]
} else {
if let firstDate = calendar.getUserSelectedDate.first {
startDateValueLbl.text = firstDate
}else {
startDateValueLbl.text = ""
}
endDateValueLbl.text = ""
}
updateConstraintForDateLabels()
}
}
fileprivate var currentSelectedDates:[Date] = [] // Temp var
fileprivate var lastSelectedDate: Date = Date()
@IBOutlet weak var containerView: UIView!
@IBOutlet weak var startDateValueLbl: UILabel!
@IBOutlet weak var endDateValueLbl: UILabel!
@IBOutlet weak var calendarContainerView: UIView!
@IBOutlet weak var previousBtn: UIButton!
@IBOutlet weak var nextBtn: UIButton!
@IBOutlet weak var calendarHeight: NSLayoutConstraint! {
didSet {
calendarHeight.constant = Device.size() == .screen3_5Inch ? 250 : 280
}
}
@IBOutlet weak var startDateTopConstraint: NSLayoutConstraint!
@IBOutlet weak var endDateTopConstraint: NSLayoutConstraint!
@objc lazy var calendar: FSCalendar = {
let calender = FSCalendar()
calender.translatesAutoresizingMaskIntoConstraints = false
calender.scope = .month
calender.delegate = self
calender.dataSource = self
calender.swipeToChooseGesture.isEnabled = true
calender.allowsMultipleSelection = true
// First Row - DEC 2017
calender.appearance.headerTitleFont = FontBook.Bold.of(size: 14)
calender.appearance.headerTitleColor = Color.Tuna.instance()
calender.appearance.headerMinimumDissolvedAlpha = 0
calender.appearance.headerDateFormat = "MMMM yyyy"
// Second Row
calender.appearance.weekdayTextColor = Color.heather.instance()
calender.appearance.weekdayFont = FontBook.Bold.of(size: 9)
// day text color
calender.appearance.titleDefaultColor = Color.Tuna.instance()
calender.appearance.titleFont = FontBook.Bold.of(size: 14)
// Uppercase text for first and second row
calender.appearance.caseOptions = [.headerUsesUpperCase,.weekdayUsesUpperCase]
calender.today = nil
calender.register(SkyCalendarCell.self, forCellReuseIdentifier: "cell")
calender.headerHeight = 50
//calender.pagingEnabled = true
calender.scrollEnabled = false
calender.hero.isEnabled = true
//calender.heroID = "calendar"
calender.backgroundColor = .clear
calender.subviews[1].backgroundColor = .white
return calender
}()
override func viewDidLoad() {
super.viewDidLoad()
commonInit()
}
@objc func commonInit() {
setupCalendar()
startDateValueLbl.text = ""
endDateValueLbl.text = ""
}
@IBAction func didPressDoneBtn(_ sender: UIButton) {
if calendar.getUserSelectedDate.count >= 2 {
delegate?.didSelectDates?(dates: calendar.getUserSelectedDate)
dismiss(animated: true, completion: nil)
return
}
print("Select start and end date")
}
@IBAction func didPressPrevNextCalendarBtn(_ sender : UIButton) {
let tag = sender.tag
doTimeTravel(tag)
}
@IBAction func didPressBackBtn(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
@IBAction func didPressClearBtn(_ sender: UIButton) {
if calendar.selectedDates.count > 0 {
calendar.selectedDates.forEach { calendar.deselect([=19=]) }
configureVisibleCells()
startSelectingDate = false
didSetDayScope = false
calendar.reloadData()
}
}
// is called when user tap on previous / next button of calendar
@objc func doTimeTravel(_ tag: Int){
calendar.scrollEnabled = true
tag == 0 ? setPreviousAndNextForCurrentCalendarScope(value: -1) : setPreviousAndNextForCurrentCalendarScope(value: 1)
configureVisibleCells()
disablePanOnCalendar()
}
private func setupCalendar() {
calendarContainerView.addSubview(calendar)
calendar.topAnchor.constraint(equalTo: calendarContainerView.topAnchor).isActive = true
calendar.leftAnchor.constraint(equalTo: calendarContainerView.leftAnchor).isActive = true
calendar.rightAnchor.constraint(equalTo: calendarContainerView.rightAnchor).isActive = true
calendar.bottomAnchor.constraint(equalTo: calendarContainerView.bottomAnchor).isActive = true
previousBtn.leftAnchor.constraint(equalTo: calendar.leftAnchor, constant: 25).isActive = true
previousBtn.centerYAnchor.constraint(equalTo: calendar.calendarHeaderView.centerYAnchor).isActive = true
nextBtn.rightAnchor.constraint(equalTo: calendar.rightAnchor, constant: -25).isActive = true
nextBtn.centerYAnchor.constraint(equalTo: calendar.calendarHeaderView.centerYAnchor).isActive = true
calendarContainerView.bringSubview(toFront: nextBtn)
calendarContainerView.bringSubview(toFront: previousBtn)
}
@objc func updateConstraintForDateLabels() {
startDateTopConstraint.constant = 10
endDateTopConstraint.constant = 10
startDateValueLbl.isHidden = false
endDateValueLbl.isHidden = false
UIView.animate(withDuration: 0.4) {
self.view.layoutIfNeeded()
}
}
@objc var didSetDayScope = false
}
extension BookingPeriodVC: FSCalendarDataSource {
func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell {
let cell = calendar.dequeueReusableCell(withIdentifier: "cell", for: date, at: position)
return cell
}
func calendar(_ calendar: FSCalendar, willDisplay cell: FSCalendarCell, for date: Date, at position: FSCalendarMonthPosition) {
self.configure(cell: cell, for: date, at: position)
}
func minimumDate(for calendar: FSCalendar) -> Date {
if startSelectingDate {
return lastSelectedDate
}
return Date()
}
func maximumDate(for calendar: FSCalendar) -> Date {
let cldr = Calendar.current
if startSelectingDate {
return cldr.date(byAdding: .day, value: 28, to: calendar.selectedDate!) ?? Date()
}
return cldr.date(byAdding: .day, value: 499, to: Date()) ?? Date()
}
func calendar(_ calendar: FSCalendar, shouldSelect date: Date, at monthPosition: FSCalendarMonthPosition) -> Bool {
return monthPosition == .current
}
func calendar(_ calendar: FSCalendar, shouldDeselect date: Date, at monthPosition: FSCalendarMonthPosition) -> Bool {
return monthPosition == .current
}
}
/*
* Responsible For drawing circle for selected dates
*/
extension BookingPeriodVC {
@objc func configureVisibleCells() {
calendar.visibleCells().forEach { (cell) in
let date = calendar.date(for: cell)
let position = calendar.monthPosition(for: cell)
self.configure(cell: cell, for: date!, at: position)
}
}
// marking cell when user select or drag on day
@objc func configure(cell: FSCalendarCell, for date: Date, at position: FSCalendarMonthPosition) {
let skyCell = cell as! SkyCalendarCell
if position == .current {
var selectionType = SelectionType.none
if calendar.selectedDates.contains(date) {
let previousDate = self.currentCalendar.date(byAdding: .day, value: -1, to: date)!
let nextDate = self.currentCalendar.date(byAdding: .day, value: 1, to: date)!
if calendar.selectedDates.contains(date) {
if calendar.selectedDates.contains(previousDate) && calendar.selectedDates.contains(nextDate) {
selectionType = .middle
}
else if calendar.selectedDates.contains(previousDate) && calendar.selectedDates.contains(date) {
selectionType = .rightBorder
}
else if calendar.selectedDates.contains(nextDate) {
selectionType = .leftBorder
}
else {
selectionType = .single
}
}
}else {
selectionType = .none
}
if selectionType == .none {
skyCell.selectionLayer.isHidden = true
return
}
skyCell.selectionLayer.isHidden = false
skyCell.selectionType = selectionType
} else {
skyCell.selectionLayer.isHidden = true
}
}
}
extension BookingPeriodVC: FSCalendarDelegate {
func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
self.configureVisibleCells()
if calendar.selectedDates.count > 1 {
selectAllDateBetween(start: lastSelectedDate, end: date)
}
lastSelectedDate = date
startSelectingDate = true
if startSelectingDate && !didSetDayScope {
calendar.reloadData()
didSetDayScope = true
}
}
func calendar(_ calendar: FSCalendar, didDeselect date: Date) {
deselectDateStartingFrom(date: date, selectedDates: calendar.selectedDates)
lastSelectedDate = currentCalendar.date(byAdding: .day, value: -1, to: date) ?? date
configureVisibleCells()
startSelectingDate = false
if calendar.selectedDates.count < 1 {
didSetDayScope = false
calendar.reloadData()
}
}
func calendarCurrentPageDidChange(_ calendar: FSCalendar) {
let selectedcomponents = self.currentCalendar.dateComponents([.month], from: calendar.currentPage)
let currentComponents = self.currentCalendar.dateComponents([.month], from: Date())
guard let selectedMonth = selectedcomponents.month, let currentMonth = currentComponents.month else { return }
if calendar.currentPage > Date() {
previousBtn.isEnabled = true
}else{
if calendar.scope == .week {
if currentMonth == selectedMonth {
previousBtn.isEnabled = true
}else{
previousBtn.isEnabled = false
}
}else{
previousBtn.isEnabled = false
}
}
}
}
/*
* Responsible for auto selecting/deselecting dates which is between start and end dates
*/
extension BookingPeriodVC {
@objc func changeCalendarScope(scope: FSCalendarScope ) {
self.calendar.setScope(scope, animated: true)
}
@objc func deselectDateStartingFrom(date: Date,selectedDates: [Date]) {
let dates = selectedDates.sorted { [=19=].compare() == .orderedAscending }
dates.forEach {
if [=19=] > date {
calendar.deselect([=19=])
}
}
}
fileprivate func selectAllDateBetween(start: Date, end: Date) {
getMiddleDays(start: start, end: end).forEach {
calendar.select([=19=])
configureVisibleCells() // make it out of this scope
}
}
fileprivate func getMiddleDays(start: Date, end: Date) -> [Date] {
var fromDate = start
let toDate = end
var middleDates: [Date] = []
if fromDate < toDate {
while fromDate < toDate {
if let nextDay = currentCalendar.date(byAdding: .day, value: 1, to: fromDate){
middleDates.append(nextDay)
fromDate = nextDay
}
}
}else {
while fromDate > toDate {
if let previousDay = currentCalendar.date(byAdding: .day, value: -1, to: fromDate){
middleDates.append(previousDay)
fromDate = previousDay
}
}
}
return middleDates
}
fileprivate func disablePanOnCalendar(){
DispatchQueue.main.asyncAfter(deadline: .now()+0.5) {
self.calendar.scrollEnabled = false
}
}
fileprivate func setPreviousAndNextForCurrentCalendarScope(value: Int) {
if calendar.scope == .month {
let date = currentCalendar.date(byAdding: .month, value: value, to:calendar.currentPage)!
calendar.setCurrentPage(date, animated: true)
}else {
let date = currentCalendar.date(byAdding: .weekOfMonth, value: value, to:calendar.currentPage)!
calendar.setCurrentPage(date, animated: true)
}
}
}
extension FSCalendar {
@objc var getUserSelectedDate: [String] {
var userSelectedDates:[Date] = []
if selectedDates.count >= 2 {
userSelectedDates = selectedDates.sorted { [=19=].compare() == .orderedAscending }
guard let firstDate = userSelectedDates.first, let lastDate = userSelectedDates.last else {
return []
}
return [firstDate,lastDate].map{ DateFormatter.getDateFor(type: .ddMMMyyyyE, date: [=19=]) }
}
return selectedDates.map{ DateFormatter.getDateFor(type: .ddMMMyyyyE, date: [=19=]) }
}
}
enum DateFormatType: String {
case dmy = "d MMM yyyy" // 5 Dec 2017
case hmi = "h:mm a" // 12:20 AM
case ddm = "E, dd MMM" // Tue 02 Dec
case hm = "HH,mm" // 03:02
case m = "M,y" // 1
case ddMMMyyyyE = "dd MMM yyyy (E)"
case yyyyMMdd = "yyyy-MM-dd"
}
extension DateFormatter {
static func getDateFor(type: DateFormatType, date: Date) -> String {
let formatter = DateFormatter()
switch type {
case .dmy:
formatter.dateFormat = type.rawValue
case .hmi:
formatter.dateFormat = type.rawValue
case .ddm:
formatter.dateFormat = type.rawValue
case .hm:
formatter.dateFormat = type.rawValue
case .m:
formatter.dateFormat = type.rawValue
case .ddMMMyyyyE:
formatter.dateFormat = type.rawValue
case .yyyyMMdd:
formatter.dateFormat = type.rawValue
}
return formatter.string(from: date)
}
}
奇怪的是我没有收到红色错误,日志消息让我很困惑。我是 iOS 的新手,请帮助我并准确说明我应该在什么地方解决这个问题。非常感谢。
编辑: 最终解决方案:
改变
calender.subviews[1].backgroundColor = .white
至
calender.subviews.count >= 2 {
calender.subviews[1].backgroundColor = .white
}
let vc: BookingPeriodVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ViewController") as BookingPeriodVC
vc.delegate = self
vc.modalTransitionStyle = .crossDissolve
vc.modalPresentationStyle = .overCurrentContext
self.presentViewController(vc, animated: false, completion: nil)
喜欢这样...
你的问题在startSelectingDate
首先像这样
let vc: BookingPeriodVC = UIStoryboard(name: "EAN Scene", bundle: nil).instantiateViewControllerWithIdentifier("identifier for your controller") as BookingPeriodVC
if let vc = UIStoryboard.bookingPeriodVC() as? BookingPeriodVC{
vc.delegate = self
vc.modalTransitionStyle = .crossDissolve
vc.modalPresentationStyle = .overCurrentContext
tabBarController?.present(vc, animated: true, completion: nil)
}
其次 calender.subviews[1].backgroundColor = .white
检查是否
calender.subviews.count >= 2 {
calender.subviews[1].backgroundColor = .white
}
第三,你试图在下面的行中访问 selectedDates 的第一个元素会让你崩溃,因为你的 calendar.getUserSelectedDate
数组是空的
当你发起 viewController
if let firstDate = calendar.getUserSelectedDate.first
你必须像
if let calenderDates = calendar.getUserSelectedDate{
if calenderDates.count>0{
firstDate = calendar.getUserSelectedDate.first
}
}