- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我正在尝试实现一种更流畅的方式来为 myCollectionView
添加新的 collectionViewCells
(一次只显示一个单元格)。我希望它类似于当用户向左滑动时,如果用户在最后一个单元格上,myCollectionView
在用户滑动时插入一个新单元格,以便用户向左滑动“进入”单元格。而且我一次只允许用户滚动一个单元格。
编辑:所以我觉得用语言来描述它有点困难,所以这里有一个 gif 来展示我的意思
所以在过去的几周里,我一直在尝试以多种不同的方式实现它,我发现最成功的一种是使用 scrollViewWillEndDragging
委托(delegate)方法,我已经这样实现了:
func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
// Getting the size of the cells
let flowLayout = myCollectionView.collectionViewLayout as! UICollectionViewFlowLayout
let cellWidth = flowLayout.itemSize.width
let cellPadding = 10.0 as! CGFloat
// Calculating which page "card" we should be on
let currentOffset = scrollView.contentOffset.x - cellWidth/2
print("currentOffset is \(currentOffset)")
let cardWidth = cellWidth + cellPadding
var page = Int(round((currentOffset)/(cardWidth) + 1))
print("current page number is: \(page)")
if (velocity.x < 0) {
page -= 1
}
if (velocity.x > 0) {
page += 1
}
print("Updated page number is: \(page)")
print("Previous page number is: \(self.previousPage)")
// Only allowing the user to scroll for one page!
if(page > self.previousPage) {
page = self.previousPage + 1
self.previousPage = page
}
else if (page == self.previousPage) {
page = self.previousPage
}
else {
page = self.previousPage - 1
self.previousPage = page
}
print("new page number is: " + String(page))
print("addedCards.count + 1 is: " + String(addedCards.count + 1))
if (page == addedCards.count) {
print("reloading data")
// Update data source
addedCards.append("card")
// Method 1
cardCollectionView.reloadData()
// Method 2
// let newIndexPath = NSIndexPath(forItem: addedCards.count - 1, inSection: 0)
// cardCollectionView.insertItemsAtIndexPaths([newIndexPath])
}
// print("Centering on new cell")
// Center the cardCollectionView on the new page
let newOffset = CGFloat(page * Int((cellWidth + cellPadding)))
print("newOffset is: \(newOffset)")
targetContentOffset.memory.x = newOffset
}
虽然我认为我几乎得到了预期的结果,但仍然存在一些问题和我发现的错误。
我主要担心的是,我没有在 myCollectionView
的末尾插入单个单元格,而是重新加载了整个表格。我这样做的原因是,如果我不这样做,那么 myCollectionView.contentOffset
将不会更改,因此在创建新单元格时,myCollectionView
是' t 以新创建的单元格为中心。
1. 如果用户滚动非常缓慢然后停止,则会创建新的单元格,但随后 myCollectionView
会卡在两个单元格之间,它不会居中新创建的单元格。
2. 当 myCollectionView
由于 1. 而位于倒数第二个单元格和最后一个单元格之间时,下一次用户向右滑动,而不是创建一个单元格,而是创建两个单元格。
我还使用了不同的方法来实现此行为,例如使用 scrollViewDidScroll
和其他各种方法,但均无济于事。任何人都可以指出我正确的方向,因为我有点迷路了。
如果您想查看交互,可以通过以下链接下载我的项目: My Example Project
如果您有兴趣,可以使用这些旧方法:
为此我尝试了两种方法,
第一个是:
func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint,
targetContentOffset: UnsafeMutablePointer<CGPoint>) {
// page is the current cell that the user is on
// addedCards is the array that the data source works with
if (page == addedCards.count + 1) {
let placeholderFlashCardProxy = FlashCardProxy(phrase: nil, pronunciation: nil, definition: nil)
addedCards.append(placeholderFlashCardProxy)
let newIndexPath = NSIndexPath(forItem: addedCards.count, inSection: 0)
cardCollectionView.insertItemsAtIndexPaths([newIndexPath])
cardCollectionView.reloadData()
}
}
使用这种方法的问题在于:
NSInternalInconsistencyException',原因:'无效更新:第 0 节中的项目数量无效。
collectionCell
时,它有时会显示用户从前一个单元格写入的输入(这有时可能与单元格的出队和重用有关,尽管再次,我不确定,如果有人回答我将不胜感激)我使用的第二种方法是:
let swipeLeftGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "swipedLeftOnCell:")
swipeLeftGestureRecognizer.direction = .Left
myCollectionView.addGestureRecognizer(swipeLeftGestureRecognizer)
swipeLeftGestureRecognizer.delegate = self
虽然滑动手势很少响应,但如果我使用点击手势,myCollectionView
总是响应,这很奇怪(我再次知道这本身就是一个问题)
我的问题是哪种方法更好地实现我上面描述的内容?如果没有一个是好的,我应该用什么来创造想要的结果,我已经尝试了两天了,我想知道是否有人可以指出我正确的方向。谢谢!
最佳答案
我希望这能以某种方式帮助您 :)
更新
我更新了代码以解决向任一方向滚动的问题。可以在此处找到更新的要点
首先我要为卡片定义一些模型
class Card {
var someCardData : String?
}
接下来,创建一个 Collection View 单元格,里面有一个卡片 View ,我们将对其应用转换
class CollectionViewCell : UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(cardView)
self.addSubview(cardlabel)
}
override func prepareForReuse() {
super.prepareForReuse()
cardView.alpha = 1.0
cardView.layer.transform = CATransform3DIdentity
}
override func layoutSubviews() {
super.layoutSubviews()
cardView.frame = CGRectMake(contentPadding,
contentPadding,
contentView.bounds.width - (contentPadding * 2.0),
contentView.bounds.height - (contentPadding * 2.0))
cardlabel.frame = cardView.frame
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
lazy var cardView : UIView = {
[unowned self] in
var view = UIView(frame: CGRectZero)
view.backgroundColor = UIColor.whiteColor()
return view
}()
lazy var cardlabel : UILabel = {
[unowned self] in
var label = UILabel(frame: CGRectZero)
label.backgroundColor = UIColor.whiteColor()
label.textAlignment = .Center
return label
}()
}
接下来使用 Collection View 设置 View Controller 。正如您将看到的,有一个 CustomCollectionView 类,我将在最后定义它。
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
var cards = [Card(), Card()]
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
collectionView.frame = CGRectMake(0, 0, self.view.bounds.width, tableViewHeight)
collectionView.contentInset = UIEdgeInsetsMake(0, 0, 0, contentPadding)
}
lazy var collectionView : CollectionView = {
[unowned self] in
// MARK: Custom Flow Layout defined below
var layout = CustomCollectionViewFlowLayout()
layout.contentDelegate = self
var collectionView = CollectionView(frame: CGRectZero, collectionViewLayout : layout)
collectionView.clipsToBounds = true
collectionView.showsVerticalScrollIndicator = false
collectionView.registerClass(CollectionViewCell.self, forCellWithReuseIdentifier: "CollectionViewCell")
collectionView.delegate = self
collectionView.dataSource = self
return collectionView
}()
// MARK: UICollectionViewDelegate, UICollectionViewDataSource
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cards.count
}
func collectionView(collectionView : UICollectionView, layout collectionViewLayout:UICollectionViewLayout, sizeForItemAtIndexPath indexPath:NSIndexPath) -> CGSize {
return CGSizeMake(collectionView.bounds.width, tableViewHeight)
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cellIdentifier = "CollectionViewCell"
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellIdentifier, forIndexPath: indexPath) as! CollectionViewCell
cell.contentView.backgroundColor = UIColor.blueColor()
// UPDATE If the cell is not the initial index, and is equal the to animating index
// Prepare it's initial state
if flowLayout.animatingIndex == indexPath.row && indexPath.row != 0{
cell.cardView.alpha = 0.0
cell.cardView.layer.transform = CATransform3DScale(CATransform3DIdentity, 0.0, 0.0, 0.0)
}
return cell
}
}
已更新 - 现在是真正棘手的部分。我要定义 CustomCollectionViewFlowLayout。协议(protocol)回调返回流布局计算的下一个插入索引
protocol CollectionViewFlowLayoutDelegate : class {
func flowLayout(flowLayout : CustomCollectionViewFlowLayout, insertIndex index : NSIndexPath)
}
/**
* Custom FlowLayout
* Tracks the currently visible index and updates the proposed content offset
*/
class CustomCollectionViewFlowLayout: UICollectionViewFlowLayout {
weak var contentDelegate: CollectionViewFlowLayoutDelegate?
// Tracks the card to be animated
// TODO: - Adjusted if cards are deleted by one if cards are deleted
private var animatingIndex : Int = 0
// Tracks thje currently visible index
private var visibleIndex : Int = 0 {
didSet {
if visibleIndex > oldValue {
if visibleIndex > animatingIndex {
// Only increment the animating index forward
animatingIndex = visibleIndex
}
if visibleIndex + 1 > self.collectionView!.numberOfItemsInSection(0) - 1 {
let currentEntryIndex = NSIndexPath(forRow: visibleIndex + 1, inSection: 0)
contentDelegate?.flowLayout(self, insertIndex: currentEntryIndex)
}
} else if visibleIndex < oldValue && animatingIndex == oldValue {
// if we start panning to the left, and the animating index is the old value
// let set the animating index to the last card.
animatingIndex = oldValue + 1
}
}
}
override init() {
super.init()
self.minimumInteritemSpacing = 0.0
self.minimumLineSpacing = 0.0
self.scrollDirection = .Horizontal
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// The width offset threshold percentage from 0 - 1
let thresholdOffsetPrecentage : CGFloat = 0.5
// This is the flick velocity threshold
let velocityThreshold : CGFloat = 0.4
override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
let leftThreshold = CGFloat(collectionView!.bounds.size.width) * ((CGFloat(visibleIndex) - 0.5))
let rightThreshold = CGFloat(collectionView!.bounds.size.width) * ((CGFloat(visibleIndex) + 0.5))
let currentHorizontalOffset = collectionView!.contentOffset.x
// If you either traverse far enought in either direction,
// or flicked the scrollview over the horizontal velocity in either direction,
// adjust the visible index accordingly
if currentHorizontalOffset < leftThreshold || velocity.x < -velocityThreshold {
visibleIndex = max(0 , (visibleIndex - 1))
} else if currentHorizontalOffset > rightThreshold || velocity.x > velocityThreshold {
visibleIndex += 1
}
var _proposedContentOffset = proposedContentOffset
_proposedContentOffset.x = CGFloat(collectionView!.bounds.width) * CGFloat(visibleIndex)
return _proposedContentOffset
}
}
并在您的 View Controller 中定义委托(delegate)方法,以便在委托(delegate)告诉它需要新索引时插入新卡片
extension ViewController : CollectionViewFlowLayoutDelegate {
func flowLayout(flowLayout : CustomCollectionViewFlowLayout, insertIndex index : NSIndexPath) {
cards.append(Card())
collectionView.performBatchUpdates({
self.collectionView.insertItemsAtIndexPaths([index])
}) { (complete) in
}
}
下面是自定义的 Collection View ,它在相应地滚动时应用动画 :)
class CollectionView : UICollectionView {
override var contentOffset: CGPoint {
didSet {
if self.tracking {
// When you are tracking the CustomCollectionViewFlowLayout does not update it's visible index until you let go
// So you should be adjusting the second to last cell on the screen
self.adjustTransitionForOffset(NSIndexPath(forRow: self.numberOfItemsInSection(0) - 1, inSection: 0))
} else {
// Once the CollectionView is not tracking, the CustomCollectionViewFlowLayout calls
// targetContentOffsetForProposedContentOffset(_:withScrollingVelocity:), and updates the visible index
// by adding 1, thus we need to continue the trasition on the second the last cell
self.adjustTransitionForOffset(NSIndexPath(forRow: self.numberOfItemsInSection(0) - 2, inSection: 0))
}
}
}
/**
This method applies the transform accordingly to the cell at a specified index
- parameter atIndex: index of the cell to adjust
*/
func adjustTransitionForOffset(atIndex : NSIndexPath) {
if let lastCell = self.cellForItemAtIndexPath(atIndex) as? CollectionViewCell {
let progress = 1.0 - (lastCell.frame.minX - self.contentOffset.x) / lastCell.frame.width
lastCell.cardView.alpha = progress
lastCell.cardView.layer.transform = CATransform3DScale(CATransform3DIdentity, progress, progress, 0.0)
}
}
}
关于ios - 向左滑动创建一个新的 collectionViewCell,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36348464/
在 Vim 中,是否可以将窗口“移动”到左侧或右侧?例如,类似于 r或 x ,但是左/右而不是上/下? 例如,如果我有这个布局: +---+---+---+ | | +---+ | A +--
我有一系列枚举的网页,目前每个页面上都有一个向左和向右箭头图标,用于导航到该系列中的上一页/下一页。 我的问题:实现向左/向右滑动手势以执行相同操作的最简单(!)方法是什么? (没有其他手势 - 请不
在我的网站上,我有三个 div、一个标题、一个主要内容和右侧的侧边栏。侧边栏是实际问题。我希望侧边栏始终位于浏览器窗口的右侧,如果用户调整窗口大小使宽度低于主栏和侧边栏,则侧边栏会停在主要内容的左边缘
请访问http://lindseymotors.com/v然后单击银色卡车顶部的照片。当展开的 View 弹出时,主照片下方有一排照片。当您点击它们时,它会切换到该照片并在您选择的图像上方显示“正在查
请看一下这个 DEMO 。 使用 this post 中的代码我已成功通过单击左侧或右侧链接使图像水平移动。 我想要实现的是以某种方式使图像跳跃更平滑。 这是我的代码: var scroll
我正在尝试向子菜单添加一个不错的小效果,但我不能使用 marginLeft,因为它会弄乱容器。所以我试图通过使用 left 而不是 marginLeft 来实现它,但我无法让它工作......任何想法
我有一个日历,我想让左右滑动和切换月份成为可能。 是否有任何听众向左/向右滑动? 谢谢! 最佳答案 您可以像下面的示例一样使用 SimpleOnGestureListener:(显然,将 toast
我正在尝试实现一个ng-repeats 水平用户媒体对象的小部件,我应该能够向左/向右滑动以显示下一张媒体卡。小部件应该看起来像这样 如图所示 下一张卡片头像是半可见的,暗示还有更多卡片可以刷。 这就
我正在创建一个显示多个“页面”内容的应用程序。通常,这代表基于桌面的应用程序上的选项卡。 在 android 上,我希望能够在屏幕上的任意位置使用手指在信息选项卡之间轻拂。这些选项卡的内容将只是 XM
我正在使用: ViewController = [[ViewControllerClass alloc] initWithNibName:@"ViewControllerClass" bundle:[
这个问题已经存在: How to make this into a sliding left/right div 已关闭10 年前。 下面提供的是我的 html 和 css 代码的片段,我不仅需要在
尝试将 h3 向左滑动,p 向右滑动,.info 从下到上滑动 slider .实际上它在没有 jQuery 的 Chrome 和 Opera 中工作得很好,因为我使用了 Animate.css,但它
我发现这个很好的进度条 CSS 动画。 但我想transform: translate而不是left/right。 如何切换到 transform 它?我试过了,但它不起作用: https://cod
我想用 bootstrap 创建一个表,并在列中插入一些左拉和右拉的内容: http://jsfiddle.net/Zoker/sgdfgkvL/ Some content Some
我怎样才能得到 重叠并停在 Logo 的左边缘?当屏幕宽度扩展时,我试图让绿色条向左扩展,但我希望它停在 Logo 的左边缘。 我试过了 position: absolute;在 #green_bar
试图将这个“手势”功能添加到我的第一个程序中,几乎我所做的每一次搜索都来到了这个线程: Fling gesture detection on grid layout 我能够让它工作..但就我而言,我不
我有带 tablayout 的 viewpager,在 ViewPager 内部我有一个正在创建 RecyclerView 的 fragment , 在 RecyclerVIew roe 项目中,我创
我一直在研究图片库 [html5],它在桌面版本中运行良好,我想为 Ipad/平板电脑设备添加基于触摸的事件。 您能否建议如何使用 javascript/jquery 添加基于触摸的事件。 谢谢,斯里
所以我想通过将盒子相乘然后去掉下半部分来使盒子向各个方向移动。向上和向右有效,但向下和向左无效。单击 4 个按钮中的 1 个即可调用这些功能。基本的 CSS 和 HTML。我该如何解决这个问题? va
我已经像这样创建了一个 RecyclerView ItemTouchHelper - public class MyItemTouchHelper extends android.support.v7
我是一名优秀的程序员,十分优秀!