금주의 iOS 버그: View Controller가 여러 번 푸시됨
소개
금주의 iOS 버그(반드시 매주는 아님)에서 흔하지 않거나 특정한 특성으로 인해 많이 문서화되지 않은 흥미로운 엣지 케이스 버그를 살펴보겠습니다.
이번 회에서는 상세 뷰 컨트롤러를
UINavigationController
의 스택으로 푸시할 때 나타나는 흥미로운 탐색 문제에 대해 설명하겠습니다.설정
나는 앱이 모든 버튼에 대해 일관된 모양과 느낌을 가질 수 있도록 기본값으로 재사용 가능한 버튼 스타일을 만드는 것을 목표로 했습니다.
이를 달성하기 위해 이 유형의 모든 버튼에 적용하고 싶은 속성 세트로 확장
UIButton
했습니다.extension UIButton {
static let defaultStyle: UIButton = {
let button = UIButton()
button.layer.cornerRadius = 15.0
button.backgroundColor = .blue
return button
}()
}
이 확장 기능을 사용하면 호출자는 이 구성을 사용하여 다른 버튼과 일관된 스타일을 유지하는 버튼을 쉽게 만들 수 있습니다.
여태까지는 그런대로 잘됐다. 이 버튼을 사용합시다.
예제 개요
이 예제를 간단하게 유지하기 위해 부모 뷰 컨트롤러의 버튼을 사용하여 세부 뷰 컨트롤러를 탐색 스택으로 푸시하여 탐색합니다.
우리의 예에서는 상위 뷰 컨트롤러에서 로그인 화면으로 "로그아웃"할 수도 있습니다. 이는 상위에서 세부 정보로의 탐색 흐름과 별개입니다.
화면 만들기
부모 보기 컨트롤러인
MainViewController
에서 버튼 확장을 사용할 lazy
의 UIButton
인스턴스를 생성해 보겠습니다. 따라서 defaultStyle
에서 모든 속성을 상속해야 합니다.class MainViewController {
private lazy var proceedButton: UIButton = {
let button = UIButton.defaultStyle
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
}
이제 뷰 컨트롤러의
viewDidLoad
에 있는 이 버튼에 선택기를 할당해 보겠습니다.선택기를 사용하여 비어 있는 항목
DetailViewController
을 만들고 탐색 스택에 푸시합니다.@objc
private func didTapProceedButton() {
let viewController = DetailViewController()
navigationController?.pushViewController(viewController, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
proceedButton.addTarget(self, action: #selector(didTapProceedButton), for: .touchUpInside)
}
버그
앱을 로드하고 진행 버튼을 클릭하면 상세 보기 컨트롤러로 이동합니다. 문제가 없습니다.
하지만 앱의 다른 부분으로 이동한 후 다시
MainViewController
로 다시 이동하면 어떻게 될까요?이 예에서는 일부 로그아웃 버튼을 클릭하고 내
MainViewController
에 다시 로그인하기로 결정했다고 가정합니다.MainViewController
에 다시 로그인하면 진행 버튼을 클릭하면 자세히 보기 컨트롤러가 탐색 스택으로 두 번 푸시됩니다.이 프로세스를 다시 반복하면 버튼이 이제 디테일 뷰 컨트롤러의 세 인스턴스를 푸시합니다. 등등.
우리는 이 동작이
MainViewController
로 돌아가는 횟수와 일치한다는 것을 관찰했습니다. 하지만 이 버그의 원인은 무엇입니까?수정
이 문제는
UIButton
확장 프로그램의 설정으로 인해 발생했습니다.버튼 스타일에 대한 코드에서
()
구문을 사용하여 이 함수를 호출한 다음 그 결과를 defaultStyle
속성으로 static let
에 할당한다는 점에 유의하십시오.// Incorrect approach
static let defaultStyle: UIButton = {
let button = UIButton()
button.layer.cornerRadius = 15.0
button.backgroundColor = .blue
return button
}()
이것이 왜 문제가 됩니까? 음, 한 번만 생성되므로 이 버튼 확장을 호출할 때마다 다른 곳에서 사용되는 동일한 인스턴스를 다시 가져옵니다.
이 설정을 수정해 보겠습니다.
extension UIButton {
// Correct approach
static func defaultStyle() -> UIButton {
let button = UIButton()
button.layer.cornerRadius = 15.0
button.backgroundColor = .blue
return button
}
}
우리의 새로운 접근 방식에서는 해당 함수가 호출될 때마다 버튼 생성을 연기합니다. 이렇게 하면 동일한 인스턴스를 호출하는 것과는 반대로 매번 새
UIButton
인스턴스가 생성됩니다.viewDidLoad
의 MainViewController
에 있는 버튼에 선택기를 할당했음을 기억하십시오. 따라서 이 뷰 컨트롤러를 로드할 때마다 해당 선택기를 버튼에 다시 쌓을 것입니다.버튼의 동일한 인스턴스이므로 선택기의 각 반복을 유지합니다. 따라서 사용자가 진행 버튼을 탭할 때마다 뷰 컨트롤러를 로드할 때 할당된 선택기의 모든 인스턴스가 트리거됩니다. 따라서 한 번의 버튼 누름에 대해
DetailViewController
의 여러 인스턴스가 푸시되는 이유입니다.보시다시피 버그는 여러 단계를 거쳐 생성되며 근본 원인이 어디에 있는지 식별하는 것이 중요합니다.
Reference
이 문제에 관하여(금주의 iOS 버그: View Controller가 여러 번 푸시됨), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/jeriel/ios-bug-of-the-week-view-controller-gets-pushed-multiple-times-41e9텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)