#swift #uiscrollview #uistackview
#swift #uiscrollview #uistackview
Вопрос:
У меня есть UIScrollView
, который содержит представление стека — я в основном копирую функцию вкладок.
Одна вкладка имеет более высокий вид, чем другая, поэтому, когда я скрываю представление в стековом представлении, его размер изменяется.
Это приводит к тому, что вид прокрутки переходит к смещению, которое соответствует более короткому виду, в случае, если пользователь прокрутил до верха.
Возможно ли вместо этого анимировать это изменение? Вместо перехода представление прокручивается до правильного смещения? Я не уверен, как этого добиться.
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:
Вероятно, вы захотите внести некоторые дополнительные изменения, но это может помочь вам продвинуться по пути.
В вашем блоке 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
}
}
}