2015-08-31 3 views
0

Я реализую коллекцию с календарем На основе Саппоро от nghialv (https://github.com/nghialv/Sapporo) для совершенно другой цели. Я хочу определить импульсы, которые будут отправляться по шести различным каналам в 10-секундном кадре. Поэтому у меня есть PulseCollectionView Seconds vs. Channels.gestureRecognizerShouldBegin: Непризнанный селектор, отправленный в экземпляр

Я хочу реализовать перетаскивание ячеек после долгого нажатия, используя этот учебник: http://karmadust.com/drag-and-rearrange-uicollectionviews-through-layouts/.

Моя проблема заключается в том, что я получаю сообщение об ошибке, когда я долгое нажатие на ячейку:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSConcreteMapTable gestureRecognizerShouldBegin:]: unrecognized selector sent to instance 0x7b6bea30' 

Я попытался найти неисправную линию через отладчик, но мне кажется, что я не могу найти его. Он ломает, прежде чем даже добраться до gestureRecognizerShouldBegin

Это мой макет файла:

import UIKit 
import Sapporo 

let SecondsMaximum  : CGFloat = 10 
let ChannelsMaximum  : CGFloat = 6 
let HorizontalSpacing : CGFloat = 10 
let VerticalSpacing  : CGFloat = 4 
//let WidthPerSecond  : CGFloat = 100 
let SecondHeaderHeight : CGFloat = 40 
let ChannelHeaderWidth  : CGFloat = 100 

class PulseLayout: SALayout, UIGestureRecognizerDelegate { 
    override func collectionViewContentSize() -> CGSize { 

     let contentHeight = collectionView!.bounds.size.height 
     let contentWidth = collectionView!.bounds.size.width 
     return CGSizeMake(contentWidth, contentHeight) 
    } 

    override func awakeFromNib() { 
     super.awakeFromNib() 
     self.setupGestureRecognizer() 
    } 

    override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? { 
     var layoutAttributes = [UICollectionViewLayoutAttributes]() 

     // Cells 
     let visibleIndexPaths = indexPathsOfItemsInRect(rect) 
     layoutAttributes += visibleIndexPaths.map { 
      self.layoutAttributesForItemAtIndexPath($0) 
     } 

     // Supplementary views 
     let secondHeaderViewIndexPaths = indexPathsOfSecondHeaderViewsInRect(rect) 
     layoutAttributes += secondHeaderViewIndexPaths.map { 
      self.layoutAttributesForSupplementaryViewOfKind(PulseHeaderType.Second.rawValue, atIndexPath: $0) 
     } 

     let channelHeaderViewIndexPaths = indexPathsOfChannelHeaderViewsInRect(rect) 
     layoutAttributes += channelHeaderViewIndexPaths.map { 
      self.layoutAttributesForSupplementaryViewOfKind(PulseHeaderType.Channel.rawValue, atIndexPath: $0) 
     } 

     // Decoration Views 
     let verticalGridViewIndexPaths = indexPathsOfVerticalGridLineViewsInRect(rect) 
     layoutAttributes += verticalGridViewIndexPaths.map { 
      self.layoutAttributesForDecorationViewOfKind(GridLineType.Vertical.rawValue, atIndexPath: $0) 
     } 
     let horizontalGridViewIndexPaths = indexPathsOfHorizontalGridLineViewsInRect(rect) 
     layoutAttributes += horizontalGridViewIndexPaths.map { 
      self.layoutAttributesForDecorationViewOfKind(GridLineType.Horizontal.rawValue, atIndexPath: $0) 
     } 

     return layoutAttributes 
    } 

    override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! { 
     var attributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath) 

