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
- clean architecture
- 리펙토링
- HIG
- Protocol
- rxswift
- swift documentation
- Observable
- RxCocoa
- UITextView
- SWIFT
- swiftUI
- tableView
- 리펙터링
- uitableview
- Xcode
- MVVM
- UICollectionView
- 스위프트
- 애니메이션
- ios
- 리팩토링
- Clean Code
- Refactoring
- uiscrollview
- 클린 코드
- combine
- collectionview
- Human interface guide
- ribs
- map
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] custom UIStackView 활용 방법 (UIStackView 상속, flexible한 custom view, StackView margin, padding값) 본문
iOS 응용 (swift)
[iOS - swift] custom UIStackView 활용 방법 (UIStackView 상속, flexible한 custom view, StackView margin, padding값)
jake-kim 2021. 8. 20. 01:59CustomView를 만들 때 UIStackView를 사용하면 좋은점
- StackView가 기본적으로 가지고 있는 align 속성 사용 가능 (가운데 정렬도 alignment = .center로 쉽게 레이아웃 설정)
- 스택뷰에 `addArrangedSubview()`를 통해 view들을 넣어놓고 view들을 hidden시켜도, 자동으로 정렬되기 때문에 stack안에 들어가있는 view들의 레이아웃을 신경쓰지 않아도 되는 장점이 존재
UIStackView를 상속받아서 구현할때 알아야 하는 점
- StackView의 속성들의 값을 모두 기억
- stackView.spacing = 0
- stackView.axis = .horizontal
- stackView.alignment = .fill (.leading, .trailing, fill ,center, top, bottom, .firstBaseline, .lastBasline)
- stackView.distribution = .fill (.fill, fillEqually, .fillProportionally, .equalSpacing, .equalCentering)
- StackView안에 속해있는 특정 뷰를 기준으로 spacing을 주는 방법: setCustomSpacing(:after:) 사용
stackView.setCustomSpacing(8, after: pointyView)
- margin을 주는 방법: 아래 stackView에 label이 들어가 있을때, label이 보이게끔 stackView margin값을 주는 방법
// margin값 부여
stackView.isLayoutMarginsRelativeArrangement = true
stackView.layoutMargins = UIEdgeInsets(top: 5.0, left: 12.0, bottom: 5.0, right: 12.0)
- 전체 코드
class ViewController: UIViewController {
lazy var label: UILabel = {
let label = UILabel()
label.text = "jake iOS 블로그"
label.textColor = .white
return label
}()
lazy var stackView: UIStackView = {
let view = UIStackView()
view.backgroundColor = .lightGray
view.isLayoutMarginsRelativeArrangement = true
view.layoutMargins = UIEdgeInsets(top: 5.0, left: 12.0, bottom: 5.0, right: 12.0)
view.layer.cornerRadius = 14.0
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOffset = CGSize(width: 0.0, height: 3.0)
view.layer.shadowOpacity = 0.1
view.layer.shadowRadius = 3.0
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(stackView)
stackView.addArrangedSubview(label)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
}
UIStackView를 상속받아서 구현 방법
- 동적인 custom view를 만들기 위해서는 auto layout 사용을 지양
- 단, 만약 Label을 감싸는 View가 동적으로 변하게 하려면, label.intrinsicContentSize를 사용하여 view를 updateConstraints를 이용하면 가능
- stackView안의 뷰들을 hidden시키고 보이고, 동적으로 변하는 경우가 있다면 UIStackView를 사용하여 addArrangedSubview에 담고 hidden시켜도 Layout을 따로 신경쓰지 않도록 설계
- UIStackView의 frame은 따로 설정하지 않으면 외부에서 사용 시 크기가(0,0)으로 되므로 주의 (내부에서 frame 값을 계산하여 대입)
- alignment = .fill (디폴트)로 설정되어 있는 경우 addArrangedSubview()로 마지막에 추가된 view의 크기가 UIStackView의 frame값에 따라 작아지거나 커지기 때문에 주의
UIStackView를 상속하여 구현
- BaseStackView 정의
import UIKit
class BaseStackView: UIStackView {
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
@available(*, unavailable)
required init(coder: NSCoder) {
fatalError("init(coder: NSCoder) has not been implemented")
}
func configure() {}
func bind() {}
}
- MyStackView 구현: `타이틀`부분을 쉽게 hidden시키면 서브 타이틀만 표출되는 코드
- 구성: StackView안에 bubleStackView, titleLabel, pointyView, subTitleLabel 존재
- addArrangedSubviews
private func addSubviews() {
addArrangedSubview(bubbleStackView)
bubbleStackView.addArrangedSubview(titleLabel)
addArrangedSubview(pointyView)
addArrangedSubview(subTitleLabel)
}
- UIStackView를 상속받아서 커스텀 뷰를 만들때의 핵심
- label, ImageView와 같은 내부 contents를 대입 후 sizeToFit() 호출 > intrinsicContentSize 값 획득
- intrinsicContentSize값을 통해서 UIStackView를 담고 있는 가장 밖의 frame.size값 세팅 > sizeToFit() 호출
// 데이터 입력되면 bind() 호출 > bind()에서 UI 세팅
var title: String? { didSet { bind() } }
var subTitle: String? { didSet { bind() } }
override func bind() {
super.bind()
var frameWidth: CGFloat = 0.0
var frameHeight: CGFloat = 0.0
if let title = title {
titleLabel.isHidden = false
pointyView.isHidden = false
titleLabel.text = title
titleLabel.sizeToFit()
frameWidth = titleLabel.intrinsicContentSize.width
frameHeight = titleLabel.intrinsicContentSize.height
setPointyShapeLayerAtBubbleStackView()
setCustomSpacing(8, after: pointyView)
} else {
titleLabel.isHidden = true
pointyView.isHidden = true
}
if let subTitle = subTitle {
subTitleLabel.text = subTitle
subTitleLabel.sizeToFit()
frameWidth = max(frameWidth, subTitleLabel.intrinsicContentSize.width)
frameHeight = max(frameHeight, subTitleLabel.intrinsicContentSize.height)
}
// frame값을 설정해주지 않으면 (0,0)으로 되는것 주의
frame.size = CGSize(width: frameWidth, height: frameHeight)
sizeToFit()
}
- 사용하는 쪽: width, height없이 위치만 지정해주면 크기가 동적으로 변하여 그대로 사용 가능
class ViewController: UIViewController {
lazy var stackView: MyStackView = {
let view = MyStackView(title: "타이틀", subTitle: "서브 타이틀")
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
}
* 전체 소스 코드: https://github.com/JK0369/StackView_flexible
'iOS 응용 (swift)' 카테고리의 다른 글
Comments