2017-09-24 86 views
0

此代碼似乎崩潰我的代碼。無效索引路徑錯誤

***終止應用程序由於未捕獲的異常 'NSInvalidArgumentException',原因: '嘗試滾動到無效 索引路徑:{長度= 2,路徑= 0 - 3}'

func handleKeyboardNotification(notification: NSNotification) { 
    if let userinfo = notification.userInfo { 
     let keyboardFrame = (userinfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue 
     self.bottomConstraint?.constant = -(keyboardFrame.height) 

     let isKeyboardShowing = notification.name == NSNotification.Name.UIKeyboardWillShow 
     self.bottomConstraint?.constant = isKeyboardShowing ? -(keyboardFrame.height) : 0 

     UIView.animate(withDuration: 0, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: { 
      self.view.layoutIfNeeded() 
     }, completion: { (completion) in 
      if self.comments.count > 0 && isKeyboardShowing { 
       let indexPath = NSIndexPath(item: self.comments.count-1, section: 0) 
       self.collectionView.scrollToItem(at: indexPath as IndexPath, at: .top, animated: true) 
      } 
     }) 
    } 
} 

我也使用IGListKit來填充它包含在集合視圖。下面是我的評論控制器。

import UIKit 
import IGListKit 
import Firebase 

class NewCommentsViewController: UIViewController, UITextFieldDelegate { 
    //array of comments which will be loaded by a service function 
    var comments = [CommentGrabbed]() 
    var messagesRef: DatabaseReference? 
    var bottomConstraint: NSLayoutConstraint? 
    public var eventKey = "" 
    //This creates a lazily-initialized variable for the IGListAdapter. The initializer requires three parameters: 
    //1 updater is an object conforming to IGListUpdatingDelegate, which handles row and section updates. IGListAdapterUpdater is a default implementation that is suitable for your usage. 
    //2 viewController is a UIViewController that houses the adapter. This view controller is later used for navigating to other view controllers. 
    //3 workingRangeSize is the size of the working range, which allows you to prepare content for sections just outside of the visible frame. 

    lazy var adapter: ListAdapter = { 
     return ListAdapter(updater: ListAdapterUpdater(), viewController: self, workingRangeSize: 0) 
    }() 

    // 1 IGListKit uses IGListCollectionView, which is a subclass of UICollectionView, which patches some functionality and prevents others. 
    let collectionView: UICollectionView = { 
     // 2 This starts with a zero-sized rect since the view isn’t created yet. It uses the UICollectionViewFlowLayout just as the ClassicFeedViewController did. 
     let view = UICollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout()) 
     // 3 The background color is set to white 
     view.backgroundColor = UIColor.white 
     return view 
    }() 

    //will fetch the comments from the database and append them to an array 
    fileprivate func fetchComments(){ 
     messagesRef = Database.database().reference().child("Comments").child(eventKey) 
     print(eventKey) 
     print(comments.count) 
     messagesRef?.observe(.childAdded, with: { (snapshot) in 
      print(snapshot) 
      guard let commentDictionary = snapshot.value as? [String: Any] else{ 
       return 
      } 
      print(commentDictionary) 
      guard let uid = commentDictionary["uid"] as? String else{ 
       return 
      } 
      UserService.show(forUID: uid, completion: { (user) in 
       if let user = user { 
        var commentFetched = CommentGrabbed(user: user, dictionary: commentDictionary) 
        commentFetched.commentID = snapshot.key 
        let filteredArr = self.comments.filter { (comment) -> Bool in 
         return comment.commentID == commentFetched.commentID 
        } 
        if filteredArr.count == 0 { 
         self.comments.append(commentFetched) 
        } 
        print(self.comments) 
        self.adapter.performUpdates(animated: true) 
       } 
       self.comments.sort(by: { (comment1, comment2) -> Bool in 
        return comment1.creationDate.compare(comment2.creationDate) == .orderedAscending 
       }) 
       self.comments.forEach({ (comments) in 
       }) 
      }) 
     }, withCancel: { (error) in 
      print("Failed to observe comments") 
     }) 

     //first lets fetch comments for current event 
    } 

    lazy var submitButton : UIButton = { 
     let submitButton = UIButton(type: .system) 
     submitButton.setTitle("Submit", for: .normal) 
     submitButton.setTitleColor(.black, for: .normal) 
     submitButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 14) 
     submitButton.addTarget(self, action: #selector(handleSubmit), for: .touchUpInside) 
     submitButton.isEnabled = false 
     return submitButton 
    }() 

    //allows you to gain access to the input accessory view that each view controller has for inputting text 
    lazy var containerView: UIView = { 
     let containerView = UIView() 
     containerView.backgroundColor = .white 
     containerView.addSubview(self.submitButton) 
     self.submitButton.anchor(top: containerView.topAnchor, left: nil, bottom: containerView.bottomAnchor, right: containerView.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 12, width: 50, height: 0) 

     containerView.addSubview(self.commentTextField) 
     self.commentTextField.anchor(top: containerView.topAnchor, left: containerView.leftAnchor, bottom: containerView.bottomAnchor, right: self.submitButton.leftAnchor, paddingTop: 0, paddingLeft: 12, paddingBottom: 0, paddingRight: 180, width: 0, height: 0) 
     self.commentTextField.delegate = self 
     let lineSeparatorView = UIView() 
     lineSeparatorView.backgroundColor = UIColor.rgb(red: 230, green: 230, blue: 230) 
     containerView.addSubview(lineSeparatorView) 
     lineSeparatorView.anchor(top: containerView.topAnchor, left: containerView.leftAnchor, bottom: nil, right: containerView.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0.5) 

     return containerView 
    }() 

    lazy var commentTextField: UITextField = { 
     let textField = UITextField() 
     textField.placeholder = "Add a comment" 
     textField.delegate = self 
     textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged) 
     return textField 
    }() 

    func textFieldDidChange(_ textField: UITextField) { 
     let isCommentValid = commentTextField.text?.characters.count ?? 0 > 0 
     if isCommentValid { 
      submitButton.isEnabled = true 
     }else{ 
      submitButton.isEnabled = false 
     } 
    } 

    func handleSubmit(){ 
     guard let comment = commentTextField.text, comment.characters.count > 0 else{ 
      return 
     } 
     let userText = Comments(content: comment, uid: User.current.uid, profilePic: User.current.profilePic!) 
     sendMessage(userText) 
     // will remove text after entered 
     self.commentTextField.text = nil 
    } 

    func flagButtonTapped (from cell: CommentCell){ 
     guard let indexPath = collectionView.indexPath(for: cell) else { return } 

     // 2 
     let comment = comments[indexPath.item] 
     _ = comment.uid 

     // 3 
     let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) 

     // 4 
     if comment.uid != User.current.uid { 
      let flagAction = UIAlertAction(title: "Report as Inappropriate", style: .default) { _ in 
       ChatService.flag(comment) 

       let okAlert = UIAlertController(title: nil, message: "The post has been flagged.", preferredStyle: .alert) 
       okAlert.addAction(UIAlertAction(title: "Ok", style: .default)) 
       self.present(okAlert, animated: true) 
      } 
      let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) 
      alertController.addAction(cancelAction) 
      alertController.addAction(flagAction) 
     }else{ 
      let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) 
      let deleteAction = UIAlertAction(title: "Delete Comment", style: .default, handler: { _ in 
       ChatService.deleteComment(comment, self.eventKey) 
       let okAlert = UIAlertController(title: nil, message: "Comment Has Been Deleted", preferredStyle: .alert) 
       okAlert.addAction(UIAlertAction(title: "Ok", style: .default)) 
       self.present(okAlert, animated: true) 
      }) 
      alertController.addAction(cancelAction) 
      alertController.addAction(deleteAction) 

     } 
     present(alertController, animated: true, completion: nil) 

    } 

    func handleKeyboardNotification(notification: NSNotification){ 
     if let userinfo = notification.userInfo{ 

      let keyboardFrame = (userinfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue 
      self.bottomConstraint?.constant = -(keyboardFrame.height) 

      let isKeyboardShowing = notification.name == NSNotification.Name.UIKeyboardWillShow 
      self.bottomConstraint?.constant = isKeyboardShowing ? -(keyboardFrame.height) : 0 

      UIView.animate(withDuration: 0, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: { 
       self.view.layoutIfNeeded() 
      }, completion: { (completion) in 
       if self.comments.count > 0 && isKeyboardShowing{ 
        let indexPath = NSIndexPath(item: self.comments.count-1, section: 0) 
        self.collectionView.scrollToItem(at: indexPath as IndexPath, at: .top, animated: true) 
       } 
      }) 
     } 
    } 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     view.addSubview(collectionView) 
     collectionView.addSubview(containerView) 
     collectionView.alwaysBounceVertical = true 
     view.addConstraintsWithFormatt("H:|[v0]|", views: containerView) 
     view.addConstraintsWithFormatt("V:[v0(48)]", views: containerView) 
     bottomConstraint = NSLayoutConstraint(item: containerView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0) 
     view.addConstraint(bottomConstraint!) 
     adapter.collectionView = collectionView 
     adapter.dataSource = self 
     NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification), name: NSNotification.Name.UIKeyboardWillShow, object: nil) 

     NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification), name: NSNotification.Name.UIKeyboardWillHide, object: nil) 
     collectionView.register(CommentCell.self, forCellWithReuseIdentifier: "CommentCell") 
     fetchComments() 
     // Do any additional setup after loading the view. 
    } 

    override func viewWillAppear(_ animated: Bool) { 
     super.viewWillAppear(animated) 
     tabBarController?.tabBar.isHidden = true 
     submitButton.isUserInteractionEnabled = true 

    } 
    //viewDidLayoutSubviews() is overridden, setting the collectionView frame to match the view bounds. 
    override func viewDidLayoutSubviews() { 
     super.viewDidLayoutSubviews() 
     collectionView.frame = view.bounds 
    } 
    override func didReceiveMemoryWarning() { 
     super.didReceiveMemoryWarning() 
     // Dispose of any resources that can be recreated. 
    } 
} 

