티스토리 뷰

일반 StickyHeader는 스크롤을 아래로 내렸을 때 위쪽에 윗변이 붙어있는 것 처럼 보이는 화면을 말한다. 하지만 헤더에는 스크롤을 아래로 내렸을 때도 계속 보여야 하는 필수적인 정보가 포함될 수도 있다. StickyHeader 효과를 그대로 가지고 있으면서 특헤더 중 특정 아래 영역을 항상 보이게 하는 autolayout을 만들어보자.

스토리보드로 만들면 만들 때는 쉽지만 유지보수 하기가 어렵기 때문에 스토리보드를 사용하지 않고 모두 코드로 작성해보도록 하겠다. 스토리보드를 사용하지 않고 코드로 화면을 만드는 방법은 아래 글을 참고하면 된다.

 

스토리보드 탈출

 

먼저 scrollView를 추가한다. autolayout이 중요하기 때문에 constraint 설정 부분을 자세히 봐야 한다. 참고로 모든 view를 추가하는 코드는 viewDidLoad()에 작성한다.

 

let scrollView = UIScrollView()
scrollView.backgroundColor = .systemBlue
// 1
scrollView.delegate = self
scrollView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView)
// 2
NSLayoutConstraint.activate([
	scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
	scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
	scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
	scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
  1. 스크롤을 할 떄마다 헤더의 layout을 바꿔줘야 하기 때문에 delegate를 설정한다.
  2. 화면에 꽉 차도록 layout을 설정한다.

 

let contentView = UIView()
contentView.backgroundColor = .systemGreen
contentView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(contentView)
// 1
NSLayoutConstraint.activate([
	contentView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
	contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
	contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
])
  1. 이렇게 까지만 하면 layout 에러가 발생하지만 밑에서 추가할 다른 view들에 맞게 contentView의 bottomAnchor를 설정할 것이기 때문에 일단 부분적으로 적용시켰다.

 

// title
let titleLabel = UILabel()
titleLabel.text = "제목"
titleLabel.font = .boldSystemFont(ofSize: 24)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(titleLabel)
NSLayoutConstraint.activate([
	titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12),
	titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 12)
])

// dummy cell
let cell = UIView()
cell.backgroundColor = .systemPink
cell.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(cell)
NSLayoutConstraint.activate([
	cell.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 12),
	cell.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
	cell.widthAnchor.constraint(equalTo: contentView.widthAnchor, constant: -24),
	cell.heightAnchor.constraint(equalToConstant: 1000),
	// 1
	cell.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -12)
])

contentView.layoutIfNeeded()
scrollView.contentSize = CGSize(width: view.frame.width, height: contentView.frame.height)
  1. 스크롤을 할 수 있게 충분히 큰 view를 추가하고 bottom을 설정해서 자동으로 스크롤 범위가 정해질 수 있도록 한다.

 

// 1
scrollView.contentInset.top = headerHeightMax

let header = UIView()
header.backgroundColor = .systemPurple
header.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(header)
NSLayoutConstraint.activate([
	// 2
	header.bottomAnchor.constraint(greaterThanOrEqualTo: view.safeAreaLayoutGuide.topAnchor, constant: headerHeightMin),
	header.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
	header.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
])
// 3
headerHeightAnchor = header.heightAnchor.constraint(greaterThanOrEqualToConstant: headerHeightMax)
headerHeightAnchor?.isActive = true
headerTopAnchor = header.topAnchor.constraint(greaterThanOrEqualTo: scrollView.topAnchor, constant: -headerHeightMax)
headerTopAnchor?.isActive = true
  1. header 크기 만큼 inset을 준다.
  2. 아래쪽 부분이 언제나 화면에 보이게 하기 위해서 bottomAnchor를 설정한다.
  3. 스크롤 할 때 마다 sticky 효과를 만드려면 height와 top anchor는 delegate함수에서 추가적인 layout이 필요하다. delegate에서 접근하기 쉽게 따로 변수로 빼서 설정한다.

 

extension MainViewController: UIScrollViewDelegate {
	func scrollViewDidScroll(_ scrollView: UIScrollView) {
		// 1
		if scrollView.contentOffset.y < -scrollView.adjustedContentInset.top {
			headerHeightAnchor?.constant = -scrollView.contentOffset.y
			headerTopAnchor?.constant = scrollView.contentOffset.y
		}
	}
}
  1. 스크롤을 내릴 때만 layout을 다시 계산해서 sticky 효과를 만든다.

 

기본 상태. 각 뷰를 구분하기 위해 색을 다르게 설정했다.
스크롤을 올렸을 때 보라색 헤더가 상단에 고정된다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함