ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • iOS)BoostCourse) PTJ2 SignUp
    iOS 2019. 8. 1. 02:54

    부스트코스 PTJ1을 진행하면서 배운 내용과 리뷰받은 내용을 정리해보고자 합니다. 스스로 공부하며 이해한 내용을 적은 것으로 내용에 오류가 있을 수 있습니다. 오류 또는 수정이 필요한 부분은 댓글로 남겨주시면 정말 감사하겠습니다!

    Contents

    • Delegation
    • UIImagePickerControllerDelegate
    • imageView Clickable
    • dismiss
    • Singleton

    Delegation

    Delegation 은 클래스 또는 구조체가 자신의 일부 역할을 다른 유형의 객체에게 양도 또는 위임할 수 있도록 하는 디자인 패턴이다.

    delegate 란 사전적 의미로 위임, 대리(자), 위임하다 등의 뜻을 갖고 있다. 사전적 의미에서 추측해볼 수 있듯이 하나의 객체가 다른 객체를 대신해서 어떠한 역할을 수행해주는 것이다.

    Delegation 은 위임할 역할, 기능 등을 캡슐화하는 정의한 프로토콜을 구현함으로써 구현되는데, 이로써 외부에서 자세한 내부 코드를 모르더라도 특정 행동을 취하거나 정보를 검색할 수 있다. 다음은 주사위 게임을 위한 protocol 의 예이다.

    protocol DiceGame {
        var dice: Dice { get }
        func play()
    }
    protocol DiceGameDelegate: AnyObject {
        func gameDidStart(_ game: DiceGame)
        func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
        func gameDidEnd(_ game: DiceGame)
    }

    DiceGame 프로토콜은 단순히 주사위를 포함한 모든 게임에서 채택할 수 있는 프로토콜이다. 하지만 이것만 가지고는 게임을 할 수가 없다. 게임을 위해서는 좀 더 복잡한 규칙들이 필요하다. 여기서 DiceGameDelegate 가 그 역할들을 해준다고 볼 수 있다.

    이러한 프로토콜들을 이용해서 주사위를 이용한 새로운 게임을 해볼 수 있다.

    class SnakesAndLadders: DiceGame {
        let finalSquare = 25
        let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
        var square = 0
        var board: [Int]
        init() {
            board = Array(repeating: 0, count: finalSquare + 1)
            board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
            board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
        }
        weak var delegate: DiceGameDelegate?
        func play() {
            square = 0
            delegate?.gameDidStart(self)
            gameLoop: while square != finalSquare {
                let diceRoll = dice.roll()
                delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
                switch square + diceRoll {
                case finalSquare:
                    break gameLoop
                case let newSquare where newSquare > finalSquare:
                    continue gameLoop
                default:
                    square += diceRoll
                    square += board[square]
                }
            }
            delegate?.gameDidEnd(self)
        }
    }

    이처럼 delegate 에 구현된 자세한 내용은 모르더라도 역할을 대신해주는 delegate 를 이용해 감사하게도 여러 역할을 손쉽게 할 수가 있다.

    우리는 이미 delegate 를 여러 곳에서 사용하고 있다. 대표적으로 이번 프로젝트를 진행하면서 사용했던 textField, imagePicker 등에서도 사용해볼 수 있고, tableView, collectionview 에서도 delegate 를 사용해 여러 기능을 손쉽게 구현할 수 있다.

    UIImagePickerControllerDelegate

    Delegation 디자인 패턴에 대해 살펴보고 나니 UIImagePickerController 의 어떤 역할들을 대신 해주는 녀석이겠거니 느낌이 온다. UIImagePickerControllerDelegate 는 사용자가 image 나 movie 등을 선택할 때, 또 취소시 dismiss 등의 역할을 담당한다. 실제로 두 가지의 함수가 정의되어 있다.

    @available(iOS 2.0, *)
        optional func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any])
    
        @available(iOS 2.0, *)
        optional func imagePickerControllerDidCancel(_ picker: UIImagePickerController)

    첫번째 함수를 살펴보면, 사용자가 사진을 선택했을 때 그 이미지를 가져오는 함수이다. 사용자가 선택이 끝나면 그 정보를 함수 내부에서 info 라는 dictionary 형태로 가지고 있다. InfoKey 는 다음과 같이 정의되어 있으니 원하는 타입을 선택해 이미지를 가져오면 되겠다.

    extension UIImagePickerController.InfoKey {
    
        public static let mediaType: UIImagePickerController.InfoKey
    
        public static let originalImage: UIImagePickerController.InfoKey // a UIImage
    
        public static let editedImage: UIImagePickerController.InfoKey // a UIImage
    
        public static let cropRect: UIImagePickerController.InfoKey // an NSValue (CGRect)
    
        public static let mediaURL: UIImagePickerController.InfoKey // an NSURL
    
        @available(iOS, introduced: 4.1, deprecated: 11.0, message: "Replace with public API: UIImagePickerControllerPHAsset")
        public static let referenceURL: UIImagePickerController.InfoKey // an NSURL that references an asset in the AssetsLibrary framework
    
        @available(iOS 4.1, *)
        public static let mediaMetadata: UIImagePickerController.InfoKey // an NSDictionary containing metadata from a captured photo
    
        @available(iOS 9.1, *)
        public static let livePhoto: UIImagePickerController.InfoKey // a PHLivePhoto
    
        @available(iOS 11.0, *)
        public static let phAsset: UIImagePickerController.InfoKey // a PHAsset
    
        @available(iOS 11.0, *)
        public static let imageURL: UIImagePickerController.InfoKey // an NSURL
    }

    imageView Clickable

    프로필 사진을 채우는 건 실제 사용자의 입장에서 봤을 때 굉장

    ~

    히 많이 사용된다. 우선 빈 화면을 클릭해 이미지를 선택해야 하니까 클릭이 되야하는데 UIImageView 는 클릭이 되지 않는다. 그렇다면 버튼에 이미지를 넣는 방법을 사용할 수도 있겠지만, 과제에 ImageView 라고 명시가 되어 있기도 하고 프로필 사진의 경우 버튼보다는 이미지 뷰를 사용하는게 더 정상적인 수순일듯 하다.

    그럼 어떻게 할까?

    Tapgesture

    Tapgesture 는 말 그대로 사용자가 탭 했을 때 어떤 반응을 취할지를 결정한다. 사실 모바일에서는 탭이나 버튼 클릭이나 같기 때문에 딱 우리가 원하던 녀석이라고 할 수 있겠다. 구체적으로 어떻게 사용하는지 알아보자. 방법은 아주아주 간단하다.

    1. UIGestureRecognizer 를 만든다.
    2. 원하는 곳에 붙인다.

    우리는 Tapgesture 가 필요하니까 UITapGestureRecognizer 가 필요할 것 같은데 UIGestureRecognizer 라고 적어 놓았다. 맞으면서도 틀린 표현이라고 생각하는데, 실제로 현재 우리 코드에서는 UITapGestureRecognizer 를 사용해야 하는 것이 맞다. 하지만 이는 UIGestureRecognizer 클래스 타입으로 정의되어 있다. UIGestureRecognizer 문서를 살펴보면 우리가 필요한 UITapGestureRecognizer 이외에도 여러 제스쳐들이 subclass 로 잘 정의되어 있다.

    따라서 같은 방식으로 여러 제스쳐를 익혔다고 생각하면 기분도 좋아지고 일석이조!

    dismiss

    한 화면에서 다른 화면으로의 전환을 위해 push 를 하면 현재 화면에서 다른 화면이 위에 쌓인다. 말 그대로 화면이 스택구조로 차곡차곡 쌓이는 방식이다. 반대로 돌아갈 때는 pop 을 한다고 생각할 수 있다. UINavigationtionController 를 사용하고 있다면 pop 과 관련된 함수들을 사용할 수 있지만, 그렇지 않다면 다른 방법을 사용해야 한다.

    답은 간단하다. 현재 화면을 없애버리면 된다. dismiss 말 그대로다.

    문제는 UINavigationController 였다면 쉽게할 수 있는 일을 못할 때 발생한다. 현재 내가 여러 화면들을 쌓아놨는데 root 화면으로 돌아가고 싶다면 어떻게 해야할까?

    최상위 화면이 나올 때 까지 계속 dismiss 를 할 수도 없는 노릇이다. 사실 dismiss 의 정의를 잘 살펴보면 답이 쉽게 나오는데,

    Dismisses the view controller that was presented modally by the view controller.

    view controller 에 의해 띄워진 뷰 컨트롤러를 해제한다. 그렇다면 root 화면으로 돌아가고 싶다면, root 화면에서 dismiss 를 호출하면 그 위에 띄워진 view controller 들은 모두 dismiss 가 될 것이다.

    사실 이 부분은 스스로의 추측이 짙어… 오류가 많을 수 있을 것 같네요. 제 이해에 오류가 있다면 댓글로 남겨주시면 감사하겠습니다!!!

    Singleton

    사실 이해하고 나면 간단한데, 개인적으로 이해하기까지 시간이 좀 걸렸던 주제인 것 같습니다. (갑자기 구어체네요.. 의식의 흐름 양해를 부탁드려요….)

    개념은 아주 씸플합니다. 이름에서도 알 수 있듯이 단 하나임을 보장하겠다 는 뜻인데요, "애플리케이션 내에서 특정 클래스의 인스턴스가 딱 하나만 있기 때문에 다른 인스턴스들이 공유해서 사용할 수 있다." 다음과 같은 식으로 설명이 많이 되어있는데 사실 저는 이 말이 조금 어렵게 느껴졌던 것 같아요.

    이를 이해하기 위해서는 왜 사용해야 하는지를 이해하는게 가장 빠르고 확실한 길인 것 같아요. 저는 이번 프로젝트의 회원가입처럼 특정 데이터들을 계속해서 지니고 있는 경우를 생각하면서 싱글턴의 필요성을 제대로 느끼게 되었습니다.

    현재 내가 갖고 있는 데이터들을 다음 화면에서도 갖고 있기 위해서는 어떻게 해야할까요? 직접 다음 화면에 전달한다, 디비에 저장한다 등의 방법들이 있겠죠. 하지만 문제가 조금씩 있습니다. 화면 화면마다 정보를 조금씩 조금씩 불려나간다면, 처음 화면의 데이터를 끝까지 계속해서 전달해야 하겠죠? 혹시 되돌아오는 경우가 생긴다면, 다음 화면이 아니라 이전 화면에도 똑같이 전달을 해줘야 할거에요. 딱 봐도 매우 비효율적이겠죠. 디비에 저장 역시 좋은 방법이지만 매번 다시 읽어와야 하는 불편함이 있습니다. 무엇보다 디비까지 가고싶지 않을 경우도 있겠네요. 이러한 상황에 가장 적합한 것이 바로 싱글턴이 아닐까 싶습니다. 각 화면들이 공통의 데이터를 공유하고 있다면 이런 문제들은 한 방에 해결될 수 있는거죠! 이 흐름이 이해되셨다면 이미 싱글턴 또한 이해가 되신겁니다 ! (아마도요 :D)

    다시 원론적인 이야기로 돌아가보면, 단 하나임이 보장되어있는 싱글턴이기 때문에 마음껏 공유해서 사용할 수가 있다는 것이죠. 따라서 싱글턴 클래스는 정의할 때, 객체를 생성해놓고 이 생성된 객체 하나를 다른 객체들이 바로 이 객체를 공유해 사용합니다. 일반적으로 바로 이 싱글턴 객체는 shared 라는 이름이 많이 사용된다고 하는데, 이해하고 나니 정말 지극히 당연하고 자연스러운 수순인 것 같네요.

     

     

    긴 글 읽어주셔서 감사합니다.

     

     

    참고:
    Apple Document
    BoostCourse 자료

    'iOS' 카테고리의 다른 글

    KVC(Key-Value-Coding)  (0) 2019.09.20
    MVC 패턴  (2) 2019.08.07
    iOS) BoostCourse) PTJ1 MusicPlayer  (0) 2019.07.14
    iOS) Core Data document 뿌시기 - 1  (0) 2019.07.04
    iOS) Status Bar, Navigation Bar 바꾸기  (0) 2019.06.07

    댓글

Designed by Tistory.