1. 과제 설명
2. CalculatorView 리팩토링
3. CalculatorViewController 리팩토링
4. 트러블슈팅: 이게 정녕 오토레이아웃잉교,,
5. 번외
preview를 여태 swiftUI를 import 해서 사용하고 있었음
-> 그리고 이것을 생각해낸 나 스스로에게 뿌듯함을 느꼈지만,,,,
UIKit도 Preview 기능이 생겼다는 것을 이번에 처음, , , , 알았다!
코드도 아주 간단하다!
import UIKit
#Preview {
CalculatorViewController()
}
이것이 끝임
하지만 처음에 UIKit을 import 안해서 작동이 안했다는..
요런 꿀팁을 slack에 공유해주신 분 감사해요 , , ,
UIKit으로 계산기 앱을 구현하는 것이었다.
필수 구현 기능
Lv.1: 수식 UILabel 구현-속성 및 AutoLayout 설정
Lv.2: 숫자 UIButton 및 horizontalStackView 생성
Lv.3: verticalStackView 생성 및 숫자 UIButton 배치
Lv.4: 연산 버튼 (+, -, *, /, AC, =) orange로 색상 변경
Lv.5: 모든 버튼 원형 설정
Lv.6: 초기 수식 UILabel 0으로 설정 및 UIButton 클릭 시 UILabel에 숫자 표시 기능 구현
Lv.7: 초기화 버튼 (AC) 기능 구현
Lv.8: 등호 버튼(=) 연산 기능 구현
*모든 예외 처리 필수 및 AutoLayout 설정
import UIKit
class CalculatorView: UIView {
lazy var numberLabel: UILabel = {
let label = UILabel()
label.text = "0"
label.textAlignment = .right
label.font = UIFont.boldSystemFont(ofSize: 60)
label.textColor = .white
label.backgroundColor = .black
return label
}()
var buttonNumbers = [["7","8","9","+"],
["4","5","6","-"],
["1","2","3","*"],
["AC", "0","=","/"]]
var numberButtons: [UIButton] = [] // 버튼 저장
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.black
configureNumberLabel()
configureNumberButtons()
}
func configureNumberLabel() {
addSubview(numberLabel)
numberLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
numberLabel.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor, constant: 200),
numberLabel.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 30),
numberLabel.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -30),
numberLabel.heightAnchor.constraint(equalToConstant: 100) // 원하는 높이 설정
])
}
func makeButton(withTitle title: String) -> UIButton {
let button = UIButton()
button.setTitle(title, for: .normal) // 이 부분을 추가하여 버튼에 제목을 설정
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = .boldSystemFont(ofSize: 30)
button.frame.size.height = 80
button.frame.size.width = 80
button.layer.cornerRadius = button.frame.size.height / 2
// 특정 버튼을 주황색으로 설정
if title == "AC" || title == "=" || title == "+" || title == "-" || title == "*" || title == "/" {
button.backgroundColor = UIColor.orange
} else {
button.backgroundColor = UIColor(red: 58/255, green: 58/255, blue: 58/255, alpha: 1.0)
}
button.translatesAutoresizingMaskIntoConstraints = false
return button
}
func makeHorizontalStackView(withTitle titles: [String]) -> UIStackView {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .horizontal
stackView.backgroundColor = .black
stackView.distribution = .fillEqually
stackView.spacing = 10
stackView.heightAnchor.constraint(equalToConstant: 80).isActive = true
titles.forEach { title in
let button = makeButton(withTitle: title)
numberButtons.append(button)
stackView.addArrangedSubview(button)
}
return stackView
}
func makeVerticalStackView(withTitles titlesArray: [[String]]) -> UIStackView {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.backgroundColor = .black
stackView.distribution = .fillEqually
stackView.spacing = 10
titlesArray.forEach { titles in
let horizontalStackView = makeHorizontalStackView(withTitle: titles)
stackView.addArrangedSubview(horizontalStackView)
}
return stackView
}
func configureNumberButtons() {
// 스택뷰 생성
let numberStackView = makeVerticalStackView(withTitles: buttonNumbers)
// 스택뷰를 CalculatorView에 추가
addSubview(numberStackView)
// 스택뷰 제약 설정
NSLayoutConstraint.activate([
numberStackView.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 30),
numberStackView.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -30),
numberStackView.centerXAnchor.constraint(equalTo: self.centerXAnchor),
numberStackView.topAnchor.constraint(equalTo: numberLabel.bottomAnchor, constant: 60),
numberStackView.widthAnchor.constraint(equalToConstant: 350)
])
}
}
import UIKit
class CalculatorView: UIView {
lazy var numberLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .black
label.textColor = .white
label.text = "0"
label.textAlignment = .right
label.font = UIFont.boldSystemFont(ofSize: 60)
return label
}()
var buttonNumbers = [["7","8","9","+"],
["4","5","6","-"],
["1","2","3","*"],
["AC", "0","=","/"]]
var numberButtons: [UIButton] = []
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.black
configureNumberLabel() // 계산기 라벨 생성
configureNumberButtons() // 계산기 버튼 생성
}
override func layoutSubviews() {
super.layoutSubviews()
numberButtons.forEach { button in
button.widthAnchor.constraint(equalTo: button.heightAnchor).isActive = true // 버튼을 정사각형으로 유지
button.layer.cornerRadius = button.frame.height / 2
}
}
func configureNumberLabel() {
numberLabel.translatesAutoresizingMaskIntoConstraints = false
addSubview(numberLabel)
NSLayoutConstraint.activate([
numberLabel.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 30),
numberLabel.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -30),
numberLabel.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor, constant: 200),
numberLabel.heightAnchor.constraint(equalToConstant: 100)
])
}
func configureNumberButtons() {
let numberStackView = makeVerticalStackView(withTitles: buttonNumbers)
addSubview(numberStackView)
NSLayoutConstraint.activate([
numberStackView.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 10),
numberStackView.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -10),
numberStackView.centerXAnchor.constraint(equalTo: self.centerXAnchor),
numberStackView.topAnchor.constraint(equalTo: numberLabel.bottomAnchor, constant: 60)
// numberStackView.widthAnchor.constraint(equalToConstant: 350) // 기존 조건 주석 처리
])
}
func makeButton(withTitle title: String) -> UIButton {
let button = UIButton()
button.setTitle(title, for: .normal)
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = .boldSystemFont(ofSize: 30)
//button.frame.size = CGSize(width: 80, height: 80) // 기존 조건 주석 처리
//button.layer.cornerRadius = button.frame.size.height / 2 // 기존 조건 주석 처리
// 연산자, 특수버튼과 숫자 버튼 배경 분리
button.backgroundColor = ["AC","=","+", "-", "*", "/"].contains(title) ?
.orange : UIColor(red: 58/255, green: 58/255, blue: 58/255, alpha: 1.0)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}
// 수평 StackView
func makeHorizontalStackView(withTitle titles: [String]) -> UIStackView {
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.backgroundColor = .black
stackView.spacing = 10
stackView.distribution = .fillEqually
//stackView.heightAnchor.constraint(equalToConstant: 80).isActive = true // 기존 조건 주석 처리
stackView.translatesAutoresizingMaskIntoConstraints = false
titles.forEach { title in
let button = makeButton(withTitle: title)
numberButtons.append(button)
stackView.addArrangedSubview(button)
}
return stackView
}
// 수직 StackView
func makeVerticalStackView(withTitles titlesArray: [[String]]) -> UIStackView {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.backgroundColor = .black
stackView.spacing = 10
stackView.distribution = .fillEqually
//stackView.widthAnchor.constraint(equalToConstant: 350).isActive = true // 기존 조건 주석처리
stackView.translatesAutoresizingMaskIntoConstraints = false
titlesArray.forEach { titles in
let horizontalStackView = makeHorizontalStackView(withTitle: titles)
stackView.addArrangedSubview(horizontalStackView)
}
return stackView
}
}
전체적인 Constraint 변경
layoutSubviews() 함수 추가
숫자와 연산 버튼 색상 구분 방법 변경
// 특정 버튼을 주황색으로 설정
if title == "AC" || title == "=" || title == "+" || title == "-" || title == "*" || title == "/" {
button.backgroundColor = UIColor.orange
} else {
button.backgroundColor = UIColor(red: 58/255, green: 58/255, blue: 58/255, alpha: 1.0)
}
// 연산자, 특수버튼과 숫자 버튼 배경 분리
button.backgroundColor = ["AC","=","+", "-", "*", "/"].contains(title) ?
.orange : UIColor(red: 58/255, green: 58/255, blue: 58/255, alpha: 1.0)
코드가 더 깔끔해졌다!
import UIKit
class CalculatorViewController: UIViewController {
var calculatorView: CalculatorView!
// var calculatorModel = CalculatorModel()
override func loadView() {
calculatorView = CalculatorView()
view = calculatorView
}
override func viewDidLoad() {
super.viewDidLoad()
calculatorView.numberButtons.forEach { button in
if button.title(for: .normal) == "AC" { //AC 기능 분리
button.addTarget(self, action: #selector(clearLabel), for: .touchUpInside)
} else if button.title(for: .normal) == "=" {
button.addTarget(self, action: #selector(calculateNumbers), for: .touchUpInside)
} else {
button.addTarget(self, action: #selector(numberButtonTapped(_:)), for: .touchUpInside)
}
}
}
@objc func numberButtonTapped(_ sender: UIButton) {
guard let title = sender.title(for: .normal) else { return }
// 숫자 버튼을 눌렀을 때 레이블을 해당 숫자로 변경
if calculatorView.numberLabel.text == "0" {
calculatorView.numberLabel.text = title
} else {
calculatorView.numberLabel.text? += title
}
}
// AC 버튼으로 초기화
@objc func clearLabel() {
calculatorView.numberLabel.text = "0"
}
// = 버튼 누르면 연산 시작
@objc func calculateNumbers() {
// 옵셔널 바인딩을 사용하여 nil이 아닌 경우에만 결과를 numberLabel에 할당
if let result = calculate(expression: calculatorView.numberLabel.text ?? "Error") {
calculatorView.numberLabel.text = String(result)
} else {
calculatorView.numberLabel.text = "Error"
}
}
/// 수식 문자열을 넣으면 계산해주는 메서드.
///
/// 예를 들어 expression 에 "1+2+3" 이 들어오면 6 을 리턴한다.
/// 잘못된 형식의 수식을 넣으면 앱이 크래시 난다. ex) "1+2++"
func calculate(expression: String) -> Int? {
let expression = NSExpression(format: expression)
if let result = expression.expressionValue(with: nil, context: nil) as? Int {
print(result)
return result
} else {
print("error")
return nil
}
}
}
import UIKit
class CalculatorViewController: UIViewController {
var calculatorView: CalculatorView!
// var calculatorModel = CalculatorModel()
var finished = false // 연산이 끝났는지 확인
override func loadView() {
calculatorView = CalculatorView()
view = calculatorView
}
override func viewDidLoad() {
super.viewDidLoad()
calculatorView.numberButtons.forEach { button in
if button.title(for: .normal) == "AC" { // AC 버튼 클릭 -> 초기화
button.addTarget(self, action: #selector(clearNumbers), for: .touchUpInside)
} else if button.title(for: .normal) == "=" { // = 버튼 클릭 -> 연산 수행
button.addTarget(self, action: #selector(calculateNumbers), for: .touchUpInside)
} else { // 숫자 버튼 클릭 -> 숫자 입력
button.addTarget(self, action: #selector(enterNumbers(_:)), for: .touchUpInside)
}
}
}
//숫자 입력
@objc func enterNumbers(_ sender: UIButton) {
guard let title = sender.title(for: .normal) else{
print("enterNumbers 오류입니다.")
return
}
if finished == true { //이전 연산이 끝나고 새로운 연산을 시작할 경우
calculatorView.numberLabel.text = title
finished = false
} else {
if calculatorView.numberLabel.text == "0" {
calculatorView.numberLabel.text = title
} else {
calculatorView.numberLabel.text? += title
}
}
}
// AC 버튼 초기화
@objc func clearNumbers() {
calculatorView.numberLabel.text = "0"
}
// 연산 진행
@objc func calculateNumbers() {
if let result = calculate(expression: calculatorView.numberLabel.text ?? "Error") {
calculatorView.numberLabel.text = String(result)
} else {
calculatorView.numberLabel.text = "Error"
}
}
// 연산 함수
func calculate(expression: String) -> Int? {
let expression = NSExpression(format: expression)
if let result = expression.expressionValue(with: nil, context: nil) as? Int {
print(result)
finished = true // 연산이 끝남을 알림
return result
} else {
print("error")
return nil
}
}
}
var finished = false // 연산이 끝났는지 확인
//숫자 입력
@objc func enterNumbers(_ sender: UIButton) {
guard let title = sender.title(for: .normal) else{
print("enterNumbers 오류입니다.")
return
}
if finished == true { //이전 연산이 끝나고 새로운 연산을 시작할 경우
calculatorView.numberLabel.text = title
finished = false
} else {
if calculatorView.numberLabel.text == "0" {
calculatorView.numberLabel.text = title
} else {
calculatorView.numberLabel.text? += title
}
}
}
// 연산 함수
func calculate(expression: String) -> Int? {
let expression = NSExpression(format: expression)
if let result = expression.expressionValue(with: nil, context: nil) as? Int {
print(result)
finished = true // 연산이 끝남을 알림
return result
} else {
print("error")
return nil
}
}
finished로 연산이 끝을 확인 후, 새로운 연산 시작
오토레이아웃 특강을 듣던 중 , , , ,
튜터님: 오토레이아웃 적용이 잘 됐나요? . . . 🧐
.
.
.

우리 16프로로 봤을 때 조왓잔아. . . . . . . . .🥹
.
.
하지만 SE의 작은 화면 지나칠 수 없어
나 또한 13미니 유저이기 때문이다................
작은 핸드폰 만만세!! 또 출시해줘잉
버튼이 잘리는 것도 문제이지만, 자세히 보면 버튼이 미묘하게 찌그러져있는,, 원이 아닌 느낌이 있다.
1. 동그래야할 버튼이 찌그러진다!
2. 버튼이 잘린다!
AutoLayout이 제대로 적용이 되지 않을 것임을 알고 있었음에도.. 모든 기기에 적용이 될 수 있도록 시도할 수 있었음에도 그저 과제에서 주어진 속성 값대로만 설정했다는 점도 문제로 삼을 수 있겠다.
NSLayoutConstraint.activate([
numberStackView.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 30),
numberStackView.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -30),
numberStackView.centerXAnchor.constraint(equalTo: self.centerXAnchor),
numberStackView.topAnchor.constraint(equalTo: numberLabel.bottomAnchor, constant: 60),
numberStackView.widthAnchor.constraint(equalToConstant: 350)
])
numberStackView (숫자 버튼)의 constraint이다.
과제에서는 trailing, leading에 constraint을 주라는 말이 없었는데, 위의 코드에서는 StackView 양 옆에 constraint을 주고 있어서 아무리 button의 frame 사이즈를 조절해도, StackView의 width, height를 조절해도 묘하게 원이 찌그러져보이는 것이었다..
numberStackView.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 30),
numberStackView.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -30),
문제의 두 줄,,
이걸 진짜 온갖 방법을 다 시도해보고 주어진 문제 조건과 비교하다가 초반부터 설정한 constraint가 문제였음 발견하였고.. 참으로 허무했다.
문제의 조건을 잘 확인해야겠다.
constraint를 제대로 설정하지 않고, 직접 frame의 height, width를 지정해준 것이 문제라고 생각했다.
원이 찌그러지는 문제의 경우, numberStackView (숫자 버튼)의 trailing, leading에 constraint 설정값을 지우니 해결되었다.
그러나.. 버튼이 잘리는 문제에 대한 해결 방안을 완벽히 터득하지 못했다.
나느 버튼의 크기를 특정 숫자로 설정하기 때문에 버튼이 잘린다고 판단했다.
기종마다 화면 크기가 다르기 때문에 어떤 기종에는 설정한 버튼의 크기가 너무 클 수 있다고 생각했다.
따라서 Autolayout이 설정될 때 stackview에 버튼이 들어가는 개수만큼의 동일한 크기를 가진 정사각형 공간을 확보한 후,
한 칸의 크기에 따라 버튼 크기를 설정하고 radius를 주면 되겠다는 생각을 했다.
constraint, frame의 설정 값을 이리저리 조절해보고 여러 방법을 찾아본 결과,
위의 방법대로 할 경우 버튼이 들어갈 공간의 크기를 미리 계산할 수 없어서 프로그램이 실행된 후 한 칸의 크기를 확인하는 방법이 있다는 것을 알게 되었다.
이후, viewDidLayoutSubviews()와 layoutSubviews() 함수가 있다는 것을 추가로 알게 되어 이를 토대로 코드를 바꿔보았다.
- width를 기준으로 height를 동일하게 맞춰주기 -> 버튼 모양을 정사각형으로!
- layoutSubviews() 내부에서 Autolayout으로 설정된 버튼 한 변을 기준으로 radius 설정하기 (
한 변 / 2는 원의 반지름이기 때문)
하지만.. 실패하다 . . . . 정사각형은 만들었는데 동그라미가 되질 않음..
버튼은 여전히 잘리고 네모 버튼이 동그래지는 마법의 계산기를 만들다..
머리가 띵 했다,, 아직 viewDidLayoutSubviews, layoutSubviews도 정확히 모르겠을 뿐더러.. layoutSubviews를 사용할 경우 앱에 더 무리가 간다는 아주 가볍고 부정확한 지식만을 가지고 있었다.
layoutSubviews 함수 내부에 작성한 코드를 다른 부분에 작성해야하는 건가? 혹은 constraint priority를 설정해줘야하는건가? 아니면.... width, height를 지정해줬어도 됐나? 라는 여러 생각을 하게 됐다.
그러던 와중에 같은 원이 찌그러지는 문제는 위에서 말한 constraint가 원인이었다는 것을 알게 되었고 우선 그것을 처리하고,
뒤죽박죽 코드를 정리하고, 다시 한 번 layoutSubviews에 내가 하고자하는 코드를 작성했다.
그리하여 완성된 코드..
override func layoutSubviews() {
super.layoutSubviews()
numberButtons.forEach { button in
button.widthAnchor.constraint(equalTo: button.heightAnchor).isActive = true // 버튼을 정사각형으로 유지
button.layer.cornerRadius = button.frame.height / 2
}
}
다행스럽게도, 화면의 크기에 맞게 원형 버튼이 설정되었다.
미니에서도 잘 보이는 계산기, , ,ㅎㅎ
예쁜 UI의 계산기를 만들어보았다..
ㅠㅠ 실패했다. se에서는 아무리 설정해도 버튼이 잘린다..
버튼이 안잘리기 위해 찾아낸 방법은 아래 코드에서 topAnchor.constraint를 더 작게 조정하는 방법이었는데.. 더이상 주어진 조건을 바꾸고 싶지도 않을 뿐더러 이게 맞는 방법인지도 확신이 없어서 여기서 멈췄다 ㅠㅠ
NSLayoutConstraint.activate([
numberLabel.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 30),
numberLabel.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: -30),
numberLabel.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor, constant: 200),
numberLabel.heightAnchor.constraint(equalToConstant: 100)
])
이번 과제를 하면서, 주어진 과제 조건대로 하면 위와 같이 특정 기종에서는 제대로 보이지 않는 이슈가 발생하는데.. 또 지금까지는 언제나 무슨 문제를 해결하든 주어진 조건 을 따르는게 중요하다고 생각하여서.. 이것 또한 과제이기 때문에 내가 마음대로 해도 되는지 확신이 안섰다.. ㅠㅠ
또한 AutoLayout에 대해 추가적으로 공부하고 싶은 부분들이 생겼다.
- 생각보다 많은 메커니즘을 통해 View가 실행되고, 그 중 layoutSubviews처럼 오토레이아웃 frame 크기가 확정된 후 실행되는 함수, 먼저 실행되는 함수 등이 있다는 사실을 알게 되었다.
viewDidLoad, viewWillAppear 등등..
- constraint priority -> defaultLow, defaultHight 등
위와 같은 부분 추가로 공부하여 업로드 해야겠다!
업로드 하게 되면 해당 게시글에도 링크를 남기겠습니다..
- 객체지향 프로그래밍의 특징
캡슐화 (Encapsulation), 상속 (Inheritance), 다형성 (Polymorphism), 추상화 (Abstraction) 가 있습니다.
캡슐화란, 객체의 속성과 메서드를 하나의 클래스로 묶어, 외부에서 직접 접근하지 못하도록 하는 것입니다.
코드의 보안성을 높이고, 객체 내부의 데이터를 보호할 수 있습니다.
예를 들어 private이 있습니다.
상속이란, 부모 클래스의 속성과 메서드를 자식 클래스가 물려받는 것입니다.
코드의 재사용성을 높이고, 코드 중복을 줄일 수 있습니다.
상속을 통해 자식 클래스는 부모 클래스의 기능을 그대로 사용할 수 있으며, 필요한 경우 오버라이딩으로 특정 메서드만 수정할 수 있습니다.
다형성이란, 같은 인터페이스나 메서드가 서로 다른 동작을 수행하게 하는 특징입니다.
예를 들어, 하나의 메서드 이름으로 여러 객체에서 서로 다른 동작을 할 수 있습니다.
오버로딩(메서드 이름은 같지만 매개변수 타입이나 개수가 다름)과 오버라이딩(상속받은 메서드를 자식 클래스에서 재정의)으로 구현됩니다.
추상화란, 복잡한 시스템의 구조에서 필수적인 정보만 추려내는 것입니다.
객체의 구체적인 구현보다 필요한 인터페이스와 기능에만 집중할 수 있게 하여, 더 효율적인 설계를 가능하게 합니다.
추상 클래스나 인터페이스가 추상화의 대표적인 예입니다.
- Autolayout에서 Leading Constraint 와 Left Constraint의 차이점은?
Leading Constraint는 언어의 읽기 방향을 기준으로 뷰의 시작 위치를 정의합니다.
영어(왼쪽에서 오른쪽으로 읽음)에서는 leading이 왼쪽을 의미하지만, 아랍어(오른쪽에서 왼쪽으로 읽음)에서는 오른쪽을 의미합니다.
주로 다국어 지원이 필요한 앱에서 사용하여, 언어에 따라 자동으로 방향을 조정할 수 있습니다.
Left Constraint는 뷰의 위치를 화면의 절대적인 왼쪽으로 고정합니다.
언어와 관계없이 언제나 화면의 왼쪽에서 시작하는 위치를 지정합니다.
따라서 다국어 지원이 필요하지 않고, 왼쪽 정렬이 고정된 경우에 사용하기 적합합니다.
이 부분에 대해서는 이번에 AutoLayout 특강을 들으며 튜터님이 말씀해주신 덕분에 알 수 있었다.
나도 왜 leading과 trailing을 쓸까? 라는 생각을 한 적이 있음...