     if let event = (getCellModel(indexPath) as? PulseEventCellModel)?.event { 
      attributes.frame = frameForEvent(event) 
     } 
     return attributes 
    } 

    override func layoutAttributesForSupplementaryViewOfKind(elementKind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! { 
     var attributes = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: elementKind, withIndexPath: indexPath) 

     let totalHeight = collectionViewContentSize().height 
     let totalWidth = collectionViewContentSize().width 

     if elementKind == PulseHeaderType.Second.rawValue { 
      let availableWidth = totalWidth - ChannelHeaderWidth 
      let widthPerSecond = availableWidth/SecondsMaximum 
      attributes.frame = CGRectMake(ChannelHeaderWidth + widthPerSecond * CGFloat(indexPath.item), 0, widthPerSecond, SecondHeaderHeight) 
      attributes.zIndex = -10 
     } else if elementKind == PulseHeaderType.Channel.rawValue { 
      let availableHeight = totalHeight - SecondHeaderHeight 
      let heightPerChannel = availableHeight/ChannelsMaximum 
      attributes.frame = CGRectMake(0, SecondHeaderHeight + (heightPerChannel * CGFloat(indexPath.item)), ChannelHeaderWidth, heightPerChannel) 
      attributes.zIndex = -10 
     } 
     return attributes 
    } 


    override func layoutAttributesForDecorationViewOfKind(elementKind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! { 
     var attributes = UICollectionViewLayoutAttributes(forDecorationViewOfKind: elementKind, withIndexPath: indexPath) 

     let totalHeight = collectionViewContentSize().height 
     let totalWidth = collectionViewContentSize().width 

     if elementKind == GridLineType.Vertical.rawValue { 
      let availableWidth = totalWidth - ChannelHeaderWidth 
      let widthPerSecond = availableWidth/SecondsMaximum 
      attributes.frame = CGRectMake(ChannelHeaderWidth + widthPerSecond * CGFloat(indexPath.item), 0.0, 1, totalHeight) 
      attributes.zIndex = -11 
     } else if elementKind == GridLineType.Horizontal.rawValue { 
      let availableHeight = totalHeight - SecondHeaderHeight 
      let heightPerChannel = availableHeight/ChannelsMaximum 
      attributes.frame = CGRectMake(0.0, SecondHeaderHeight + (heightPerChannel * CGFloat(indexPath.item)), totalWidth, 1) 
      attributes.zIndex = -11 
     } 
     return attributes 
    } 



    override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool { 
     return true 
    } 


    struct Bundle { 
     var offset : CGPoint = CGPointZero 
     var sourceCell : UICollectionViewCell 
     var representationImageView : UIView 
     var currentIndexPath : NSIndexPath 
     var canvas : UIView 
    } 
    var bundle : Bundle? 

    var canvas : UIView? { 
     didSet { 
      if canvas != nil { 
       // self.calculateBorders() 
      } 
     } 
    } 

    func setupGestureRecognizer() { 
     if let collectionView = self.collectionView { 

      let longPressGestureRecogniser = UILongPressGestureRecognizer(target: self, action: "handleGesture:") 

      longPressGestureRecogniser.minimumPressDuration = 0.2 
      longPressGestureRecogniser.delegate = self 

      collectionView.addGestureRecognizer(longPressGestureRecogniser) 

      if self.canvas == nil { 

       self.canvas = self.collectionView!.superview 

      } 
     } 
    } 


    func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool { 

     if let ca = self.collectionView!.superview { 

      if let cv = self.collectionView { 

       let pointPressedInCanvas = gestureRecognizer.locationInView(ca) 

       for cell in cv.visibleCells() as! [PulseEventCell] { 

        let cellInCanvasFrame = ca.convertRect(cell.frame, fromView: cv) 

        if CGRectContainsPoint(cellInCanvasFrame, pointPressedInCanvas) { 

         let representationImage = cell.snapshotViewAfterScreenUpdates(true) 
         representationImage.frame = cellInCanvasFrame 

         let offset = CGPointMake(pointPressedInCanvas.x - cellInCanvasFrame.origin.x, pointPressedInCanvas.y - cellInCanvasFrame.origin.y) 

         let indexPath : NSIndexPath = cv.indexPathForCell(cell as UICollectionViewCell)! 

         self.bundle = Bundle(offset: offset, sourceCell: cell, representationImageView:representationImage, currentIndexPath: indexPath, canvas: ca) 
         break 
        } 

       } 

      } 

     } 
     return (self.bundle != nil) 
    } 

    func handleGesture(gesture: UILongPressGestureRecognizer) -> Void { 


     if let bundle = self.bundle { 

      let dragPointOnCanvas = gesture.locationInView(self.collectionView!.superview) 

      if gesture.state == UIGestureRecognizerState.Began { 

       bundle.sourceCell.hidden = true 
       self.canvas?.addSubview(bundle.representationImageView) 

       UIView.animateWithDuration(0.5, animations: {() -> Void in 
        bundle.representationImageView.alpha = 0.8 
       }); 

      } 


      if gesture.state == UIGestureRecognizerState.Changed { 

       // Update the representation image 
       var imageViewFrame = bundle.representationImageView.frame 
       var point = CGPointZero 
       point.x = dragPointOnCanvas.x - bundle.offset.x 
       point.y = dragPointOnCanvas.y - bundle.offset.y 
       imageViewFrame.origin = point 
       bundle.representationImageView.frame = imageViewFrame 


       let dragPointOnCollectionView = gesture.locationInView(self.collectionView) 


       if let indexPath : NSIndexPath = self.collectionView?.indexPathForItemAtPoint(dragPointOnCollectionView) { 

        // self.checkForDraggingAtTheEdgeAndAnimatePaging(gesture) 


        if indexPath.isEqual(bundle.currentIndexPath) == false { 

         // If we have a collection view controller that implements the delegate we call the method first 
         /*if let delegate = self.collectionView!.delegate as UICollectionViewDelegate? { 
         delegate.moveDataItem(bundle.currentIndexPath, toIndexPath: indexPath) 
         }*/ 

         self.collectionView!.moveItemAtIndexPath(bundle.currentIndexPath, toIndexPath: indexPath) 

         self.bundle!.currentIndexPath = indexPath 

        } 

       } 


      } 

      if gesture.state == UIGestureRecognizerState.Ended { 


       bundle.sourceCell.hidden = false 
       bundle.representationImageView.removeFromSuperview() 

       if let delegate = self.collectionView!.delegate as UICollectionViewDelegate? { // if we have a proper data source then we can reload and have the data displayed correctly 
        self.collectionView!.reloadData() 
       } 

       self.bundle = nil 
      } 
     } 
    } 
} 
extension PulseLayout { 
    func indexPathsOfEventsBetweenMinSecondIndex(minChannelIndex: Int, maxChannelIndex: Int, minStartSecond: Int, maxStartSecond: Int) -> [NSIndexPath] { 
     var indexPaths = [NSIndexPath]() 

     if let cellmodels = getCellModels(0) as? [PulseEventCellModel] { 
      for i in 0..<cellmodels.count { 
       let event = cellmodels[i].event 
       if event.channel >= minChannelIndex && event.channel <= maxChannelIndex && event.startSecond >= minStartSecond && event.startSecond <= maxStartSecond { 
        let indexPath = NSIndexPath(forItem: i, inSection: 0) 
        indexPaths.append(indexPath) 
       } 
      } 
     } 
     return indexPaths 
    } 

