본문 바로가기

학습 노트/iOS (2021)

123. Status Bar and Home Indicator

Status Bar

Status Bar는 화면 상단에서 현재 시간과 시스템 상태를 표시하는 역할을 한다.
Status Bar에 표시되는 내용은 기본적으로 검은색을 표시하도록 되어있고,
어두운 배경에서는 흰색으로 표시되도록 Status Bar Style을 직접 설정해야 한다.

Status Bar Style은 두 가지로 고정되어있고, 색을 직접 지정하는 것은 불가능하다.
이를 설정하기 위해서는 앱에서 공통적으로 사용할 Style을 설정하거나, View Controller에서 사용할 Style을 개별적으로 설정한다.

사용할 Scene은 위와 같다.

현재 상태의 Status Bar가 기본 스타일이다.

Project의 Deployment Info 중 Status Bar Style을 통해 앱 전체에 공통으로 적용되는 스타일을 지정할 수 있다.

 

 

하지만 설정을 바꾸더라도 Status Bar는 변하지 않는다.

Status Bar Style은 공통적으로 사용되는 Style과 View Controller에서 개별적으로 사용되는 Style을 설정할 수 있다.
둘 다 활성화가 기본 상태이고, View Controller의 기본 Style이 Default이다.
즉, View Controller 쪽의 Style이 우선순위가 높기 때문에 변화가 없다.

프로젝트의 info에서 'View controller-based status bar appearance'를 추가하고,
값이 NO로 되어있는지 확인한다.
지금 상태가 View Controller의 Style을 무시하고 공통적으로 사용되는 Style을 표시한다.

 

왼쪽이 Dark Content이고, 오른쪽이 Light Content이다.
App 전체에서 Defualt 설정을 그냥 사용하면 문제가 없다.
하지만 Light Content나 Dark Content를 고정으로 사용해야 한다면
Project 설정에서 값을 변경하고 info에 'View controller-based status bar appearance'를 추가해야 한다.

 

기본 상태에선 왼쪽의 밝은 화면에선 문제없지만 오른쪽의 어두운 화면에선 눈에 잘 띄지 않는다.
만약 지금처럼 어두운 화면과 밝은 화면이 모두 존재한다면 Status Bar의 Style을 갱신해 가독성을 높여야 한다.

Status Bar Style Update 하기

//
//  DarkViewController.swift
//  ViewControllerPractice
//
//  Created by Martin.Q on 2021/11/10.
//

import UIKit

class DarkViewController: UIViewController {
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}
}

해당 Scene에 연결된 코드는 위와 같다.

override func viewDidLoad() {
	super.viewDidLoad()
	
	preferredStatusBarStyle
}

UIViewController 클래스에는 preferredStatusBarStyle 속성이 선언되어있고,
Default가 기본값으로 정해져 있다.
새로운 화면이 표시되면 해당 속성이 반환하는 값에 따라서 Status Bar의 Style이 결정된다.

//
//  DarkViewController.swift
//  ViewControllerPractice
//
//  Created by Martin.Q on 2021/11/10.
//

import UIKit

class DarkViewController: UIViewController {
	
	override var preferredStatusBarStyle: UIStatusBarStyle {
		return .lightContent
	}
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}
}

이렇게 해당 속성을 override 해 반환 값을 변경해 주면 해당 Style로 표시된다.

정상적으로 값이 상태가 업데이트된다.

Landscape mode에서 Status Bar 표시하기

iOS13 이후 Landscape mode에서 Status Bar가 강제로 숨겨지도록 변경됐다. 

Status Bar는 항상 표시되지만 예외가 존재한다.
iOS에서 Landscape 모드로 전환되면 Vertical Size가 Compact로 변경된다.
또한 Status Bar가 표시되지 않는다.
만약 항상 표시하고 싶다면 연관된 속성을 Override 해야 한다.

//
//  DarkViewController.swift
//  ViewControllerPractice
//
//  Created by Martin.Q on 2021/11/10.
//

import UIKit

class DarkViewController: UIViewController {
	
	override var preferredStatusBarStyle: UIStatusBarStyle {
		return .lightContent
	}
	
	override var prefersStatusBarHidden: Bool {
		return false
	}
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}
}

prefersStatusBarStyle 속성에서 false를 반환하면 항상 Status Bar가 표시된다.

