관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] Thumbnail 나열된 뷰 구현 방법 (Twitter 썸네일 나열 뷰) 본문

UI 컴포넌트 (swift)

[iOS - swift] Thumbnail 나열된 뷰 구현 방법 (Twitter 썸네일 나열 뷰)

jake-kim 2023. 1. 31. 22:58

Thumbnail이 나열된 뷰

  • 트위터에서 프로필이 나열된 형태의 뷰가 존재
  • 이 뷰에서 동그란 썸네일이 나열된 형태이며, 얼핏 보기에 어려워 보이지만 UIStackView를 사용하면 쉽게 구현이 가능

구현 아이디어

  • 1. stackView의 spacing 프로퍼티에 음수값을 대입
  • 2. 안에 넣는 뷰들은 나중에 추가되는 뷰가 아래로 가야하므로 layer.zPosition을 통해 제어
  • 3. 안에 넣는 뷰들의 borderWidth와 color를 배경 색과 같게하여, 마치 썸네일이 겹치는 부분은 배경색으로 표현되게끔구현

구현

  • 상수값 color 준비
import UIKit

class ViewController: UIViewController {
    private let color = UIColor.lightGray
}
  • 필요한 stackView와 데이터 준비
private let stackView: UIStackView = {
    let stackView = UIStackView()
    stackView.axis = .horizontal
    // 1.
    stackView.spacing = -15
    stackView.translatesAutoresizingMaskIntoConstraints = false
    return stackView
}()

private var images = (0...5).map { _ in UIImage(named: "dog") }
  • viewDidLoad에서 레이아웃 관련 메소드와, 이미지 뷰들을 배치하는 메소드 호출
override func viewDidLoad() {
    super.viewDidLoad()
    setUpLayout()
    setData()
}
  • setUpLayout에서 스택 뷰 레이아웃 결정
private func setUpLayout() {
    view.backgroundColor = color

    view.addSubview(stackView)
    NSLayoutConstraint.activate([
        stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
    ])
}
  • setData()에서 데이터 만큼 UIImageView를 만들어서 stackView에 넣을것인데, 모두 원 형태여야 하므로, RoundableImageView 따로 정의
    • contentMode도 미리 scaleAspectFill로 설정 (비율에 맞게 커지며 나머지 부분은 잘리는 속성)
final class RoundableImageView: UIImageView {
    init() {
        super.init(frame: .zero)
        layer.masksToBounds = true
        contentMode = .scaleAspectFill
    }
    required init?(coder: NSCoder) {
        fatalError()
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        layer.cornerRadius = frame.height / 2
    }
}
  • 다시 되돌아 와서, setData() 구현
    • 핵심 부분은 imageViews를 구하는 부분
private func setData() {
    let imageViews = { ... }
    imageViews.forEach(stackView.addArrangedSubview(_:))
}
  • 데이터를 순회하며 constraint 설정
let imageViews = images.enumerated().map { index, image in
    let imageView = RoundableImageView()
    imageView.image = image
    imageView.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        imageView.heightAnchor.constraint(equalToConstant: 50),
        imageView.widthAnchor.constraint(equalToConstant: 50),
    ])

    // TODO
}
  • 안에 넣는 뷰들은 나중에 추가되는 뷰가 아래로 가야하므로 layer.zPosition을 통해 제어
imageView.layer.zPosition = CGFloat(-index)
  • borderWidth와 borderColor를 적절히 설정하여 마치 썸네일이 겹치는 부분은 배경색으로 표현되게끔구현
imageView.layer.borderWidth = 3
imageView.layer.borderColor = color.cgColor
  • setData() 완성 코드
private func setData() {
    let imageViews = images.enumerated().map { index, image in
        let imageView = RoundableImageView()
        imageView.image = image
        imageView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            imageView.heightAnchor.constraint(equalToConstant: 50),
            imageView.widthAnchor.constraint(equalToConstant: 50),
        ])
        
        // 2.
        imageView.layer.zPosition = CGFloat(-index)
        
        // 3.
        imageView.layer.borderWidth = 3
        imageView.layer.borderColor = color.cgColor
        
        return imageView
    }
    
    imageViews.forEach(stackView.addArrangedSubview(_:))
}

* 전체 코드: https://github.com/JK0369/ExThumbnailView

Comments