在Tableview滚动时对UIView位置进行动画处理,然后再次拖动TableView

时间:2020-06-03 11:25:55

标签: swift uitableview uiview uiscrollview uiviewanimation

我有一个项目列表,顶部是一个标题菜单。

当用户向上滚动菜单时,菜单也应在屏幕外滚动;当用户返回列表顶部时,菜单应该可见。

行为与tableViewHeader大致相同。

但是,如果标题不在屏幕上,并且用户结束了列表上的向下拖动,则此标题视图应从顶部向下进行动画处理。如果用户随后在列表上结束了向上拖动,则标题应在离开屏幕的情况下进行动画处理。

我已经实现了下面的第一部分,但是正在努力实现动画效果。

我曾考虑过使用菜单的2个视图,其中一个进行滚动,而一个用于对其位置进行动画处理,但是这种感觉还是不错的,我相信必须有一种更好的方法来避免重复视图。

class TableViewScene: UIViewController {

  let data = Array(0...99)

  var headerViewOneTopConstraint: NSLayoutConstraint!
  var tableViewTopConstraint: NSLayoutConstraint!

  lazy var headerViewOne: UIView = {
    let view = UIView(frame: .zero)
    view.translatesAutoresizingMaskIntoConstraints = false
    view.backgroundColor = .purple
    return view
  }()

  lazy var tableView: UITableView = {
    let view = UITableView(frame: .zero)
    view.translatesAutoresizingMaskIntoConstraints = false
    view.delegate = self
    view.dataSource = self
    view.tableFooterView = .init()
    view.refreshControl = .init()
    return view
  }()

  override func viewDidLoad() {
    super.viewDidLoad()

    navigationController?.navigationBar.isTranslucent = false

    headerViewOneTopConstraint = headerViewOne.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor)
    tableViewTopConstraint = tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 184)

    [headerViewOne, tableView].forEach(view.addSubview)

    NSLayoutConstraint.activate([
      headerViewOneTopConstraint,
      headerViewOne.leadingAnchor.constraint(equalTo: view.leadingAnchor),
      headerViewOne.trailingAnchor.constraint(equalTo: view.trailingAnchor),
      headerViewOne.heightAnchor.constraint(equalToConstant: 184),

      tableViewTopConstraint,
      tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
      tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
      tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
    ])
  }
}

extension TableViewScene: UITableViewDelegate {
  func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let offsetY = scrollView.contentOffset.y

    headerViewOneTopConstraint.constant = max(-184, min(0, -offsetY))
    tableViewTopConstraint.constant = 184 - max(0, offsetY)
  }
}

我还尝试过将其添加到视图中

  func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    let translation = scrollView.panGestureRecognizer.translation(in: scrollView.superview)

    if translation.y > 0 {
      headerViewOne.transform = .init(translationX: 0, y: 184)
    } else if translation.y < 0 {
      headerViewOne.transform = .identity
    }
  }

这不起作用,而只是在滚动到顶部时呈现一个用于填充视图的黑色空间。

1 个答案:

答案 0 :(得分:0)

很难确切地说出您要查找的内容,但这可能对您有用。在此实现中,当用户向上拖动时,标题将隐藏,而当用户向下拖动时,标题将显示。无论滚动位置如何,都是如此。

我对您的约束之一进行了更改,以便tableView的顶部固定在标题的底部:

    tableViewTopConstraint = tableView.topAnchor.constraint(equalTo: headerViewOne.bottomAnchor)

我也在跟踪标头的状态,如下所示:

enum HeaderState {
    case hidden
    case revealed
    case hiding
    case revealing
}

var headerState: HeaderState = .revealed

现在为滚动逻辑。如果滚动位置在顶部附近,则我们要手动显示/隐藏标题。但是,如果滚动位置在底部或中央附近,那么我们要设置显示/隐藏功能的动画。

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let translation = scrollView.panGestureRecognizer.translation(in: scrollView.superview)

    if scrollView.contentOffset.y < 184 {
        if translation.y > 0 && headerState != .revealed && headerState != .revealing {
            headerViewOneTopConstraint.constant = max(-184, min(0, -scrollView.contentOffset.y))
        } else if translation.y < 0 {
            headerViewOneTopConstraint.constant = max(-184, min(0, -scrollView.contentOffset.y))
        }
        return
    }

    if translation.y > 0 && headerState == .hidden {

        self.headerState = .revealing
        UIView.animate(withDuration: 0.4, animations: {
            self.headerViewOneTopConstraint.constant = 0
            self.view.layoutIfNeeded()
        }, completion: { _ in
            self.headerState = .revealed
        })

    } else if translation.y < 0 && headerState == .revealed {

        self.headerState = .hiding
        UIView.animate(withDuration: 0.4, animations: {
            self.headerViewOneTopConstraint.constant = -184
            self.view.layoutIfNeeded()
        }, completion: { _ in
            self.headerState = .hidden
        })
    }
}

试一下,看看它是否满足您的需求。