[SeSAC] August 6, 2023

✔︎ 오늘의 정리

  • 첫 화면 실행 시 분기 전환
  • View의 backgroundColor / Label의 textColor 한번에 설정하기
  • unexpected nil window in... 오류 해결
  • 코드로 backgroundColor에 투명도 주기
  • textView editing 막기
  • alertAction에 함수 넣기(handler)
  • keyboard가 올라옴에 따라 view도 같이 올리고 내리기

 


Damagochi 과제 화면 구성

etc-image-0

너무 헷갈리길래 그려봤다.
 
 
 

 

첫 화면 실행 시 분기 전환

//
//  SceneDelegate.swift
//  Damagochi
//
//  Created by yeoni on 2023/08/05.
//

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        
        UserDefaults.standard.set(0, forKey: "chooseDamagochi")
        
        
        // 첫 화면을 띄워주는 친구
        guard let scene = (scene as? UIWindowScene) else { return }
        window = UIWindow(windowScene: scene)
        
        // 처음 실행시
        let chooseDamagochi = UserDefaults.standard.integer(forKey: "chooseDamagochi") // 0
        
        // 첫 화면 구성 코드
        let sb = UIStoryboard(name: "Main", bundle: nil)
        if chooseDamagochi == 0 {
            guard let vc = sb.instantiateViewController(withIdentifier: "StartViewController") as? StartViewController else { return }
            let nav = UINavigationController(rootViewController: vc)
            window?.rootViewController = nav
        } else {
            guard let vc = sb.instantiateViewController(withIdentifier: "MainViewController") as? MainViewController else { return }
            window?.rootViewController = vc
        }
        
        window?.makeKeyAndVisible()
        // 다마고치 고르고 실행 시
        
    }

    func sceneDidDisconnect(_ scene: UIScene) {
        // Called as the scene is being released by the system.
        // This occurs shortly after the scene enters the background, or when its session is discarded.
        // Release any resources associated with this scene that can be re-created the next time the scene connects.
        // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
    }

    func sceneDidBecomeActive(_ scene: UIScene) {
        // Called when the scene has moved from an inactive state to an active state.
        // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
    }

    func sceneWillResignActive(_ scene: UIScene) {
        // Called when the scene will move from an active state to an inactive state.
        // This may occur due to temporary interruptions (ex. an incoming phone call).
    }

    func sceneWillEnterForeground(_ scene: UIScene) {
        // Called as the scene transitions from the background to the foreground.
        // Use this method to undo the changes made on entering the background.
    }

    func sceneDidEnterBackground(_ scene: UIScene) {
        // Called as the scene transitions from the foreground to the background.
        // Use this method to save data, release shared resources, and store enough scene-specific state information
        // to restore the scene back to its current state.
    }


}

SceneDelegate에서 한다.
UserDefault 값을 이용하여 분기를 전환하며, UIWindow를 이용하여 실행한다.
 
 
 
 
 
 
 
 
 
 

View의 backgroundColor / Label의 textColor 한번에 설정하기

//
//  AppDelegate.swift
//  Damagochi
//
//  Created by yeoni on 2023/08/05.
//

import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {



    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        UINavigationBar.appearance().tintColor = .fontColor
        UINavigationBar.appearance().barTintColor = .fontColor
        UILabel.appearance().textColor = .fontColor
        UIView.appearance().backgroundColor = .bgColor
        return true
    }

    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }


}

AppDelegate에서 일괄적으로 색깔을 설정해 주면 앱 실행 시 지정해준 것의 색깔이 일괄적으로 변경된다.
 
 
 
 
 
 
 
 

아무리 눌러도 Description이 안 올라갈 때

