- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
出于完全不同的目的,我正在基于 nghialv ( https://github.com/nghialv/Sapporo ) 的札幌实现带有日历的 Collection View 。我想定义将在 10 秒帧中通过六个不同 channel 发送的脉冲。因此,我有一个秒与 channel 的 PulseCollectionView。
我想使用本教程实现长按后单元格拖动: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
最佳答案
我不久前解决了这个问题:
struct Bundle
func setupGestureRecognizer()
func gestureRecognizerShouldBegin
func handleGesture(gesture: UILongPressGestureRecognizer) -> Void
应该全部放入 ViewController 而不是布局文件中。
关于ios - 手势识别器应该开始 : Unrecognized selector sent to instance,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32313629/
我最近将 Xcode 更新到 11.3.1 版,之后我无法运行我的应用程序。我一直收到以下异常: 2020-02-11 16:13:04.767795-0600 UVM[5204:80616] -[E
我正在尝试使用 horton 模式注册表在 kafka 中使用 avro 格式的消息。问题是,当我尝试发布 avro 消息时,我收到此错误: Caused by: com.fasterxml.jack
我正在尝试在 Red Hat Linux 上安装 MySql Server。 我已经下载了 tar文件并将其解压缩。 然后,我跑了: rpm -qpl mysql-community-server-5
在 fedora 22 上,我发现所有标准的 go 库在 go 的路径上都不可见。 注意 我确实清理了我的 golang 系统 - 所以我很确定这不是升级 go 时经常发生的混合包版本控制问题。 注意
这个问题在这里已经有了答案: systemctl command doesn't work inside docker-container (2 个回答) 8 个月前关闭。 我是 docker 新手,
如何保护注销操作?我读了default configuration并设置了 logout: csrf_parameter: _token csrf_provider:
我按照说明here按照以下步骤在 WSL 上安装 mariadb。我运行这个sudo service mysql start我有mysql: unrecognized service知道如何解决这个问
我需要选择一个隐藏字段才能将其删除。我想按类型、自定义数据属性和名称选择它。我的选择器如下所示: $("input[type=hidden] data-supplied='Cola' name='co
我正在Ubuntu 16.04上针对ARM体系结构交叉编译gpsd3.20。如您所知,gpsd使用Sconsctruct来编译源代码。在我进行交叉编译时,需要创建libgps.so的那一刻显示了unr
我正在实现 Skobbler SDK (v2.5),但我在第一步中遇到了问题。 应用程序因以下错误而崩溃:[SKVectorMapView displayTrafficWithMode:]:无法识别的
我正在使用以下命令运行 Java: java -Xms3G -Xmx3G -Xmn1G -XX:TargetSurvivorRatio=80 -XX:MaxTenuringThreshold=15 -
我正在使用 django 和 VB Linux Red Hat。我尝试过使用命令 python manage.py runserver - 192.168.1.100:8000 为了访问我的网站。到目
我正在处理使用 CodeIgniter 和 HTML5 布局的新网站。 我从我的旧网站复制了一些代码,但当我在我的新网站上尝试这个时,它给了我这个错误: Error: Syntax error, un
在用 Flex 编写 token 生成器时,我遇到了这个恼人的错误:“无法识别的规则” 我的代码是: /* Keywords */ TYPE int|double|bool|char L
我正在测试 Android Pay API。我使用命令生成了公钥 $ openssl ec -in merchant-key.pem -pubout -text -noout 和 echo $PUBL
我有几行代码可以用 Java 读取文件的内容。基本上我使用的是 FileReader 和 BufferedReader。我正在正确阅读这些行,但是,第一行的第一个字符似乎是一个 undefined s
UIView *v2 = ({ UIView *view = [UIView new]; [self.view addSubview:view]; [v
我遇到了一种情况,我有一个用 javascript 编写的表单,它为人们创建个人资料,详细说明了他们的姓名简历等。无论如何,我尝试向此表单添加一些新字段,但我添加的每个新字段都是当我尝试编辑个人资料信
在我的 ViewController 类中,我有一个函数: func updateTimes() { // (code) } 我创建了一个计时器: class ViewController: NS
我正在尝试运行 Behat(对我来说是第一次)并且成功了。 但是我有一个配置问题。我尝试像这样更改功能和 Bootstrap 的路径: #behat.yml default: paths:
我是一名优秀的程序员,十分优秀!