안녕하세요! caution입니다.
오늘은, String을 사용하면서 자주 사용되는 Extension과 헷갈리는 녀석들을 다 모아보았습니다.
URL에서 ‘/’를 ‘_‘로 교체하고 싶다면?
let url = URL(string: "https://caution-dev.github.io/swift/2021/03/26/ReplayKit.html")
var urlString = url?.absoluteString
// "https://caution-dev.github.io/swift/2021/03/26/ReplayKit.html"
urlString.replacingOccurrences(of: "/", with: "_")
// "https:__caution-dev.github.io_swift_2021_03_26_ReplayKit.html"
prefix(Int)
사용하기
let fullName: String = "caution"
let prefix3: Substring = fullName.prefix(3)
// "cau"
let prefixString: String = String(prefix3)
안녕하세요! caution입니다. 오늘은 이전 포스팅에 이어서 Anatomy of a Constraint을 번역합니다.
하기 컨텐츠는 Apple이 작성한 Auto Layout Guide를 번역, 재사용한 컨텐츠임을 밝힙니다.
뷰 계층 구조의 레이아웃은 일련의 선형 방정식으로 정의됩니다. 각 제약 조건은 단일 방정식을 나타냅니다. 당신의 목표는 원하는 레이아웃을 표현할 수 있는 일련의 방정식을 선언하는 것입니다. 샘플 방정식은 다음과 같습니다.
이 제약 조건은 빨간색 뷰의 leading 엣지가 파란색 뷰의 trailing 엣지 이후 8.0 포인트 여야 함을 나타냅니다. 방정식에는 다음과 같은 여러 부분이 있습니다.
대부분의 제약 조건은 사용자 인터페이스에서 두 항목 간의 관계를 정의합니다. 이러한 항목은 뷰 또는 레이아웃 가이드를 나타낼 수 있습니다. 제약 조건은 항목의 높이와 너비 간의 비율 설정과 같이 단일 항목의 두 가지 속성 간 관계를 정의 할 수도 있습니다. 항목의 높이 또는 너비에 상수 값을 지정할 수도 있습니다. 상수 값을 사용하여 작업 할 때 두 번째 아이템을 공백으로 두고 두 번째 속성은 Not An Attribute으로 설정되고 승수는 0.0으로 설정됩니다.
Auto Layout에서, 속성들은 제약될 수 있는 특성들을 정의합니다. 일반적으로, 4가지 모서리와 (leading, trailing, top, bottom) 높이, 너비, 그리고 수직 수평 정들을 포함합니다. 텍스트 아이템들은 하나 이상의 baseline 속성들을 가집니다.
속성 리스트 전체를 확인하려면 NSLayoutAttribute enum 을 확인하세요.
Note OS X과 iOS 둘 다 NSLayoutAttribute를 사용하긴 하지만 그 값들이 조금 다르게 정의되어 있습니다. 전체 속성들을 보려면 정확한 플랫폼의 문서를 보고 있는지 확인하세요.
이 방정식에 다양한 매개 변수와 속성을 사용하면 여러 가지 유형의 제약 조건을 만들 수 있습니다. 뷰 사이의 여백을 정의하거나 뷰 가장자리를 정렬하거나 두 뷰의 상대적인 크기를 정의하거나 뷰의 비율을 정의 할 수도 있습니다. 그러나 모든 속성이 호환되는 것은 아닙니다.
기본 유형의 속성은 두 가지가 있습니다. 크기 특성 (예 : height 및 width) 및 위치 특성 (예 : leading, left 및 top). 크기 속성은 항목의 위치를 가리키지 않고 항목의 크기를 지정하는 데 사용됩니다. 위치 속성은 다른 항목과 관련된 항목의 위치를 지정하는 데 사용됩니다. 그러나 항목의 크기는 표시하지 않습니다.
이러한 차이점을 염두에 두고, 다음 규칙을 적용하세요.
다음은 다양한 방정식들을 나타냅니다.
// 높이 제약조건 설정
View.height = 0.0 * NotAnAttribute + 40.0
// 두 버튼 사이에 상수 거리 설정
Button_2.leading = 1.0 * Button_1.trailing + 8.0
// 두 버튼의 leading 정렬
Button_1.leading = 1.0 * Button_2.leading + 0.0
// 두 버튼의 너비를 동일하게 설정
Button_1.width = 1.0 * Button_2.width + 0.0
// 뷰를 상위 뷰의 중앙에 위치
View.centerX = 1.0 * Superview.centerX + 0.0
View.centerY = 1.0 * Superview.centerY + 0.0
// 종횡비율 설정
View.height = 2.0 * View.width + 0.0
Note 이 챕터의 모든 방정식들은 의사코드를 사용했습니다. 실제 코드로 작성된 제약조건을 보고자 한다면 Programming Creating Constraints 나 Auto Layout Cookbook을 활용하세요.
Note에 표기된 방정식들은 대입이 아니라 서로 같음을 나타내고 있다는 것이 중요합니다.
Auto Layout이 이러한 방정식을 풀어나갈 때 단순히 오른쪽의 값을 왼쪽에 대입하는 것이 아닙니다. 대신에 이러한 관계를 만족할 수 있는 속성 1과 속성 2의 값을 계산합니다. 이는 우리가 방정식을 재정렬할 필요가 없다는 것을 의미합니다. 예를 들어, 다음 방정식들은 위의 방정식들과 동일합니다.
// 두 버튼 사이에 상수 거리 설정
Button_1.trailing = 1.0 * Button_2.leading - 8.0
// 두 버튼의 leading 정렬
Button_2.leading = 1.0 * Button_1.leading + 0.0
// 두 버튼의 너비를 동일하게 설정
Button_2.width = 1.0 * Button.width + 0.0
// 뷰를 상위 뷰의 중앙에 위치
Superview.centerX = 1.0 * View.centerX + 0.0
Superview.centerY = 1.0 * View.centerY + 0.0
// 종횡비율 설정
View.width = 0.5 * View.height + 0.0
Note 항목들을 재정렬할 때 승수(Multiplier)와 상수를 역으로 배치했는지 확인해야 합니다. 예를 들어, 상수 8.0은 -8.0이 되고, 승수 2.0은 0.5가 되어야 합니다. 상수 0.0과 승수 1.0은 바꿀 필요가 없습니다.
Auto Layout을 사용할 때 목표는 만족하는 해결책이 단 하나만 존재하는 일련의 방정식을 제공하는 것입니다. 모호한 제한 조건에는 가능한 한 가지 이상의 솔루션이 있습니다. 불만족스러운 제약 조건은 유효한 해결책이 없습니다.
일반적으로 제약조건들은 각 뷰마다 크기와 위치를 모두 정의해야합니다. 상위뷰의 크기가 이미 정해져있다고 가정한다면,(예를 들어 iOS에서 화면의 root view), 애매모호하지 않고 만족스러운 레이아웃을 적용하려면 뷰 마다, 방면(수직,수평)마다 적어도 은 두 제약조건이 필요합니다.(상위 뷰는 세지 않았을 때) 하지만 어떤 제약조건을 사용할 것인지 선택할 수 있는 광범위한 옵션들이 있습니다. 예를 들어, 다음 세 가지 레이아웃 모두 모호하지 않고 만족스러운 레이아웃을 생성합니다.(수평 제약만 표시됨)
각 레이아웃은 하나의 뷰와 두 개의 수평적인 제약조건이 있습니다. 이 경우에 제약조건들이 뷰의 너비와 수평적 위치를 완벽히 정의합니다. 이는 모든 레이아웃들이 수평방면에서 애매모호하지 않고 만족스러운 레이아웃을 만들어낸다는 것을 의미합니다. 하지만 이러한 레이아웃들이 완전히 유용하지만은 않습니다. 상위 뷰의 너비가 변경되었을 때를 고려해봅시다.
첫 번째 레이아웃에서 뷰의 너비는 변하지 않습니다. 대개의 경우 우리가 원하는 상황이 아니죠. 사실 일반적으로는 뷰에 상수 크기를 할당하는 것을 피해야합니다. Auto Layout은 환경에 적응할 수 있는 동적인 레이아웃을 만들기 위해 디자인되었습니다. 고정된 크기를 뷰에 줄 때마다 그 능력을 낭비하게 됩니다.
분명하지는 않지만, 두 번째와 세 번째 레이아웃은 동일한 동작을 만들어냅니다. 이들은 상위 뷰의 너비가 변하더라도 뷰와 상위 뷰 간에 고정된 여백을 유지합니다. 하지만 완전히 같지는 않습니다. 일반적으로, 두 번째 예제는 이해하기 쉽지만, 세 번째는 여러 항목을 가운데 정렬할 때에 더 유용할 수 있습니다. 항상 그렇듯이 특정 레이아웃에 가장 적합한 방법을 선택하세요.
이제 좀 더 복잡한 레이아웃을 고려해봅시다. iPhone에 두 개의 뷰를 나란히 표시하려고 한다고 가정해봅시다. 당신은 이 두 뷰들이 모든 방면에서 좋은 여백을 가지고 항상 같은 너비를 가지고 있기를 원합니다. 또한 장치가 회전 할 때 크기가 올바르게 조정되어야합니다.
다음 그림은 세로 및 가로 방향의보기를 보여줍니다.
그럼 어떠한 제약조건을 주어야 할까요? 다음 그림은 올바른 해결책 중 하나를 보여줍니다.
이 해결책은 다음과 같은 제약조건들을 사용합니다.
// 수직 제약조건
Red.top = 1.0 * Superview.top + 20.0
Superview.bottom = 1.0 * Red.bottom + 20.0
Blue.top = 1.0 * Superview.top + 20.0
Superview.bottom = 1.0 * Blue.bottom + 20.0
// 수평 제약조건
Red.leading = 1.0 * Superview.leading + 20.0
Blue.leading = 1.0 * Red.trailing + 8.0
Superview.trailing = 1.0 * Blue.trailing + 20.0
Red.width = 1.0 * Blue.width + 0.0
앞서 말했던 법칙에 따라, 이 레이아웃에는 두 개의 뷰가 있고, 각 뷰 마다 두 개의 수평 제약조건, 두 개의 수직 제약조건이 존재합니다. 이 제약조건들이 정확한 가이드가 아니더라도 , 올바른 방향에 있다는 것을 알 수 있습니다. 더욱 중요한 것은 제약 조건이 두 뷰의 크기와 위치를 고유하게 지정하여 모호하지 않고 만족스런 레이아웃을 생성한다는 것입니다. 이러한 제약 조건 중 하나라도 제거하면 레이아웃이 모호해집니다. 추가 제약 조건을 추가하면 충돌이 발생할 위험이 있습니다.
하지만 이 방법이 유일한 해결책은 아닙니다. 다음은 똑같이 유효한 접근 방식입니다.
파란색 상자의 위쪽과 아래쪽을 상위 뷰에 고정하는 대신 파란색 상자의 위쪽을 빨간색 상자의 위쪽에 맞춥니다. 마찬가지로 파란색 상자의 아래쪽을 빨간색 상자의 아래쪽에 맞춥니다. 제약 조건은 다음과 같습니다.
// 수직 제약조건
Red.top = 1.0 * Superview.top + 20.0
Superview.bottom = 1.0 * Red.bottom + 20.0
Red.top = 1.0 * Blue.top + 0.0
Red.bottom = 1.0 * Blue.bottom + 0.0
// 수평 제약조건
Red.leading = 1.0 * Superview.leading + 20.0
Blue.leading = 1.0 * Red.trailing + 8.0
Superview.trailing = 1.0 * Blue.trailing + 20.0
Red.width = 1.0 * Blue.width + 0.0
위와 동일하게 두 개의 뷰와 네 개의 수평 제약조건, 네 개의 수직 제약조건을 가집니다. 여전히 애매모호하지 않고 만족스러운 레이아웃을 제공하고요.
Note 어떤 게 더 나을까요? 이 해결책들은 모두 유효한 레이아웃을 제공합니다. 그럼 어떤 게 더 나을까요? 불행히도, 객관적으로 한 접근법이 다른 접근법보다 완전히 우수하다는 것을 증명하는 것은 사실상 불가능합니다. 각 접근방식에는 강약점이 존재합니다. 첫 번째 솔루션은 뷰를 삭제해야 할 때 더 권장됩니다. 뷰 계층에서 뷰를 삭제하면 해당 뷰를 참조하는 모든 제약조건들도 제거해야합니다. 그렇기 때문에 만약 빨간 뷰를 삭제하면 파란 뷰는 세 개의 제약조건만을 가진 채 그 자리에 남게 됩니다. 다시금 유효한 레이아웃을 만들기 위해서는 하나의 제약조건을 추가해주기만 하면 됩니다. 두 번째 솔루션에서는 빨간 뷰를 삭제하면 파란 뷰는 하나의 제약조건만을 가지게됩니다. 다른 방면에서 첫 번째 솔루션은 뷰의 상단과 하단을 정렬하고자 한다면 두 개의 제약조건이 모두 같은 상수 값을 가지도록 해야합니다. 만약 하나의 제약조건을 변경한다면, 다른 쪽도 변경해야한다는 걸 잊지 말아야겠죠.
이제까지의 모든 예제들은 서로 같은(=) 제약조건들만을 살펴보았지만 이는 이야기의 한 부분에 불과합니다. 제약조건은 서로 같지 않음 또한 잘 나타낼 수 있습니다. 특히, 제약 조건의 관계는 같거나 크거나 같거나 작을 수 있습니다. 예를 들어 제약 조건을 사용하여 뷰의 최소 또는 최대 크기를 정의 할 수 있습니다 (코드 목록 3-3).
코드 목록 3-3 최대 크기와 최소 크기 할당
// 최소 너비 설정
View.width >= 0.0 * NotAnAttribute + 40.0
// 최대 너비 설정
View.width <= 0.0 * NotAnAttribute + 280.0
서로 같지 않은 제약조건을 사용하면 ‘뷰에는 방면별로 두 개의 제약 조건이 있어야한다’는 규칙이 무너집니다. 하나의 ‘서로 같은’ 관계를 두 개의 부등식으로 대체 할 수 있습니다. 목록 3-4에서 단일 등호 관계와 두 개의 부등식은 동일한 동작을 생성합니다.
코드 목록 3-4 하나의 등식을 두 개의 부등식으로 변경
// 단일 등식 관계
Blue.leading = 1.0 * Red.trailing + 8.0
// 두 개의 부등식 관계로 대체할 수 있다.
Blue.leading >= 1.0 * Red.trailing + 8.0
Blue.leading <= 1.0 * Red.trailing + 8.0
하지만 두 개의 부등식 관계가 항상 단일 등식 관계와 같은 것은 아니므로, 두 개의 부등식을 단일 등식으로 전환하는 것은 그 결과가 다를 수 있습니다. 예를 들어, 코드 3-3 에서는 뷰의 가능한 너비의 범위를 정의했지 뷰의 크기를 정의한 것은 아닙니다. 뷰의 위치와 크기를 알기 위해서는 추가적인 수평 제약조건이 필요합니다.
기본적으로 모든 제약조건은 충족되어야 합니다. Auto Layout은 이 모든 제약조건들을 만족시키는 해결책을 계산해야만합니다. 만일 그럴 수 없다면 오류가 발생합니다. Auto Layout은 만족되지 않는 제약조건에 대한 정보를 콘솔에 출력하며, 깨트릴 제약조건을 선택합니다. 그리고 깨뜨린 제약조건을 제외하고 다시 해결책을 계산합니다. 더 많은 정보를 알고 싶다면 Unsatisfiable Layout을 확인하세요.
선택적 제약 조건을 만들 수도 있습니다. 모든 제약 조건은 1과 1000 사이의 우선 순위를 갖습니다. 우선 순위가 1000인 제약 조건은 항상 충족되어야 합니다. 다른 모든 제약 조건은 선택 사항입니다.
솔루션을 계산할 때 Auto Layout은 우선 순위가 높은 순서에서 가장 낮은 순서까지 모든 제약 조건을 충족 시키려고 시도합니다. 옵션 제약 조건을 만족할 수 없다면, 그 제약 조건은 건너 뛰고 다음 제약 조건으로 넘어갑니다.
선택적 제약 조건을 충족시킬 수 없더라도 여전히 레이아웃에 영향을 미칠 수 있습니다. 제약 조건을 건너 뛰고 나서 레이아웃에 애매한 점이 있으면 시스템은 제약 조건에 가장 가까운 솔루션을 선택합니다. 이런 식으로 만족스럽지 않은 선택적인 제약 조건은 뷰가 어떤 방향으로 나타날 지에 대해 영향을 주게됩니다.
선택적 제약 조건과 불평등은 종종 서로 작용합니다. 예를 들어, 코드 목록 3-4에서 두 가지 부등식에 대해 다른 우선 순위를 제공 할 수 있습니다. 보다 크거나 같음 관계 (1000의 우선 순위)가 충족되어야 하며, 보다 작거나 같은 관계가 낮은 우선 순위(우선 순위 250)를 가지도록 해봅시다. 즉, 파란색 뷰는 빨간색에서 8.0 포인트보다 가까울 수 없습니다. 그러나 다른 제약 조건으로 인해 더 멀리 떨어질 수 있습니다. 그래도 선택적 구속 조건은 레이아웃에서 다른 제약 조건이 주어지면 파란색 뷰를 빨간색 뷰로 끌어 당겨 8.0 지점 간격에 최대한 가깝게 만듭니다.
Note 1000 개의 우선 순위 값 모두를 사용해야 할 필요는 없습니다. 사실, 일반적으로 우선 순위는 시스템이 정의해 둔 낮은 (250), 중간 (500), 높음 (750) 및 필수 (1000) 우선 순위를 중심으로 묶여야합니다. 동일 우선 순위를 가지는 것을 막기 위해서 이 값들보다 1~2 정도 높고 낮은 값을 사용하려 할 수 있습니다. 그 이상의 우선순위를 가지는 관계를 만들어야한다면 레이아웃을 재점검해보는 것이 필요할 수 있습니다. iOS에 미리 정의 된 제약 상수의 목록을 보려면 UILayoutPriority 열거 형을 참조하세요. OS X의 경우 레이아웃 우선 순위 상수를 참조하십시오.
이제까지 모든 예제는 뷰의 위치와 크기를 정의하기 위해 제약 조건을 사용했습니다. 그러나 일부 뷰는 이들의 최근 컨텐츠에서 자연스럽게 그 크기를 가집니다. 이것을 고유 컨텐츠 크기라고합니다. 예를 들어 버튼의 고유 콘텐츠 크기는 제목의 크기와 작은 여백을 더한 크기입니다.
모든 뷰에 고유 콘텐츠 크기가 있는 것은 아닙니다. 뷰의 경우, 고유 컨텐츠 크기는 뷰의 높이, 폭 또는 둘 다를 정의 할 수 있습니다. 표 3-1에 몇 가지 예가 나와 있습니다.
표 3-1 일반적인 control에서 고유 컨텐츠 크기 |View|고유 컨텐츠 크기| |:—:|:—:| |UIView, NSView|고유 컨텐츠 크기 없음| |Sliders - iOS | 너비만 가짐| |Sliders - macOS | slider 타입에 따라 너비, 높이 혹은 둘 다를 정의| |Labels, buttons, switches, text fields|너비와 높이를 모두 정의| |Text views, image views|고유 컨텐츠 크기가 달라질 수 있음|
고유 컨텐츠 사이즈는 뷰의 현재 컨텐츠에 기반합니다. 레이블이나 버튼의 고유 컨텐츠 사이즈는 보여지는 텍스트의 양과 사용된 폰트에 기반합니다. 다른 뷰들은 고유 컨텐츠 사이즈가 좀 더 복잡합니다. 예를 들어, 이미지가 없는 이미지 뷰는 고유 컨텐츠 사이즈를 가지지 않습니다. 하지만 이미지를 추가하면 그 이미지의 사이즈가 이미지 뷰의 고유 컨텐츠 사이즈로 설정됩니다.
텍스트 뷰의 고유 컨텐츠 사이즈는 컨텐츠에 따라, 스크롤이 가능한지 아닌지에 따라, 이 뷰에 적용된 다른 제약 조건들이 있느냐에 따라 달라질 수 있습니다. 예를 들어, 스크롤이 가능하다면, 뷰는 고유 컨텐츠 사이즈를 가지지 않습니다. 스크롤이 불가능하다면 뷰의 고유 컨텐츠 사이즈는 기본적으로 줄 바꿈을 제외한 텍스트의 사이즈에 기반하여 계산됩니다. 예를 들어 만약 text에 줄바꿈이 없다면, 컨텐츠를 한 줄의 텍스트로 레이아웃하여 높이와 너비를 계산합니다. 만일 이 뷰에 특정 너비에 대한 제약조건을 추가한다면 주어진 너비에서 텍스트를 보여줄 수 있도록 고유 컨텐츠 사이즈의 높이를 정의합니다.
Auto Layout은 각 방면의 제약조건들의 쌍을 사용하여 뷰의 고유 컨텐츠 사이즈를 나타냅니다. content hugging은 뷰를 안쪽으로 잡아 당겨 콘텐츠 주위에 꼭 맞게 합니다. compression resistance은 내용을 올바르게 다 보일 수 있도록 뷰를 바깥쪽으로 밀어냅니다.
이 제약 조건들은 목록 3-5에 있는 부등식을 통해 정의됩니다. 여기에서 IntrinsicHeight 및 IntrinsicWidth 상수는 뷰의 고유 콘텐츠 크기에서 높이 및 너비 값을 나타냅니다.
코드 목록 3-5
// Compression Resistance
View.height >= 0.0 * NotAnAttribute + IntrinsicHeight
View.width >= 0.0 * NotAnAttribute + IntrinsicWidth
// Content Hugging
View.height <= 0.0 * NotAnAttribute + IntrinsicHeight
View.width <= 0.0 * NotAnAttribute + IntrinsicWidth
이러한 제약조건들도 각자의 우선순위를 가질 수 있습니다. 기본적으로 뷰는 content hugging에 대해서는 250의 우선순위를, compression resistance에 대해서는 750의 우선순위를 가집니다. 그러므로 뷰를 늘리는 것이 줄이는 것보다 쉽습니다. 대부분의 control에서 원하는 동작이기도 합니다. 예를 들어 버튼을 고유 컨텐츠 크기보다 크게 늘릴 수 있지만, 고유 컨텐츠 크기보다 줄여버린다면 내부 컨텐츠가 잘리게 됩니다. Interface Builder에서 이러한 우선 순위를 변경할 수 있습니다. 더 많은 정보가 필요하다면 Setting Content-Hugging and Compression-Resistance Priorities를 확인하세요.
가능하다면 레이아웃에서 뷰의 고유 컨텐츠 사이즈를 사용하세요. 이는 뷰의 내용이 변경됨에 따라 레이아웃이 동적으로 적응하도록 할 수 있습니다. 이는 또한 애매모호하지 않고 충돌되지 않는 레이아웃을 만들기 위해 필요한 제약조건의 수를 줄여주지만, 뷰의 content-hugging과 compression-resistance (CHCR) 우선순위를 관리할 필요가 있을 겁니다. 다음은 고유 컨텐츠 사이즈를 다루기 위한 가이드라인입니다.
공간을 채우기 위해서 일련의 뷰들이 자동적으로 늘려져야 할 때, 모든 뷰가 동일한 content-hugging 우선순위를 가진다면 레이아웃이 모호할 수 있습니다. Auto Layout은 어떤 뷰가 늘려져야 하는지 알 수 없습니다. 일반적인 예로 레이블과 텍스트 필드 쌍이 있습니다. 일반적으로는 레이블이 고유 사이즈 그대로 남아있고 텍스트 필드가 남은 공간을 채우기를 바랄겁니다. 이를 명확히 하기 위해서는 텍스트 필드의 수평 content-hugging 우선순위를 레이블의 값보다 낮추어야 합니다. 사실 이 예는 너무 일반적이어서 Interface Builder가 자동으로 처리하여 모든 레이블의 content-hugging 우선 순위를 251로 설정합니다. 프로그램적으로 레이아웃을 만들어야 한다면 content-hugging 우선순위를 직접 수정해야합니다.
이상하고 예상치 못한 레이아웃은 보이지 않는 배경(예 : 단추 또는 레이블)이 포함된 뷰가 고유 콘텐츠 크기를 초과하여 우연히 늘어날 때 자주 발생합니다. Odd and unexpected layouts often occur when views with invisible backgrounds (like buttons or labels) are accidentally stretched beyond their intrinsic content size. The actual problem may not be obvious, because the text simply appears in the wrong location. To prevent unwanted stretching, increase the content-hugging priority.
Baseline constraints work only with views that are at their intrinsic content height. If a view is vertically stretched or compressed, the baseline constraints no longer align properly.
Some views, like switches, should always be displayed at their intrinsic content size. Increase their CHCR priorities as needed to prevent stretching or compressing.
Avoid giving views required CHCR priorities. It’s usually better for a view to be the wrong size than for it to accidentally create a conflict. If a view should always be its intrinsic content size, consider using a very high priority (999) instead. This approach generally keeps the view from being stretched or compressed but still provides an emergency pressure valve, just in case your view is displayed in an environment that is bigger or smaller than you expected.
안녕하세요! caution입니다. 오늘은 이전 포스팅에 이어서 Auto Layout Without Constraints을 번역합니다.
하기 컨텐츠는 Apple이 작성한 Auto Layout Guide를 번역, 재사용한 컨텐츠임을 밝힙니다.
스택 뷰는 복잡한 레이아웃을 적용하지 않고도 Auto Layout 기능을 쉽게 활용할 수있는 방법을 제공합니다. 단일 스택 뷰는 사용자 인터페이스 요소의 행 또는 열을 정의합니다. 스택보기는 이러한 요소들을 다음 특성에 따라 정렬합니다.
스택 뷰를 사용하려면, 인터페이스 빌더에서 세로 또는 가로 스택 뷰를 캔버스 위로 드래그하십시오. 그런 다음 스택 뷰에 넣을 요소를 끌어서 스택 뷰에 놓습니다.
스택 뷰는 배치 된 뷰의 content-hugging 및 compression-resistance 우선 순위에 대한 레이아웃을 기반으로합니다. 이 속성은 Size Inspector를 사용하여 수정할 수 있습니다.
Note 정렬 된 뷰에 직접 제약 조건을 추가하여 레이아웃을 추가로 수정할 수 있습니다. 하지만 가능한 충돌을 피하려면, 일반적으로 뷰의 크기가 주어진 방향에서 intrinsic content (내장 컨텐츠 크기)로 설정된 상태에서 해당 방향에 대한 제약 조건을 추가해야 안전합니다. 충돌하는 제약 조건에 대한 자세한 내용은 Unsatisfiable Layouts을 참조하십시오. 덧붙이는 말 수평 스택 뷰를 사용하고 있고, 하위에 넣을 뷰가 스택뷰 공간에서 수평으로 10만큼의 공간을 떨어져 있었으면 좋겠다라고 생각될 경우 이 뷰의 너비가 픽스된 상태에서 수평방향에 대한 제약 조건을 주어야 안전합니다. 뷰의 크기는 스택뷰의 속성과 뷰의 content-hugging, compression-resistance에 따라 달라질 수 있기 때문에 원하는 방향(수직 혹은 수평)에 따른 제약조건을 추가로 주고 싶다면 그 방향에 대한 뷰의 크기(높이 혹은 너비)를 지정해주는 것이 안전합니다.
또한 스택 뷰 내에 다른 스택 뷰를 중첩시켜 좀 더 복잡한 레이아웃을 만들 수 있습니다.
일반적으로 가능한 한 많은 뷰를 스택뷰로 관리하십시오. 스택뷰만으로 원하는 목표를 달성 할 수 없는 경우에만 제약 조건을 생성하세요. 스택 뷰 사용에 대한 자세한 내용은 UIStackView Class Reference 또는 NSStackView Class Reference를 참조하십시오.
Note 중첩된 스택 뷰를 창의적으로 사용하면 복잡한 사용자 인터페이스가 생성 될 수 있지만 제약 조건을 완전히 피할 수는 없습니다. 최소한 가장 바깥 쪽 스택의 위치(및 크기)를 정의하기 위해 항상 제약 조건이 필요합니다.
안녕하세요! caution입니다. 본격적으로 Auto Layout 공부를 하려고 하는데 어떻게 시작할까 하다가 Apple의 가이드 문서인 Auto Layout Guide를 번역해보려고 합니다!
하기 컨텐츠는 Apple이 작성한 Auto Layout Guide를 번역, 재사용한 컨텐츠임을 밝힙니다.
오토 레이아웃은 뷰 계층 안의 모든 뷰들의 사이즈와 위치를 해당 뷰에 적용된 제약 조건에 기반하여 동적으로 계산합니다. 예를 들어, 이미지 뷰와 가로로 중앙 정렬하고, 이미지의 하단 8 point 아래에 위쪽 가장자리가 존재하도록 버튼에 제약 조건을 줄 수 있습니다. 만약 이미지 뷰의 사이즈나 위치가 변경된다면, 버튼의 위치는 자동적으로 제약 조건에 일치하도록 조정됩니다.
이러한 제약 조건 기반 접근을 사용하면 사용자 인터페이스를 내 외부 변화에 맞추어서 동적으로 반응하도록 만들 수 있습니다.
외부 변화는 상위뷰의 사이즈나 모양이 변경되었을 때 발생합니다. 변경이 일어날 때마다 사용가능한 공간을 최대한 활용하기 위해 뷰 계층을 업데이트 해야합니다. 외부 변화에 대한 예:
이러한 변화들은 대개 런타임에서 발생하고 앱에서 동적인 응답이 필요합니다. 다른 사이즈의 화면을 지원하는 것과 같은 기능은 앱이 다양한 환경에 적응할 수 있음을 나타냅니다. 화면 크기가 일반적으로 런타임에 변경되지는 않지만, 적응형 인터페이스를 만들면 iPhone 4S, iPhone 6 Plus 혹은 iPad에서도 앱을 동일하게 실행할 수 있습니다. Auto Layout은 iPad에서 Slide Over 및 Split View를 지원하기 위한 핵심 구성 요소이기도 합니다.다른 환경에 앱을 적응시키는 대표적인 요소입니다.
내부 변화는 사용자 인터페이스에서 뷰 또는 컨트롤의 크기가 변경될 때 발생합니다. 다음은 이러한 내부 변경의 일반적인 예입니다.:
앱의 컨텐츠가 변경되었을 때, 새로운 콘텐츠는 기존과 다른 레이아웃을 요구할 수 있습니다. 이는 흔히 앱에서 텍스트나 이미지를 표시할 때 발생합니다. 예를 들어, 뉴스 앱은 개별 뉴스 아티클의 사이즈에 기반해서 레이아웃을 조정할 필요가 있습니다. 유사하게, 사진 콜라주는 다양한 이미지 사이즈와 비율을 다루어야만 합니다.
국제화란 앱을 다른 언어, 지역, 문화에 적용될 수 있도록 만드는 과정입니다. 국제화된 앱에서 레이아웃은 이러한 차이점을 고려하여 앱이 지원하는 모든 언어 및 지역에서 올바르게 표시되어야 합니다.
국제화는 레이아웃에 주로 3가지 영향을 줍니다. 첫번재로, 사용자 인터페이스를 다른 언어로 번역할 때 레이블은 언어마다 다른 공간이 필요합니다. 예를 들어 독일어는 일반적으로 영어보다 더 많은 공간이 필요하며, 일본어는 보통 더 적은 공간이 필요합니다. 두번째로, 언어가 변경되지 않더라도 날짜와 숫자를 나타내는 데 사용되는 형식이 지역마다 다를 수 있습니다. 이러한 변경은 일반적으로 언어 변경보다 미묘하긴 하지만 사용자 인터페이스는 약간씩 변형되어야 합니다.
셋째로, 언어를 변경하는 것은 텍스트 사이즈에만 영향을 주는 것이 아니라 레이아웃 설계에도 영향을 줍니다. 다른 언어들은 다른 언어 방향을 사용합니다. 예를 들어 영어는 왼쪽에서 오른쪽으로 가는 레이아웃 방향을 사용하며, 아랍어와 히브리어는 오른쪽에서 왼쪽으로 가는 레이아웃을 사용합니다. 일반적으로 사용자 인터페이스 요소들을 정렬하는 것은 레이아웃 방향과 일치해야합니다. 만약 버튼이 영어에서는 뷰의 오른쪽 하단 모서리에 있어야 한다면 아랍어에서는 왼쪽 하단에 있어야 합니다.
결론적으로, 만약 iOS 앱이 동적 타입을 지원한다면, 사용자는 앱에서 사용할 글씨 크기를 조절할 수 있습니다. 이는 사용자 인터페이스에서 글자 요소들의 높이나 너비를 변경할 수 있습니다. 만일 사용자가 앱이 실행되고 있을 때 글자 크기를 변경하더라도 폰트와 레이아웃이 모두 적용되어야 합니다.
사용자 인터페이스를 레이아웃하는 데에는 주요 세가지 접근법이 있습니다. 사용자 인터페이스를 프로그래밍으로 짤 수도 있고, 외부 변화에 대한 자동적인 응담을 할 수 있도록 autoresizing mask를 사용할 수 있으며, Auto Layout을 사용할 수도 있습니다.
전통적으로, 앱은 뷰 계층의 각 뷰에 대한 프레임을 프로그래밍 방식으로 설정하여 사용자 인터페이스를 레이아웃합니다. 프레임은 상위 뷰의 좌표계에서 뷰의 원점, 높이 및 너비를 정의합니다.
사용자 인터페이스를 레이아웃하기 위해서는 뷰 계층 안의 모든 뷰에 대한 사이즈와 위치를 계산해야 합니다. 그리고 나서, 변경이 일어나면, 영향 받는 모든 뷰에 대한 프레임을 다시 계산해야합니다.
다양한 방법으로, 프로그램방식으로 정의된 뷰의 프레임은 매우 유연하고 강력합니다. 변화가 발생했을 때 그야말로 원하는 대로 변경할 수 있습니다. 아직까지 모든 변화를 스스로 관리해야하기 때문에 간단한 사용자 인터페이스를 레이아웃 하는 것 조차도 디자인, 디버그, 유지하는데 많은 양의 노력이 필요합니다. 완전한 적응형 사용자 인터페이스를 만드는 것은 난이도를 증가시킵니다.
이러한 노력을 줄일 수 있도록 autoresizing mask를 사용할 수 있습니다. autoresizing mask는 상위 뷰의 프레임이 변경되었을 때 어떻게 뷰 프레임을 변경할 것인가에 대해 정의합니다. 이는 외부 변화에 적응하는 레이아웃을 만드는 것을 단순화합니다.
그러나 autoresizing masks는 가능한 레이아웃의 비교적 작은 하위 세트를 지원합니다. 예를 들어 사용자 인터페이스에서, 일반적으로 프로그래밍을 통해 autoresizing mask를 보완해야 합니다. 추가적으로 autoresizing mask는 외부 변화에 대해서만 적용됩니다. 내부 변화에 대해서는 지원하지 않습니다.
비록 autoresizing mask가 프로그램적인 레이아웃의 반복적인 개선사항일 뿐이지만, auto layout은 완전히 새로운 패러다임을 나타냅니다. 뷰 프레임을 생각하지 않는 대신에 뷰들의 관계에 대해 생각합니다.
Auto Layout은 제약조건들을 사용해서 사용자 인터페이스를 정의합니다. 제약조건은 일반적으로 두 개의 뷰 간의 관계를 나타냅니다. Auto Layout은 이러한 제약조건에 기반하여 사이즈와 위치를 계산합니다. 이는 내 외부 변화에 대해 동적인 응답을 하는 레이아웃을 만들어냅니다.
특정 동작을 하는 제약조건들을 설계하는 데 사용되는 논리는 절차적 혹은 객체 지향 코드를 작성하는 것과는 매우 다릅니다. 다행히도 Auto Layout을 마스터하는 것은 여느 다른 프로그래밍 과제를 달성하는 것과 다르지 않습니다. 2가지 기본적인 단계가 있습니다.: 첫 번째로 제약조건에 기반한 레이아웃 뒤의 로직을 이해할 필요가 있으며, API를 공부해야합니다. 당신은 이미 다른 프로그래밍 과제를 배울 때 이러한 단계들을 성공적으로 달성해왔을 겁니다. Auto Layout 또한 예외는 아닙니다.
이 가이드의 나머지 부분은 당신이 Auto Layout으로 쉽게 전환할 수 있도록 설계되었습니다. [Auto Layout Without Constraints] 챕터는 Auto Layout 기반 사용자 인터페이스 생성을 단순화 하는 상위 레벨의 추상화에 대해서 설명합니다. [Anatomy of a Constraint] 챕터는 Auto Layout과 원활히 상화작용하기 위해 이해해야 하는 배경 이론을 제공합니다. Working with Constraints in Interface Builder는 Auto Layout을 설계하는 도구들에 대해 설명하고, Programmatically Creating Constraints 와 Auto Layout Cookbook 챕터는 API를 상세하게 설명합니다. 마지막으로 Auto Layout Cookbook은 다양한 수준과 넓은 범위의 예제 레이아웃을 보여줌으로써, Auto Layout을 공부하고 이를 프로젝트에 가져다 쓸 수 있도록 합니다. 그리고 Debugging Auto Layout 은 문제가 발생할 경우 이를 해결하기 위한 조언과 도구들을 제공합니다.
안녕하세요! caution입니다. 오늘은 Caching에 대해 간략히 정리하고 Apple Framework가 제공하는 NSCache를 정리합니다.
캐싱은 간단히 말하면 재사용될 수 있을 만한 자원을 특정영역에 저장해놓는 것을 의미합니다. 캐싱된 데이터가 있다면 추가적인 자원을 소모하지않고 캐싱 데이터를 가져다 쓸 수 있기 때문에 자원을 절약할 수 있고 애플리케이션의 처리 속도가 향상됩니다.
예를 들어 웹 브라우저를 사용하다 보면 새로고침을 사용할 때가 있는데요. 크롬에서 개발자 도구를 열어 놓은 상태에서 보조 클릭으로 새로고침을 누르면 다음과 같은 화면을 볼 수 있습니다.
마지막에 캐시 비우기 및 강력 새로고침 메뉴를 누르면 기존에 웹브라우저가 캐싱했던 리소스들을 제거하고 모든 데이터를 새롭게 요청하게 됩니다.
이렇듯 캐싱은 웹 브라우저부터 웹 서버, 하드디스크 및 CPU에 이르기까지 다양한 방면에서 적용되고 있습니다.
모바일 애플리케이션에서 매우 고화질의 이미지를 반복해서 보여주어야 하는데 캐싱이 없다면 어떻게 될까요? 고화질, 고용량의 이미지를 계속해서 다운로드하기 때문에 사용자의 네트워크 리소스를 소모할 것이고, 다운로드가 완료되기까지의 시간동안 사용자는 이미지를 확인할 수 없을 겁니다.
하지만 애플리케이션에 다운로드 받은 이미지를 캐싱하여 저장해둔다면, 이 이미지를 다운로드 받은 이후에는 별도의 리소스를 소모하지 않고 이미지를 빠르게 보여줄 수 있겠죠.
애플리케이션을 만들 때 캐싱은 크게 두 가지로 이루어집니다. Memory Caching과 Disk Caching이죠. Memory Caching은 애플리케이션의 메모리 영역의 일부분을 Caching에 사용하는 것입니다. 그 말인즉 애플리케이션이 종료되어 메모리에서 해제되면 이 영역에 있던 리소스들은 OS에 반환되면서 Memory Caching되어 있던 리소스들은 사라지게 됩니다.
반면 Disk Caching은 데이터를 파일 형태로 디스크에 저장하는 것입니다. Disk Caching이 반복적으로 발생하면 애플리케이션이 차지하는 용량이 커지지만, 앱을 껏다 켠다고 해서 데이터가 사라지지는 않습니다. Disk Caching의 가장 가까운 예로는 카카오톡에서 찾아볼 수 있겠네요.
카카오톡을 사용하다 보면 채팅방에서 이미지나 동영상 등 파일을 주고 받을 수 있습니다. 사용자가 동영상을 모바일로 저장하지 않더라도 동영상을 재생하기 위해서는 파일을 다운로드 받아야 하는데, 이때 이 다운로드 받은 파일들이 캐시 데이터에 포함됩니다. 계속 캐시 데이터가 늘어나면 저장공간을 많이 차지하게 되므로 필요하다면 사용자가 캐시 데이터를 삭제할 수 있도록 하거나, 특정 용량 이상을 차지하면 시스템에서 캐시 데이터를 삭제하도록 할 필요가 있습니다.
iOS 애플리케이션에서 Memory Caching 에 주로 사용되는 클래스입니다. NSCache가 무엇인지, Apple 공식문서를 통해 알아보겠습니다.
key-value 형태의 데이터를 임시로 저장하는 데 사용할 수 있는 가변 컬렉션입니다. 자원이 부족할 때 삭제 대상이 됩니다.
class NSCache<KeyType, ObjectType> : NSObject where KeyType : AnyObject, ObjectType : AnyObject
캐시 대상은 다른 가변 컬렉션과는 다른 몇 가지 특성을 가집니다.
일반적으로 NSCache 객체를 사용하여 생성 비용이 많이 드는 일시적인 데이터 객체를 저장합니다. 값을 다시 계산할 필요가 없으므로 이러한 객체를 재사용하는 것은 성능상의 이점을 얻을 수 있습니다. 하지만 이러한 임시 객체들은 응용프로그램에 중요하지 않은 데이터이며, 메모리가 부족할 경우 삭제될 수 있습니다. 만약 삭제되었다면 이 값들이 필요할 때 다시 계산해야 합니다.
사용되지 않을 때 삭제될 수 있는 하위 구성 요소들은 NSDiscardableContent 프로토콜을 채택하여 캐시 제거 동작을 향상시킬 수 있습니다. 기본적으로 캐시의 NSDiscardableContent 객체는 내용이 삭제된 경우 자동으로 제거되지만 이 자동 제거 정책은 변경할 수 있습니다. NSDiscardableContent 객체가 캐시에 저장되면 캐시는 삭제 명령 수행 시 discardContentIfPossible()
을 호출합니다.
Managing the Name
var name: String
: 캐시의 이름Managing Cache Size
var countLimit: Int
: 캐시가 가질 수 있는 최대한의 객체 수var totalCostLimit: Int
: 객체를 제거하기 전에 캐시가 보유할 수 있는 최대 비용Managing Discardable Content
var evictsObjectsWithDiscardedContent: Bool
: 캐시가 내용이 삭제된 NSDiscardableContent를 자동으로 제거할지의 여부protocol NSDiscardableContent
: 클래스의 객체의 하위 구성 요소가 사용되지 않을 때 삭제되어도 된다면 이 프로토콜을 채택함으로써 응용 프로그램의 메모리 사용 공간을 줄일 수 있습니다.Managing the Delegate
var delegate: NSCacheDelegate?
: 캐시의 위임자protocol NSCacheDelegate
: NSCache의 위임자 객체는 이 프로토콜을 채택합니다. 이 프로토콜을 채택하면 캐시에서 객체가 추출되거나 제거될 때의 작업들을 특수화할 수 있습니다.Getting a Cached value
func object(forKey: KeyType) -> ObjectType?
: 주어진 Key과 연결된 값을 반환합니다.Adding and Removing Cached Values
func setObject(ObjectType, forKey: KeyType)
: 캐시에 주어진 키와 그에 대응되는 값을 넣습니다.func setObject(ObjectType, forKey: KeyType, cost: Int)
: 캐시에 주어진 키와 그에 대응되는 값, 그리고 그 비용을 넣습니다.func removeObject(forKey: KeyType)
: 캐시에 주어진 키에 대응되는 값을 제거합니다.removeAllObjects()
: 캐시를 비웁니다.안녕하세요! caution입니다. 오늘은 Escaping과 Non-Escaping에 대해서 살펴보겠습니다. Escaping 개념을 확립하기 위해서 Swift 공식 문서를 먼저 읽어보겠습니다!
클로저가 함수의 인수로 전달 되었지만 함수가 반환 된 후에 호출될 경우 클로저가 escape 되었다 고 부릅니다. 클로저를 매개 변수 중 하나로 사용하는 함수를 선언하면 매개 변수 유형 앞에 @escaping
을 사용하여 클로저가 escape 할 수 있음을 나타낼 수 있습니다.
클로저가 이스케이프 할 수 있는 한 가지 방법은 함수 외부에 정의 된 변수에 저장하는 것입니다. 예를 들어 비동기 작업을 시작하는 많은 함수는 completion handler로 closure 인수를 사용합니다. 이 함수가 작업을 시작한 후에 작업이 완료되어 반환될 때(return)까지 클로저(completion handler)가 호출되지 않습니다. 즉 클로저를 이스케이프 처리해야 나중에 호출 할 수 있습니다.
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
someFunctionWithEscapingClosure(_:)
함수는 인수로 클로저를 받아서 함수 외부에서 선언된 배열에 추가합니다. 이 때 이 클로저를 @escaping
으로 표시하지 않으면 컴파일 타임 오류가 발생합니다.
@escaping
으로 클로저를 표시하는 것은 클로저 내에서 명시적으로 자신을 참조해야 함을 의미합니다. 예를 들어, 아래 코드에서 someFunctionWithEscapingClosure(_:)
에 전달된 클로저는 이스케이프된 클로저이므로 self를 명시적으로 참조해야합니다. 반대로 someFunctionWithNonescapingClosure(_:)
에 전달 된 클로저는 이스케이프 처리되지 않았으므로 self를 암시적으로 참조 할 수 있습니다.
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNonescapingClosure { x = 200 }
}
}
let instance = SomeClass()
instance.doSomething()
print(instance.x)
// Prints "200"
completionHandlers.first?()
print(instance.x)
// Prints "100"
일반적인 로컬 변수들은 함수가 return 됨과 동시에 사라집니다. 이를 흔히들 함수와 같은 scope 를 가진다고 이야기 합니다. 하지만 인자로 넘어온 클로저가 함수의 scope와 다른 scope를 가져야 한다
면 이 클로저가 (함수의 scope를) escape 했다 라고 이야기합니다.
위의 예를 다시 살펴봅시다.
someFunctionWithEscapingClosure(_:)
: 인자로 받은 closure를 전역 변수인 completionHandlers
에 넣습니다.someFunctionWithNonescapingClosure(_:)
: 인자로 받은 closure를 실행합니다.doSomething()
위 두 함수를 호출합니다.someFunctionWithNonescapingClosure(_:)
는 인자로 받은 클로저를 실행하고 바로 return 하기 때문에 함수가 종료되면서 이 클로저 또한 사라지게 됩니다. 하지만 someFunctionWithEscapingClosure(_:)
는 전역변수에 클로저를 저장하기 때문에 doSomething()
함수가 return 되어도 클로저는 살아있어야 합니다. 즉 함수의 인자와 함수의 scope가 달라지기 때문에 이 경우 인자 앞에 @escaping
을 붙여주어야 합니다.