먹고 기도하고 코딩하라

[SwiftUI] Text 뷰와 단짝인 modifiers 본문

앱/Swift

[SwiftUI] Text 뷰와 단짝인 modifiers

2G Dev 2022. 6. 28. 13:31
728x90
728x90

 

Text 뷰는 진짜 많이 쓴다. 사실상 Text 뷰를 안 쓰고 뭔가 만들기는 참 어렵달까..

Text 뷰 자체는 1줄 혹은 여러 줄의 수정할 수 없는 read-only 텍스트를 보여주는 뷰이다.

@frozen struct Text

 

폰트 스타일 지정

텍스트 뷰는 body 폰트 스타일(기본)을 사용해 앱의 UI의 글자들을 실행 중인 플랫폼에 어울리게 표시한다. 다른 표준 폰트를 사용할 수도 있다. font(_:) modifier를 사용한다.

다음은 기본으로 주어진 폰트들을 실험해본 결과다.

VStack {
            VStack(spacing: 10) {
                Text("largeTitle")
                    .font(.largeTitle)
                Text("title")
                    .font(.title)
                Text("title2")
                    .font(.title2)
                Text("title3")
                    .font(.title3)
                Text("headline")
                    .font(.headline)
                Text("subheadline")
                    .font(.subheadline)
                Text("body")
                    .font(.body)
                Text("callout")
                    .font(.callout)
                Text("caption")
                    .font(.caption)
                Text("caption2")
                    .font(.caption2)
            }
            VStack(spacing: 10) {
                Text("footnote")
                    .font(.footnote)
                Text("Custom style")
                    .font(.system(size: 24, weight: .bold, design: .monospaced))
                Text("Custom style")
                Text("Custom style 2")
                    .italic()
            }
            .font(.system(size: 16, weight: .light, design: .serif))
            .padding(5)
        }

 

폰트 스타일 커스텀

.font modifier에 .system(size, weight, design)을 줄 수도 있다. 애플에서 제공하는 폰트 타입이 아닌 커스텀 타입으로 텍스트를 나타내고 싶을 때 사용하면 된다. weight는 텍스트의 굵기, design은 서체이다.

.italic(), .bold() 등을 Text 뷰에 붙여 사용할 수 있다. .font modifier를 Text 뷰들을 감싼 VStack 등에 붙여 사용할 수 있는 반면, italic() 같은 것들은 Text 뷰에 직접 붙여야 사용할 수 있다. 

 

이걸 테스트해보다가 아주 흥미로운 문제를 발견했다. 하나의 VStack에 10개가 넘는 Text 뷰를 배치할 수 없는 문제였다. 만약 11개째 추가하면 그 때부터 Extra arguments at positions #11 in call 하면서 프리뷰도 안 되고 빌드도 안 된다. 관련 스택오버플로우 질문을 참고하자.

이것은 비단 VStack만의 문제가 아니라 List 등에서도 같은 문제가 나타난다. 이유는 @ViewBuilder 클로저의 파라미터가 0~10개만 허용하기 때문이라고 한다. 이것도 답변 참고. 

답변들은 10개 단위로 나눠서 Group 뷰로 묶어버릴 것을 권하고 있다.

 

마크다운으로 부분 스타일

텍스트의 특정 부분에만 스타일을 주고 싶다면, AttributedString으로 텍스트 뷰를 만들어 사용할 수 있다. 마크다운 스타일을 사용해 텍스트를 만들 수 있다.

    let attributedString = try! AttributedString(
        markdown: "~~**Hamlet**~~ by William Shakespeare"
    )
    var body: some View {
        Text(attributedString)
            .font(.system(size: 12, weight: .light, design: .monospaced))
            .italic()
    }

AttributedString에 markdown 프로퍼티로 문자열을 주면 된다. 이 때, 마크다운 스타일로 써주면 특정 부분만 다르게 디자인된다.

 

 

텍스트 뷰 크기 조정

텍스트 뷰는 항상 콘텐츠를 렌더링하는 데 필요한 만큼의 공간만 차지하지만, 뷰 레이아웃을 조정할 수 있다. frame(width:height:alignment:) modifier를 사용하면 된다.

Text("To be, or not to be, that is the question:")
    .frame(width: 100)

 

 

lineLimit(_:), allowsTightening(_:), minimumScaleFactor(_:), truncationMode(_:) 등을 사용해 어떻게 공간을 제한할지 설정할 수도 있다.

 

줄 수 제한

lineLimit(_:)은 말 그대로 Text 뷰가 표시할 수 있는 최대 줄 수를 제한하는 것이다. Int를 넘겨주면 된다.

예를 들면, 100으로 너비를 제한하지만 최대 라인 개수도 1개라고 하면 남은 글자는 ...으로 표시하게 된다.

Text("Brevity is the soul of wit.")
    .frame(width: 100)
    .lineLimit(1)

 

allowsTightening은 텍스트를 줄에 맞추기 위해 글자 사이 간격을 줄일 수 있는지를 설정한다. Bool을 넘겨주면 된다.

 

글자 축소의 최소 설정