extension NewCommentsViewController: ListAdapterDataSource { 
    // 1 objects(for:) returns an array of data objects that should show up in the collection view. loader.entries is provided here as it contains the journal entries. 
    func objects(for listAdapter: ListAdapter) -> [ListDiffable] { 
     print("comments = \(comments)") 
     return comments 
    } 

    // 2 For each data object, listAdapter(_:sectionControllerFor:) must return a new instance of a section controller. For now you’re returning a plain IGListSectionController to appease the compiler — in a moment, you’ll modify this to return a custom journal section controller. 
    func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController { 
     //the comment section controller will be placed here but we don't have it yet so this will be a placeholder 
     return CommentsSectionController() 
    } 

    // 3 emptyView(for:) returns a view that should be displayed when the list is empty. NASA is in a bit of a time crunch, so they didn’t budget for this feature. 
    func emptyView(for listAdapter: ListAdapter) -> UIView? { 
     let view = UIView() 
     view.backgroundColor = UIColor.white 
     return view 
    } 
} 

extension NewCommentsViewController { 
    func sendMessage(_ message: Comments) { 
     ChatService.sendMessage(message, eventKey: eventKey) 

    } 
} 

另外這是我的部分控制器。現在它正在加載數據,但只要我嘗試添加一些測試來測試它是否正常使用鍵盤崩潰時正常工作。

