안녕하세요. 엘림입니다🙇🏻♀️
오늘은 코드로 UI를 구현한 과정을 정리해보았습니다..
이번 포스팅은 야곰닷넷의 오토레이아웃 정복하기 - Constraint with Code부분을 공부하며 정리한 내용입니다.
UILabel과 UIButton은 Anchor를 이용하여 제약을 주고,
UISlider는 NSLayoutConstraint를 사용해서 제약을 주는 과정이 담겨있습니다.
이 글은 공부하면서 작성한 글이기 때문에 잘못된 정보가 있을 수 있습니다.🥺
금방 잊어버릴... 미래의 저에게 다시 알려주기 위한 글이다보니
혹시라도 틀린 부분이 있다면, 댓글로 친절하게 알려주시길 부탁드립니다.🙏
저는 UIKit으로 작업하지만, 작업 과정을 빌드하지 않고 바로바로 확인하기 위해 SwiftUI에서 사용하는 canvas를 세팅하였습니다!
방법이 궁금하시다면
[iOS][Swift] - 코드로 UI 구현하기(part.0)를 참고해주세요!
override func viewDidLoad() {
super.viewDidLoad()
let testLabel = UILabel()
testLabel.text = "test 하는 중이라규"
}
우선 viewDidLoad
에 UILabel
을 만들어주고, label의 텍스트를 지정해주었습니다. 하지만 화면에는 나오는게 없네요.🤦🏻♀️
override func viewDidLoad() {
super.viewDidLoad()
let testLabel = UILabel()
testLabel.text = "test 하는 중이라규"
view.addSubview(testLabel)
}
view
에 넣어줘야 하는걸까해서 addSubview로 추가도 해봤지만, 아무 반응이 없습니다... 왜일까요?
공식문서에서 Label을 살펴보니, 위에서 두번째 줄에!! Set up Auto Layout이라고 되어있네요! 아💡, 위치가 잡히지 않아서 뜨지 않았나봅니다.
그럼 위치를 잡아볼게요.
let testLabel = UILabel()
testLabel.text = "test 하는 중이라규"
view.addSubview(testLabel)
let safeArea = view.safeAreaLayoutGuide
let leadingConstraint = testLabel.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor, constant: 16)
우선 기준이 될 safeArea를 설정하고, 거기에 맞춰서 제약을 걸어봤습니다.
가장 아랫줄 코드의 의미는, testLabel의 leading과 safeArea의 leading을 16만큼의 간격을 두게 하라는 뜻입니다. 각각의 anchor을 이용한 것이지요.
자 여러분 나오나요...?
아니요...🤦🏻♀️
사실 지금 Label에는 translatesAutoresizingMaskIntoConstraints
라는 설정이 true
로 되어있답니다. 이름을 보면 알 수 있듯, 자동으로 제약을 변환하는 기능이 on💡 되어있는거죠. 그래서 제가 준 제약이 작동하지 않아요. 이걸 false
로 바꿔볼게요!
드으으으디어! 화면에 등장했습니다🥳
그런데 뭔가 이상하죠..? 등장을 하긴 했지만, leading에서의 제약이 전혀 없어요. 저는 분명히 16만큼 떨어져달라고 했는데 말이죠. 그리고 노랗게, 저 상수가 사용되지 않았다고 뜹니다. 저 제약이 적용되지 않은거 같아요.
위에서 translatesAutoresizingMaskIntoConstraints
를 false
가 되면서, 제약을 받아들일 준비가 되었어요. 하지만 따로 제약을 실제로 적용되진 않아서, 0.0에 위치해있는 것 같군요!
그럼 저 제약을 적용해볼까요?
잘 보이시나요? 방금 전에
let leadingConstraint =
testLabel.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor, constant: 16)
위와 같이 선언했던 것을
testLabel.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor, constant: 16).isActive = true
이렇게 변경하여 실제로 적용이 될 수 있게 해주었습니다.
let leadingConstraint = testLabel.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor, constant: 16)
leadingConstraint.isActive = true
이렇게 할 수도 있고요!
크으 드디어 16만큼 떨어졌네요.👍
여기서 참고로 말씀드리자면, 지금은 view에 add하는 것을 미리 해주고, safeArea와의 제약을 지정해줬는데요.
먼저 제약을 주고, view에 추가해서는 안됩니다!
view에 라벨에 올라가있어야 safeArea와의 제약을 줄 수 있는 것이기 때문에, 제약을 active하기전에 view에 먼저 꼭꼭 올려줘야 하는것이지요.
이해가 안가신다면, view.addSubview(testLabel)
을 한번 코드블럭의 가장 아래쪽에 두고 실행해보세요🙃
그럼 이번에는 UILable이 가지고 있는 속성들을 변경해볼까요?
UILabel
이 기본적으로 가지고있는, text
, font
, textColor
, numberOfLines
, textAligment
를 변경해보았습니다. 이름과 내용을 보면 다들 알 수 있으시겠죠...?
다만 여기서 numberOfLines
만 설명 드리자면!
label의 라인 수, 즉 줄의 수를 지정해주는 것인데요. 원래는 1으로 되어있어서 \n이 들어가도 줄바꿈이 되지않지만, 원하는 줄 수를 지정해주면 \n을 이용하여 줄바꿈을 할 수 있답니다.
또한, leading과 trailing의 제약이 잡혀있거나, width가 정해져있을때 0으로 설정해두면, 정해진 넓이까지 글씨가 찼을때 자동으로 줄바꿈이 됩니다!🥳
스토리보드로 해보신 분들! 거기있는 그 설정과 똑같아요~
추가적으로,
testLabel.adjustsFontSizeToFitWidth = true
를 설정해주시면, 줄바꿈이 아니라 width에 맞게 글씨 크기가 줄어들게 만들 수도 있답니다!
그럼 이번에는 UIButton도 한번 코드로 작성해볼까요?
override func viewDidLoad() {
super.viewDidLoad()
configureTestButton()
configureTestLabel()
}
func configureTestButton() {
let testButton = UIButton()
testButton.setTitle("클릭해주세요!", for: .normal)
testButton.backgroundColor = .purple
testButton.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(testButton)
let safeArea = view.safeAreaLayoutGuide
testButton.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor, constant: 20).isActive = true
testButton.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor, constant: -20).isActive = true
testButton.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor, constant: -20).isActive = true
}
func configureTestLabel() { ... }
아까 작성한 라벨을 따로 함수로 빼고, Button을 만들 함수도 따로 빼서 적용했습니다.
아까 UILabel을 만든 것과 크게 다르지 않죠?
사실 지금까지는 Anchor을 이용한 방법을 사용했는데요.
Anchor말고 직접 제약을 만들어서 적용하는 방법도 한번 해볼게요!
let testSlider = UISlider()
testSlider.translatesAutoresizingMaskIntoConstraints = false
testSlider.maximumValue = 10
testSlider.minimumValue = 0
testSlider.value = 5
view.addSubview(testSlider)
let safeArea = view.safeAreaLayoutGuide
여기까진 아까와 비슷하죠?
이번엔 NSLayoutConstraint
를 사용하기로 했으니, leading에 해당하는 제약을 만들어볼게요.
let leading = NSLayoutConstraint(item: <#T##Any#>,
attribute: <#T##NSLayoutConstraint.Attribute#>,
relatedBy: <#T##NSLayoutConstraint.Relation#>,
toItem: <#T##Any?#>,
attribute: <#T##NSLayoutConstraint.Attribute#>,
multiplier: <#T##CGFloat#>,
constant: <#T##CGFloat#>)
지금처럼 NSLayoutConstraint
를 생성하니, 자동으로 채워야 할 것들이 나와요.
위에서부터 설명드리자면,
item
은 이 제약을 사용할 item 주체라고 보시면 되고요.
attribute
는 그 item의 속성 중 어디를 기준으로 할건지 정해주는 부분, ex) top, bottom
relatedBy
는 어떤 방법으로 속할지를 얘기하는 부분이에요. ex) 같다, 보다 크다, 보다 작다
toItem
은 처음에 지정한 item의 제약을 어디 기준으로 할지이고,
attribute
는 toItem의 속성 중에 어디를 기준으로 할지를 정하는 것입니다.
multiplier
는 곱하기와 비슷한 느낌인데, 지금은 사용하지 않아서 기본 1로 둡니다.
constant
는 Anchor를 사용할때와 마찬가지로 얼마나 떨어질 것인지에 대한 거리입니다.
let leading = NSLayoutConstraint(item: testSlider,
attribute: .leading,
relatedBy: .equal,
toItem: safeArea,
attribute: .leading,
multiplier: 1,
constant: 50)
위를 바탕으로 다시 한 번 살펴보면,
testSlider의 leading을 safeArea의 leading과 50과 같게 떨어지게 하겠다~
는 내용인거죠. 이해가 가시나요?😮
자 그러면 한번 만들어볼게요!
이번에도 역시 열심히 작성을 해보았지만, 노랗게 적용이 안되어있네요.
이번에는 아까와 다른 방식으로 적용해보려해요~
이번엔 코드 아래에
NSLayoutConstraint.activate([leading, trailing, top])
요렇게 layoutContraint를 활성화시켜볼게요.
아주 적용이 잘 되었어요 👏👏👏👏
지금까지 UILabel과 UIButton, UISlider을 화면에 띄워봤는데요. 그 과정에서 잠깐잠깐 등장했던 제약은 무엇인지 궁금하신 분이 있을 수 있다고 생각해서, 애플 공식문서에서 한번 캡쳐 해봤습니다.
제약 기반 레이아웃 시스템에서 충족해야하는 두 사용자 인터페이스 개체 간의 관계입니다... 는데 이게 무슨 소리고..?
그니까 대애충 말하지면, 2개의 위치 관계를 표현하기 위한 것인데요.
이건 여기서 풀기에 너무나도 많은 내용이라, 다른 포스팅에서 다루기로 하고
아직 제가 포스팅을 못했으니..(언제할지 모르니) 혹시라도 궁금하신 분들은 Auto Layout Guide와 함께 이 공식문서를 읽어보시길 추천드립니다🙏
추가로 저희가 오늘 했던 두 가지 내용에 대한 링크도 남겨두려고 하는데요. 요 링크는 Anchor에 대한 내용이지만, 아래와 같이 오늘 한 2가지를 함께 보여주기도해요!
그래서 시간나실때 꼭 한번 읽어보시길 추천드립니다🙏
참고로 참고라기엔 좀 중요한!, 다시 한번 자세히 제 글을 읽어보시면 (혹은 이미 눈치채신 분들도 있겠지만)
b.bottomAnchor.constraint(equalTo: s.bottomAnchor, constant: -20)
b.trailingAnchor.constraint(equalTo: s.trailingAnchor, constant: -20)
제약을 사용할때 지금처럼 constant가 마이너스로 설정된 것을 보실 수 있으실거에요!(NSLayoutConstraint도 마찬가지로)
코드로 제약을 줄 때 주의해야할 것 중에 하나가 바로 이 마이너스 값인데요.
trailing과 bottom의 경우에는 마이너스로 값을 주어야 화면 안에서 만날 수 있어요.
이 부분 살짝 짚어드리면서 오늘의 포스팅 마무리하겠습니다.
잘못된 것이 있거나, 궁금한 것이 있다면 언제든지 댓글✍🏻이나 메일📬 주세요오🙇🏻♀️
참고 링크👍👍👍
UILabel https://developer.apple.com/documentation/uikit/uilabel
NSLayoutConstraint https://developer.apple.com/documentation/uikit/nslayoutconstraint
Anchor https://developer.apple.com/documentation/uikit/nslayoutanchor
Auto Layout Guide
https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/index.html#//apple_ref/doc/uid/TP40010853
야곰 닷넷 - 오토레이아웃 정복하기
https://yagom.net/courses/autolayout/