
오토레이아웃을 구현하는 방법 - 시험에 한문제 나옴. 어떤 방법이 있는지
1장. AutoLayout 기본 개념
AutoLayout은 아이폰, 아이패드처럼 화면 크기가 달라도 UI가 깨지지 않게 배치하는 시스템이다.
핵심은 버튼이나 레이블의 위치를 좌표로 고정하는 게 아니라, 조건(constraint) 으로 정한다는 점이다.
예를 들면 다음과 같다.
버튼은 부모 뷰의 위쪽에서 10pt 아래에 둔다.
레이블은 오른쪽에서 20pt 떨어지게 둔다.
즉, AutoLayout은 뷰의 위치와 크기를 관계로 정하는 방식이다.

2장. NSLayoutConstraint 직접 생성
NSLayoutConstraint는 AutoLayout 제약조건을 Swift 코드로 직접 작성하는 방식이다.
예시 코드의 의미는 다음과 같다.
view1.top = superview.top + 20
즉, view1의 위쪽을 부모 뷰의 위쪽보다 20pt 아래에 배치한다는 뜻이다.
또 다른 코드에서는 버튼을 만들고 다음 조건을 건다.
button.centerXAnchor.constraint(equalTo: view.centerXAnchor)
button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
이 코드는 버튼을 화면 정중앙에 배치한다.
핵심은 이것이다.
button.translatesAutoresizingMaskIntoConstraints = false
코드로 AutoLayout을 사용할 때는 이 설정을 꺼야 제약조건이 제대로 적용된다.
3장. NSLayoutAnchor
NSLayoutAnchor는 NSLayoutConstraint를 더 짧고 읽기 쉽게 작성하게 해주는 방식이다.
예시는 다음 의미다.
view1.topAnchor.constraint(equalTo: view2.bottomAnchor, constant: 10)
뜻은 이거다.
view1의 위쪽을 view2의 아래쪽보다 10pt 아래에 배치한다.
또 다른 예시다.
view1.leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: 5)
뜻은 이거다.
view1의 왼쪽을 부모 뷰의 왼쪽보다 5pt 오른쪽에 배치한다.
즉, NSLayoutAnchor는 topAnchor, leadingAnchor, centerXAnchor 같은 속성으로 제약조건을 더 간단하게 작성하는 API다.
4장. SnapKit
SnapKit은 AutoLayout을 더 쉽게 쓰게 해주는 외부 라이브러리다.
기본 UIKit 코드보다 훨씬 짧고 읽기 쉽다.
view1.snp.makeConstraints { make in
make.top.equalTo(view2.snp.bottom).offset(10)
make.leading.equalToSuperview().offset(5)
}
뜻은 다음과 같다.
view1의 top = view2의 bottom + 10pt
view1의 leading = superview의 leading + 5pt
장점은 코드가 직관적이고 가독성이 좋다는 점이다.
단점은 기본 기능이 아니라서 외부 라이브러리를 설치하고 관리해야 한다는 점이다.
5장. SwiftUI
SwiftUI는 UIKit과 다르게 선언형 UI 프레임워크다.
UIKit은 버튼을 만들고, 화면에 추가하고, 제약조건을 거는 방식이다.
SwiftUI는 화면 구조를 바로 선언한다.
VStack {
Text("안녕하세요!")
Button("버튼 눌러보기") {
print("버튼이 눌렸습니다")
}
}
슬라이드 코드에서는 VStack 안에 텍스트와 버튼을 세로로 배치한다.
VStack(alignment: .leading, spacing: 16)
의미는 다음과 같다.
뷰들을 세로로 쌓고,
왼쪽 정렬하며,
각 요소 사이를 16pt 띄운다.
SwiftUI의 핵심은 제약조건을 하나씩 거는 방식보다, VStack/HStack 같은 컨테이너로 화면 구조를 표현한다는 점이다.

전체 핵심 정리
AutoLayout
→ 화면 크기가 달라도 UI가 깨지지 않게 조건으로 배치하는 시스템
NSLayoutConstraint
→ 제약조건을 코드로 직접 작성하는 방식
NSLayoutAnchor
→ Anchor를 사용해서 제약조건을 더 짧고 명확하게 작성하는 방식
SnapKit
→ AutoLayout 코드를 더 직관적으로 쓰게 해주는 외부 라이브러리
SwiftUI
→ 제약조건보다 화면 구조를 선언하는 최신 UI 방식


우측 하단에 4개의 툴을 이용해 Constraint Layout을 잡

Size Inspector : 삼각형 모양

auto layout을 제대로 지정하 않을 경우 생기는 오류


