코드 실행 시, "실행 시 검은 화면만 나오는 경우" 혹은 "Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value"의 오류가 발생하는 경우 (로드 과정에서 컴포넌트 속성을 수정하는 경우 ex) label.text = "...")
이번 경우에는 화면 이동을 위해 SceneDelegate.swift 내에서, 아래와 같이 코드를 추가해서 문제가 발생함
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
let rootVC = HomeViewController() // 오류 발생 원인 코드
let navigationController = UINavigationController(rootViewController: rootVC)
window.rootViewController = navigationController
self.window = window
window.makeKeyAndVisible()
}
StoryBoard 기반의 UIKit를 사용하기 때문에, 단순히 저렇게 생성자를 호출하는 방식으로는 VC가 정상적으로 생성되지 않음
아래와 같이 identifier를 기반으로 instantiateViewController 메소드를 호출하여 초기화해야함
// 해결 코드
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rootVC = storyboard.instantiateViewController(identifier: "HomeViewController") as! HomeViewController
SDK does not contain 'libarclite' at the path '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_iphonesimulator.a'; try increasing the minimum deployment target
podfile 내 platform 관련 코드도 주석 해제 해제 후 11.0으로 변경해주고,
iOS Deployment Target 값도 13.0으로 변경해줬음
그리고 실행했더니 냅다 또 오류 4개 추가로 뜸
Sandbox: rsync.samba(59873) deny(1) ...(생략)
찾아보니 아래와 같이 Build Setting 내 "User Script Sandboxing"의 값을 Yes -> No 로 변경하면 실행 성공함
lazy var leftTitleLabel1 = getLabelView(titleStr: "서울특별시", fontSize: 20.0, fontWeight: .bold, fontColor: .white)
lazy var leftTitleLabel2 = getLabelView(titleStr: "9°", fontSize: 40.0, fontWeight: .medium, fontColor: .white)
lazy var leftStackView = UIStackView().then {
$0.backgroundColor = .green
// 스택 요소로 포함할 subView들을 선언
let stackView = UIStackView(arrangedSubviews: [leftTitleLabel1, leftTitleLabel2])
$0.axis = .vertical // 수직 방향으로 스택을 구성
$0.distribution = .fill // 요소들이 스택을 채울 수 있게 분포되도록 구성
$0.alignment = .leading // 왼쪽 끝에 붙어서 요소가 나열되도록 구성
$0.spacing = 0 // 요소 간의 간격 0
}
// helper methods - 컨테이너 뷰를 가진 UILabel 컴포넌트를 반환
func getLabelView(titleStr: String, fontSize: Double, fontWeight: UIFont.Weight, fontColor: UIColor) -> UIView {
let titleLabel = UILabel().then {
$0.text = titleStr
$0.font = .systemFont(ofSize: fontSize, weight: fontWeight)
$0.textColor = fontColor
}
let containerView = UIView().then {
$0.backgroundColor = .systemPink
$0.backgroundColor = .clear
}
containerView.addSubview(titleLabel)
titleLabel.snp.makeConstraints { make in
make.top.bottom.leading.trailing.equalToSuperview()
}
return containerView
}
2-3. 우측 상단의 구름 이미지, 날씨, 최고/최저 기온을 포함한 수직 UIStackView
let cloudImgView = UIImageView(image: UIImage(systemName: "cloud.fill")).then {
$0.tintColor = .white
}
// getLabelView는 상단 helper method 참조
lazy var rightTitleLabel1 = getLabelView(titleStr: "대체로 흐림", fontSize: 18.0, fontWeight: .medium, fontColor: .white)
lazy var rightTitleLabel2 = getLabelView(titleStr: "최고: 21° 최저 7°", fontSize: 18.0, fontWeight: .medium, fontColor: .white)
lazy var rightStackView = UIStackView().then {
$0.backgroundColor = .blue
let stackView = UIStackView(arrangedSubviews: [cloudImgView, rightTitleLabel1, rightTitleLabel2])
$0.axis = .vertical
$0.distribution = .fill
$0.alignment = .trailing // 요소들이 오른쪽 끝에 붙어 나열될 수 있도록 구성
$0.spacing = 3
}
좌우측 상단에 스택뷰를 추가한 후
2-4. - 하단의 각 시간대별 날씨를 포함한 수평 UIStackView
let weatherWithTimeList = [
["오전 8시", "10°"],
["오전 9시", "12°"],
["오전 10시", "15°"],
["오전 11시", "17°"],
["오후 12시", "19°"],
["오후 1시", "20°"],
]
lazy var weatherViews: [UIView] = []
// helper method - 시간 및 기온 정보 배열을 토대로 하단 수평 스택뷰의 요소로 쓰이는 UIView 반환
// viewDidLoad 내에서 호출하여 weatherViews 배열 구성
func configureWeatherOfTime() {
weatherWithTimeList.map { info in
weatherViews.append(getWeatherViewOfTime(timeStr: info[0], temper: info[1]))
}
}
lazy var bottomStackView = UIStackView().then {
let stackView = UIStackView(arrangedSubviews: weatherViews)
$0.axis = .horizontal // 수평 방향으로 요소가 나열되도록 지정
$0.distribution = .fillEqually // 모든 요소가 스택 내에서 동등한 크기를 가지도록 지정
$0.alignment = .leading
$0.spacing = 5
}
하단 스택뷰를 추가한 후
3. 레이아웃 구성
private func setLayouts() {
// add views
self.view.addSubview(backView)
// 전체 컨테이너 뷰에 좌측 상단, 우측 상단, 하단 스택뷰를 추가
backView.addSubview(leftStackView)
backView.addSubview(rightStackView)
backView.addSubview(bottomStackView)
// add views in StackView
// 좌측 상단 스택뷰 내에 요소로 사용될 sub view들을 추가
leftStackView.addArrangedSubview(leftTitleLabel1)
leftStackView.addArrangedSubview(leftTitleLabel2)
// 우측 상단 스택뷰 내에 요소로 사용될 sub view들을 추가
rightStackView.addArrangedSubview(cloudImgView)
rightStackView.addArrangedSubview(rightTitleLabel1)
rightStackView.addArrangedSubview(rightTitleLabel2)
// 하단 스택뷰 내에 요소로 사용될 sub view들을 추가
weatherViews.map { view in
bottomStackView.addArrangedSubview(view)
}
// set constraints
backView.snp.makeConstraints { make in
make.top.equalToSuperview().offset(130)
make.leading.equalToSuperview().offset(20)
make.trailing.equalToSuperview().inset(20)
}
leftStackView.snp.makeConstraints { make in
make.leading.top.equalToSuperview().offset(15)
}
cloudImgView.snp.makeConstraints { make in
make.top.equalToSuperview()
}
rightStackView.snp.makeConstraints { make in
make.top.trailing.equalToSuperview().inset(15)
make.bottom.equalTo(leftStackView.snp.bottom)
}
bottomStackView.snp.makeConstraints { make in
make.top.equalTo(leftStackView.snp.bottom).offset(30)
make.leading.trailing.equalToSuperview().inset(10)
make.bottom.equalToSuperview().inset(15)
}
}
4. 추가) 수직 스택뷰 요소 간 spacing 달리 주기
// 구름 이미지 UIImageView를 감싸는 컨테이너 뷰 정의
let cloudImgContainerView = UIView().then {
$0.backgroundColor = .clear
}
let cloudImgView = UIImageView(image: UIImage(systemName: "cloud.fill")).then {
$0.tintColor = .white
$0.contentMode = .scaleAspectFill
}
// 레이아웃 구성 변경
cloudImgView.snp.makeConstraints { make in
make.top.bottom.leading.trailing.equalToSuperview()
}
// 구름 이미지 컴포넌트 하단 요소에 대해 top 방향으로 원하는 만큼의 여백값을 추가
rightTitleLabel1.snp.makeConstraints { make in
make.top.equalTo(cloudImgView.snp.bottom).offset(20)
}
날짜 라벨은 날짜 딱 한 줄만 보여주도록, 내용 라벨은 여러줄에 걸쳐 보여줄 수 있도록 제약조건 설정을 해주었다 (각각 모두 12로 맞춰줌)
이 때 hugging priority 관련의 제약조건 오류가 생기는데, Content Hugging Priority가 두 라벨 모두 같은 값이고, 셀 내의 제약조건에 따라 요소들을 위치시키려고 하자니, 어떤 요소를 더 늘려서 맞춰넣어야 하는지 애매해져버려 발생하는 오류인 것 같다
Content Hugging Priority값이 더 낮으면, 그 값이 더 높은 요소 대신에 크기가 늘려져서 제약조건에 맞도록 설정된다
값이 더 높은 요소는 정해진 조건값에 따라 그 크기를 유지할 수 있게 된다
내용 라벨의 Content Hugging Priority 값을 1 낮추어서, 날짜 라벨의 그 값보다 더 낮도록 설정해주면 아래와 같이 오류가 해결된다
content compression resistance priority는 요소가 가지는 크기가 커지는 경우에 대해, 찌그러질 것이냐 말것이냐에 대한 우선순위를 정하는 것이다
이미지에서의 컬렉션뷰 셀과 같이 정해진 범위내에서 만약 내용 라벨에 담길 컨텐츠가 많아지면, 내용 라벨이 자신의 크기를 줄이던지, 혹은 날짜 라벨이 줄어들던지 해야할 것이다
이 경우에 대해, content compression resistance priority값이 날짜 라벨이 더 높게 설정된다면 날짜 라벨은 크기를 유지하고, 값이 더 낮은 내용 라벨이 찌그러지게(크기가 줄어들게) 되는 것이다
일단 내가 이해한 바로는,,,
Content Hugging Priority
-> 자리 남는다; 누가 몸집 더 키울래? (값 높은 애 : 전 아닙니다 / 값 낮은 애 : 아휴 제가 늘릴게요;)
Content Compression Resistance Priority -> 뭐야 자리 부족하잖아; 누가 찌그러질래? (값 높은 애 : 전 아니라고요 / 값 낮은 애 : 예예 저요)