[기초] Overview

Apple에서 정의하는 UI Navigation Bar의 정의는 화면 최상단의 스크린을 탐색바이다. 이해 쉬운 그림은 다음(https://developer.apple.com/documentation/uikit/uinavigationbar)과 같다. 

실무에서는 Navigation Bar를 없애고 Custom 하게 구성하는 경우가 대부분 이었다. iOS를 직접 만들지 않는 이상 앱 튜닝의 끝은 순정 API라는 말처럼 iOS 업데이트에 자동으로 맞춰주고, 테마(Theme)에 따라 자동 변하는 것의 구현. 정상적인 뷰컨트롤러라이프 사이클과 신기술에 기민하게 대응하기 위해 NavigationBar를 애플에서 제공하는 기본으로 유지하는 경우가 많아졌다. 그러나 여전히 legacy code에서의 사용률은 높기 때문에 기본적 사용법과 더불어 커스터마이징 하는 방법도 살펴본다. API를 소개할 때도 네비게이션 바를 혼용해서 사용되는 프로젝트에서 동작하는 코드를 예제로 실무에서 바로 적용이 가능토록 한다.


Basics

일반적으로 Android 에서 화면 구성 단위는 Activity 이고, iOS에서는 ViewController 이다. Android 에서 startActivity로 화면을 연결한다고 하면, iOS의 경우 pushViewController로 연결한다. 주의할 점은 Activity LifeCycle이던, ViewController LifeCycle 이던 임베디드 환경인 휴대폰에서는 Resource 보존에 더 취약하다는 것이다. 화면 실행 단위별로 세팅된 변수, 메소드 리턴값들이 화면이 바뀌었을 때 100% 그대로 유지된다는 보장은 하기 힘들다. 이 점을 간과하면 앱이 죽는데 이유를 찾지 못하는 경우가 많다. 이에, 안드로이드에서는 SharedPreferences를, iOS 에서는 UserDefaults를 적극 활용한다. SQLite3 로컬 DB 로 통일하기도 하지만 복잡한 모듈에서는 속도가 매우 느린다. 이런 기초를 미리 설명하는 이유는 화면 단위를 PUSH, POP 으로 저장하는 스택(stack)이라고 해도 해당 스택에서 튀어나온(pop) 액티비티 혹은 뷰 컨트롤러가 완벽하게 모든 자원(resource)를 가지고 있을 거라는 추측은 삼가해야 한다는 점이다. 필자는 안드로이드 플랫폼만 수년 간 만들다 앱 개발로 갔을 때 하부에서는 심각하게 고민하는 Resource leak 을 단순히 폰 문제로만 단정하는 경우를 많이 보았다. 그도 그럴 것이 프레임웍은 정적 분석이 필수인데 앱의 경우 다양한 이유로 정적 분석을 하지 않기 때문이었다. 다양한 이유 중 하나는 특별히 큰 기능이 없고, 복잡한 모듈의 경우 통합 플랫폼이나 엔진이라고 불리는 컴포넌트, 라이브러리, 서비스 등의 API 덩어리에 던져 버리기 때문이다. 이에, C/C++ 시니어 프로그래머가 앱 개발로 전직할 경우 가장 안정적인 앱을 만들 수 있다는 선입견이 있다.

UX 설계

인스타그램이 나온지 10년이 넘었는데 갤럭시 패드나, 아이패드 용 앱은 아직 화면이 작다. 즉, 전혀 맞지 않는다. 그럼에도 모바일 기획자라고 하면 모든 모바일 기기에 맞춰서 개발해야 한다는 UX 설계자를 만난다. 모바일 쪽에 깊은 지식이 있는 경우가 아니라 모바일 산업이 대세가 되니 갑자기 전직한 경우, 타블렛과 휴대폰은 화면 크기와 비율이 달라 UX 자체가 바뀌는데도 똑같이 보여야 한다는 생각을 가진 사람을 만난다. 심지어 facebook도 초창기엔 Android와 iOS UI를 다르게 갔었는데 그 사용경험(UX)은 동일하게 유지하였었다. Android와 iOS 모두 각 회사만의 철학이 있다. 그러나 하이브리드 앱을 제창하며 모두 회사가 정한 같은 디자인 이어야 한다는 생각이 있는 것 같다. 그 정도의 생각이 있다면, 운영체제도 만들고 성공한 모바일 폰 개발도 직접 하면 좋겠다는 생각이 많이 든다. 휴대폰 회사에서 제공하는 기본 API로 만들어야 운영체제 자체가 업그레이드 되었을 때 바뀐 UI 컨셉으로 앱의 통일성을 유지할 수 있다. 이 몇 줄 안되는 지식을 몰라 출발점부터 어긋나는 경우가 많았다. 그리고 iOS의 경우 화면 호출 방식이 다음과 같은 stack 방식이다.

 

즉, 유저 화면 이동을 고려할 때 TREE 형태로 구성하고 화면을 노드로 하면 유저 경로 탐색은 DFS를 먼저 고려해야 한다는 것이다. 굳이 정의하지 않아도 모든 ViewController는 NavigationBar를 기본 장착하고 있기 때문에 self 예약어로 접근 가능하다.


API

Swift_커스텀 네비게이션바

처음부터 모든 앱을 만든다면 네비게이션 바 관련 코드는 애플 개발자 사이트 혹은 스택오버플로만 참고하면 된다. 그러나 이미 커스텀네비게이션 바를 사용하는 경우 새로운 ViewController를 삽입할 때는 동작 코드가 궁금해 진다. 커스텀 네비게이션 바를 쓰는 뷰에 다음과 같이 viewWillAppear, viewWillDisappear 이벤트 콜백에 각각 해당 코드를 넣어, 커스텀 네비게이션 바를 유지 해준다.

 

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(true)
        self.navigationController?.navigationBar.isHidden = true
}



