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
- UITextView
- 애니메이션
- MVVM
- 리팩토링
- ribs
- SWIFT
- UICollectionView
- Refactoring
- RxCocoa
- clean architecture
- uiscrollview
- Clean Code
- 리펙터링
- swift documentation
- map
- combine
- HIG
- swiftUI
- tableView
- 스위프트
- Observable
- Protocol
- rxswift
- 클린 코드
- ios
- 리펙토링
- uitableview
- Xcode
- collectionview
- Human interface guide
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 인스타그램 썸네일 프로필 UI 구현 방법 본문
사용한 프레임워크
구현 아이디어
- 동그란 뷰가 여기저기서 많이 사용되고 있으므로, RoundView, RoundImageView를 새로 만들어서 사용
- 동그란 뷰를 서브클래싱하여 구현
- 핵심은 온라인 상태를 암시해주는 초록색 뷰의 위치
- 가장 아래에 있는 UIView에 이미지가 들어갈 containerView와 온라인 상태를 암시하는 greenDotContainerView를 삽입
- containerView에는 이미지가 들어가는 형태로 구현
- 바깥 테두리의 그라데이션 효과는, layoutSubviews()에서 CAShapeLayer를 통해 테두리의 radius, width값을 구해서 사용
구현
- 동그란 뷰 RoundView, RoundImageView
// RoundView.swift
class RoundView: UIView {
override func layoutSubviews() {
super.layoutSubviews()
self.clipsToBounds = true
self.layer.cornerRadius = self.bounds.height / 2.0
}
}
// RoundImageView.swift
class RoundImageView: UIImageView {
override func layoutSubviews() {
super.layoutSubviews()
self.clipsToBounds = true
self.layer.cornerRadius = self.bounds.height / 2.0
}
}
- 썸네일 뷰 - RoundView를 서브클래싱
import UIKit
import SnapKit
import Then
class ThumbnailView: RoundView {
}
- Constants 선언
- imageOuterSpacing: 이미지 뷰와 이 뷰를 감싸는 컨테이너 사이의 간격이며, gradient를 사용하면 안쪽부터 채워지므로 borderWidth만큼 gradient 너비를 정할 것이므로 이 값까지 더함
- greenDotOuterSpacing: 그린 닷과 이 뷰를 감싸고 있는 컨테이너 사이의 간격
- greenDotViewInset: 그린 닷의 위치를 지정할때 사용할 값
- greenDotViewSize: 그릿 닷의 크기
- gradationWidth: 그라데이션의 크기
// MARK: Constants
private enum Const {
// view
static let imageOuterSpacing = Self.borderWidth + 4.0 // borderWidth만큼 안쪽으로 바깥 라인이 채워지므로 borderWidth값을 더함
static let greenDotOuterSpacing = 4.0
static let greenDotViewInset = UIEdgeInsets(top: 0, left: 0, bottom: 11, right: 11)
static let greenDotViewSize = CGSize(width: 16, height: 16)
// shapeLayer
static let gradationWidth = 2.0
}
- UI 선언
- 썸네일 이미지 UI = 이미지 컨테이너 + 이미지 뷰
- 그린 닷 이미지 UI = 그린닷 컨테이너 + 그린닷
- 바깥 테두리의 그라데이션 = shapeLayer로 바깥 테두리의 path를 구할 수 있기 때문에 gradationLayer와 같이 선언 (layoutSubviews에서 접근)
private let containerView = RoundView().then {
$0.backgroundColor = .clear
$0.isUserInteractionEnabled = false
$0.clipsToBounds = true
}
private let thumbnailImageView = RoundImageView().then {
$0.clipsToBounds = true
$0.contentMode = .scaleAspectFill
}
private let greenDotContainerView = RoundView().then {
$0.isUserInteractionEnabled = false
$0.backgroundColor = .white
$0.clipsToBounds = true
let greenDotView = RoundView().then {
$0.backgroundColor = .green
}
$0.addSubview(greenDotView)
greenDotView.snp.makeConstraints {
$0.edges.equalToSuperview().inset(Const.greenDotOuterSpacing)
}
}
private var gradientLayer = CAGradientLayer().then {
$0.colors = [UIColor.red, .systemPink, .yellow].map(\.cgColor)
}
private var shapeLayer = CAShapeLayer().then {
$0.lineWidth = Const.gradationWidth
$0.strokeColor = UIColor.black.cgColor
$0.fillColor = UIColor.clear.cgColor
}
- 초기화 (레이아웃)
required init?(coder: NSCoder) {
fatalError()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.containerView.layer.addSublayer(self.gradientLayer)
self.gradientLayer.mask = self.shapeLayer
self.addSubview(self.containerView)
self.addSubview(self.greenDotContainerView)
self.containerView.addSubview(self.thumbnailImageView)
self.containerView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
self.thumbnailImageView.snp.makeConstraints {
$0.edges.equalToSuperview().inset(Const.imageOuterSpacing)
}
self.greenDotContainerView.snp.makeConstraints {
$0.right.bottom.equalToSuperview().inset(Const.greenDotViewInset)
$0.size.equalTo(Const.greenDotViewSize)
}
}
- 그라데이션 효과 - layoutSubviews()에서 구현
override func layoutSubviews() {
super.layoutSubviews()
guard self.containerView.bounds != .zero else { return }
self.gradientLayer.frame = self.containerView.bounds
self.shapeLayer.path = UIBezierPath(
roundedRect: self.containerView.bounds.insetBy(dx: Const.gradationWidth, dy: Const.gradationWidth),
cornerRadius: self.bounds.height / 2.0
).cgPath
}
'UI 컴포넌트 (swift)' 카테고리의 다른 글
Comments