Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- HIG
- 애니메이션
- UITextView
- 리팩토링
- uiscrollview
- Observable
- MVVM
- Refactoring
- rxswift
- swiftUI
- clean architecture
- collectionview
- ios
- tableView
- Human interface guide
- 클린 코드
- Protocol
- 스위프트
- Xcode
- 리펙터링
- swift documentation
- map
- RxCocoa
- UICollectionView
- 리펙토링
- ribs
- combine
- Clean Code
- SWIFT
- uitableview
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] BaseViewController (Reachability, toast, dialog, loading - JGProgressHUD, setting, back pressed) 본문
iOS 응용 (swift)
[iOS - swift] BaseViewController (Reachability, toast, dialog, loading - JGProgressHUD, setting, back pressed)
jake-kim 2020. 12. 19. 16:53* 한 프로젝트에 프레임워크를 추가하여 구분하기: ios-development.tistory.com/217
Toast View
- UIView를 interface builder로 초기화 할 때 사용될 함수를 common extension에 정의
// CommonExtension/Common/UIView
public extension UIView {
func xibSetup() {
guard let view = loadViewFromNib(nib: type(of: self).className) else {
return
}
view.translatesAutoresizingMaskIntoConstraints = false
view.frame = bounds
addSubview(view)
view.fillToSuperview(withPadding: .zero)
}
func fillToSuperview(withPadding padding: UIEdgeInsets) {
anchor(top: superview?.topAnchor, leading: superview?.leadingAnchor, bottom: superview?.bottomAnchor, trailing: superview?.trailingAnchor, padding: padding)
}
func loadViewFromNib(nib: String) -> UIView? {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: nib, bundle: bundle)
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
func anchor(top: NSLayoutYAxisAnchor? = nil, leading: NSLayoutXAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil, trailing: NSLayoutXAxisAnchor? = nil, padding: UIEdgeInsets = .zero, size: CGSize = .zero, centerX: NSLayoutXAxisAnchor? = nil, centerY: NSLayoutYAxisAnchor? = nil) {
translatesAutoresizingMaskIntoConstraints = false
if let top = top {
topAnchor.constraint(equalTo: top, constant: padding.top).isActive = true
}
if let leading = leading {
leadingAnchor.constraint(equalTo: leading, constant: padding.left).isActive = true
}
if let bottom = bottom {
bottomAnchor.constraint(equalTo: bottom, constant: -padding.bottom).isActive = true
}
if let trailing = trailing {
trailingAnchor.constraint(equalTo: trailing, constant: -padding.right).isActive = true
}
if let centerX = centerX {
centerXAnchor.constraint(equalTo: centerX).isActive = true
}
if let centerY = centerY {
centerYAnchor.constraint(equalTo: centerY).isActive = true
}
if size.width != 0 {
widthAnchor.constraint(equalToConstant: size.width).isActive = true
}
if size.height != 0 {
heightAnchor.constraint(equalToConstant: size.height).isActive = true
}
}
}
// CommonExtension/Common/NSObject
public extension NSObject {
var className: String {
return String(describing: type(of: self))
}
class var className: String {
return String(describing: self)
}
}
- custom view생성: ToastMessageView.xib
- ToastMessageView.swift 생성
import Foundation
import UIKit
import CommonExtension
class ToastMessageView: UIView {
@IBOutlet weak var lblToastMessage: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
xibSetup()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
xibSetup()
}
}
- toast view로 띄울 수 있는 프레임워크 다운 (Toast_Swift)
pod 'Toast-Swift'
- BaseViewController
import UIKit
import Toast_Swift
class BaseViewController: UIViewController {
// init
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
}
// toast
func showToastView(message: String, point: CGPoint? = nil) {
let messageView = ToastMessageView(frame: CGRect(x: 0, y: 0, width: 248, height: 48))
messageView.layer.cornerRadius = messageView.bounds.height / 2
messageView.clipsToBounds = true
messageView.lblToastMessage.text = message
if let point = point {
view.showToast(messageView, point: point)
} else {
view.showToast(messageView)
}
}
}
- 테스트)
class ViewController: BaseViewController {
@IBAction func showToast(_ sender: Any) {
showToastView(message: "jake블로그(토스트 메세지)")
}
}
Loading
- cocoapods
pod 'JGProgressHUD'
- 보조적으로 사용될 Rx
pod 'RxSwift'
pod 'RxCocoa'
- 로딩 객체 hud 생성
// BaseViewController
lazy var hud: JGProgressHUD = {
let loader = JGProgressHUD(style: .dark)
return loader
}()
- loading함수 추가
// Loading
func showLoading() {
DispatchQueue.main.async {
self.hud.show(in: self.view, animated: true)
}
}
func hideLoading() {
DispatchQueue.main.async {
self.hud.dismiss(animated: true)
}
}
- Rx바인딩을 위한 Reactive extension
extension Reactive where Base: BaseViewController {
var showLoading: Binder<Void> {
return Binder(self.base) { (vc, show) in
vc.showLoading()
}
}
var hideLoading: Binder<Void> {
return Binder(self.base) { (vc, show) in
vc.hideLoading()
}
}
}
- 테스트)
// ViewController
@IBAction func showLoading(_ sender: Any) {
showLoading()
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.hideLoading()
}
}
// Reactive extension 테스트
@IBOutlet weak var btnLoading: UIButton!
let bag = DisposeBag()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
btnLoading.rx.tap.asDriver(onErrorRecover: {_ in .never()})
.drive(rx.showLoading)
.disposed(by: bag)
}
Setting화면으로 이동
- alert띄우는 showAlert함수를 extension 추가
import UIKit
// CommonExtension/Common/UIViewController
public extension UIViewController {
func showAlert(title: String? = "", message: String?, actionTitle: String = "OK", actionCallback: (() -> Void)? = nil) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: actionTitle, style: .default, handler: { (_) in
actionCallback?()
}))
present(alertController, animated: true, completion: nil)
}
}
- 알림 및 설정 화면으로 이동
// Alert and open setting
func showAlertAndSetting(alertTitle: String, actionTitle: String) {
showAlert(title: alertTitle, message: nil, actionTitle: actionTitle) { [weak self] in
self?.openSettingsInApp()
}
}
private func openSettingsInApp() {
if let url = URL(string: UIApplication.openSettingsURLString) {
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, completionHandler: nil)
}
}
}
- 테스트)
@IBAction func openSetting(_ sender: Any) {
showAlertAndSetting(alertTitle: "alert타이틀", actionTitle: "action타이틀(설정으로 이동)")
}
back pressed (화면전환)
- 이 함수를 호출하면 NavigationController로 push한 경우, present한 경우 모두, 현재 노출된 화면을 제거하도록 하는 함수
extension Reactive where Base: BaseViewController {
var backPressed: Binder<Void> {
return Binder(base) { vc, _ in
vc.view.endEditing(true)
if vc.navigationController != nil {
vc.navigationController?.popViewController(animated: true)
} else {
vc.dismiss(animated: true)
}
}
}
}
공통 dialog
- 공통 dialog 추가: ios-development.tistory.com/244
- BaseViewController에 Reactive extension 추가
var showServerErrorDialog: Binder<String> {
return Binder(base) { (vc, errorMsg) in
debugPrint(errorMsg)
let dialogVC = DialogBuilder.serverErrorDialog()
vc.present(dialogVC, animated: true)
}
}
- 테스트)
// ViewController
btnDialog.rx.tap.asDriver(onErrorRecover: { _ in .never()})
.map { "server error in viewWillAppear" }
.drive(rx.showServerErrorDialog)
.disposed(by: bag)
network 상태 체크 - Reachability
- cocoapods
pod 'RxReachability'
- 네트워크 상태 바인딩 할 변수와 바인딩 함수 추가
// BaseViewController
var reachability: Reachability? // 이 변수를 바인딩 하여 네트워크 상태 점검
let isConnected: PublishSubject<Bool> = .init() // 사용하는 입장에서 network 상태를 보고 사용할 용도의 변수
var networkListener: NetworkListener = .off { // 네트워크 체크가 필요할 시 VC에서 netwrokListener = .on으로 설정
didSet {
if networkListener == .on {
setupReachabilityBindings()
}
}
}
private func setupReachabilityBindings() {
// 변화 감지 - 메세지 출력
reachability?.rx.reachabilityChanged
.subscribe(onNext: { reachability in
print("reachability: changed => \(reachability.connection)")
})
.disposed(by: bag)
// 네트워크가 꺼질 때 에러 dialog띄우는 부분
reachability?.rx.isReachable
.filter { !$0 }
.map { _ in "server Error" }
.asDriver(onErrorRecover: { _ in .never()})
.drive(rx.showServerErrorDialog)
.disposed(by: bag)
// 네트워크 연결한 경우, isConnected에 true로 저장
reachability?.rx.isConnected
.map { _ in true }
.bind(to: isConnected)
.disposed(by: bag)
// 네트워크 끊긴 경우, isConnected에 false로 저장
reachability?.rx.isDisconnected
.map { _ in false }
.bind(to: isConnected)
.disposed(by: bag)
}
- reachability변수 초기화
// BaseViewController
override func viewDidLoad() {
super.viewDidLoad()
reachability = Reachability()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
try? reachability?.startNotifier()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
reachability?.stopNotifier()
}
- 테스트)
// ViewController
override func viewDidLoad() {
super.viewDidLoad()
networkListener = .on
}
* 전체 소스코드: github.com/JK0369/BaseViewController
'iOS 응용 (swift)' 카테고리의 다른 글
[iOS - swift] 정규식 (Regex) (0) | 2021.01.05 |
---|---|
[iOS - swift] SideMenu 사용 방법 (SideMenu 프레임워크) (0) | 2020.12.19 |
[iOS - swift] 서버 푸시 (remote notification), APNs (Apple Push Notification service) (0) | 2020.12.17 |
[iOS - swift] 앱을 첫 번째 실행 시, keychain정보를 삭제하는 방법 (0) | 2020.12.17 |
[iOS - swift] String에 substring, removeAt, insertAt 구현 (0) | 2020.12.10 |
Comments