带有 UIPanGestureRecognizer 的 UITableView 导致 "jumping" 触摸
UITableView with UIPanGestureRecognizer causes "jumping" touch
案例:https://github.com/ArtworkAD/ios_jumping_y.git
容器
class ContainerController: UIViewController {
@IBOutlet weak var container: UIView!
@IBOutlet weak var foregroundTopSpace: NSLayoutConstraint!
@IBOutlet weak var foregroundBottomSpace: NSLayoutConstraint!
}
容器连接到其右侧的 UITableViewController。
我想做什么
用户滚动 table,当他到达顶部时,我想用相同的动作将 table 向下移动。所以我干脆把容器视图的top/bottomspace改一下。然而,这会导致 table 跳转。
class MainController: UITableViewController, UIGestureRecognizerDelegate {
var panRecognizer: UIPanGestureRecognizer!
var gotStart: Bool = true
var startTouch: CGPoint!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.bounces = false
self.panRecognizer = self.tableView.panGestureRecognizer
self.panRecognizer.addTarget(self, action: "pan:")
self.tableView.addGestureRecognizer(self.panRecognizer)
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func pan(rec:UIPanGestureRecognizer){
if let parent = self.parentViewController as? ContainerController {
var y = self.tableView.contentOffset.y
var touch = rec.locationInView(self.view)
// JUMPING y
println(touch.y)
if y == -64 {
if self.gotStart || y < -64 || y > -64 {
self.startTouch = touch
self.gotStart = false
} else {
if rec.state == .Changed {
var changedVerticalHeight = (self.startTouch.y - touch.y)*(-1)
if changedVerticalHeight <= parent.view.frame.size.height && touch.y >= changedVerticalHeight {
parent.foregroundTopSpace.constant = changedVerticalHeight
parent.foregroundBottomSpace.constant = -changedVerticalHeight
}
} else if rec.state == .Ended {
parent.foregroundTopSpace.constant = 0
parent.foregroundBottomSpace.constant = 0
self.gotStart = true
}
}
}
}
}
}
结果:
有什么想法吗?容器视图肯定有问题,因为当我直接移动 table 视图时没有跳转。
您正在根据 self.view
系统坐标打印位置。 UITableViewController
使用像 self.view 这样的 table 视图。所以当用户滚动时,原点 (x:0 y:0) 开始移动。
您必须使用静态系统坐标。正确使用 UIViewController
和 UITableView
panGestureRecognizer
属性 打印位置并滚动:
- 新建
UIViewController
.
- 添加
UITableview
并设置 AutoLayout
。
- 连接
IBOutlet
与 UITableView
将代码添加到 UIViewController
class:
var panRecognizer: UIPanGestureRecognizer!
@IBOutlet weak var tableView: UITableView?
override func viewDidLoad() {
super.viewDidLoad()
self.panRecognizer = tableView?.panGestureRecognizer;
self.panRecognizer .addTarget(self, action: "pan:")
tableView?.addGestureRecognizer(self.panRecognizer)
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func pan(rec:UIPanGestureRecognizer){
println(rec.locationInView(self.view).y)
}
您可以下载项目示例here。
如果您只需要控制滚动,请记住 UIScrollView delegate method:
optional func scrollViewDidScroll(_ scrollView: UIScrollView)
可以帮到你。这是一个更清洁更简单的解决方案:
祝你好运。
我相信正是你想要的:
import UIKit
class MainController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
let beginingContentOffset: CGFloat = -64
if scrollView.contentOffset.y < beginingContentOffset {
scrollView.bounces = false
UIView.animateWithDuration(0.05, animations: { () -> Void in
scrollView.contentOffset = CGPointMake(0, 0)
}, completion: { (finished: Bool) -> Void in
scrollView.bounces = true
})
}
}
// Default table stuff, not related to problem
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell_ : UITableViewCell? = tableView.dequeueReusableCellWithIdentifier("CELL_ID") as? UITableViewCell
if(cell_ == nil){
cell_ = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "CELL_ID")
}
cell_!.textLabel!.text = "Row " + String(indexPath.row)
return cell_!
}
}
P.S。那个“-64”值。这是情节提要的保证金。您可以取消选中约束边距,它将为“0”。会最清楚
在我放完整代码之前,我会在我的答案中澄清一些事情
你可以使用函数scrollViewWillEndDragging
来知道contentOffset
的确切位置,看下面的代码:
override func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
println(targetContentOffset.memory.y)
}
您可以使用函数scrollViewDidEndDecelerating
知道滚动何时结束。
然后您可以通过以下方式合并以上两个技巧来实现您的目标:
class MainController: UITableViewController {
var position : CGFloat!
override func viewDidLoad() {
super.viewDidLoad()
}
override func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
// Save where the contentOffset go
position = targetContentOffset.memory.y
}
override func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
// If reach the top or more than the top when the user move the scroll
if (position < 0) {
var to = scrollView.contentOffset.y + 213
scrollView.setContentOffset(CGPoint(x: 0, y: to), animated: true)
}
}
// Default table stuff, not related to problem
/* Here comes the rest of your code of TableView */
}
我为您的项目删除了一些相对于 panGestureRecognizer
的代码并跳出进行测试。
在var to = scrollView.contentOffset.y + 213
行你可以添加position
变量或者你想要的值,我放213
只是为了测试下行。
希望对你有所帮助。
案例:https://github.com/ArtworkAD/ios_jumping_y.git
容器
class ContainerController: UIViewController {
@IBOutlet weak var container: UIView!
@IBOutlet weak var foregroundTopSpace: NSLayoutConstraint!
@IBOutlet weak var foregroundBottomSpace: NSLayoutConstraint!
}
容器连接到其右侧的 UITableViewController。
我想做什么
用户滚动 table,当他到达顶部时,我想用相同的动作将 table 向下移动。所以我干脆把容器视图的top/bottomspace改一下。然而,这会导致 table 跳转。
class MainController: UITableViewController, UIGestureRecognizerDelegate {
var panRecognizer: UIPanGestureRecognizer!
var gotStart: Bool = true
var startTouch: CGPoint!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.bounces = false
self.panRecognizer = self.tableView.panGestureRecognizer
self.panRecognizer.addTarget(self, action: "pan:")
self.tableView.addGestureRecognizer(self.panRecognizer)
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func pan(rec:UIPanGestureRecognizer){
if let parent = self.parentViewController as? ContainerController {
var y = self.tableView.contentOffset.y
var touch = rec.locationInView(self.view)
// JUMPING y
println(touch.y)
if y == -64 {
if self.gotStart || y < -64 || y > -64 {
self.startTouch = touch
self.gotStart = false
} else {
if rec.state == .Changed {
var changedVerticalHeight = (self.startTouch.y - touch.y)*(-1)
if changedVerticalHeight <= parent.view.frame.size.height && touch.y >= changedVerticalHeight {
parent.foregroundTopSpace.constant = changedVerticalHeight
parent.foregroundBottomSpace.constant = -changedVerticalHeight
}
} else if rec.state == .Ended {
parent.foregroundTopSpace.constant = 0
parent.foregroundBottomSpace.constant = 0
self.gotStart = true
}
}
}
}
}
}
结果:
有什么想法吗?容器视图肯定有问题,因为当我直接移动 table 视图时没有跳转。
您正在根据 self.view
系统坐标打印位置。 UITableViewController
使用像 self.view 这样的 table 视图。所以当用户滚动时,原点 (x:0 y:0) 开始移动。
您必须使用静态系统坐标。正确使用 UIViewController
和 UITableView
panGestureRecognizer
属性 打印位置并滚动:
- 新建
UIViewController
. - 添加
UITableview
并设置AutoLayout
。 - 连接
IBOutlet
与UITableView
将代码添加到 UIViewController
class:
var panRecognizer: UIPanGestureRecognizer!
@IBOutlet weak var tableView: UITableView?
override func viewDidLoad() {
super.viewDidLoad()
self.panRecognizer = tableView?.panGestureRecognizer;
self.panRecognizer .addTarget(self, action: "pan:")
tableView?.addGestureRecognizer(self.panRecognizer)
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func pan(rec:UIPanGestureRecognizer){
println(rec.locationInView(self.view).y)
}
您可以下载项目示例here。
如果您只需要控制滚动,请记住 UIScrollView delegate method:
optional func scrollViewDidScroll(_ scrollView: UIScrollView)
可以帮到你。这是一个更清洁更简单的解决方案:
祝你好运。
我相信正是你想要的:
import UIKit
class MainController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
let beginingContentOffset: CGFloat = -64
if scrollView.contentOffset.y < beginingContentOffset {
scrollView.bounces = false
UIView.animateWithDuration(0.05, animations: { () -> Void in
scrollView.contentOffset = CGPointMake(0, 0)
}, completion: { (finished: Bool) -> Void in
scrollView.bounces = true
})
}
}
// Default table stuff, not related to problem
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell_ : UITableViewCell? = tableView.dequeueReusableCellWithIdentifier("CELL_ID") as? UITableViewCell
if(cell_ == nil){
cell_ = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "CELL_ID")
}
cell_!.textLabel!.text = "Row " + String(indexPath.row)
return cell_!
}
}
P.S。那个“-64”值。这是情节提要的保证金。您可以取消选中约束边距,它将为“0”。会最清楚
在我放完整代码之前,我会在我的答案中澄清一些事情
你可以使用函数
scrollViewWillEndDragging
来知道contentOffset
的确切位置,看下面的代码:override func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) { println(targetContentOffset.memory.y) }
您可以使用函数
scrollViewDidEndDecelerating
知道滚动何时结束。
然后您可以通过以下方式合并以上两个技巧来实现您的目标:
class MainController: UITableViewController {
var position : CGFloat!
override func viewDidLoad() {
super.viewDidLoad()
}
override func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
// Save where the contentOffset go
position = targetContentOffset.memory.y
}
override func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
// If reach the top or more than the top when the user move the scroll
if (position < 0) {
var to = scrollView.contentOffset.y + 213
scrollView.setContentOffset(CGPoint(x: 0, y: to), animated: true)
}
}
// Default table stuff, not related to problem
/* Here comes the rest of your code of TableView */
}
我为您的项目删除了一些相对于 panGestureRecognizer
的代码并跳出进行测试。
在var to = scrollView.contentOffset.y + 213
行你可以添加position
变量或者你想要的值,我放213
只是为了测试下行。
希望对你有所帮助。