The bounds rectange, which describes the view's location and size in its own coordinate system.
자신만의 좌표 시스템
기본 좌표: (0, 0)
bounds의 origin을 변경한다는 것은? 곧, subView들이 화면상에서 drawing 되는 위치가 변경됨을 의미 => subView들의 frame 값을 변화시키는 것이 아니다. 부모뷰 좌표축이 변하면서 subView가 그려져야 하는 위치가 달라졌기 때문 ScrollView/TableView 등을 스크롤할 때, scrollView.bounds가 변하고, 그리하여 subView들이 그려지는 위치가 대표적인 예 (subView들의 frame이 달라지는 게 아님)
ex ) targetView.bounds.origin.x = 60; targetView.bounds.origin.y = 50; 이라고 하면 targetView가 x축-> 60, y축-> 50으로 이동된 자식 뷰가 그려짐.!!!!!!!!!!
언제 사용? - View 내부에 그림을 그릴 때 (drawRect) - transformation 후, View의 크기를 알고 싶을 때 - 하위 View를 정렬하는 것과 같이 내부적으로 변경하는 경우
A stored property is a constant or variable that is stored as part of an instance of a particular class or structure.
Default property values
Assiging Constant properties during initalization (초기화 중에 할당되는 프로퍼티)
1-1) 상수 Structure 인스턴스의 stored property
If you create an instance of a structure and assign that instance to a constant, you cannot modify the instance’s properties, even if they were declared as variable properties:
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// this range represents integer values 0, 1, 2, and 3
rangeOfFourItems.firstValue = 6 // this will report an error, even though firstValue is a variable property
결론) Value type의 인스턴스를 상수로 생성하면, 모든 property는 변경할 수 없다. 반대로, Reference type의 인스턴스를 constant로 생성하면 변경 가능하다.
lazy stored property: 최초 사용될 때까지 초기 값이 계산되지 않는 프로퍼티
var keyword를 사용해야 함 => lazy stored property는 인스턴스 초기화가 완료될 때 까지 초기값을 가져올 수 없으므로 (let: 초기화가 완료되기 전에 값을 가져야 함.)
2. Computed Properties
Class, structure, enumeration은 computed property 정의 가능
실제로 값을 저장하는 것이 아님
getter와 optional setter를 제공하여 다른 property의 값을 간접적으로 세팅할 수 있다.
struct CompactRect {
var origin = Point()
var size = Size()
var center: Point {
get {
Point(x: origin.x + (size.width / 2),
y: origin.y + (size.height / 2))
}
set {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}
}
}
3. Property Observers
property observer는 property의 값의 변화에 관찰하고 반응한다!(observe and respond)
property의 값이 세팅되는 매 순간마다 호출됨. (같은 값으로 세팅되더라도)
lazy stored property를 제외한 모든 property(stored, computed property)에 추가 가능
overriding된 상속받은 property에도 property observer를 추가할 수 있다.
observer 종류 - willSetis called just before the value is stored. - didSetis called immediately after the new value is stored.
in-out 으로 observer를 가지는 property가 parameter로 전달되어도, willSet과 didSet observer는 항상 호출됨. => copy-in copy-out memory model이기 때문에!
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("Added \(totalSteps - oldValue) steps")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps
4. Global and Local Variables
Global constants and variables are always computed lazily, in a similar manner toLazy Stored Properties. Unlike lazy stored properties, global constants and variables do not need to be marked with thelazymodifier.
Local constants and variables are never computed lazily.
5. Type Properties
클래스 변수
static keyword로 정의
struct AudioChannel {
static let thresholdLevel = 10
static var maxInputLevelForAllChannels = 0
var currentLevel: Int = 0 {
didSet {
if currentLevel > AudioChannel.thresholdLevel {
// cap the new audio level to the threshold level
currentLevel = AudioChannel.thresholdLevel
}
if currentLevel > AudioChannel.maxInputLevelForAllChannels {
// store this as the new overall maximum input level
AudioChannel.maxInputLevelForAllChannels = currentLevel
}
}
}
}
var leftChannel = AudioChannel()
var rightChannel = AudioChannel()
leftChannel.currentLevel = 7
print(leftChannel.currentLevel)
// Prints "7"
print(AudioChannel.maxInputLevelForAllChannels)
// Prints "7"
rightChannel.currentLevel = 11
print(rightChannel.currentLevel)
// Prints "10"
print(AudioChannel.maxInputLevelForAllChannels)
// Prints "10"
- Swift provides signed and unsigned integers in 8, 16, 32, and 64 bit forms.
- UInt8, Int32, ...
Int
Swift provides an additional integer type,Int, which hasthe same size as the current platform’s native word size:
On a 32-bit platform,Intis the same size asInt32.
On a 64-bit platform,Intis the same size asInt64.
UInt
Swift also provides an unsigned integer type,UInt, which has the same size as the current platform’s native word size:
On a 32-bit platform,UIntis the same size asUInt32.
On a 64-bit platform,UIntis the same size asUInt64.
NOTE UseUIntonly when you specifically need an unsigned integer type with the same size as the platform’s native word size. If this isn’t the case,Intis preferred, even when the values to be stored are known to be nonnegative. => This aids codeconsistencyandinteroperability.
let double: Double = 2.0
let inferredDouble = 2.0
var mutableInt: Int = 1
2. Numeric type conversion
let integerValue = 8
let doubleValue = 8.0
// error: type mismatch
//let sum = integerValue + doubleValue
let sum = Double(integerValue) + doubleValue
3. String - TODO: string 비교
let helloWorld = "Hello, world"
let helloWorldProgram = """
dfdf
dfdf
"""
// emptyString
let emptyString = "" // Using string literal
let anotherEmptyString = String()
var mutableString = "Swift"
mutableString += " is awesome!"
print("This is my opinion: \(mutableString)")
4. Tuple
- 여러 값을 group화 할 수 있는 자료형 => Tuples are useful for simple groups of related values
- decompose - let (code, reason) = httpError
- underscore (_): ignoring parts of the tuple
- 인덱스 숫자로 접근하여 값을 가져올 수 있음
- element에 이름 추가 가능
// Group multiple values into a single compound value
// : a number and a human-readable description.
let httpError = (503, "Server Error")
// decompose
let (code, reason) = httpError
let codeByIndex = httpError.0
let reasonByIndex = httpError.1
// _ : ignoring parts of the tuple
let (_, justTheReason) = httpError
// access using index numbers
print("The status code is \(http404Error.0)")
/-----------------------------------
// element 이름 추가 가능
let http200Status = (statusCode: 200, description: "OK")
print("The status code is \(http200Status.statusCode)")
5. Optionals
- 해당 자료형 값 또는 nil 값을 갖는 변수
var catchphrase: String? // optional String
let count3: Int? = catchphrase?.count // optional Int (catchphrase가 nil일 수도 있으니까요)
// optional: String or nil 값을 갖는 변수
var catchphrase: String? // called optional String
catchphrase = "Hey, everybody?"
// Forced uwrapping operation(!)
// catchphrase이 nil이 아닐 경우 count 값을 가짐, nil이면 crash
let count1: Int = catchphrase!.count
// Optional binding
// catchphrase?count : optional Int
// optional Int에 값이 포함되어 있으면 count라는 constant 생성
if let count = catchphrase?.count {
print(count)
}
// Coalescing operator (??) - coalesce: 합치다
//catchphrase가 nil이 아니면 count2는 cathphrase의 count을 넣음; nil이면 0
let count2: Int = catchphrase?.count ?? 0
// Chaining operator (?)
// Optional Int: catchphrase의 count or nil
let count3: Int? = catchphrase?.count
// Implicitly unwrapped optionals (implicit: 절대적인)
let forcedCatchphrase: String! = "Hey, everybody?"
let implicitCatchphrase = forcedCatchphrase // 값을 넣을 때 ! mark 필요 없음
6. Collection Types: Array
- contains
- append, insert
- remove
- arr[0]
// let collection : reassign과 element 바꾸는 것 안됨
let immutableArray: [String] = ["Alice", "Bob"]
// 타입 추론: mutableArray [String]
var muttableArray = ["Eve", "Frank"]
let isEveThere = immutableArray.contains("Eve")
let name: String = immutableArray[0]
// index가 out of range이면 crash
mutableArray[1] = "Bart"
// immutableArray[1] = "Bart" // Error: 바꿀 수 없음
mutableArray.append("Ellen")
mutableArray.insert("Gemma", at: 1)
let removedPerson = mutableArray.remove(at: 1)
mutableArray = ["Ilary", "David"]
mutableArray[0] = "John"
7. Collection Types: Dictionary
- key가 존재하지 않으면 nil return : optional 값 리턴
- dic["name], removeValue(forKey: )
let immutableDict: [String: String] = ["name", "Kirk", "rank": "captain"]
var mutableDict = ["name": "Picard", "rank": "captain"]
// key가 존재하지 않으면 nil return (String? - optional String)
let name2: String? = immutableDic["name"]
// update value for key
mutableDic["name"] = "Janeway"
// add new key and value
mutableDic["ship"] = "Voyager"
// delete by key, 해당 key가 nil이 아니면
let rankWasRemoved: String? = mutableDic.removeValue(forKey: "rank")
8. Collection Types: Set
- list와 비슷하지만 중복된 item은 ignore
- contains, insert, remove
// Set: 중복된 item은 ignore
let immutableSet: Set = ["chocolate", "vanilla", "chocolate"]
var mutableSet: Set = ["butterscotch", "strawberry"]
immutableSet.contains("chocolate")
mutableSet.insert("green tea")
// 해당 item을 찾지 못하면 nil return
let flavorWasRemoved: String? = mutableSet.remove("strawBerry")
9. Control Flow: Loops
// Iterator list or set
for item in listOrSet {
print(item)
}
// Iterator over dictionary
for (key, value) in dictionary {
print("\(key) = \(value)")
}
for i in 0...10 {
print(i) // 0 to 10
}
for i in 0..<10 {
print(i) // 0 to 9
}
var x = 0
while x < 10 {
x += 1
print(x)
}
repeat {
x -= 1
print(x)
} while(x > 0)
10. Control Flow: Conditionals
let number = 88
if (number <= 10) {
} else if (number > 10 && number < 100) {
} else {
}
// Ternary operator (ternary: 3으로 이루어진)
let height = 100
let isTall = height > 200 ? true : false
// guard: program의 제어를 변형하기 위해
for n in 1...30 {
guard n % 2 == 0 else {
continue
}
print("\(n) is even")
}
let year = 2012
switch year {
case 2003, 2004:
print("Panther or Tiger")
case 2010:
print("Lion")
case 2012...2015:
print("Mountain Lion through El Captain")
default:
fatalError("Not already classified")
}
11. Functions
_: argument label 제외
// void function
func sayHello() {
print("Hello")
}
// function with parameters
func sayHello(name: String) {
print("Hello \(name)!")
}
// function with default parameters
func sayHello(name: String = "Lorenzo") {
print("Hello \(name)!")
}
func sayHello(name: STring = "Lozenzo", age: Int) {
print("\(name) is \(age) years old!")
}
// using just the non default value
sayHello(age: 35)
// function with parameters and return value
func add(x: Int, y: Int) -> Int {
return x + y
}
let value = add(x: 8, y:10)
// single expression만 포함할 경우 return 키워드 제거 가능
func multiply(x: Int, y: Int) -> Int {
x + y
}
// arguments label 명시
func add(x xVal: Int, y yVal: Int) -> Int {
return xVal + yVal
}
// TODO: argement label 제외
func add(_ x: Int, y: Int) -> Int {
return x + y
}
let value = add(8, y: 10)
// parameter로 다른 함수 접근
func doMath(operation: (Int, Int) -> Int, a: Int, b: Int) -> Int {
return operation(a, b)
}
12. Closures: TODO: 실행하기
// TODO:
let adder: (Int, Int) -> Int = {(x, y) in x + y}
// argument의 약어 사용
let square: (Int) -> Int = {$0 * $0}
// 함수에 closure 전달
let addWithClosure = doMath(operation: adder, a: 2, b: 3)
13. Enumerations
- allCases : CaseIterable protocol을 채택하면 사용 가능
- rawValue
- associated value(연관값): enum case문에 추가 정보를 덧붙일 수 있음
case text(String)
- switch문으로 associated value를 캐치할 수 있다.
case .text(let value):
enum Taste {
case sweet, sour, salty, bitter, umami
}
let vinegarTaste = Taste.sour
// Iterating through an enum class
enum Food: CaseIterable {
case pasta, pizza, hamburger
}
for food in Food.allCases {
print(food)
}
// enum with String raw values
enum Currency: String {
case euro = "EUR"
case dollar = "USD"
case pound = "GBP"
}
let euroSymbol = Currency.euro.rawValue
print("The currency symbol for Euro is \(euroSymbol)")
// enum with associated values
// associated value(연관값):
// 열거형의 케이스(case)에 추가적인 정보를 덧 붙일 수 있는 독특한 방법
enum Content {
case empty
case text(String)
case number(Int)
}
// matching enumeration values with a switch statement
let content = Content.text("Hello")
switch content {
case .empty:
print("Value is empty")
case .text(let value): // String value 추출
print("Value is \(value)")
case .number(_): // Int value ignore
print("Value is a number")
}
14. Structs
- member initializer 자동 생성됨
struct User {
var name: String
var age: Int = 40
}
// member initilizer는 자동적으로 생성됨.
let john = User(name: "John", age: 35)
// member initilizer는 기본 파라미터 값을 사용.
// TODO: User()이렇게 해도 돼?
let dave = User(name: "Dave")
print("\(john.name) is \(john.age) years old")
15. Classes
- 반드시 하나의 designated initializer를 가져야 함
- computed property
- deinit: object의 리소스가 해제될 때 수행
class Person {
let name: String
// class initializer(designated initializer)
init(name: String) {
self.name = name
}
// using deinit to perform object's resources cleanup
deinit {
print("Perfom the deinitialization")
}
var numberOfLaughts: Int = 0
func laugh() {
numberOfLaughts += 1
}
// define a computed property
var isHappy: Bool {
return numberOfLaughs > 0
}
}
let david = Person(name: "David")
david.laugh()
let happy = david.isHappy
16. Inheritance
- override computed property
- final: subclass를 방지하기 위해 사용
class Student: Person {
var numberOfExams: Int = 0
// override isHappy computed property providing additional logic
override var isHappy: Bool {
numberOfLaughs > 0 && numberOfExams > 2
}
}
let ray = Student(name: "Ray")
ray.numberOfExams = 4
ray.laugh()
let happy = ray.isHappy
// final: subclass를 방지하기 위해 사용
final class Child: Person { }
17. designated & Convenience Initializers
- designated initializer: class는 반드시 하나 가져야 함
- convenience initializer: 옵션
// class는 반드시 최소한의 하나의 designated initilizer를 가져야 함
// convenience initilizer는 옵션
// TODO: designated initilizer 정의
class ModeOfTransportation {
let name: String
// designated initilizer 정의
init(name: String) {
self.name = name
}
// convenience initilizer 정의
convenience init() {
// 내부 designated initilizer에게 위임
self.init(name: "Not classified")
}
}
class Vehicle: ModeOfTransportation {
let wheels: Int
init(name: String, wheels: Int) {
self.wheels = wheels
// superclass의 designated initilizer로 위임
super.init(name: name)
}
// override superclass convenience initilizer
override convenience init(name: String) {
// 내부 designated initilizer에게 위임
self.init(name: name, wheels: 4)
}
}
18. Extensions
- 존재하는 class, structure, enumeration, protocol 타입에 새로운 기능 추가
- TODO: Category와 extension의 차이점은?
extension String {
// String type 확장 - String instance가 truthy or falsy인지 판단
var boolValue: Bool {
if self == "1" {
return true
}
return false
}
}
let isTrue = "0".boolValue
19. Error Handling
- Error protocol 채택하여 enum으로 정의
- do catch 문으로 에러 캐치
- try? : 해당 값에 error가 던져지면 nil로 반환 (optional 반환)
try! : 해당 값에 error가 던져지면 런타임 에러(앱이 죽는다는 얘기... 절대로 에러가 발생하지 않을 경우만 사용)
// error 상속받아 enum으로 기술
enum BeverageMatchineError: Error {
case invalidSelection
case insufficientFunds
case outOfStock
}
func selectBeverage(_ selection: Int) throws -> String {
return "Waiting for beverage"
}
// do clause의 code에 의해서 error가 던져지면 처리
let message: String
do {
message = try selectBeverage(20)
} catch BeverageMatchineError.invalidSelection {
print("Invalid selection")
} catch BeverageMatchineError.insufficientFunds {
print("Insufficient funds")
} catch BeverageMatchineError.outOfStock {
print("Out of stock")
} catch {
print("Generic error")
}
// try? error가 던져지면, 그 expression의 값은 nil로 처리
let nillableMessage = try? selectBeverage(10)
// try! : 런타임에 error를 받으면 error를 받음.
let throwableMessage = try! selectBeverage(10)
20. Coding Protocols
- Codable: Decodable + Encodable protocol
- JSONDecoder: JSON String -> Data
- JSONEncoder: Data -> JSON String
import Foundatation
// Codable: Decodable, Encodable protocol
struct UserInfo: Codable {
let username: String
let loginCount: Int
}
// CustomStringConvertible 채택
extension UserInfo: CustomStringConvertible {
var description: String {
return "\(username) has tried to login \(loginCount) time(s)"
}
}
// string literal
let json = """
{"username" : "David", "loginCount" : 2}
"""
// JSONDecoder를 사용하여 JSON serialize : JSON string -> Data
let decoder = JSONDecoder()
// String을 data(struct)로 표현
let data = json.data(using: .utf8)!
let userInfo = try! decoder.decode(UserInfo.self, from: data)
print(userInfo)
// JSONEncoder 사용하여 struct serialize :
let encoder = JSONEncoder()
let userInfoData = try! encoder.encode(userInfo)
// Data -> JSON String으로 표현
let jsonString = String(data: userInfoData, encoding: .utf8)!
print(jsonString)
21. Access Control
- public
- fileprivate
- private
- access관련이 없을 경우
// module(framework or application): 코드가 배포의 single unit
// public: 다른 모듈에서 접근 가능
public class AccessLevelsShowcase {
public var somePublicProperty = 0
// ?
var someInternalProperty = 0
// 소스 파일을 정의한 것으로부터 접근 가능
fileprivate func someFilePrivateMethod() { }
//
private func somePrivateMethod() { }
}
22. 스위프트에서의 물음표와 느낌표
스위프트에서 이 두 가지 구두 문자 (? 와 !)는 사용된 모든 용법에서 공통된 의미를 가지고 있음
물음표 : 무언가 확실하지 않다는 뜻
느낌표 : 경고 표지판 같은 용도, 잠재적으로 위험 동작을 수행한다.
옵셔널의 언랩핑과 마찬가지로 몇 라인 코드를 더 작성해야한다 할지라도try!대신에try와try?를 사용할 것을 권장합니다. 물론try!와 옵셔널 강제 언랩핑이 필요한 경우가 분명 존재합니다.
GCDis a low-level C-based API. NSOperationandNSOperationQueueare Objective-C classes. NSOperationQueueis objective C wrapper overGCD. If you are using NSOperation, then you are implicitly usingGrand Central Dispatch.
GCD advantage over NSOperation:
implementation ForGCDimplementation is very light-weight NSOperationQueueis complex and heavy-weight
NSOperation advantages over GCD:
Control On Operation you can Pause, Cancel, Resume anNSOperation
Dependencies you can set up a dependency between twoNSOperations operation will not started until all of its dependencies return true for finished.
State of Operation can monitor the state of an operation or operation queue. ready ,executing or finished
Max Number of Operation you can specify the maximum number of queued operations that can run simultaneously
When to Go forGCDorNSOperation
when you want more control over queue (all above mentioned) useNSOperationand for simple cases where you want less overhead (you just want to do some work "into the background" with very little additional work) useGCD
Both are used to unwrapped the Optional variable. (Optional Binding)
if let
Can not access out the scope.
no need to return statement. But we can write
if let a = a() {
let x = b(a)
x.fn()
if let u = x.nxt() {
let ux = u.brm()
if let uxt = ux.nxt() {
perform(uxt)
}
}
}
guard let
Early exist process from the scope
Require score existing like return, Throw etc.
Create a new variable those can be access out the scope.
guard let a = a() else { return }
let x = b(a)
x.fn()
guard let u = x.nxt() else { return }
let ux = u.brm()
guard let uxt = ux.nxt() else { return }
perform(uxt)