    func indexPathsOfItemsInRect(rect: CGRect) -> [NSIndexPath] { 
     let minVisibleChannel = channelIndexFromYCoordinate(CGRectGetMinY(rect)) 
     let maxVisibleChannel = channelIndexFromYCoordinate(CGRectGetMaxY(rect)) 
     let minVisibleSecond = secondIndexFromXCoordinate(CGRectGetMinX(rect)) 
     let maxVisibleSecond = secondIndexFromXCoordinate(CGRectGetMaxX(rect)) 

     return indexPathsOfEventsBetweenMinSecondIndex(minVisibleChannel, maxChannelIndex: maxVisibleChannel, minStartSecond: minVisibleSecond, maxStartSecond: maxVisibleSecond) 
    } 

    func channelIndexFromYCoordinate(yPosition: CGFloat) -> Int { 

     let contentHeight = collectionViewContentSize().height - SecondHeaderHeight 
     let HeightPerChannel = contentHeight/ChannelsMaximum 

     let channelIndex = max(0, Int((yPosition - SecondHeaderHeight)/HeightPerChannel)) 
     return channelIndex 
    } 

    func secondIndexFromXCoordinate(xPosition: CGFloat) -> Int { 

     let contentWidth = collectionViewContentSize().width - ChannelHeaderWidth 
     let WidthPerSecond = contentWidth/SecondsMaximum 

     let secondIndex = max(0, Int((xPosition - ChannelHeaderWidth)/WidthPerSecond)) 
     return secondIndex 

    } 

    func indexPathsOfSecondHeaderViewsInRect(rect: CGRect) -> [NSIndexPath] { 
     if CGRectGetMinY(rect) > SecondHeaderHeight { 
      return [] 
     } 

     let minSecondIndex = secondIndexFromXCoordinate(CGRectGetMinX(rect)) 
     let maxSecondIndex = secondIndexFromXCoordinate(CGRectGetMaxX(rect)) 

     return (minSecondIndex...maxSecondIndex).map { index -> NSIndexPath in 
      NSIndexPath(forItem: index, inSection: 0) 
     } 
    } 

    func indexPathsOfChannelHeaderViewsInRect(rect: CGRect) -> [NSIndexPath] { 
     if CGRectGetMinX(rect) > ChannelHeaderWidth { 
      return [] 
     } 

     let minChannelIndex = channelIndexFromYCoordinate(CGRectGetMinY(rect)) 
     let maxChannelIndex = channelIndexFromYCoordinate(CGRectGetMaxY(rect)) 

     return (minChannelIndex...maxChannelIndex).map { index -> NSIndexPath in 
      NSIndexPath(forItem: index, inSection: 0) 
     } 
    } 