minimumScaleFactor는 뷰의 텍스트가 가능한 공간 안에 맞추기 위해 쭈그러들 수 있는 최소치를 지정해준다. CGFloat를 넘겨주면 된다. 그러니까 어느 이하로는 쭈그러들면 안 된다는 제한을 주는 것이다. 0~1까지 줄 수 있다.

        HStack {
            Text("This is a long label that will be scaled to fit:")
                .lineLimit(1)
                .minimumScaleFactor(0.7)
            Text("My Long Text Field")
        }

위 사진은 minimumScaleFactor를 각각 0.7, 0.3으로 줬을 때이다.

 

남는 문자 생략

truncationMode는 가능한 공간 안에 맞추기에는 텍스트 줄이 너무 길어질 때 남은 내용의 어디를 버릴지 결정하는 것이다. .head, .middle, .tail로 나뉘어 있다.

다음 사진은 각각 .head, .middle, .tail로 나누었을 때이다. 텍스트가 잘리기 시작하는 줄부터 시작해서 head, middle, tail을 계산한다.

여러 줄의 텍스트 정렬

텍스트가 한 줄로 끝나지 않고 여러 줄로 나타날 때, 텍스트의 정렬을 정하는 데에는 multilineTextAlignment(_:)를 사용할 수 있다.

.leading, .center, .trailing으로 왼쪽, 가운데, 우측 정렬을 할 수 있다.

줄 간 간격 

여러 줄일 때 줄간 간격을 설정하고 싶다면, lineSpacing(_:)을 사용하면 된다. lineSpacing의 경우, 0 미만의 음수를 줬을 때는 아예 적용하지 않은 것과 같은 효과를 보인다. 다음은 lineSpacing을 적용하지 않았을 때와 5로 적용했을 때의 차이다.

 

 

텍스트 색깔 변경

foregroundColor(Color?) modifier를 사용하면 된다.

HStack {
    Text("Red").foregroundColor(.red)
    Text("Green").foregroundColor(.green)
    Text("Blue").foregroundColor(.blue)
}

 

텍스트 세부 스타일

텍스트를 굵게(bold), 흘려쓰기(italic), 취소선(strikethrough), 밑줄(underline) 등을 그어서 표현하려면 다음과 같은 것을 쓰면 된다.

        VStack {
            Text("bold")
                .bold()
            Text("italic")
                .italic()
            Text("strikethrough")
                .strikethrough()
            Text("strikethrough")
                .strikethrough(true, color: Color.red)
            Text("underline")
                .underline()
            Text("underline")
                .underline(true, color: Color.blue)
        }

 

모든 숫자에 고정 너비 주기

.monospacedDigit() modifier를 사용하면, 모든 숫자에 고정된 너비를 줄 수 있다. 다른 문자들은 그대로 내버려둔다. 

    VStack(alignment: .leading) {
        Text(myDate.formatted(date: .long, time: .complete))
            .font(.system(size: 20))
        Text(myDate.formatted(date: .long, time: .complete))
            .font(.system(size: 20))
            .monospacedDigit()
    }

 

자간 주기 (kerning, tracking)

.kerning(_:)야말로 사실 진정한 자간 설정 modifier이다.

        VStack {
            Text("Talk to me goose")
                .kerning(-2)
            Text("Talk to me goose")
            Text("Talk to me goose")
                .kerning(2)
        }
        .font(.system(size: 24))

합자 이슈가 있는데 한글에서는 딱히 신경쓸 필요가 없을 것 같지만 영어나 독일어 등 외국어 표기에서 가끔 나타나는 것이므로 살펴보자. kerning은 ligature(합자)를 유지하려고 한다.

아래 사진이 바로 그 예시이다. kerning을 줬음에도 ffl이 붙어서 떨어지지 않는 모습인데, 어느 임계점 이상으로 kerning을 주면 떨어지기는 한다고 한다.

.tracking(_:)도 자간 설정 modifier이긴 한데, 효과는 비슷하거나 거의 똑같아서 사진과 코드를 따로 첨부하지는 않는다. (그냥 .kerning을 .tracking으로 바꿔서 하면 되기 때문에)

차이점이라면 kerning이 글자 offsets을 변경하는 거라면 tracking은 trailing whitespace를 없애거나 더하는 식으로 자간을 설정한다. 또한 tracking에 0이 아닌 값을 주면 불필요한 합자를 바로 없애준다. (kerning의 경우 합자를 유지한다)

 

수직 오프셋

.baselineOffset(_:) modifier로 텍스트의 베이스라인에 연관된 세로 오프셋을 만들어준다. 이건 직접 보는 게 더 이해하기 편하다.

HStack(alignment: .top) {
    Text("Hello")
        .baselineOffset(-10)
        .border(Color.red)
    Text("Hello")
        .border(Color.green)
    Text("Hello")
        .baselineOffset(10)
        .border(Color.blue)
}
.background(Color(white: 0.9))

 

소문자/대문자로 치환

어떤 텍스트를 통째로 소문자나 대문자로 바꾸고 싶을 때는 .textCase(_:)를 사용하면 된다.

        VStack {
            Text("Beam me up Scotty!")
                .textCase(.lowercase)
            Text("Beam me up Scotty!")
                .textCase(.uppercase)
        }

 

 

728x90
반응형
0 Comments
댓글쓰기 폼