 Apple Lover Developer & Artist

영속적인 디자인에 현대의 공감을 채워넣는 공방입니다

 Apple/Stanford iOS Programming (SwiftUI)

Lecture 9 Review Part 1: EmojiArt Drag and Drop Multithreading

singularis7 2021. 9. 24. 19:56
반응형
  • 기존에 만들어온 Memorize 데모앱 대신 새로운 EmojiArt 를 만들어 볼 것이다.
  • 앞으로 나올 강의에서는 EmojiArt를 기준으로 수업한다.

 

Collections of Identifiable

  • Memorize 앱에서 다음과 같은 코드를 사용해본 경험이 있을 것이다.
func choose(_ card: Card) {
    if let index = cards.firstIndex(where: { $0.id == card.id }) {
        cards[index].isFaceUp = true
    }
}
  • 카드 배열에서 선택된 카드를 찾아서 isFaceUp 혹은 isMatch 와 같은 것들을 설정해줘야하기 때문이다.
  • 카드는 구조체이고 구조체는 값 타입이며 값타입은 복사되기 때문에 배열에서 바로 변경해야한다.
  • 위와 같은 행위를 extension을 통해 손쉽게 사용하도록 구현할 수 있다.
func index(matching element: Element) -> Int? {
    return firstIndex(where: { $0.id == element.id })
}
  • 위와 같은 함수를 작성해주면 다음과 같이 사용할 수 있으며 읽기에도 쉬워진다.
func choose(_ card: Card) {
    if let index = cards.index(matching: card) {
        cards[index].isFaceUp = true
    }
}
  • index(matching:) 함수를 어디에 추가하면 좋을까?
  • Identifiable을 만족하는 배열에 추가하면 된다!
extension Array where Element: Identifiable {
    func index(matching element: Element) -> Int? {
        return firstIndex(where: { $0.id == element.id })
    }
}
  • 만약 위 코드를 Set에서 사용하고 싶다면 Set에서도 별도의 index(matching:)을 추가해줘야 하는 것일까?
  • 사실 Array와 Set은 Collection 프로토콜을 만족하고 있기 때문에 Collection에 확장시켜주면 된다.
extension Collection where Element: Identifiable {
    func index(matching element: Element) -> Self.Index? {
        firstIndex(where: { $0.id == element.id })
    }
}
  • 이제 Collection 프로토콜을 만족시키는 것에서 위 메서드를 호출할 수 있게 되었다.
  • 이것이 프로토콜 지향 프로그래밍에서 하는 일이며 프로토콜에 기능을 추가할 수 있다.
  • 반환 타입을 보면 특이하게도 Self.Index이다.
  • Collection 프로토콜의 Index는 제네릭이며 Set과 Array는 Index로 Int를 사용할 뿐이다.
  • String은 Character의 Collection이며 Int를 사용하여 인덱싱하지 않는다.
  • String의 요소는 Character이며 identifiable하지 않다. 위 코드는 String에서 동작하지 않는다.
  • where clause를 추가함으로써 위 기능이 String에서 동작하지 않도록 할 수 있다.
extension Collection where Element: Identifiable {
    mutating func remove(_ element: Element) {
        if let index = index(matching: element) {
            remove(at: index)
        }
    }
}
  • 특정 Element를 컬렉션에서 제거하는 기능을 추가할 수 있을 것이다.
  • 입력받은 element와 동일한 자료가 컬렉션에 있다면 제거시켜주는 기능이다.
  • 불행이도 Collection에는 remove 메서드가 존재하지 않기 때문에 위 코드는 동작하지 않는다.
extension RangeReplaceableCollection where Element: Identifiable {
    mutating func remove(_ element: Element) {
        if let index = index(matching: element) {
            remove(at: index)
        }
    }
}
  • 대신에 RangeReplaceableCollection을 사용하면 remove 메서드를 사용할 수 있게 된다.
  • Array와 Set 모두 RangeReplaceableCollection를 만족한다.
  • 따라서 Collection에서 Identifiable을 제거하는기능을 RangeReplaceableCollection에 추가할 수 있다.
  • RangeReplaceableCollectiondms Collection을 상속받기 때문에 Collection도 만족한다.
  • Collection과 RangeReplaceableCollection의 차이점은 수정할 수 없는가? 수정할 수 있는가이다.
  • RangeReplaceableCollection에 Subscript(배열 첨자[])를 추가할 수 있다.
cards[card].isFaceUp = true
  • 식별 가능한 Array 혹은 Set의 subscript에 대하여 Identifiable하도록 만들 수 있다.
  • Int 인덱스가 아닌 card를 넣어서 인덱싱을 수행할 수 있다.
  • subscript를 사용했을 때 장점: 변수를 수정할 수 있다.
  • 위 코드는 cards 배열에서 card의 id를 subscript하고 있기 때문에 배열 값을 변경할 수 있다.
  • 정리하자면 프로토콜 확장을 통해 기능을 추가할 수 있으며 subscript를 통해 변경할 수 있다는 점을 알아두자!
  • Emoji Art 데모 코드에서 한번더 확인해보자! 강력한 기능이다.