    func indexPathsOfVerticalGridLineViewsInRect(rect: CGRect) -> [NSIndexPath] { 
     if CGRectGetMaxX(rect) < SecondHeaderHeight { 
      return [] 
     } 

     let minSecondIndex = secondIndexFromXCoordinate(CGRectGetMinX(rect)) 
     let maxSecondIndex = secondIndexFromXCoordinate(CGRectGetMaxX(rect)) 

     return (minSecondIndex...maxSecondIndex).map { index -> NSIndexPath in 
      NSIndexPath(forItem: index, inSection: 0) 
     } 
    } 
    func indexPathsOfHorizontalGridLineViewsInRect(rect: CGRect) -> [NSIndexPath] { 
     if CGRectGetMaxY(rect) < ChannelHeaderWidth { 
      return [] 
     } 

     let minChannelIndex = channelIndexFromYCoordinate(CGRectGetMinY(rect)) 
     let maxChannelIndex = channelIndexFromYCoordinate(CGRectGetMaxY(rect)) 

     return (minChannelIndex...maxChannelIndex).map { index -> NSIndexPath in 
      NSIndexPath(forItem: index, inSection: 0) 
     } 
    } 

    func frameForEvent(event: PulseEvent) -> CGRect { 
     let totalHeight = collectionViewContentSize().height - SecondHeaderHeight 
     let HeightPerChannel = totalHeight/ChannelsMaximum 

     let contentWidth = collectionViewContentSize().width - ChannelHeaderWidth 
     let WidthPerSecond = contentWidth/SecondsMaximum 


     var frame = CGRectZero 
     frame.origin.x = ChannelHeaderWidth + WidthPerSecond * CGFloat(event.startSecond) 
     frame.origin.y = SecondHeaderHeight + HeightPerChannel * CGFloat(event.channel) 
     frame.size.height = HeightPerChannel 
     frame.size.width = CGFloat(event.durationInSeconds) * WidthPerSecond 

     frame = CGRectInset(frame, 2.0, HeightPerChannel/8.0) 
     return frame 
    } 
} 

И это мой ViewController:

import UIKit 
import Sapporo 

enum PulseHeaderType: String { 
    case Second = "SecondHeaderView" 
    case Channel = "ChannelHeaderView" 
} 
enum GridLineType: String { 
    case Vertical = "VerticalGridLineView" 
    case Horizontal = "HorizontalGridLineView" 
} 

class PulseViewController: UIViewController { 

    @IBOutlet var collectionView: UICollectionView! 

    lazy var sapporo: Sapporo = { [unowned self] in 
     return Sapporo(collectionView: self.collectionView) 
     }() 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     sapporo.delegate = self 
     sapporo.registerNibForClass(PulseEventCell) 
     sapporo.registerNibForSupplementaryClass(PulseHeaderView.self, kind: PulseHeaderType.Second.rawValue) 
     sapporo.registerNibForSupplementaryClass(PulseHeaderView.self, kind: PulseHeaderType.Channel.rawValue)   

     let layout = PulseLayout() 
     layout.registerClass(GridLineView.self, forDecorationViewOfKind: GridLineType.Vertical.rawValue) 
     layout.registerClass(GridLineView.self, forDecorationViewOfKind: GridLineType.Horizontal.rawValue) 
     sapporo.setLayout(layout) 


     let randomEvent = {() -> PulseEvent in 
      let randomID = arc4random_uniform(10000) 
      let title = "Event \(randomID)" 

      let randomChannel = Int(arc4random_uniform(6)) 
      let randomStartSecond = Int(arc4random_uniform(8)) 
      let randomDuration = Int(arc4random_uniform(2) + 1) 

      return PulseEvent(title: title, channel: randomChannel, startSecond: randomStartSecond, durationInSeconds: randomDuration) 
     } 

     let cellmodels = (0...20).map { _ -> PulseEventCellModel in 
      let event = randomEvent() 
      return PulseEventCellModel(event: event) { _ in 
       println("Selected event: \(event.title)") 
      } 
     } 

     sapporo[0].append(cellmodels) 
     sapporo.bump() 
    } 
} 

extension PulseViewController: SapporoDelegate { 
    func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView { 
     if kind == PulseHeaderType.Second.rawValue { 
      let view = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: PulseHeaderView.reuseIdentifier, forIndexPath: indexPath) as! PulseHeaderView 
      view.titleLabel.text = "\(indexPath.item)" 
      return view 
     } 

     let view = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: PulseHeaderView.reuseIdentifier, forIndexPath: indexPath) as! PulseHeaderView 
     view.titleLabel.text = "Channel \(indexPath.item + 1)" 
     return view 
    } 
} 

Можете ли вы помочь мне определить, что мне не хватает?

Привет,

C

ответ

0

Я решил эту проблему некоторое время назад .:

структура Bundle

Func setupGestureRecognizer()

FUNC gestureRecognizerShouldBegin

FUNC handleGesture (жест: UILongPressGestureRecognizer) -> Пустота

все должны идти в ViewController вместо Layout File.

Смежные вопросы