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
- ribs
- collectionview
- 클린 코드
- Refactoring
- tableView
- 리팩토링
- ios
- Protocol
- combine
- uitableview
- Xcode
- 리펙터링
- RxCocoa
- uiscrollview
- Human interface guide
- UICollectionView
- 애니메이션
- map
- swiftUI
- Clean Code
- swift documentation
- 스위프트
- clean architecture
- Observable
- 리펙토링
- SWIFT
- MVVM
- HIG
- UITextView
- rxswift
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 동적인 Custom View, Custom Cell 구현 방법 (xib, dynamic width, dynamic height) 본문
UI 컴포넌트 (swift)
[iOS - swift] 동적인 Custom View, Custom Cell 구현 방법 (xib, dynamic width, dynamic height)
jake-kim 2021. 12. 1. 04:03
Custom View (xib) 기본 개념
- custom view 인스턴스를 사용하기까지의 개념 이해
- xib -> nib -> instance
(아래 구현부에서 계속 상세히 설명) - instance는 UIView를 상속한 커스텀 뷰를 만들때 필요하고, UITableViewCell과 같은 커스텀 셀에서는 불필요
> tableView.register(nib, forCellReuseIdentifier:)할 때 nib파일을 넣어주므로 커스텀 셀에서는 불필요
- xib -> nib -> instance
Custom Cell 준비
- Cocoa Touch Class로 UITableViewCell 생성
- cf) cell이 아닌 일반적인 view를 만들때는 .swift파일과 .xib파일 생성
주의) UITableViewCell을 아래처럼 swift, views 파일 따로 만들면 런타림 오류 발생
"this class is not key value coding-compliant for the key"
- .swift 클래스
// MyTableViewCell.swift
class MyTableViewCell: UITableViewCell {
}
- (Custom Cell이 아닌 Custom View인 경우) .xib 파일 설정
- safe Area Layout Guide 체크 해제 (좌)
- Size를 Freeform으로 변경 (우)
- (Custom Cell이 아닌 Custom View인 경우) .xib
- Files's Owner를 설정하여 해당 .xib를 소유할 클래스를 지정
- (Custom Cell인 경우) Cell의 id 입력
- .xib: .swift파일이 아니므로 다른 형식의 파일이고, 컴파일러를 통해 Unarchive되면서 nib파일로 변경
- 압축해제된 nib데이터를 가지고 앱에서 사용될 instance로 변경
- custom cell에서는 register 시에 nib파일을 사용
- 주의) UIView를 상속한 커스텀뷰에서는 아래처럼 커스텀 뷰에서 작업 수행
// UIView를 상속받은 커스텀 뷰를 만들때 nib파일로 인스턴스 작업이 필요
class MyView: UIView {
required init?(coder: NSCoder) {
super.init(coder: coder)
setUpView()
}
private func setUpView() {
guard let myView = loadViewFromNib(nib: "MyView") else {
return
}
addSubview(myView)
}
}
extension UIView {
func loadViewFromNib(nib: String) -> UIView? {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: nib, bundle: bundle)
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
}
Custom Cell 구현
- MyTableViewCell.xib에 UI 컴포넌트 생성 (위치는 코드에서 layoutSubViews에서 계산할 것이므로 오토레이아웃 설정 x)
- MyTableViewCell.swift에 상수 선언, IBOutlet 연결
import UIKit
class MyTableViewCell: UITableViewCell {
// MARK: Constants
struct LabelConstant {
static let maximumNumberOfLines = 5
static let font = UIFont.systemFont(ofSize: 16)
}
struct Metric {
static let cellPadding = 16.0
}
// MARK: UI
@IBOutlet weak var messageLabel: UILabel!
}
- 바인딩 model - UI
// MARK: Binding func bind(myModel: MyModel) { messageLabel.text = myModel.message accessoryType = myModel.isDone ? .checkmark : .none }
- 동적 사이즈 적용 방법
- origin.y, origin.x, width: layoutSubviews()를 재정의하여 계산
// MARK: Layout override func layoutSubviews() { super.layoutSubviews() setupDynamicLayout() } private func setupDynamicLayout() { messageLabel.numberOfLines = LabelConstant.maximumNumberOfLines messageLabel.font = LabelConstant.font messageLabel.layer.frame.origin.y = Metric.cellPadding // top messageLabel.layer.frame.origin.x = Metric.cellPadding // left messageLabel.layer.frame.size.width = contentView.layer.frame.size.width - Metric.cellPadding * 2 messageLabel.sizeToFit() }
- height: 뷰 컨트롤러에서 tableView(_:heightForRowAt:)에서 Cell쪽에 접근하여, Cell에서 계산된 height값을 리턴
// MARK: Cell Height class func height(width: CGFloat, myModel: MyModel) -> CGFloat { let message = myModel.message let contentWidth = width - Metric.cellPadding * 2 let contentsHeight = message.getCalculatedHeight(contentWidth: contentWidth, font: LabelConstant.font, maximumNumberOfLines: LabelConstant.maximumNumberOfLines) let heightWithPadding = contentsHeight + Metric.cellPadding * 2 return heightWithPadding }
- 위에서 getCalculatedHeight는 contentWidth와 font, maximumNumberOfLines 값을 인수로 받아서 높이를 리턴하는 함수 정의
// String+Height import UIKit extension String { func getCalculatedHeight(contentWidth width: CGFloat, font: UIFont, maximumNumberOfLines: Int = 0) -> CGFloat { let size = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude) let height = self.size(padding: size, font: font, maximumNumberOfLines: maximumNumberOfLines).height return height } private func size(padding size: CGSize, font: UIFont, maximumNumberOfLines: Int = 0) -> CGSize { let attributes: [NSAttributedString.Key: Any] = [.font: font] var size = self.boundingRect(with: size, attributes: attributes).size if maximumNumberOfLines > 0 { size.height = min(size.height, CGFloat(maximumNumberOfLines) * font.lineHeight) } return size } private func boundingRect(with size: CGSize, attributes: [NSAttributedString.Key: Any]) -> CGRect { let options: NSStringDrawingOptions = [.usesLineFragmentOrigin, .usesFontLeading] let rect = self.boundingRect(with: size, options: options, attributes: attributes, context: nil) return rect } }
- height 값 사용하는 쪽
// ViewController.swift func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let message = messageDataSource[indexPath.row] let myModel = MyModel(message: message) let calculatedHeight = MyTableViewCell.height(width: tableView.layer.frame.size.width, myModel: myModel) return calculatedHeight }
- origin.y, origin.x, width: layoutSubviews()를 재정의하여 계산
* 전체 소스 코드: https://github.com/JK0369/ExDynamicHeightCell
'UI 컴포넌트 (swift)' 카테고리의 다른 글
[iOS - swift] CustomPopup (커스텀 팝업) 구현 방법 (0) | 2022.01.28 |
---|---|
[iOS - swift] 가로 슬라이드 뷰 (SliderCollectionView) 구현 (0) | 2021.12.15 |
[iOS - swift] 채팅 UI (UICollectionView를 사용하여 chatting UI 구현) (0) | 2021.10.31 |
[iOS - swift] UI 컴포넌트 - Horizontal Scroll View (수평 스크롤 뷰) (0) | 2021.09.29 |
[iOS - swift] UI 컴포넌트 - Rating View (별점, 평점, tag 사용) (1) | 2021.07.23 |
Comments