Color vs. UIColor vs. CGColor

  • Color에 관련된 3가지 자료구조가 있다. Color, UIColorm, CGColor
  • 결론적으론 Apple Document에서 어떤 기능을 제공해주는지 확인하게 될 것이다.

Color

  • Color은 정말 익숙하다. 다양한 맥락(context)로 사용될 수 있다.
  • 우선 color-specifier 측면에서 색상을 지정하는데 사용할 수 있다. (예를 들어: .foregroundColor(Color.green))
  • ShapeStyle처럼 행동하도록 사용할 수 있다. (예를 들어: .fill(Color.blue))
  • View처럼 행동하도록 사용할 수 있다. (예를 들어: Color.white)

UIColor

  • UIColor은 color을 수정(manipulating)하기위한 더 많은 API를 제공한다.
  • UIColor에게 Red, Green, Blue 값이 무엇인지 물어볼 수 있다.
  • 색조(Hue), 채도(Saturation), 밝기(Brightness) 값이 무엇인지 물어볼 수 있다.
  • UIColor에서 채도를 높이고 색상을 변경하는 등 수정 작업을 할 수 있다.
  • Color에는 수정하는 API가 없기 때문에 수정할 수 없다.
  • UIColor을 통해 수정 작업이 끝났다면 Color 생성자를 통해 Color로 변환시킬 수 있다.
  • 위 두가지는 서로 교환 관계가 성립해서 자신의 역할이 끝났다면 상대방의 생성자를 통해 변환시킬 수 있다.

CGColor

  • Core Graphic System(CG)에서의 기본적인 색상 표현법이다.
  • 제한된 API를 제공하며 그림을 그릴때 색상을 어떻게 표현하는지에 관한 것이다.
  • color.cgColor을 사용하여 color에서 CGColor로 변환시킬 수 있다. (optional 타입이다.)
  • color로 표현되는 것들이 CGColor로 반드시 표현될 수 있음을 보장할 수 없기 때문이다.
  • SwiftUI에 있는 다른 API를 사용하여 생성한 경우 99.99% CGColor 표현 가능한 색상을 생성한다.

Image vs. UIImage

  • Image 및 UIImage와 같은 또다른 쌍이 존재한다.

Image

  • Image는 이미지를 보여주는 View이며 image 값을 갖는 변수의 type이 아니다.
  • Xcode에서 Assets.xcassets를 통해 이미지에 접근할 수 있으며 (첫번째 강의에 언급하였다)
  • 위 폴더에 jpeg 이미지를 가져다 두고 Image(_ name: String)을 통해 불러올 수 있다.
  • SFSymbol 이미지에 접근하려면 Image(systemName:)을 통해 불어올 수 있다.
  • System 이미지의 크기.imageScale() View modifier를 통해 조정할 수 있다.
  • System 이미지의 경우 .font modifier에 영향을 받는다. 즉, 폰트 크기를 키우면 이미지도 커진다.
  • System 이미지는 마스크(예를 들어 Gradient)하는데 유용하다. 색상, 모노크롬 형태로 변경할 수 있다.

UIImage

  • 실제 JPEG, TIFF와 같은 이미지이다. 
  • 크기 조정 및과 같은 것들이 UIImage를 통해 가능하다.
  • Image(uiImage:)를 통해 이미지를 표시할 수 있다.
  • Color와 UIColor 관계와 정확히 동일하다.
  • UIImage type을 통해 변수에 이미지를 담을 수 있으며 이미지를 보여주고 싶다면 Image View를 사용하면 된다.

Drag and Drop

  • EmojiArt 데모에서는 Drag and Drop 기능을 사용할 것이다.
  • Drag and Drop은 하나의 앱에서 다른 앱으로 자료를 전송하는 기능이다.
  • NSItemProvider 라는 핵심 Class가 사용된다.
  • 프로세스간의 데이터 전송이 이루어지며 과정의 범위를 벗어나기 때문에 깊게 다루지는 않는다.
  • 보안 문제와도 연관지을 수 있으며 다중 스레드와 관련되어있다.
  • 두 앱의 UI 중 하나가 중단되는 것을 바라지 않으며 여튼 복잡하다 ㅎㅎㅎ
  • SwiftUI와 NSItemProvider 간의 상호 작용을 관리하는 코드가 제공될 것이다.

 

  • 본질적으로 NSItemProvider가 할 수 있는 것은 다양한 type의 데이터 전송을 용이하게 해주는 것이다. 예시는 다음과 같다.
  • NSAttributedString (a String with formatting) and NSString
  • NSURL (https://cs193p.stanford.edu/something)
  • UIImage and UIColor
  • 위와 같은 것을 UI로부터 Drag and Drop할 수 있다.

 

  • 불행이도 "NS" 시리즈들이 Swift에서 사용되고 있으며 Swift 이전에 사용되온 것들이다.
  • NSItemProvider를 사용할 때 귀찮기는 하지만 다음과 같이 Swift타입을 과거의 타입으로 연결(bridging)해줘야 한다.
  • Objective-C에 대하여 자세하게 언급하지는 않을 것이다.
String as NSString

 

다음편에서는 Demo 앱 구현에 관하여 포스팅할 것이다!
반응형