API로 정보를 텍스트로 하여 알림으로 보여주는 뷰를 구성하고 해당 뷰의 쉐도우를 준다고 가정할 때, 정보의 크기에 따라서 뷰의 크기가 달라지는데, 이때 쉐도우가 높이에 맞지않게 들어가는 경우가 일어난다.
이것의 이유로는, API로 정보를 받아오는 과정보다 쉐도우를 넣는 함수가 내부에서 먼저 실행되므로 크기가 맞지 않게 들어가는 것이다. 따라서 API를 사용하여 뷰의 크기가 달라지는 경우에는, 쉐도우를 넣는 함수를 API로 정보를 전부 다 불러오고 나서 호출이 되야 한다.
UI를 설정해주는 UIKit 시스템 함수인 updateConstraints()
는 DispatchQueue.main.async
를 통해 실행되기 때문에, mainQueue(serial)
안에서는 Queue
에 들어간 순서대로 실행이 되지만, Queue
에 들어 있지 않은 코드들과는 비동기적으로 수행되므로 실행 순서를 장담할 수 없슴니당
이러한 이유로, updateShadow()
라는 UI를 업데이트하는 함수는 메인큐에서 실행되어야 함다
DispatchQueue.main.async {
self.updateShadow()
}
위치 및 크기에 관한 것은 CoreGraphics를 사용합니당 UIKit 내부에 있으므로 별도로 추가하지 않아도 ㄱㅊ
제일 중요한 차이는 frame은 자기자신의 view를이동, bounds는 subview들을 반대 방향으로 이동 하는 것을 알아두면 되겠슴더
이는 슈퍼뷰를 기준으로 해당 뷰의 크기나 위치를 표현한다고 합니다.
size
는 view
를 감싸는 크기를 정의함다. 회전해도 view
의 크기는 동일하지만, Size
는 커짐다
이는 자기자신을 기준으로 해당 뷰의 크기나 위치를 표현합니다
size
는 view
자체 크기를 의미함다. 회전을 하여도 size
가 변하지 않슴다.
이 친구의 핵심은, 해당 view
가 이동하는 것이 아닌, subview
들을 반대 방향으로 이동시킴당
마지막으로, frame
이라는 것은 super view
를 기준으로 배치하는 것을 명심해야 합니드, center속성도 역시 frame 기준 !
self.view를 사용함다, 해당 뷰 컨트롤러에 등록된 Rootview를 가리킵니당
override func viewDidLoad() {
btn = UIButton(type: .system)
btn.frame = ...
btn.setTitle(...)
...
self.view.addSubViews(btn)
}
서치바 구현중에 아무리해도 서치바 위 아래에 있는 디바이더? 줄 같은걸 못 지워서 검색 하다가 찾은 코드 !
좋지만,,, 뒤에가 보이는 부분이 조금 error;; 미니멀로 해야할둣
// 맨 앞 부분에 넣을 SearchBar 변수명을 적어주면 된다.
UISearchBar.setBackgroundImage(UIImage(), for: .any, barMetrics: .default)
// 내부 폰트사이즈에 맞게 크기가 변함
searchBar.searchTextField.font = UIFont.systemFont(ofSize: 14)
프레임 같은게 아닌 내부 폰트로 조절하면 사이즈가 변하는걸 알아버렸으 ~
먼저 dismiss
직후에 바로 present
하는 경우를 살펴보겠슴다
dismiss
함수의 클로저로 present
를 하는 형식을 사용하고, present
시에는 dismiss
된 뷰가 아닌 dismiss
된 뷰를 불러준 뷰를 찾아서 변수에 지정한 후에 present
를 걸어줘야한다.
조금 헷갈릴수도 있는데 코드를 보면 이해가 될거 같슴다.
// currentVC => FirstViewController
// self.dismiss를 실행하고, self.present를 하면 띄워줄 뷰가 없으므로 아래의 코드로 해야한다.
guard let pvc = self.presentingViewController else { return }
self.dismiss(animated: true) {
pvc.present(SecondViewController(), animated: true, completion: nil)
}
다음방식은 rootViewController
까지 dismiss
하고 present
하는 방식입니다.
이 방식은 뷰 계층에 스택이 많이 쌓여있는 경우에 유용함니당
self.view.window?.rootViewController?.dismiss(animated: false, completion: {
let homeVC = HomeViewController()
homeVC.modalPresentationStyle = .fullScreen
let sd = UIApplication.shared.connectedScenes.first?.delegate as! SceneDelegate
sd.window?.rootViewController?.present(homeVC, animated: true)
})
만약 뷰 계층에 루트 뷰, A뷰, B뷰, C뷰 컨트롤러가 존재한다면 A,B,C 뷰컨트롤러를 없애고 HomeViewController
를 띄워줍니다. 뷰 계층이 좀 깔끔해지는 모습을 볼 수 있슴다.
제시되는 뷰 컨트롤러의 transitioningDelegate
속성이 유효한 객체를 포함하고 있을 때, UIkit
은 직접 생성한 커스텀 애니메이터를 사용해 해당 뷰 컨트롤러를 제시함다.
프레젠테이션이 준비되면 UIKit
은 커스텀 애니메이터 객체를 회수하기 위해 전환 딜리게이트의 animationControllerForPresentedController:presentingController:sourceController:
메소드를 호출함다.
객체가 사용이 가능하다면 다음 과정을 수행합니당
UIKit
은 상호 작용 애니메이터 객체가 사용이 가능한 경우를 살펴보기 위해 전환 딜리게이트의 interactionControllerForPresentation:
메소드를 호출합니다. 이 메소드가 nil
을 반환하면, 사용자 상호작용 없이 애니메이션을 수행함다.transitionDuration
메소드 호출animateTransition:
호출startInteractiveTransition:
메소드 호출completeTransition:
메소드 호출 위해 애니메이터 객체를 기다리고, 애니메이션이 끝난 후 커스텀 애니메이터가 이 메소드를 호출 (completion block에서 주로 이루어짐) 이 메소드 호출은 전환을 마무리하고 UIKit
에게 presentViewController:animated:completion:
메소드의 컴플리션 핸들러를 처리 할 수 있다는 것을 알려줌다. 또 애니메이터 객체의 animationEnded:
메소드 호출도 가능함을 알려줌이제 뷰 컨트롤러를 해제 하는 시점의 단계입니다.
UIKit
은 전환 딜리게이트의 anumationControllerForDismissedController:
메소드를 호출interactionCntrollerForDismissal:
메소드 호출, 다음 단계는 위에 과정과 같습니다.애니메이션이 마무리 되는 시점에
completeTransition:
메소드 호출은 필수적,UIKit
은 전환 프로세스를 마무리하지 않고, 이 메소드를 호출할 때까지 앱에서 컨트롤을 반환함다
참조
https://ios-development.tistory.com/111
https://developer-fury.tistory.com/56
https://velog.io/@panther222128/Presentations-and-Transitions#initiating-a-segue-programmatically