'*** -[__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
    }
}