UILabel size를 Text size에 맞추는 방법(혹은 그 반대)
by 사슴비행기Label의 text 길이가 길어지면
text를 화면에 다 보이게 하기 위한 방법이
있는지 알아보게 될 것이다
나는 custom popup view를 만드는 과정에서
text가 길어지면 popup view의 크기를
커지게 해야 하는 상황이라서
이 기능을 좀 더 자세히 살펴보게 되었다
그래서 오늘은 일단 화면 크기 기준으로
한 줄짜리의 text의 Label 사이즈를 정하는 방법과
한 줄 뿐만 아니라 여러 줄짜리 text를 가진
Label의 size를 정하는 방법에 대해서
공부하고 실습하고 기록해보려고 한다.
Apple문서 해석하고 내 견해 적고...
여러 실험을 해보면서 글이 너무 길어졌다..
그래서 추후에 요약본 글을 따로 쓰려고 한다;;
목차
1. 한 줄짜리의 짧은 텍스트
sizeToFit()
intrinsicContentSize
2. 한 줄 & 여러 줄 텍스트 둘 다 가능
sizeThatFits(_:) → 추천
adjustFontSizeToFitWidth
ceil()
1. 한 줄짜리의 짧은 텍스트
화면 크기를 기준으로
한 줄을 넘지 않는 텍스트를 가진 UILabel의
size를 정하는 방법들은 여러 개가 있다.
2. 한 줄 & 여러 줄 텍스트 둘 다 기능
에서 살펴볼 것들도 모두
한 줄이 가능한 방법이라서
여기서는 한 줄만 가능한 것들만 볼 것이다
이해가 쉽도록 Playground를 이용해서
실습을 하면서 글을 쓸 예정인데,
그래서 그전에 아래와 같이
view와 textLabel을 먼저 만들어 놓고
시작할 것이다.
view의 크기는 너무 크면 상대적으로 Label이
너무 작아져서 임의로 줄였고,
보기 쉽게 view와 label의 배경색을 주었다.
let view: UIView = {
let v = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width/2, height: UIScreen.main.bounds.height/4))
v.backgroundColor = .lightGray
return v
}()
var testLabel: UILabel = {
let label = UILabel(frame: CGRect(origin: .zero, size: CGSize(width: 30, height: 30)))
label.backgroundColor = .green
return label
}()
view.addSubview(testLabel)
testLabel.text = "신데렐라는 어려서 부모님을 잃고요,"
view
위의 코드를 작성하고
마지막 줄에서 실행하면 아래 사진과 같이
잘려버린 UILabel을 확인할 수 있다.
sizeToFit()
extension UIView {
//...
open func sizeToFit()
}
이 함수는 UIView의 확장으로 선언되어 있다
그리고 UILabel은 UIView를 상속받았다
: 수신기 보기가 하위 보기를 둘러싸도록 크기를 조정하고 이동합니다.
= 이 함수를 사용한 라벨의 크기를 Text를 둘러싸도록 조정하고 이동한다.
라고 할 수 있겠다.
이 함수를 예제에 맞춰 사용하면 아래와 같다.
그리고 어떻게 나오는지 확인해보면,
Text크기에 맞춰서 Label의 크기가
늘어난 걸 알 수 있다.
testLabel.sizeToFit()
view //playground에서 view의 최종 결과를 확인하기 위함
그리고 이 함수를 긴 Text를 가진
UILabel에 사용하면 이상한 결과를 얻을 수 있다.
일단 Text에 따라서 여러 줄을 가질 수 있도록
label의 속성 중에 numberOfLines를 바꿔주고
Text를 더 길게 바꾼 뒤 실행해보면
Lable의 크기가 상하로 늘어난 걸 볼 수 있다.
(심지어 잘려 있다)
label.numberOfLines = 0
testLabel.text = "신데렐라는 어려서 부모님을 잃고요, 계모와 언니들에게 괴롭힘을 당했더래요~"
Apple 문서의 문구를 보면 힌트를 얻을 수 있는데,
이 함수를 사용한 라벨의 크기를 Text를 둘러싸도록 조정하고 이동한다.
Text를 둘러싸도록 조정하긴 하는데,
그게 가로 일지 세로 일지는 알려주지 않았다.
예제를 실습해보니
기본적으로 line이 계속 늘어날 수 있는 상태이면
(numberOfLines속성이 0일 때를 말함)
일단 frame은 정해져 있고
줄 수는 늘어날 수 있으니
늘릴 수 있는 걸 먼저 늘리는 느낌적인 느낌이다.
intrinsicContentSize
extension UIView {
//...
@available(iOS 6.0, *)
open var intrinsicContentSize: CGSize { get }
//...
}
마찬가지로 UIView에 선언되었다.
이 프로퍼티는 get only다.
: 뷰 자신의 프로퍼티들만 고려한 수신 뷰의 자연 크기.
이해가 잘 안 가서 세부내용을 뜯어보면...
Custom View는 일반적으로 Layout System이 인식하지 못하는 내용을 표시한다.
이 속성을 설정하면 Custom View가 그것의 content에 따라 어떤 사이즈를 원하는지 시스템과 소통할 수 있도록 허용한다.
이 고유한 사이즈는 그 content frame과 독립되어야만 한다. 왜냐하면 예를 들어 변경된 높이를 기준으로 변경된 너비를 레이아웃 시스템에 동적으로 전달할 방법이 없기 때문이다.
음... 뭔 뜻인고 한참 고민하니...
우리가 만든 label을 custom view라고
생각하면 될 것 같고,
text 길이가 늘어났을 때
우리가 아무런 작업을 하지 않으면,
label은 변경된 text frame을 알지 못한다.
그래서 우리가 그토록 열심히
안에 있는 내용에 따라 그걸 띄우고 있는
view 혹은 label들의 크기를
변경하려고 하는 것...
문서에 따르면
이러한 상황이 당연하다고 하는 건데
왜냐면 그것들은 서로 동적으로
자기 사이즈가 변경됐다고
소통할 방법이 없기 때문이다.
근데 이 intrinsicContentSize 속성을 사용하면
그 소통을 할 수 있도록 한다는 건데,
get only 속성이었으니,
이 속성으로 받아온 size를
label에 설정하면 된다는 말인 것 같다...
그리고 이 intrinsicContentSize 속성을 사용한
view의 안에 있는 프로퍼티만 고려해서
이 view가 가져야 할 크기를 반환한다는 것 같다..
틀린 해석이라면 알려주세요...
let newSize = testLabel.intrinsicContentSize
testLabel.frame.size = newSize
view
아무튼, 그래서 이렇게 size를 받아와서
label에 설정해주면 된다
하지만 이것도 여러 줄의 text에는 사용하기 힘든데
label 자신의 프로퍼티를 고려해서
label 자신의 크기를 반환하기 때문에
이 label이 어디에 어떻게 쓰일지는
알바 아니라는 것 같다.
label.numberOfLines = 0
testLabel.text = "신데렐라는 어려서 부모님을 잃고요, 계모와 언니들에게 괴롭힘을 당했더래요~"
혹시나 해서
view의 크기도 해당 속성을 사용해서 바꿔봤는데
너비와 높이가 -1이 나오더라 ㅋㅋ..
let newViewSize = view.intrinsicContentSize //{w -1, h -1}
뭐지 싶어서 찾아보니,
모든 뷰가 intrinsic content size를
가지는 것이 아니라고 한다...
UIView | Intrinsic content size를 가지지 않는다. |
UISlider | 너비만 Intrinsic content size로 정의한다. |
UILabel UIButton UISwitch |
높이와 너비 모두 Intrinsic content size로 정의한다. |
UITextField UIImageView |
Intrinsic content size가 상황에 따라 다르다.(vary) |
해당 내용을 설명하는 글 ▼
2. 한 줄, 여러 줄 텍스트 둘 다 가능
이제 한 줄이던 여러 줄이던
다 가능한 방법을 알아볼 것이다.
여기에서는 위에서 사용한 intrinsicContentSize를
응용해서 쓰는 방법도 있다.
상황에 맞게 쓰면 될 것 같다.
sizeThatFits(_:) 함수
extension UIView {
//...
open func sizeThatFits(_ size: CGSize) -> CGSize
//...
}
이 함수도 UIView의 확장으로 선언되어 있다
그리고 CGSize를 반환하는 함수이다.
: 지정한 크기에 가장 적합한 크기를 계산하여 반환하도록 뷰에 요청합니다.
: 그 뷰에 가장 적합한 크기를 계산해야 하는 크기입니다.
: 수신기의 하위 보기에 맞는 새 크기입니다.
그러니깐
파라미터로 크기를 계산해야 하는 Size를 넣고
리턴 값은 파라미터로 받은 하위 뷰에 맞는 크기다.
음.. 좀 더 쉽게 말하면
파라미터로 넣은 사이즈에 가장 잘 맞게,
이 함수를 호출한 객체의 사이즈를 리턴해 준다는 것.
그런데 이 함수는 한 줄인 경우와
여러 줄인 경우에
파라미터로 넣어야 하는 값이 다르다.
일단 예제를 보면,
let newSize = testLabel.sizeThatFits(testLabel.frame.size) //1
newSize = testLabel.sizeThatFits( CGSize(width: testLabel.frame.width, height: CGFloat.greatestFiniteMagnitude)) //2
newSize = testLabel.sizeThatFits(view.frame.size) //3
testLabel.frame.size = newSize
view
파라미터로 넣을 수 있는 것들이 정말 많다
1번은 testLabel의 사이즈를 주었다.
2번은 다른 사람들의 코드에서
가장 많이 사용되는 파라미터였고
3번은 내가 문서를 읽어보고
나한텐 이게 더 나을 것 같아서 넣은 파라미터다.
먼저, 다른 사람들의 코드를 살펴보면 height를
CGFloat.greatestFiniteMagnitude
이렇게 넣었는데,
Float의 최댓값을 넣겠다는 것이다.
FLT_MAX라고도 하며,
그 값은 3.402823466e+38F이다.
그러니까 높이를 엄청~~~~ 많이 준 것이다.
일단 짧은 text는 세 파라미터 모두
같은 결과를 보여주었다.
아마도 문서에서
"가장 적합한 크기를 계산하여 반환"
이라는 문구를 보면,
Label의 text까지 고려해서
가장 적합한 크기를 주는 것 같다.
문제는 여러 줄일 경우인데,
label.numberOfLines = 0
testLabel.text = "신데렐라는 어려서 부모님을 잃고요, 계모와 언니들에게 괴롭힘을 당했더래요~"
이렇게 코드를 추가하고 실행해 보면,
좀 다른 결과물을 보여준다.
일단 내 예상이 맞다면,
numberOfLines가 우선시되어서,
줄 수를 계속 추가할 수 있으면,
일단 그것 먼저 실행한다.
2번 파라미터도 height를 엄~~~~ 청
많이 주어서 계속 줄 수를 늘린 것이다.
근데 3번 파라미터는
height가 view의 height로 고정이 되어 있으니
줄 수를 늘리다 보면
글자가 안 보인다는 것을 캐치하고는
해당 파라미터에서 가장 적합한
크기로 리턴 값을 준 것이다.
응..? 근데 나는 view의 가로는 맞추되, 안에 스크롤이 있어서 height는 엄청 커도 상관없어요!
이런 경우라면,
let newSize = testLabel.sizeThatFits(CGSize(width: view.frame.width, height: CGFloat.greatestFiniteMagnitude)) //4
파라미터로 넣을 사이즈를 이렇게 주면 되겠다.
그러면 이렇게 나온다.
아, 그리고 여기서
왜 label이 view의 가로에 딱 안 맞죠?!
라고 생각된다면,
label의 lineBreakMode 속성을
바꾸어주면 된다.
label.lineBreakMode = .byCharWrapping
adjustFontSizeToFitWidth
이건 이전에도 많이 사용했던 속성인데,
autolayout에도 있는 속성 값이다.
@available(iOS 2.0, *)
open class UILabel : UIView, NSCoding, UIContentSizeCategoryAdjusting {
//...
// these next 3 properties allow the label to be autosized to fit a certain width by scaling the font size(s) by a scaling factor >= the minimum scaling factor
// and to specify how the text baseline moves when it needs to shrink the font.
open var adjustsFontSizeToFitWidth: Bool // default is NO
//...
}
이 속성은 UILabel의 확장으로 선언되어 있다.
: 레이블의 경계 사각형에 제목 문자열을 맞추기 위해 레이블에서 텍스트의 글꼴 크기를 줄일지 여부를 결정하는 부울 값입니다.
말 그대로 Label의 기존 사이즈에
text를 다 보이게 하기 위해
text의 폰트 사이즈를 바꾼다.
testLabel.adjustsFontSizeToFitWidth = true
view
잘 안 보이지만,
텍스트가 아주 작게 보이긴 한다...ㅋㅋㅋㅋ
한 줄짜리 text가 아니어도
label 크게에 text를 맞추는 것이기 때문에
요로코롬 다 들어간다.. ㅋㅋ..
사용하기 나름... 괜찮을지도 모른다....ㅎ
ceil() 함수
마지막으로 ceil() 함수를 써볼 것이다.
이 함수에 대해서는 이전에 포스팅한 적이 있다.
ceil()은 소수점 뒤에 있는 숫자를
정수 1로 포함시켜주는 것이다.
그래서 이걸 어떻게 써먹느냐면,
위에서 다뤘던 intrinsicContentSize 값을
이용해서 사용할 것이다.
testLabel.numberOfLines = 0
testLabel.lineBreakMode = .byCharWrapping
testLabel.text = "신데렐라는 어려서 부모님을 잃고요, 계모와 언니들에게 괴롭힘을 당했더래요~"
let newSize = testLabel.intrinsicContentSize
testLabel.frame.size = newSize
if testLabel.frame.width > view.frame.width {
let shareOfDivision = ceil((testLabel.frame.width / view.frame.width))
let newHeight = testLabel.frame.height * shareOfDivision
testLabel.frame.size = CGSize(width: view.frame.width, height: newHeight)
}
view
즉, 실제 label의 width가 view의 width보다
몇 배 긴가에 따라 heigth에 곱해주는 것이다.
이렇게 하면 한 줄 일 때도 여러 줄 일 때도
사용할 수 있지만, 기본 제공되는
sizeThatFits(_:) 함수가 있는데
굳이 사용할 필요는 없어 보인다..
그저 sizeThatFits(_:) 함수를 모르는 때에
야매로 쓰는 방법이라고 생각하면 될 것 같다.
그리고 이 글을 쓸 때 참고한 블로그 글 첨부한다.
그냥 대충 알고 쓰면 해당 방법이 안 될 때
"이게 왜 안되지..?"
라는 생각이 들면서 안 해도 되는
삽질을 할 가능성이 높다..
그러니 내 방법으로 잘 공부해 두고
잘 꺼내 쓰면 좋은 것 같다..
다음 포스팅은 apple 기기의 설정(setting)에서
손쉬운 사용의
디스플레이 및 텍스트 크기에서
더 큰 텍스트 등으로 글씨 크기를 바꿀 때
이미 설정될 label의 크기를
dynamic하게 바꾸는 방법을 알아보려고 한다.
(그래 마저... 우리에겐...)
(그런 변수도 있었어.....)
'swift > basic knowlege of programming' 카테고리의 다른 글
segue 여러개 연결하는 방법 (0) | 2021.10.09 |
---|---|
UIFont.TextStyle 비교 (0) | 2021.10.03 |
where Self: 이 무엇인가.. (0) | 2021.10.02 |
Swift 소수점 다루기, 소수점 제거하기, 소수점 제거 함수 (0) | 2021.10.01 |
Swift 변수 주변에 `기호 의미 (variable surrounded backticks(grave accent = `)) (0) | 2020.01.07 |
블로그의 정보
Beautiful Coding
사슴비행기