[iOS 앱] 우리만의 디데이

설명

스크린샷

버전 1.0

버전 1.1

진행 과정

데이터 저장소 선택

  • UserDefaults
    • 앱 첫 시작 여부 확인
    • 위젯과 공유하기 위해 이미지 주소, 날짜, 문구 저장
  • Realm
    • 날짜와 일정 이벤트 저장
  • CoreData를 사용하다가 직관적이지 않고 사용하기에 불편해서 Realm으로 전환

앱 처음 실행 시 뷰 컨트롤러 지정

// SceneDelegate.swift

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        
    guard let scene = (scene as? UIWindowScene) else { return }
    window = UIWindow(windowScene: scene)
    
    // UserDefaults의 값 활용
    if LocalStorage().isFirstLaunch() {
        let nav = UINavigationController(rootViewController: FirstLaunchController())
        window?.rootViewController = nav
    } else {
        window?.rootViewController = MainTabController()
    }
    window?.makeKeyAndVisible()
}
FirstLaunchControllerMainTabController

iOS15 네비게이션바, 탭바 투명 해결

// AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
    if #available(iOS 15.0, *) {
        let navigationBarAppearance = UINavigationBarAppearance()
        navigationBarAppearance.configureWithDefaultBackground()
        navigationBarAppearance.backgroundColor = UIColor.mainColor
        navigationBarAppearance.titleTextAttributes = [.foregroundColor: UIColor.white]
        UINavigationBar.appearance().standardAppearance = navigationBarAppearance
        UINavigationBar.appearance().compactAppearance = navigationBarAppearance
        UINavigationBar.appearance().scrollEdgeAppearance = navigationBarAppearance

        let tabBarAppearance = UITabBarAppearance()
        tabBarAppearance.configureWithDefaultBackground()            
        UITabBar.appearance().standardAppearance = tabBarAppearance
        UITabBar.appearance().scrollEdgeAppearance = tabBarAppearance
    }
    
    IQKeyboardManager.shared.enable = true
    IQKeyboardManager.shared.enableAutoToolbar = false
    
    return true
}
스크롤해야 bar 표시navigationBarAppearance 설정

기념일 테이블뷰 MVVM 활용

extension EventController {
    // 해당 viewModel은 EventViewModel로 eventsCount()로 오늘 날짜에 대한 이벤트 100일, 1년 단위의 배열의 count를 return
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return viewModel.eventsCount()
    }
    
    // 해당 배열의 event를 다시 EventCellViewModel로 작성 후 EventCell View에 전달해주며 해당 View에서 UI update가 이루어진다.
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: EventCell.identifier, for: indexPath) as? EventCell else {
            return UITableViewCell()
        }
        
        let event = viewModel.event(at: indexPath.row)
                
        let eventCellViewModel = EventCellViewModel(event: event)
        cell.viewModel = eventCellViewModel
        
        cell.selectionStyle = .none
        
        return cell
    }
}

Delegate 패턴 활용

  • 일정 추가, 삭제, 편집 기능
  • 설정에서 기념일 변경, 이미지 변경, 문구 수정
  • 데이터 전달 및 최신화

일정 편집 기능

  • 일정은 Realm에 CalendarEvent 클래스로 저장되고 불려진다.
  • 해당 클래스로 전달을 하니까 수정을 하고 확인 버튼을 눌렀을 때 수정되어야 하는데 그렇지 않아도 수정되는 문제 발생
  • Realm에서 CalendarEvent 읽기 → CalendarEventStrut 생성 후 데이터 적용 및 TodoController, TodoAddController 전달 → 편집할 때 해당 id를 이용해 Realm update

navigation bar 문제

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)

    delegate?.todoControllerDidBack(self)
}
  • TodoController에서 CalendarController로 돌아갈 경우 CalendarController에서 해당 기능을 감지해서 reload를 실행
  • 이걸 뷰가 사라지기전에 delegate로 전달하니까 첫 번째 그림처럼 이미 navigation bar는 변경되어 있는 문제 발생
  • viewDidDisappear로 변경으로 해결
viewWillDisappearviewDidDisappear

백그라운드 포어그라운드 디데이 최신화