Toggle Visibillity 버튼을 선택하면 Status Bar를 Toggle 하도록 구현하고,
Toggle Style 버튼을 선택하면 Status Bar의 Style을 변경하고 Background Color를 변경하도록 구현한다.

//
//  StatusBarViewController.swift
//  ViewControllerPractice
//
//  Created by Martin.Q on 2021/11/10.
//

import UIKit

class StatusBarViewController: UIViewController {
	
	@IBAction func visibillityAction(_ sender: Any) {
	}
	
	@IBAction func styleAction(_ sender: Any) {
	}
	
	
	@IBAction func unwindToStatusBarMain(_ unwindSegue: UIStoryboardSegue) {
	}
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}
}

사용할 코드는 위와 같다.

@IBAction func visibillityAction(_ sender: Any) {
	prefersStatusBarHidden = !prefersStatusBarHidden
}

하지만 값을 변경하려고 하면 읽기 전용 속성으로 사용할 수 없다는 오류가 발생한다.
말 그대로 읽기 전용이기 때문에 값을 저장할 수 없다.
따라서 임시 속성을 생성하고, 이를 반환하는 방식으로 구현해야 한다.

class StatusBarViewController: UIViewController {
	
	var hidden = false {
		didSet {
			setNeedsStatusBarAppearanceUpdate()
		}
	}
	
	override var prefersStatusBarHidden: Bool {
		return hidden
	}
	
	@IBAction func visibillityAction(_ sender: Any) {
		hidden = !hidden
	}
	
	@IBAction func styleAction(_ sender: Any) {
	}
	
	
	@IBAction func unwindToStatusBarMain(_ unwindSegue: UIStoryboardSegue) {
	}
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}
}

 

var hidden = false {
	didSet {
		setNeedsStatusBarAppearanceUpdate()
	}
}

setNeedsStatusBarAppearanceUpdate 메서드를 호출해 Status Bar의 속성이 변경되었음을 알려 줘야 한다.

class StatusBarViewController: UIViewController {
	
	var hidden = false {
		didSet {
			setNeedsStatusBarAppearanceUpdate()
		}
	}
	var style = UIStatusBarStyle.default
	
	override var prefersStatusBarHidden: Bool {
		return hidden
	}
	
	override var preferredStatusBarStyle: UIStatusBarStyle {
		return style
	}
	
	@IBAction func visibillityAction(_ sender: Any) {
		hidden = !hidden
	}
	
	@IBAction func styleAction(_ sender: Any) {
		style = style == .default ? .lightContent : .default
		
		setNeedsStatusBarAppearanceUpdate()
		
		let color = style == .default ? UIColor.white : UIColor.darkGray
		UIView.animate(withDuration: 0.3) {
			self.view.backgroundColor = color
		}
	}
	
	
	@IBAction func unwindToStatusBarMain(_ unwindSegue: UIStoryboardSegue) {
	}
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}
}

Toggle Style도 같은 방식으로 구현한다.
단 이때는 속성의 값이 default, darkContent, lightContent 세 가지이므로 주의하자.

//
//  WhiteViewController.swift
//  ViewControllerPractice
//
//  Created by Martin.Q on 2021/11/10.
//

import UIKit
	
class WhiteViewController: UIViewController {
	
	var style = UIStatusBarStyle.default
	
	override var preferredStatusBarStyle: UIStatusBarStyle {
		return style
	}
	
	@IBAction func styleAction(_ sender: Any) {
		style = style == .default ? .lightContent : .default
		
		setNeedsStatusBarAppearanceUpdate()
		
		let color = style == .default ? UIColor.white : UIColor.darkGray
		UIView.animate(withDuration: 0.3) {
			self.view.backgroundColor = color
		}
	}
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}

}

Toggle Style 구현은 WhiteViewController에도 똑같이 적용한다.

preferredStatusBarHidden 속성은 Notch가 없는 기종에서만 결과를 확인할 수 있다.
모두 제대로 동작하는 듯 하지만, Style을 변경했을 때 Status Bar의 Style이 바뀌지 않는 것을 확인할 수 있다.

하지만 같은 코드를 적용한 WhiteViewController에선 Status Bar의 Style도 분명 적용되고 있다.

앞서 Status Bar Style이 변경되지 않은 이유는 Navigation Controller에 Embed 된 상태이기 때문이다.
이 경우엔 Navigation Controller가 Status Bar Style을 결정한다.
Status Bar View Controller 같은 Child View Contoller에서 결정하도록 하고 싶다면
Navigation Controller 속성을 Override 해야 한다.

