티스토리 뷰

앱 스토어에 앱 올리기 시리즈를 통해서 앱의 심사를 신청했다.

네트워킹이 필요한 것도 아니고 교육용 앱이라 그런지 약간의 문제가 있었지만 잘 해결하여 앱 스토어에 올라가게 되었다.

이제는 앱을 만드는 과정에서 놓쳤던 부분들을 하나하나 살펴서 업데이트를 해보기로 했다.

그 과정에서 생기는 문제들에 대해서 포스팅을 해보자.

 

앱을 다운받으면 가장 먼저 보이는 등록 화면부터 손보기로 했다.

 

  기기는 iPhone XR 이며, 실행되어있는 상태를 캡쳐하였다. 이 화면에서 발생할 수 있는 문제는 없지만, 사실은 잠재적인 문제의 소지를 가지고 있다. 바로 화면 크기가 작은 iPhone SE 나 iPod Touch 와 같은 기기에서 앱을 다운받는다면 TextField 에 글자를 입력하려고 할 때 올라오는 키보드가 TextField 를 가리게 되는 문제를 야기한다. 

 

  사실 입력하고 키보드를 내려서 확인할 수도 있겠으나, 이는 UX 관점에서 보면 치명적인 단점이다. 유저가 키보드를 사용하는 것을 최소화하는 것을 지향해야 하기 때문이다. 

 

  그럼 이 문제를 어떻게 해결해야 할 것인가? 가장 쉬운 방법은 키보드가 올라온 만큼 뷰도 함께 올라가게 하고, 키보드가 내려가게 되면 뷰도 다시 원래 자리를 찾게 하면 되는 것이다. 그럼 바로 가보자.

 

  아니 그런데, 키보드가 올라왔다는 것을 어떻게 파악해야 할까? textField 의 delegate method 를 찾아보았지만, 키보드가 나타난 상황에 대해서는 만족할만한 답지를 찾을 수가 없었다. 결국 이 문제는 NotificationCenter 를 통해서 해결할 수 있었다. 무작정 이를 사용하기 보단 먼저 이것이 무엇인지에 대해서 알아보도록 하자.

 

 

- Framework : Foundation

- SDKs : iOS 2.0+

- 등록이 된 옵저버들에게 정보를 브로드캐스팅할 수 있도록 해주는 알림 발송 메커니즘이다.

 

 

  사실 저 위의 한 줄로 모든 설명이 가능하다. 하지만 기왕 알아보기로 한 김에 조금 만 깊게 들어가보자. '등록이 된 옵저버들..' 이라는 말에서 유추할 수 있듯, 우리는 옵저버(observer)라는 것을 등록해야만 이를 사용할 수 있을 것 같다. 브로드캐스트(broadcast) 라는 말이 조금 낯설 수 있는데, 우리는 너무나 가까운 곳에서 브로드캐스트의 의미를 잘 이해하고 있어 왔다. 대표적으로 KBS 에서 B가 broadcasting(동사형) 이기 때문이다. 아무튼, '무언가를 중간에서 쏴주는 역할'인 것이다. 우리가 KBS 를 통해서 방송을 보듯.. Notification Center 의 개요(Overview)에 대해 살펴보자.

 

  객체들(objects)을 'addObserver(_:selector:name:object:)' 또는 'addObserver(forName:object:queue:using:)' 메소드를 사용함으로서 알림(NSNotification 객체)을 받을 수 있게 알림 센터(notification center)에 등록합니다. 하나의 객체가 자신이 관찰자(observer)로서 추가되는 시점에, 수신할 알림을 지정합니다. 그러므로 객체는 여러 개의 다른 알림에 대해 자신을 관찰자로서 등록하기 위해 여러 번 이 메서드를 호출할 수 있습니다. (심플하게, 개발자 입장에서 예를 들면 하나의 텍스트 필드(=객체)에 관찰자 역할을 여러 개 부여할 수 있다는 이야기입니다) 

  실행중인 각 앱은 '기본(default)' 알림 센터(notification center)를 가집니다. 그리고 개발자는 새로운 알림 센터를 만들 수 있습니다. 이를 통해 특정 컨텍스트(Context)에서 통신을 구성할 수 있습니다. 알림 센터는 오직 단일 프로그램 내에서만 알림을 전달할 수 있습니다. 예를 들어 만약 개발자가 다른 프로세스에 알림을 게시하거나 혹은 다른 프로세스로부터 알림을 수신하길 원한다면, 이 알림 센터가 아닌 'DistributedNotificationCenter' 를 대신 사용하길 바래요. (파고들어 보고 싶지만 일단 오늘의 주제에서 벗어나는 부분이니 여기까지만 알아보겠습니다)

 

  오늘 사용해볼 메서드는 다음과 같다. 기본적으로 NotificationCenter 에 접근하기 위한 'default' 클래스 변수가 필요하고, Observer 를 추가하는 메서드와 제거하는 메서드가 필요하다. 

 

