Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- rxswift
- tableView
- map
- MVVM
- Observable
- SWIFT
- uiscrollview
- UITextView
- Refactoring
- swift documentation
- ribs
- 리펙토링
- swiftUI
- 리팩토링
- HIG
- scrollview
- uitableview
- combine
- RxCocoa
- 스위프트
- clean architecture
- 클린 코드
- Protocol
- Xcode
- 애니메이션
- Clean Code
- Human interface guide
- UICollectionView
- collectionview
- ios
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] Thumbnail 나열된 뷰 구현 방법 (Twitter 썸네일 나열 뷰) 본문
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(_:))
}