// HomeController.swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    
    checkTodayCount()
    
    if #available(iOS 13.0, *) {
        NotificationCenter.default.addObserver(self, selector: #selector(checkTodayCount), name: UIScene.willEnterForegroundNotification, object: nil)
    } else {
        NotificationCenter.default.addObserver(self, selector: #selector(checkTodayCount), name: UIApplication.willEnterForegroundNotification, object: nil)
    }
}

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    
    NotificationCenter.default.removeObserver(self)
}

@objc func checkTodayCount() {
    let newTodayCount = EventManager.shared.getTodayCount()
    
    if todayCount != newTodayCount {
        todayCount = newTodayCount
    }
}
  • 현재 변수 todayCount와 newTodayCount가 다르다면 날짜가 바뀐 것이기 때문에 앱이 백그라운드 상태에서 다시 접근한 것이라면 UI를 update

앨범 이미지 불러오기

PHPickerControllerCropViewController

위젯

이미지 폴더 위치 변경

  • 참고 : https://nsios.tistory.com/175
  • 메인앱과 위젯의 폴더 위치가 다르다.
  • 공유할 수 있는 폴더로 설정 후 해당 위치에 이미지 파일을 저장
guard let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.OurDday") else { return }
        
let imageUrl = containerURL.appendingPathComponent(imageName)

이미지 reload 문제

  • 실제 기기와 시뮬레이터에서 사용 시 이미지 변경 시 바로 적용되지 않는 문제 발생
  • 실제 기기에서 png파일은 잘 동작하고 아이폰 고유의 이미지 파일은 동작을 잘 안하는 현상 발견
  • 참고 : https://zeddios.tistory.com/1004
  • pngData()를 사용했었는데 이미지가 크다는 블로그 글 발견
  • jpegData(compressionQuality: 1)로 가장 고품질로 설정
  • 설정 후 이미지 변경 시 바로 적용

앱 배포

앱스토어 스크린샷 만들기

버전 1.1 수정사항

SideMenu 라이브러리 사용

  • 설정 부분이 따로 탭 화면으로 분리되어 있는 모습에서 홈 화면에 같이 구성함으로써 변경하면 바로 확인할 수 있어서 홈 탭화면을 다시 선택해서 확인해야하는 번거러움이 사라졌다.

하루가 지났을 때 업데이트

  • 위에서 백그라운드에서 포어그라운드 접근 시 사용하는 메소드가 있었다.
  • UIApplication.significantTimeChangeNotification을 발견
  • 날짜가 변경되는 것이 파악되면 homeView의 UI 업데이트 메소드를 호출

변경 전

// HomeController.swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    
    checkTodayCount()
    
    if #available(iOS 13.0, *) {
        NotificationCenter.default.addObserver(self, selector: #selector(checkTodayCount), name: UIScene.willEnterForegroundNotification, object: nil)
    } else {
        NotificationCenter.default.addObserver(self, selector: #selector(checkTodayCount), name: UIApplication.willEnterForegroundNotification, object: nil)
    }
}

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    
    NotificationCenter.default.removeObserver(self)
}

@objc func checkTodayCount() {
    let newTodayCount = EventManager.shared.getTodayCount()
    
    if todayCount != newTodayCount {
        todayCount = newTodayCount
    }
}

변경 후

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self, selector: #selector(handleNotificationTimeChange), name: UIApplication.significantTimeChangeNotification, object: nil)
}

@objc func handleNotificationTimeChange() {
    homeView.setUser(viewModel.user())
}

색상 변경 기능

UIColorPickerController

  • iOS 14 이후부터 사용 가능

NotificationCenter

  • 색상이 변경되면 모든 tintColor 변경 알림
  • CalendarView의 select Color 변경
  • EventController의 디데이 색상 변경
extension SideMenuController: UIColorPickerViewControllerDelegate {
    @available(iOS 14.0, *)
    func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController) {
        localStorage.setColor(color: viewController.selectedColor)
        updateWidgetCenter()

        // notification center 등록
        NotificationCenter.default.post(name: Notification.Name.colorChange, object: nil)
        
        dismiss(animated: true)
    }
}
override func viewDidLoad() {
    super.viewDidLoad()

    configureNotification()
}

private func configureNotification() {
    NotificationCenter.default.addObserver(self, selector: #selector(handleNotificationColorChange), name: Notification.Name.colorChange, object: nil)
}

@objc func handleNotificationColorChange() {
    homeView.setUser(viewModel.user())
    navigationController?.navigationBar.tintColor = LocalStorage().colorForKey()
}

좋은 웹페이지 즐겨찾기