각 뷰컨트롤러끼리 객체를 주고 받는데, 변경하는 값이 실제 값이 아니라서 그랬다.
무슨 말이냐하면, 뷰 컨트롤러에서 해당 데이터를 변수로 만들어 담는다면 그 변수는 해당 데이터 자체가 아니라 그의 복사본을 담게 된다.
그 복사본을 이리저리 바꾸어도, 해당 데이터는 변경되지 않는다는 말이다.
따라서, 단순히 데이터를 출력하는 것이 아니라 실제 데이터를 변경해 주고 그 데이터를 이용하여 무언가 해야 할 때는 외부 모듈에 변수를 만들어 그 변수를 옮기기 전 뷰 컨트롤러에서 저장하고, 이후 다른 뷰 컨트롤러에서 그 변수의 데이터값을 가져오는 것이 좋다.
 
이때, 해당 데이터를 뷰 컨트롤러 안에서 직접적으로 값을 변경하는 게 좋을까 아니면 그 값을 복사한 변수를 만든 후에 그 변수를 이용하여 값을 변경하고, 뷰가 꺼질 때 데이터에 바뀐 값을 저장하는 게 좋을까?
개인적인 생각으로는 둘이 비슷할 거 같은데, 왠지 후자가 더 좋지 않을까 싶다. 모든 뷰 컨트롤러에서 직접적으로 데이터값을 변경하게 된다면 오류가 났을 때 어느 곳에서 오류가 났는지 찾기 힘들 것이다. 뭔가 디버깅에서뿐만 아니라 데이터를 저장하고 가져오는 데 시간이 조금 더 걸리게 되니까 후자가 낫지 않을까 싶다.
 
 
 
 
 
 
 
 
 
 

오류 해결

[Touch] unexpected nil window in __sendSystemGestureLatentClientUpdate, _windowServerHitTestWindow: <UIRemoteKeyboardWindow: 0x1458e3000; frame = (0 0; 393 852); opaque = NO; autoresize = W+H; layer = <UIWindowLayer: 0x600000306b20>>, touch:<UITouch: 0x14840a1d0> phase: Stationary tap count: 1 force: 0.000 window: (null) responder: (null)

진짜 해결하고 싶어 죽는 줄 알았다
 
문제 상황을 적어 보자면, 메인 화면의 TextField를 클릭하여 입력하려고 하면 빈 화면이 뜨면서 앱이 동작하지 않았다.
말 그대로 unexpected nil window... ... ^_^
검색해 보니 iOS8 업데이트가 되면서 생긴 오류라며 AppDelegate에 무슨 코드를 추가해 주기만 하면 된다! 였는데 내 상황에는 전혀 맞지 않았다.
그야 당근 해당 코드는 이제 AppDelegate가 아닌 SceneDelegate에서 담당할 것 같았기 때문이다. (사유: window...)
 
멘토님께 실쩍 여쭤보기는 싫구 아니!! 분명 어디서 문제가 생긴 거 같은데 도통 보이지는 않고 검색해 봐도 모르겠어서 일단 AppDelegate쪽과 SceneDelegate 코드를 작성한 걸 차례차례 없애봤다가 이건 문제없군.. 하구 다시 살리고 또 다른 코드를 지워보고 살려보고 반복했다.
 
그러다가!!!!!!!!
결국 찾았다.
 
 
문제가 됐던 코드는 AppDelegate에 있었다.

class AppDelegate: UIResponder, UIApplicationDelegate {



    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        UINavigationBar.appearance().tintColor = .fontColor
        UINavigationBar.appearance().barTintColor = .fontColor
        UILabel.appearance().textColor = .fontColor
        UIView.appearance().backgroundColor = .bgColor
        return true
    }
    
    ... ...
    
}

뭐가 문제인지 몰랐는데, 문제는 UINavigationBar였다. 해당 Appearance를 주석처리하니 잘 돌아가더라.
날린 시간이 너무 허망했지만.............
그래도 나중에 또 같은 오류가 나면 잘 해결할 수 있을 것 같다.
 
 
현재 AppDelegate 안의 코드는 이렇다.

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        UILabel.appearance().textColor = .fontColor
        return true
    }

 
UIView 코드까지 삭제해 준 이유는 DetailView를 팝업 형식으로 띄울 때 왜? 배경이 투명하게 보이지 않지?!?!?!?!? 했는데
알고보니 내가 설정해 준 친구 때문이었다.
 
