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
- SWIFT
- identifiable
- RxSwift
- rxdatasources
- swift bottomsheet
- swift concurrency
- UIKit
- DP
- Tuist
- swift dashed line
- domain data
- swift custom ui
- BFS
- button configuration
- swift 백준
- custom navigation bar
- task cancellation
- 타임라인 포맷팅
- tusit font 추가 방법
- reactorkit
- traits
- task cancel
- custombottomsheet
- paragraph style
- claen architecture
- uikit toast
- swift navigationcontroller
- custom ui
- coordinator
- swift 점선
Archives
- Today
- Total
김경록의 앱 개발 여정
[Swift UIKit] 공백이 포함된 커스텀 뷰 구현기 본문
개요
팀 프로젝트에서 Cell의 디자인이 개선되었습니다.
복잡한 UI였는데 이를 구현하고 개선하는 과정을 담았습니다.
포스팅은 메모장 말투로 진행하겠습니다!
구현해야 할 UI에서 주의해야 할 부분
1. 제목이 줄바꿈이 되고 카테고리 아래서부터 시작하는 점
2. 레시피의 경우 배경색, 글자색, cornerRadius까지 포함
3. 제목과 카테고리 부분이 폰트가 다름
최초 아이디어 : AttributedString 사용
func configureMainText(category: String, mainText: String) -> NSAttributedString {
let attributedString = NSMutableAttributedString()
// 카테고리 글씨 속성 설정
let categoryAttributes: [NSAttributedString.Key: Any] = [
.backgroundColor: UIColor(hex: "#FAE0E4"),
.foregroundColor: UIColor(hex: "#E84D65"),
.font: UIFont.boldSystemFont(ofSize: 12),
]
let categoryAttributedString = NSMutableAttributedString(string: category, attributes: categoryAttributes)
attributedString.append(categoryAttributedString)
// 본문 글씨 속성 설정
let mainAttributes: [NSAttributedString.Key: Any] = [
.font: UIFont.boldSystemFont(ofSize: 16),
.foregroundColor: UIColor.black
]
let mainAttributedString = NSAttributedString(string: mainText, attributes: mainAttributes)
attributedString.append(mainAttributedString)
return attributedString
}
//단순 사용 예시임, 외부에서 값 주입받아서 적용 권장
lazy var titleLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 2
label.attributedText = configureMainText(category: String, mainText: String)
return label
}()
- 두가지 AttributedString을 합쳐주는 형식으로 구현
- 하지만 해당 방식은 카테고리 부분의 cornerRadius를 포함할 수 없음
- 또한 글씨의 높낮이가 조금 어긋나게 됨
- 우선 구현을 위해 하드코딩 된 부분이 있지만 치명적 단점으로 인해 개선 진행 X
나쁘진 않지만, 요구사항을 충족시킬 수 없었다.
두 번째 아이디어: 카테고리 부분과 제목을 각 다른 레이블을 사용하여 구현
- 제목의 왼쪽 시작점을 카테고리의 오른쪽에 맞추게 되면 위와 같이 열바꿈이 의도와는 다르게 구현됨
- 그래서 카테고리와 제목의 시작점을 같게 두되, 제목의 텍스트 자체에 공백을 추가하는 방향으로 결정
- 단, 카테고리의 길이는 유동적이므로 하드코딩으론 불가능, 넓이를 유동적으로 계산하여 적용하도록 결정
1. 카테고리 뷰의 넓이를 구하고 그에 맞는 공백을 채워줘야 함
private func calculateLabelWidth(for label: UILabel) -> CGFloat {
guard let text = label.text else { return 0 }
let font = label.font ?? UIFont...(각 폰트)
let size = (text as NSString).size(withAttributes: [.font: font])
return size.width + 5 // 텍스트 패딩 포함
}
private func createDynamicSpace(forWidth width: CGFloat, withFont font: UIFont) -> String {
let spaceCharWidth = " ".size(withAttributes: [.font: font]).width // 공백 문자 하나의 넓이
let spaceCount = Int(ceil(width / spaceCharWidth)) // 필요한 공백 문자 수 계산
return String(repeating: " ", count: spaceCount)
}
- 각 폰트별로 넓이가 다를 수 있으므로 폰트를 기반으로 넓이를 구함
- VC에서 바인딩할 때 사용할 수 있도록 internal Methods 정의
func configureTitleLabelText(_ text: String) {
let categoryWidth = calculateLabelWidth(for: categoryLabel)
let space = createDynamicSpace(forWidth: categoryWidth, withFont: titleLabel.font)
let titleText = text
titleLabel.text = space + titleText
}
2. 카테고리 뷰의 반복작업 감소 및 주의사항 체크
여러 카테고리들이 존재해서 반복 작업을 줄이기 위한 UILabel 확장 구현
enum categorys: Int {
case hobby = 1
case it
case health
...
}
extension UILabel {
func setCategory(with id: Int) {
applyCommonStyle()
guard let category = categorys(rawValue: id) else {
print("Invalid category value")
return
}
switch category {
//Text 뒤 문자는 일괄적이고 자연스러운 공백을 위한 유니코드임
case .hobby:
text = "취미\u{2003}"
backgroundColor = UIColor...
textColor = UIColor...
case .it:
text = "IT\u{2003}"
backgroundColor = UIColor....
textColor = UIColor...
....
}
- API는 Category ID를 Int형으로 배출해 주므로 그에 맞는 label을 생성할 수 있도록 개선
- 기본적인 폰트, cornerRadius 등의 공통부분은 함수로 선언하여 같은 동작을 최소화
- 원하는 텍스트 이후에 공백대신 공백을 나타내주는 유니코드를 삽입하여 자연스럽게 표현 ( 이 부분은 이게 더 자연스럽다고 판단해서 채택)
더미데이터를 통한 결괏값 확인
카테고리 레이블의 길이와 상관없이 자연스러운 ui를 그리도록 개선 완료하였다.
'Trouble Shooting' 카테고리의 다른 글
[Swift] 여러 상황에 대응하는 레이아웃 만들기(with SnapKit, update Layout) (0) | 2025.03.08 |
---|---|
[Swift] Launch Screen에 Asset.xcassets 이미지가 적용되지 않는다면 (0) | 2025.02.01 |
[Swift UIKit] 비회원은 못보는 TableView Section, 어떻게 할까? (0) | 2025.01.24 |
[Swift] 코디네이터 패턴과 JWT 기반 오토로그인 (0) | 2025.01.08 |
[Swift UIKit]present의 주체 (modal을 표시하는건 누구의 역할일까?) (0) | 2025.01.08 |