여러 제약조건이 동시에 걸릴 때,
뷰가 커지려는 우선순위는 Content Hugging,
작아지지 않으려는 우선순위는 Compression Resistance로 조절한다.

스택 뷰
- 세로 방향 : Vertical Stack View
- 가로 방향 : Horizontal Stack View


import UIKit
let name = ["살목지","란 12.3","짱구","프로젝트 헤일메리","왕과 사는 남자","내 이름은","리 코로닌의 미이라",
"기동전사 건담: 섬광의 하사웨이 키르케의 마녀", " 르누아르","마녀배달부 키키"]
struct MovieData : Codable {
let boxOfficeResult : BoxOfficeResult
}
struct BoxOfficeResult : Codable {
let dailyBoxOfficeList : [DailyBoxOfficeList]
}
struct DailyBoxOfficeList : Codable {
let movieNm : String
let audiCnt : String
let audiAcc : String
let rank : String
}
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
@IBOutlet weak var table: UITableView!
var movieData : MovieData? // 프로퍼티는 반드시 초기값이 있어야함. -> 옵셔널로 지정하여 초기값을 nil로
// let movieURL = 정해진 날짜가 아닌
// 앱 실행시마다 날짜가 어제의 날짜로 설정되도록
var movieURL = "https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=e0de9bcd80f2d174852f8e5f310a213a&targetDt="
override func viewDidLoad() {
super.viewDidLoad()
table.delegate = self
table.dataSource = self
movieURL += makeYesterdayString()
getData()
}
func makeYesterdayString() -> String{
let y = Calendar.current.date(byAdding: .day, value: -1, to: Date())!
let dateF = DateFormatter()
dateF.dateFormat = "yyyyMMdd"
let day = dateF.string(from: y)
return day
}
func getData(){
//if let url = URL(string: movieURL){}
guard let url = URL(string: movieURL) else { return } //네트워크 1단계 : url (주소) 만들기
let session = URLSession(configuration: .default) // 2단계 : URLSession 만들기
//3단계
let task = session.dataTask(with: url) { data, response, error in
if error != nil {return} // 에러가 있으면 종료
guard let JSONdata = data else {return}
print(JSONdata)
let dataString = String(data:JSONdata, encoding: .utf8)
//print(dataString!)
let decoder = JSONDecoder()
do{
let decodedData = try decoder.decode(MovieData.self, from: JSONdata)
//MovieData라는 자료형을 매개변수로 쓰려면 .self를 적어야함
print(decodedData.boxOfficeResult.dailyBoxOfficeList[1].movieNm)
print(decodedData.boxOfficeResult.dailyBoxOfficeList[0].audiAcc)
self.movieData = decodedData
DispatchQueue.main.async {
self.table.reloadData()
}
}catch{
print(error)
}
//print(response!)
}
task.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
// func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
// as! MyTableViewCell
// cell.movieName.text = "\(indexPath.row+1)등 " + name[indexPath.row]
// //print(indexPath,separator: " ",terminator: " ") //화면에 보이는 셀을 모두 만듦
// return cell
// }
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//print(indexPath.description) //어떤 섹션을 눌렀는지 확인 가능
}
// func tableView(_ tableView : UITableView, cellForRowAt indexPath : IndexPath) ->
// UITableViewCell{
// let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
// cell.movieName.text = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].movieNm
// cell.audiAccumulate.text = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiAcc //누적 관객 수
// cell.audiCount.text = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiCnt // 어제 관객 수
// return cell
// }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
guard let mRank = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].rank else {return UITableViewCell()}
guard let mName = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].movieNm else {return UITableViewCell()}
cell.movieName.text = "[\(mRank)위] \(mName)"
if let aCnt = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiCnt {
let numF = NumberFormatter()
numF.numberStyle = .decimal
let aCount = Int(aCnt)!
let result = numF.string(for: aCount)!+"명"
cell.audiCount.text = "어제 : \(result)"
}
if let aAcc = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiAcc {
let numF = NumberFormatter()
numF.numberStyle = .decimal
let aAcc1 = Int(aAcc)!
let result = numF.string(for: aAcc1)!+"명"
cell.audiAccumulate.text = "누적 : \(result)"
}
return cell
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "🍿박스오피스(영화진흥위원회제공:"+makeYesterdayString()+")🍿"
}
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return "Made by dunong"
}
}
모서리 둥글게

'iOS' 카테고리의 다른 글
| iOS_3grade - 13주차 (0) | 2026.06.02 |
|---|---|
| iOS_3grade - 10주차 (1) | 2026.05.12 |
| iOS_grade3 - 9주차 (0) | 2026.04.28 |
| iOS_3grade - 7주차 (0) | 2026.04.14 |
| iOS_3grade - 6주차 (1) | 2026.04.07 |