import UIKit 
import IGListKit 
import Foundation 

class CommentsSectionController: ListSectionController { 

    var comment: CommentGrabbed? 

    override init() { 
     super.init() 
     inset = UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0) 
    } 

    // MARK: IGListSectionController Overrides 
    override func numberOfItems() -> Int { 
     print(numberOfItems) 
     return 1 
    } 

    override func sizeForItem(at index: Int) -> CGSize { 
     let frame = CGRect(x: 0, y: 0, width: collectionContext!.containerSize.width, height: 50) 
     let dummyCell = CommentCell(frame: frame) 
     dummyCell.comment = comment 
     dummyCell.layoutIfNeeded() 
     let targetSize = CGSize(width: collectionContext!.containerSize.width, height: 55) 
     let estimatedSize = dummyCell.systemLayoutSizeFitting(targetSize) 
     let height = max(40+8+8, estimatedSize.height) 

     return CGSize(width: collectionContext!.containerSize.width, height: height) 
    } 

    override var minimumLineSpacing: CGFloat { 
     get { 
      return 0.0 
     } 
     set { 
      self.minimumLineSpacing = 0.0 
     } 
    } 

    override func cellForItem(at index: Int) -> UICollectionViewCell { 
     guard let cell = collectionContext?.dequeueReusableCell(of: CommentCell.self, for: self, at: index) as? CommentCell else { 
      fatalError() 
     } 
     print(comment) 

     cell.comment = comment 
     return cell 
    } 

    override func didUpdate(to object: Any) { 
     comment = object as! CommentGrabbed 
    } 

    override func didSelectItem(at index: Int) { 

    } 
} 

這種具體方法恕不承擔返回細胞

func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController { 
     //the comment section controller will be placed here but we don't have it yet so this will be a placeholder 
     return CommentsSectionController() 
    } 

任何有識之士將是非常有益的

回答

0

因爲你設置的項目數爲您收集等於1這是發生,但是您嘗試訪問索引等於IndexPath(section: 0, item: self:comments.count-1)的單元格,在這種情況下,comments.count-1等於3,因爲您可以從錯誤日誌中猜出這是您得到此錯誤的原因。

這裏的解決方案將爲您的收藏視圖數據源返回正確數量的評論(comments.count-1)。

UPDATE:

在這種情況下,你不應該使用NSIndexPath。相反,使用IndexPath。此外,在此更改之後,您不再需要在scrollToItem調用中將索引路徑轉換爲IndexPath。更新後的版本是這樣的:

func handleKeyboardNotification(notification: NSNotification) { 

     // ... 

     UIView.animate(withDuration: 0, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: { 
      self.view.layoutIfNeeded() 
     }, completion: { (completion) in 
      if self.comments.count > 0 && isKeyboardShowing { 
       let indexPath = IndexPath(item: self.comments.count-1, section: 0) 
       self.collectionView.scrollToItem(at: indexPath, at: .top, animated: true) 
      } 
     }) 
    } 
} 
+0

現在將嘗試在此先感謝 –

+0

我會怎麼做,該函數的內部時,我不得不評論進不去裏面有 –

+0

我在你的代碼又看了一下,因爲你似乎要使用第三方庫(IGListKit),並且您的實現看起來很好,因爲您要在對象中設置註釋的數量(對於listAdapter:ListAdapter) - > [ListDiffable]數據源方法,但根據您可能需要的文檔當你的大小爲0時,考慮從你的列表適配器的初始值設定項中刪除workingRangeSize。 – Alex

相關問題