为什么我的表格视图scrollToRow仅部分滚动?

时间:2018-03-07 06:10:18

标签: swift uitableview uiscrollview uiscrollviewdelegate

我正在实现一个UITableView,我希望桌面视图在用户开始滚动后自动向上滚动到顶部。我使用了与此post类似的逻辑来检测用户滚动的方向。当用户开始向上滚动时,下面的功能会检测到滚动,但table.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)非常慢,并且会在一定程度上停止到单元格的顶部。我怎样才能解决这个问题。

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    print("Dragging has ended")
    //print("lastContentOffSet: \(self.lastContentOffset)")
    if (self.lastContentOffset < scrollView.contentOffset.y) {
        // moved to top
        print("Scrolled down")
    } else if (self.lastContentOffset > scrollView.contentOffset.y) {
        // moved to bottom
        print("Scrolled up")
        table.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
    } else {
        // didn't move
    }
}

以下是完整的代码。

import UIKit

class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {

    @IBOutlet weak var table:UITableView!
    @IBOutlet weak var photosButton: UIButton!

    var lastContentOffset: CGFloat = 0

    override func viewDidLoad() {
        super.viewDidLoad()
        table.delegate = self
        table.dataSource = self 
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return  2
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        var cell:UITableViewCell = UITableViewCell()
        if indexPath.row == 1{
            cell = table.dequeueReusableCell(withIdentifier: "cameraCell", for: indexPath) as! cameraCell
            cell.backgroundColor = .red
        }else{
            cell = table.dequeueReusableCell(withIdentifier: "photoAlbumCell", for: indexPath) as! photoAlbumCell
            cell.backgroundColor = .blue
        }
        cell.selectionStyle = UITableViewCellSelectionStyle.none
        return cell
    }
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UIScreen.main.bounds.height
    }
    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        print("Scrolling began")
        self.lastContentOffset = scrollView.contentOffset.y
    }
    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        print("Dragging has ended")
        if (self.lastContentOffset < scrollView.contentOffset.y) {
            // moved to top
            print("Scrolled down")
        } else if (self.lastContentOffset > scrollView.contentOffset.y) {
            // moved to bottom
            print("Scrolled up")
            table.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
        } else {
            // didn't move
        }
    }
    @IBAction func photoAlbumButton(_ sender: UIButton) {
        table.scrollToRow(at: IndexPath(row: 1, section: 0), at: .top, animated: true)
    }
}

class cameraCell:UITableViewCell{
    override func awakeFromNib() {
        super.awakeFromNib()
    } 
 }
class photoAlbumCell: UITableViewCell {
    override func awakeFromNib() {
        super.awakeFromNib()
    }
}

1 个答案:

答案 0 :(得分:1)

针对上述问题有两种解决方案 -

解决方案1 ​​ - 用户结束拖动时必须处理func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool)。要处理正常滚动并在减速时离开,请使用func scrollViewDidEndDecelerating(_ scrollView: UIScrollView)。请注意,您需要这两种委托方法来处理滚动。两个代理都将执行相同的代码集。唯一的变化是第一个代理执行代码的方式,即,您需要添加!decelerate条件,因为我们在减速方案的第二个代理中进行处理。请注意,抬起手指后,它会慢慢滚动,直到调用代表,但它会确保它滚动到顶部。换句话说,您的代码应如下所示 -

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    print("Dragging has ended")
    if (self.lastContentOffset < scrollView.contentOffset.y) {
        // moved to top
        print("Scrolled down")
    } else if (self.lastContentOffset > scrollView.contentOffset.y) {
        // moved to bottom
        print("Scrolled up")
        table.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
    } else {
        // didn't move
    }
}

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    if !decelerate { // decelerate logic will be handled above
        print("Dragging has ended")
        if (self.lastContentOffset < scrollView.contentOffset.y) {
            // moved to top
            print("Scrolled down")
        } else if (self.lastContentOffset > scrollView.contentOffset.y) {
            // moved to bottom
            print("Scrolled up")
            table.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
        } else {
            // didn't move
        }
    }
}

解决方案2 - 最简单的解决方法是将table.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)封装在Dispatch.main中,强制它在主线程中运行(尽管它在主线程中运行) ,为了滚动目的,你需要它来强行运行它。但是,我还没有在真实设备中测试过这个解决方案,所以不确定这是否会显示出一些混蛋效果。

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    print("Dragging has ended")
    if (self.lastContentOffset < scrollView.contentOffset.y) {
        // moved to top
        print("Scrolled down")
    } else if (self.lastContentOffset > scrollView.contentOffset.y) {
        // moved to bottom
        print("Scrolled up")
        DispatchQueue.main.async {
            self.table.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
        }

    } else {
        // didn't move
    }
}

我个人更喜欢第一种解决方案,因为这是您可以正确处理拖拽和减速方案的方式。