//
//  CustomStatusBarViewController.swift
//  ViewControllerPractice
//
//  Created by Martin.Q on 2021/11/10.
//

import UIKit

class CustomStatusBarViewController: UINavigationController {
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}
}

UINavigationController를 상속받는 클래스 파일을 생성하고

Navigation Controller의 Custom Class로 지정한다.

UINavigationController는 UIViewController를 상속한 클래스이다.
따라서 Status Bar를 표시할 때 Child에서 반환한 값이 아닌 Navigation Controller에서 반환하는 값이 사용된다.

Child View의 반환 값을 사용하도록 하려면 두 가지 메서드를 override 해야 한다.

override var childForStatusBarHidden: UIViewController? {
	return topViewController
}



override var childForStatusBarStyle: UIViewController? {
	return topViewController
}

childForStatusBarStyle속성은 Style을 설정할 Child를 지정한다.
마찬가지로 topViewController를 반환해 마지막에 표시된 Child의 반환 값을 사용하도록 구현한다.

setNeedsStatusBarAppearanceUpdate()

setNeedsStatusBarAppearanceUpdate 메서드는 변경사항을 적용하지만 Animation을 적용하진 않는다.

var hidden = false {
	didSet {
		UIView.animate(withDuration: 0.3) {
			self.setNeedsStatusBarAppearanceUpdate()
		}
	}
}

이렇게 animte 메서드의 animation 블록에서 실행하기만 하면 된다.

override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
	return .slide
}

사용할 Animation은 preferredStatusBarUpdateAnimation 속성을 통해 설정한다.
기본 값은 fade이고, 적용하지 않을 때는 none을, 슬라이드 Animation은 slide를 반환하면 된다.

@IBAction func styleAction(_ sender: Any) {
	style = style == .default ? .lightContent : .default
	
	let color = style == .default ? UIColor.white : UIColor.darkGray
	UIView.animate(withDuration: 0.3) {
		self.view.backgroundColor = color
		setNeedsStatusBarAppearanceUpdate()
	}
}

Style을 변경하는 부분도 Animation 블록으로 옮겼다.

Animation을 적용하자 Notch가 존재하는 iPhone에도 StatusBarHidden이 적용된다.
또한, Style을 변경할 때도 자연스럽게 Animation이 적용된 것을 볼 수 있다.

 

Home Indicator

Home Indicator는 iPhone X 이상의 기기에서 홈버튼이 없는 기기들에 해당하는 부분이다.

iPhone X 이상의 기기들에는 홈버튼이 없고, 화면 아래의 Bar가 홈버튼을 대체한다.
해당 Bar를 Home Indicator라고 부른다.

Home Indicator를 완전히 제어할 수는 없지만 iOS가 표시 여부를 자동으로 선택하도록 선택할 수는 있다.

//
//  HomeIndicatorViewController.swift
//  ViewControllerPractice
//
//  Created by Martin.Q on 2021/11/11.
//

import UIKit

class HomeIndicatorViewController: UIViewController {
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}

}

사용할 Scene과 코드는 위와 같다.

override var prefersHomeIndicatorAutoHidden: Bool {
	return true
}

prefersHomeIndicatorAutoHidden 속성은 Home Indicator의 자동 숨김 여부를 설정한다.
기본값은 false이고, 항상 표시한다.
true를 반환하도록 수정하면 iOS가 표시 여부를 자동으로 판단한다.
해당하는 Scene은 Navigation Controller에 Embed 되어있기 때문에 마찬가지로 한 가지 작업이 더 필요하다.

//
//  CustomStatusBarViewController.swift
//  ViewControllerPractice
//
//  Created by Martin.Q on 2021/11/10.
//

import UIKit

class CustomStatusBarViewController: UINavigationController {
	override var childForStatusBarHidden: UIViewController? {
		return topViewController
	}
	
	override var childForStatusBarStyle: UIViewController? {
		return topViewController
	}
	
	override var childForHomeIndicatorAutoHidden: UIViewController? {
		return topViewController
	}
}

childForHomeIndicatorAutoHidden 속성은 Home Indicator Hidden 상태를 설정할 Child를 지정한다.
topViewController를 반환하도록 해 항상 마지막에 표시된 Child의 반환 값을 사용해 Home Indicator를 숨긴다.

자동으로 사라졌다가 다시 나타나는 것을 볼 수 있다.