override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(true)
        self.navigationController?.navigationBar.isHidden = false
}


기본 네비게이션바 장착한 VC

스토리보드에 새로운 뷰를 만들고 연결한 뷰 컨트롤러에서 네비게이션 바를 달기 위한 코드를 살펴본다.

우선, 네비게이션바의 숨김 속성을 false로 해야 한다. 이번에는 메소드를 이용해 보자.

self.navigationController?.setNavigationBarHidden(false, animated: true)

 

제목 내용 변경 코드는 다음과 같다.

self.navigationItem.title = "First View Title"





navigationController?.setNavigationBarHidden(false, animated: true)

 

override func viewDidLoad() {

        super.viewDidLoad()

        self.navigationController?.navigationBar.isHidden = false

}




backItemTitle Icon changing

 

override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(true)
        
        let button1 = UIBarButtonItem(
            image:  imageLiteral(resourceName: "arrowThinRight"),
            style: .plain,
            target: self,
            action: #selector(self.backBtnTapped(_:))
        )
        self.navigationItem.leftBarButtonItem  = button1
    }
    
    @objc func backBtnTapped(_ sender: UIBarButtonItem) {
        self.navigationController?.popViewController(animated: true)
    }





http://minsone.github.io/programming/ios-custom-imageliteral-on-framework-bundle

imageLiteral



[실전] Obj-C_커스텀 네비게이션바

 

self.title = @"HP 점검";



    UIButton *back = [UIButton buttonWithType:UIButtonTypeCustom];
    UIImage *imgUp = [UIImage imageNamed:@"backward_arrow.png"];
    [back setBackgroundImage:imgUp forState:UIControlStateNormal];
    [back addTarget:self action:@selector(cancel:) forControlEvents:UIControlEventTouchUpInside];
    back.frame = CGRectMake(0, 0, imgUp.size.width, imgUp.size.height);
    UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithCustomView:back];
    self.navigationItem.leftBarButtonItem = cancelButton;



- (void)cancel:(id)sender
{
    [self.navigationController popViewControllerAnimated:NO];
}



'Swift & Python 실무 > {APP} SOCANNER APP' 카테고리의 다른 글

POD  (0) 2021.05.20
List  (0) 2021.05.18
오늘자 troubleshooting  (0) 2020.04.08
namedtuple  (0) 2020.04.08
python.c  (0) 2020.04.08

+ Recent posts