我怎么能让这个tableview页眉的Y位置发生变化的动画呢?

问题描述 投票:2回答:2

我想在用户向上滚动我的feed时,将我的表视图标题滑动到视图中,而标题在屏幕外。如果用户向下滚动,我想再次隐藏它。

我相信我使用下面的方法就可以实现。

final class AccountSettingsController: UITableViewController {

  let items: [Int] = Array(0...500)

  private lazy var menuView: UIView = {
    let view = UIView(frame: .zero)
    view.translatesAutoresizingMaskIntoConstraints = false
    view.backgroundColor = .systemPink
    view.heightAnchor.constraint(equalToConstant: 120).isActive = true
    view.widthAnchor.constraint(equalToConstant: 100).isActive = true
    return view
  }()

  override func viewDidLoad() {
    super.viewDidLoad()

    edgesForExtendedLayout = []
    extendedLayoutIncludesOpaqueBars = false

    tableView.tableHeaderViewWithAutolayout = menuView

    tableView.tableFooterView = .init()
    tableView.refreshControl = .init()
  }


  override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return items.count
  }

  override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = UITableViewCell()
    cell.textLabel?.text = "This is setting #\(indexPath.row)"

    return cell
  }

  override func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let contentOffset = scrollView.contentOffset.y
    let transY = scrollView.panGestureRecognizer.translation(in: scrollView).y

    if scrollView.contentOffset.y > 120 {

      if transY > 0 {
        menuView.transform = .init(translationX: 0, y: contentOffset)

      } else if transY < 0 {
        menuView.transform = .identity
      }

      return
    }


    if scrollView.contentOffset.y <= 120 {

      if transY < 0 {
         menuView.transform = .identity

      } else if transY > 0 {
        menuView.transform = .init(translationX: 0, y: contentOffset)
      }

    }

  }
}

我想把标题的滑入滑出做成动画,这样它就会向下和向上滑动,而不是仅仅出现。

我尝试添加 UIView.animate 块,如

  if scrollView.contentOffset.y <= 120 {

      if transY < 0 {
         menuView.transform = .identity

      } else if transY > 0 {
        UIView.animate(withDuration: 0.25, animations: {
          self.menuView.transform = .init(translationX: 0, y: contentOffset)
        })
      }

    }

但这样做会在头部产生随机跳动的效果,根本达不到我想要的效果。

我已经附上了一个显示当前效果的gif,你可以看到它只是出现和消失。我想让这个动画向下和向上显示隐藏。

我还有一个扩展,可以为我的表格视图头设置正确的大小,你可以在这里找到----------。


extension UITableView {
  var tableHeaderViewWithAutolayout: UIView? {
    set (view) {
      tableHeaderView = view
      if let view = view {
        lowerPriorities(view)
        view.frame.size = view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
        tableHeaderView = view
      }
    }
    get {
      return tableHeaderView
    }
  }

  fileprivate func lowerPriorities(_ view: UIView) {
    for cons in view.constraints {
      if cons.priority.rawValue == 1000 {
        cons.priority = UILayoutPriority(rawValue: 999)
      }
      for v in view.subviews {
        lowerPriorities(v)
      }
    }
  }
}

display attempt

swift uitableview uiviewanimation
2个回答
1
投票

我同意Claudio的意见,使用 tableHeaderView 可能会使事情变得复杂,因为它并不是真的要用这种方式来操作。

试试下面的方法,看看是否能得到你想要的东西。

与其操作页眉的偏移,不如操作页眉的高度。你仍然可以给人一种滚动的感觉。

你也可以引入 scrollView.panGestureRecognizer.velocity(in: scrollView) 如果你想应用一个阈值,使菜单只在特定的滚动速度后出现。

class MyTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

  private let data: [Int] = Array(0...99)

  private let menuView: UIView = {
    let view = UIView(frame: .zero)
    view.backgroundColor = .purple
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
  }()

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

  var menuViewHeightAnchor: NSLayoutConstraint!
  var tableViewTopAnchor: NSLayoutConstraint!

  override func viewDidLoad() {
    super.viewDidLoad()

    menuViewHeightAnchor = menuView.heightAnchor.constraint(equalToConstant: 120)
    tableViewTopAnchor = tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 120)

    edgesForExtendedLayout = []
    [tableView, menuView].forEach(view.addSubview)

    NSLayoutConstraint.activate([
      menuViewHeightAnchor,
      menuView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
      menuView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
      menuView.trailingAnchor.constraint(equalTo: view.trailingAnchor),

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

  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return data.count
  }

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = UITableViewCell(frame: .zero)
    cell.textLabel?.text = "Cell #\(indexPath.row)"
    return cell
  }

  private var previousOffsetY: CGFloat = 0
  private var velocityThreshold: CGFloat = 500


  func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let offsetY = min(120, max(0, scrollView.contentOffset.y))
    let translation = scrollView.panGestureRecognizer.translation(in: scrollView)
    let velocity = scrollView.panGestureRecognizer.velocity(in: scrollView)

    let offsetYDiff = previousOffsetY - offsetY
    previousOffsetY = offsetY

    let adjustedOffset = menuViewHeightAnchor.constant + offsetYDiff

    tableViewTopAnchor.constant = 120 - max(0, offsetY)
    menuViewHeightAnchor.constant = min(120, adjustedOffset)


    guard offsetY == 120 else { return }

    if translation.y > 0  { // DRAGGED DOWN

      UIView.animate(withDuration: 0.33, animations: {
        self.menuViewHeightAnchor.constant = 120
        self.view.layoutIfNeeded()
      })

    } else if translation.y < 0 { // DRAGGED UP

      UIView.animate(withDuration: 0.33, animations: {
        self.menuViewHeightAnchor.constant = 0
        self.view.layoutIfNeeded()
      })
    }
  }

}


1
投票

我相信最好的方法是通过设置 menuView 的子视图 tableViewController例如,关于 viewDidLoad 方法,同时在 tableView 像这样。

let menuHeight: CGFloat = 120
var scrollStartingYPoint: CGFloat = 0

private lazy var menuView: UIView = {
  let view = UIView(frame: .zero)
  view.translatesAutoresizingMaskIntoConstraints = false
  view.backgroundColor = .systemPink
  view.heightAnchor.constraint(equalToConstant: menuHeight).isActive = true
  view.widthAnchor.constraint(equalToConstant: 100).isActive = true
  return view
}()

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    view.addSubview(menuView)

    NSLayoutConstraint.activate([
        menuView.leftAnchor.constraint(equalTo: view.leftAnchor),
        menuView.topAnchor.constraint(equalTo: view.topAnchor)
    ])

    edgesForExtendedLayout = []
    extendedLayoutIncludesOpaqueBars = false

    tableView.contentInset.top = menuHeight
}

然后你就可以添加滚动逻辑来显示菜单了,我做的和你有点不一样,我用的是... ... scrollViewWillBeginDragging 方法来存储初始滚动偏移量,然后确定滚动方向和偏移量。

func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
    scrollStartingYPoint = scrollView.contentOffset.y
}

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let tableYScrollOffset = scrollView.contentOffset.y
    let offset = abs(tableYScrollOffset - scrollStartingYPoint)

    if tableYScrollOffset < scrollStartingYPoint { // scrolling down
        if menuView.frame.origin.y >= 0 {
            return
        }

        var translationY = -menuHeight + offset

        if translationY > 0 {
            translationY = 0
        }

        menuView.transform = CGAffineTransform(translationX: 0, y: translationY)
    } else { // scrolling up
        if menuView.frame.origin.y <= -menuHeight {
            return
        }

        var translationY = -offset

        if translationY < -menuHeight {
            translationY = -menuHeight
        }

        menuView.transform = CGAffineTransform(translationX: 0, y: translationY)
    }
}

如果你想对菜单进行动画处理,那么你可以改变之前的代码,并使用你的动画代码,就像这样。

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let tableYScrollOffset = scrollView.contentOffset.y
    let offset = abs(tableYScrollOffset - scrollStartingYPoint)

    if tableYScrollOffset < scrollStartingYPoint { // scrolling down
        UIView.animate(withDuration: 0.25, animations: {
          self.menuView.transform = .identity
        })
    } else { // scrolling up
        if menuView.frame.origin.y <= -menuHeight {
            return
        }

        var translationY = -offset

        if translationY < -menuHeight {
            translationY = -menuHeight
        }

        menuView.transform = CGAffineTransform(translationX: 0, y: translationY)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.