[스탠포드 iOS] 5강 뷰에 나타내기

5강 목표

  • Error Handling
  • Any
  • Keywords
  • Custom Drawing
  • Enum

Error Handling

// 통상적인 방법
do {
    try context.save()
} catch let error {
    throw error
}

// 100% 확률로 error가 발생하지 않을 때 사용 (보통 사용하지 않는다.)
try! context.save()
 
// 심각하지 않은 error일 경우
try? context.save()

Any & AnyObject

  • 다양한 타입이 될 수 있다.
  • 이 수업에서 데이터 구조를 사용할 때 Any를 사용하면 안 된다.
  • as? 키워드를 통해 Any를 명시한 타입으로 변환한다.
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
}
// sender가 UIButton, UITableViewCell일 수도 있기 때문에 Any 타입이다.
// sender가 nil일 수도 있기 때문에 Optional로 선언

let vc: UIViewController = ConcentrationViewController()
if let cvc = vc as? ConcentrationViewController {
    cvc.flipCard()
}
// vc는 UIViewController 여서 flipCard() 메소드를 사용할 수 없다.
// 하지만 다운캐스팅을 통해서 사용할 수 있다.

Other Interesting Classes

NSObject

  • Objective-C의 모든 클래스의 루트 클래스
  • Swift에서는 NSObject의 서브 클래스가 될 필요가 없다.

NSNumber

  • Objective-C에서 숫자를 전달할 때 사용한다.
  • API에서가 등장했다면 Int, Double형이라고 생각해도 무방하다.

Date

  • 날짜나 시간을 표현한다.
  • Calendar, DateFormatter, DateComponents 등과 종종 같이 쓰인다.

Data

  • iOS API간의 데이터 전달에 사용한다.
  • bit를 담은 가방

Views

  • iOS 클래스인 UIView의 서브클래스
  • 좌표계를 정의하는 화면의 사각형
  • 계층 구조
  • 그래픽적으로 구현된다. 하지만 코드로도 작성 가능하다.
func addSubView(_ view: UIView)
func removeFromSuperview()
// 추가할 때는 superview에서 요청
// 제거할 때는 제거될 view에게 요청

초기화

  • 초기화는 최대한 피하려한다.
init(frame: CGRect) // 코드에서 뷰를 만드는 용도
init(coder: NSCoder) // 인터페이스 빌더에서 자유롭게 편집하고 앱이 실행될 때 초기화

// 두 가지를 모두 구현
override init(frame: CGRect) {
    super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

awakeFromNib()
// 스토리보드로부터 UIView가 만들어졌을 때 호출된다.

좌표 시스템

  • CGFloat
    • CG : CoreGraphic
    • 그림을 그릴 때 CGFloat 타입을 사용한다. (부동소수점)
  • CGPoint
    • CGFloat 타입인 x, y가 들어가있는 구조체
  • CGSize
    • CGFloat 타입의 width, height가 들어가있는 구조체
  • CGRect
    • Rectangle 직사각형을 의미
    • CGPoint, CGSize를 가지고 있는 구조체

  • 원점은 좌측 상단에 있다.
  • 단위는 points
    • pixels : 스크린을 구성하고 있는 작은 점
    • contentScaleFactor : point에 얼마나 많은 pixel이 존재하는지 확인
  • bounds ⭐️
    • CGRect타입의 변수
    • drawing을 할 때 항상 사용한다.
  • frame
    • drawing과는 전혀 상관이 없다.
    • 슈퍼뷰에서 어디에 위치하는지를 말한다.
  • center
    • drawing과 상관이 없다.
    • 슈퍼 뷰의 입장에서 뷰의 중심을 이야기한다.

  • 회전을 보면 frame, center는 위치를 잡는 것과 관련이 있다.
  • frame과 bounds과 같다고 생각하면 안 된다.

Custom Views

  • 원하는 drawing을 하거나 원하는 터치 이벤트를 다룰 때 사용한다.
// UIView에서 오버라이드해서 구현 (이 방법뿐)
override func draw(_ rect: CGRect)

// 절대 직접 호출하면 안 된다.⭐️

// 새롭게 drawing하고 싶다면 아래의 메소드를 대신 호출
setNeedsDisplay()
setNeedsDisplay(_ rect: CGRect)

Core Graphics Concepts

  • context를 기반으로 한다.
  • UIGraphicsGetCurrentContext() : drawing할 context를 알려준다.
  • 경로 설정 (line, arc, etc)
  • drawing 속성 설정 (color, font, etc)
  • 경로에 대해서는 테두리 그리기와 채워 넣기 중 하나를 선택한다.

UIBezierPath


demo

ViewController.swift

class ViewController: UIViewController {

    var deck = PlayingCardDeck()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        for _ in 1...10 {
            if let card = deck.draw() {
                print("\(card)")
            }
        }
    }
}
// 카드덱을 만들어서 10개를 우선 콘솔에 출력한다.

PlayingCard.swift

// CustomStringConvertible을 활용하면 사용자가 정의한 description 형태가 출력된다.
struct PlayingCard: CustomStringConvertible {
    var description: String {
        return "\(rank)\(suit)"
    }
    
    var suit: Suit
    var rank: Rank
    
    enum Suit: String, CustomStringConvertible {
        case spades = "♠️"
        case hearts = "♥️"
        case clubs = "♣️"
        case diamonds = "♦️"
        
        static var all = [Suit.spades, .hearts, .diamonds, .clubs]
        
        var description: String {
            return rawValue
        }
    }
    
    enum Rank: CustomStringConvertible {
        case ace
        case face(String)
        case numeric(Int)
        
        var order: Int {
            switch self {
            case .ace: return 1
            case .numeric(let pips): return pips
            case .face(let kind) where kind == "J" : return 11
            case .face(let kind) where kind == "Q" : return 12
            case .face(let kind) where kind == "K" : return 13
            default: return 0
            }
        }
        // 각각의 케이스로 구현하면 13가지를 따로 다 적어줘야하지만 좋지 않은 방법이다.
        // numberic -> 카드 2 ~ 카드 9
        // face -> J, Q, K 이미지 사용하는데 where 사용이 특이했다.
        // ⭐️ self를 kind 상수에 바인딩하고 where 조건절에서 조건에 해당하면 해당 값을 return한다.
        
        static var all: [Rank] {
            var allRanks: [Rank] = [.ace]
            for pips in 2...10 {
                allRanks.append(Rank.numeric(pips))
            }
            allRanks += [Rank.face("J"), .face("Q"), .face("K")]
            return allRanks
        }
        
        var description: String {
            switch self {
            case .ace: return "A"
            case .numeric(let pips): return String(pips)
            case .face(let kind): return kind
            }
        }
    }
}

PlayingCardDeck.swift

struct PlayingCardDeck {
    private(set) var cards = [PlayingCard]()
    // private(set) : setter에 대해서만 private 선언, 쓰기 속성 막아둠
    
    // 초기화하면 모든 카드를 담는다.
    init() {
        for suit in PlayingCard.Suit.all {
            for rank in PlayingCard.Rank.all {
                cards.append(PlayingCard(suit: suit, rank: rank))
            }
        }
    }
    
    // 랜덤으로 카드하나를 return한다.
    mutating func draw() -> PlayingCard? {
        if cards.count > 0 {
            return cards.remove(at: cards.count.arc4random)
        } else {
            return nil
        }
    }
}

동작 모습



링크

좋은 웹페이지 즐겨찾기