AppDelegate에서 전체 설정을 건드리는 건 잘해야 하는구나...... 하구 새삼 느꼈다.
이거 땜에 삽질한 시간이 또........
 
아무튼간 그래서
 
 
 
 
 
 

코드로 backgroundColor에 투명도 주기

해당뷰이름.backgroundColor = .black.withAlphaComponent(0.4)

이런 식으로 하면 댄다.
 
 
 
 

etc-image-1etc-image-2

전체 View의 backgroundColor를 .black으로만 줬을 때와
withAlphaComponent method를 이용했을 때이다.
 
 

bgView.backgroundColor = .black // 첫 번째 사진
bgView.backgroundColor = .black.withAlphaComponent(0.4) // 두 번째 사진

 
 
 
 
 
 
 

textView Edit 불가능하게 하는 법

textView.isEditable = false

 
 
 
 
 
 
 
 

alertAction에 함수 넣기(handler)

UIAlertAction의 생성자를 보면,

etc-image-3

이런 식으로 되어 있다.
title과 style parameter는 필수적으로 작성해 주어야 하고, handler는 기본적으로 nil로 되어 있어 필요한 경우에만 넣어주면 된다.
handler를 잘 쓰지 않아 몰랐는데, alertAction을 눌렀을 때 특정 함수가 실행되도록 하려면 handler에 클로저를 넣으면 된다.
 
예시를 가져오겠다...

etc-image-4
    func deleteData() {
        let alert = UIAlertController(title: "초기화하시겠습니까?", message: "모든 데이터가 삭제됩니다!", preferredStyle: .alert)
        let cancel = UIAlertAction(title: "취소", style: .default)
        
        
        let accept = UIAlertAction(title: "확인", style: .default) { _ in
            user.myDamagochi = Damagochi(type: .setting, eatBab: 0, drinkWater: 0)
            
            let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene
            let SceneDelegate = windowScene?.delegate as? SceneDelegate
            
            let sb = UIStoryboard(name: "Main", bundle: nil)
            guard let vc = sb.instantiateViewController(withIdentifier: "StartViewController") as? StartViewController else { return }
            let nav = UINavigationController(rootViewController: vc)
            
            UserDefaults.standard.set(1, forKey: "currentStatus")
            SceneDelegate?.window?.rootViewController = nav
            SceneDelegate?.window?.makeKeyAndVisible()
        }
        alert.addAction(cancel)
        alert.addAction(accept)
        present(alert, animated: true)
    }

확인 버튼을 누르면 데이터를 삭제하는 코드이다.
 
 
 
 
 
적용한 alert 방식은 완전히 같지만, 클로저 내의 코드만 다른 예시를 하나 더 가져오자면...

etc-image-5
    func changeDamagochi() {
        let alert = UIAlertController(title: "다마고치를 변경하시겠어용?", message: "밥과 물을 준 횟수는 그대로 유지돼요!", preferredStyle: .alert)
        let cancel = UIAlertAction(title: "취소", style: .default)
        
        
        let accept = UIAlertAction(title: "확인", style: .default) { _ in
            let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene
            let SceneDelegate = windowScene?.delegate as? SceneDelegate
            
            let sb = UIStoryboard(name: "Main", bundle: nil)
            guard let vc = sb.instantiateViewController(withIdentifier: "StartViewController") as? StartViewController else { return }
            let nav = UINavigationController(rootViewController: vc)
            
            UserDefaults.standard.set(1, forKey: "currentStatus")
            user.status = .changeDamagochi
            SceneDelegate?.window?.rootViewController = nav
            SceneDelegate?.window?.makeKeyAndVisible()
        }
        alert.addAction(cancel)
        alert.addAction(accept)
        present(alert, animated: true)
    }

다마고치를 바꾸는 코드이다.
 
 
 
 
 
 
 
 
 

키보드 올리고 내리기

class MainViewController: UIViewController {
    
	... ...
    
    @IBOutlet weak var babTextField: UITextField!
    @IBOutlet weak var waterTextField: UITextField!
    