Source : Apple Developer

 

  여기서 잠시 살펴볼 내용은 왜 'default' 인가? 여기서 잠깐 생각해보면 이전에 default 를 쓴적이 있다. 잘 생각해보자... 바로 UserDefault 를 사용했을 때이다. 이것을 사용할 때도 UserDefault.default 이런 식으로 사용하도록 엄격하게 규칙이 정해져 있었다. 달리 말하면 인스턴스의 생성이 하나로 제한되는 것이고, 즉 '싱글톤(Singleton)' 패턴으로 디자인 되었다는 것을 알 수 있다.

 

  왜 이 싱글톤 패턴으로 디자인 되었는가? 생각해보면 쉽다. 이곳 저곳에 NotificationCenter 의 인스턴스가 생성된다면, 동시에 프로젝트의 크기가 엄청 커진다면 이 인스턴스를 관리하기도 너무나 어렵다. 그렇기 때문에 하나의 인스턴스로 제한되는 경우에는 관리가 편해진다.

 

  

  잠깐 ! 그럼 post 는 뭐하는 친구인가? 현재 이슈인 '키보드가 나왔을 때' 와 같은 경우에는 iOS가 자체적으로 Control 하기 때문에 우리가 발송 시점이라는 것을 정할 수는 없지만, 이미 정해져 있기 때문에 그 부분을 우리가 필요한 부분에 사용할 수 있었다. 하지만 정해져 있지 않는 부분은 어떻게 처리할까? 바로 post 를 통해서 전파하는 것이다. 어떠한 일이 발생했을 때 post 는 이것을 전파하는 역할. 

 

  자 이제 처음으로 돌아와서,  그럼 일단 어디에 addObserver / removeObserver 를 해줘야 할까? 바로 뷰가 나타날 때와 사라질 때 해주면 가장 깔끔할 것 같다. 참, 중요한 것은 addObserver 를 해줬으면 반드시 removeObserver 를 해줘야 한다. 전술했듯이 싱글턴으로 구현되어 있기 때문에 메모리에서 계속 상주하게 된다. addObserver 를 해주고 가만히 냅두면 메모리 낭비로 이어질 수 있기 때문이다. 

 

 

(1) addObserver 를 통해 옵저버를 설정할 대상을 self 로 지정 후

 

(2) 해당 옵저버가 실행되었을 때 실행할 함수를 지정한다.

 

(3) 어떠한 경우를 옵저버가 관찰할 것인지를 설정해준다.

  - UIResponder.keyboardWillShowNotification : 키보드가 나올 때

  - UIResponder.keyboardWillHideNotification : 키보드가 들어갈 때

 

  viewWillDisappear 를 통해서는 위와 같이 제거할 대상을 name 을 통해 정확히 지정해주면 문제없이 제거된다.

 

  그리고 해당 이벤트가 발생했을 때, 개발자가 지정한 함수의 내용이 실행된다. 위 함수의 결과는 다음과 같다.

 

 

 

 

 

  끝난 줄 알았쥬? 하지만 여기에는 치명적인 오류가 있었으니... 바로 위의 공식은 텍스트 필드가 하나 일때 문제가 발생하지 않는다. 닉네임을 누른 상태에서 소속을 누르면 'UIResponder.keyboardWillShowNotification' 이 한번 더 실행되면서 화면이 더 올라가 버린다. 어떻게 해결해야할까? 

 

  또한 사용자가 닉네임 입력 후 키보드를 내리기 위해 '소속' 을 클릭하는게 아닌 빈 화면을 터치해서 키보드를 내리려고 시도할 수도 있다는 것을 고려해야 하고, 키보드의 enter 부분을 누를 수도 있을 것이다. 이 부분들을 모두 고려해서 작성해야 할 것이다. 

 

  다음의 메서드들을 작성하고 앱에서 특정 동작을 실행했을 때 어떤 메서드가 호출되는지 확인하면서 앱을 만들어보자. 참고로 restoreFrameValue 의 경우 viewDidLoad() 가 실행되는 시점에 최초의 self.view.frame.origin.y 값을 저장하도록 되어있다. 물론 내가 작성한 방법보다 좋은 방법이 있을 것이다. 그러면 댓글좀 부탁드려요 ㅠ.ㅠ

 

 

var restoreFrameValue: CGFloat = 0.0
    
}

// MARK: TextField & Keyboard Methods
extension EnrollmentViewController: UITextFieldDelegate {
    
    @objc func keyboardWillAppear(noti: NSNotification) {
        if let keyboardFrame: NSValue = noti.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
            let keyboardRectangle = keyboardFrame.cgRectValue
            let keyboardHeight = keyboardRectangle.height
            self.view.frame.origin.y -= keyboardHeight
        }
        print("keyboard Will appear Execute")
    }
    
    @objc func keyboardWillDisappear(noti: NSNotification) {
        if self.view.frame.origin.y != restoreFrameValue {
            if let keyboardFrame: NSValue = noti.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
                let keyboardRectangle = keyboardFrame.cgRectValue
                let keyboardHeight = keyboardRectangle.height
                self.view.frame.origin.y += keyboardHeight
            }
            print("keyboard Will Disappear Execute")
        }
    }
    
    //self.view.frame.origin.y = restoreFrameValue
   
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.view.frame.origin.y = restoreFrameValue
        print("touches Began Execute")
        self.view.endEditing(true)
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        print("textFieldShouldReturn Execute")
        textField.resignFirstResponder()
        return true
    }

    func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
        print("textFieldShouldEndEditing Execute")
        self.view.frame.origin.y = self.restoreFrameValue
        return true
    }
    
}

 

 

 

위와 같은 코드에서는 정상적으로 작동한다.

 

수고하셨습니다.

 

 

 

 

 

 

 

 

  

PS) 아...! 이 NotificationCenter 프레임워크랑 착각하시면 안됩니다 !

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함