删除嵌套视图时,UIScrollView的动画偏移量更改

时间:2020-08-21 14:14:51

标签: swift uiscrollview uistackview

我有一个UIScrollView,其中包含堆栈视图-我基本上是在复制制表符功能。

一个标签页的视图比另一个标签页高,因此当我在堆栈视图中隐藏该视图时,它会调整大小。

如果用户滚动到顶部,则滚动视图将跳至适合较短视图的偏移量。

enter image description here

是否可以为该更改设置动画?视图会滚动到正确的偏移量,而不是跳转?我不确定如何实现这一目标。


final class ScrollViewController: UIViewController {

  private var visibleTab: TabState = .overview {
    didSet {
      guard oldValue != visibleTab else { return }
      switch visibleTab {
        case .overview:
          self.spacesTab.isHidden = true
          self.overviewTab.isHidden = false
        case .spaces:
          self.spacesTab.isHidden = false
          self.overviewTab.isHidden = true
      }
    }
  }

  enum TabState {
    case overview
    case spaces
  }

  private lazy var scrollView: UIScrollView = {
    let view = UIScrollView(frame: .zero)
    view.translatesAutoresizingMaskIntoConstraints = false
    view.backgroundColor = .white
    view.delegate = self
    view.alwaysBounceVertical = true
    return view
  }()

  private let contentStackView: UIStackView = {
    let view = UIStackView(frame: .zero)
    view.translatesAutoresizingMaskIntoConstraints = false
    view.axis = .vertical
    view.alignment = .fill
    view.spacing = 8
    view.distribution = .fill
    return view
  }()

  private let tabSelectorView: UIStackView = {
    let view = UIStackView(frame: .zero)
    view.axis = .horizontal
    view.distribution = .fillEqually
    return view
  }()

  private let overviewTab: UIView = {
    let view = UIView(frame: .zero)
    view.backgroundColor = .darkGray
    view.heightAnchor.constraint(equalToConstant: 100).isActive = true
    view.isHidden = false
    return view
  }()

  private let spacesTab: UIView = {
    let view = UIView(frame: .zero)
    view.backgroundColor = .lightGray
    view.heightAnchor.constraint(equalToConstant: 780).isActive = true
    view.isHidden = true
    return view
  }()

  private let profileHeader = ScrollViewProfileHeaderView(frame: .zero)

  private lazy var overviewTabButton = makeButton(title: "Overview")
  private lazy var spacesTabButton = makeButton(title: "Spaces")

  override func viewDidLoad() {
    super.viewDidLoad()
    configureUI()
  }

}

extension ScrollViewController: UIScrollViewDelegate { }

private extension ScrollViewController {
  func configureUI() {
    overviewTabButton.addTarget(self, action: #selector(showOverviewTab), for: .touchUpInside)
    spacesTabButton.addTarget(self, action: #selector(showSpacesTab), for: .touchUpInside)

    [overviewTabButton, spacesTabButton].forEach(tabSelectorView.addArrangedSubview)

    profileHeader.translatesAutoresizingMaskIntoConstraints = false
    tabSelectorView.translatesAutoresizingMaskIntoConstraints = false

    [overviewTab, spacesTab].forEach(contentStackView.addArrangedSubview)

    [profileHeader, tabSelectorView, contentStackView].forEach(scrollView.addSubview(_:))
    view.addSubview(scrollView)
    NSLayoutConstraint.activate([
      scrollView.topAnchor.constraint(equalTo: view.topAnchor),
      scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
      scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
      scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),

      profileHeader.topAnchor.constraint(equalTo: scrollView.topAnchor),
      profileHeader.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
      profileHeader.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),

      tabSelectorView.topAnchor.constraint(equalTo: profileHeader.bottomAnchor),
      tabSelectorView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
      tabSelectorView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),

      contentStackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
      contentStackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
      contentStackView.topAnchor.constraint(equalTo: tabSelectorView.bottomAnchor, constant: 8),
      contentStackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
      contentStackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
    ])
  }

  func makeButton(title: String) -> UIButton {
    let button = UIButton(type: .system)
    button.setTitle(title, for: .normal)
    button.backgroundColor = .lightGray
    return button
  }

  @objc func showOverviewTab() {
    visibleTab = .overview
  }

  @objc func showSpacesTab() {
    visibleTab = .spaces
  }
}

final class ScrollViewProfileHeaderView: UIView {

  private let headerImage: UIImageView = {
    let view = UIImageView(frame: .zero)
    view.translatesAutoresizingMaskIntoConstraints = false
    view.contentMode = .scaleAspectFill
    view.clipsToBounds = true
    view.backgroundColor = .systemTeal
    return view
  }()

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

  override init(frame: CGRect) {
    super.init(frame: frame)

    backgroundColor = .white

    [headerImage, profileCard].forEach(addSubview(_:))

    NSLayoutConstraint.activate([
      headerImage.topAnchor.constraint(equalTo: topAnchor),
      headerImage.leadingAnchor.constraint(equalTo: leadingAnchor),
      headerImage.trailingAnchor.constraint(equalTo: trailingAnchor),
      headerImage.heightAnchor.constraint(equalToConstant: 180),

      profileCard.topAnchor.constraint(equalTo: headerImage.centerYAnchor),
      profileCard.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 48),
      profileCard.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -32),
      profileCard.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor, constant: -48),
      profileCard.heightAnchor.constraint(equalToConstant: 270),
    ])
  }

  required init?(coder: NSCoder) {
    return nil
  }
}

1 个答案:

答案 0 :(得分:1)

您可能需要进行一些其他更改,但这可能会让您无法自拔。

在您的visibleTab / didSet块中,当您隐藏UIView.animate()时使用spacesTab

private var visibleTab: TabState = .overview {
    didSet {
        guard oldValue != visibleTab else { return }
        switch self.visibleTab {
        case .overview:
            // set duration longer, such as 1.0, to clearly see the animation...
            UIView.animate(withDuration: 0.3) {
                self.spacesTab.isHidden = true
                self.overviewTab.isHidden = false
            }
        case .spaces:
            self.spacesTab.isHidden = false
            self.overviewTab.isHidden = true
        }
    }
}