    ... ... 
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        ... ...
        
        babTextField.delegate = self
        waterTextField.delegate = self
        
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
        
    }
    
    ... ...
    
}

// keyboard 올리고 내리고에 따라서 view도 같이 올리고 내리기
extension MainViewController: UITextFieldDelegate {
    
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
    
    
    @objc
    func keyboardWillShow(_ sender: Notification) {
        self.view.frame.origin.y = -150 // Move view 150 points upward
    }
    
    
    @objc
    func keyboardWillHide(_ sender: Notification) {
        self.view.frame.origin.y = 0 // Move view to original position
    }
}

해당 뷰컨트롤러에 UITextFieldDelegate 추가 후, 원하는 TextField에 delegate 속성을 넣어준다.
이후, 위 방식처럼 넣어주면 댄다.. ^_^ 6
 
코드는 아래 블로그를 참조했다.
 
https://zeddios.tistory.com/127

iOS ) 키보드에 의해 TextField가 가려지는 현상 해결 (Swift)

안녕하세요 :) Zedd입니다. 오늘은 TextField에 대해서 배워볼건데요 :)TextField를 쓰다보면 반드시 접하는 이슈..!바로 이 문제죠.바로 키보드에 의해 TextField가 가려지는 현상입니다.간단하게 오늘 할

zeddios.tistory.com

 
 
 
 


 
 
 
 
 
지금 발등에 불떨어지다못해 걍 허벅지까지 탔다,,
이래도 되는건가??? 나이렇게해도되나???? 이렇게???? 되나????? 하면서 미친듯이 코딩하는 중
 
집에 일이 있어서 지금 ㅠ,ㅠ 엊그제는 UI만 짜놓고 오늘 기능구현 조진다!! 햇는데
막상 조져지는건 나엿음...
 
 
 

etc-image-6
내 코드 상태

 
 
 
하하 지금은 기능 구현 다했다 .ᐟ.ᐟ.ᐟ.ᐟ.ᐟ.ᐟ.ᐟ.ᐟ.ᐟ.ᐟ.ᐟ.ᐟ
힘들어죽겟다.............
 
 
내일 할 일
1. 이름 관련 버그 수정
2. nameLabel inset 넣기
3. 이스터에그 넣기...........
 
 
 
주말 동안 과제 하면서 깨달은 부족한 점

  • 네비게이션 바를 이용한 화면 전환에 서툴다
    가만 보면 이......... 이거 어떻게 했더라??? 하고 있다...... nav 변수를 선언해야 할 때와 하지 않아도 괜찮을 때를 잘 모르겠다... 이건 내 스스로 좀 더 공부하고 복습해야 알 것 같다.............. 약간 감으로만 알고 있고 어?? 이때 되네?? 하고 걍 넘기고 어?? 이거 왜 안되지?? 이걸로해보까 하고 이걸로 해서 되면 안일하게 넘어가다 보니까 ㄹㅇ 모르는 거 같다......
  • App의 생명 주기와 Scene의 LifeCycle을 조금 더 공부하면 좋을 거 같다
    UserDefault 저장하면서 생각한 건데 뭔가 알고 있기는 하지만 명확히 어느 때 이 함수가 실행된다고 알고 있는 건 아니어서 자꾸 print(#function) 하게 된다 print문 없으면 디버깅 어케 하지? 아무튼...

 
 
 
아......
근데 요즘 너무 재밋다.......
매일매일 새로운 걸 왕창!!!! 배우고 그걸 다 기억하지는 못하지만 어제보다 훨씬 나아지고 잇는 나를 보게 대면 너무 즐겁다...........
앞으로도재밋엇음좋겠다...
 
 
 

'TIL' 카테고리의 다른 글

[SeSAC] August 10, 2023  (0) 2023.08.11
[SeSAC] August 7, 2023  (0) 2023.08.07
[SeSAC] August 3, 2023  (2) 2023.08.04
[SeSAC] August 2, 2023  (0) 2023.08.02
[SeSAC] August 1, 2023  (0) 2023.08.01