I need some help with this problem.
I have this basic Scheme
- HomeController
- TripNavigation (view controller parent)
- Step1 (view controller child)
- Step2 (view controller child)
- TripNavigation (view controller parent)
When i tap 10 times the button (buttonTapped) in homecontroller and then dismiss everytime.
- In ui view hierarchy don't appear TripNavigation, which is good because i dismiss the controller and childs
- In View Memory Graph hierarchy:
I think i create a cycle but i don't found it.
My code:
Home Controller:
class HomeController: UIViewController {
var user: User?
init(user: User) {
self.user = user
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
// show button on view
}
@objc func buttonTapped{
DriverService.shared.observeTrip(uid: "XXX") { (userDB) in
let controller = TripNavigation(user: userDB)
controller.modalPresentationStyle = .fullScreen
self.present(controller, animated: true, completion: nil)
}
}
}
TripNavigation code:
protocol TripProtocol: AnyObject {
func userHasChanged(_ user: User?)
}
class TripNavigation: UIViewController {
var user: User
weak var delegate: TripProtocol?
// array of childs
var listOfSteps: [UIViewController] = []
// segmented control to navigate in childs
let mySegmentedControl: UISegmentedControl = {
let sc = UISegmentedControl(items: ["1","2"])
return sc
}()
init(user: User) {
self.user = user
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
loadviews()
// here i make a addsubview for a button that call dissmisView() function (bottom of this class)
// i call dissmisView to test if memory deallocated
}
func checkFirebaseNews(){
// check new info from firebase and pass new data to childs
self.delegate?.userHasChanged(user)
}
// main function that load the childs
func loadviews(){
mySegmentedControl.frame = CGRect(x: 0, y: 95, width: view.frame.width, height: 30)
mySegmentedControl.addTarget(self, action: #selector(self.segmentedValueChanged(_:)), for: .valueChanged)
view.addSubview(mySegmentedControl)
// first child of TripNavigation
let step1 = Step1(user: user, nextStep: 1)
self.addChild(step1)
step1.willMove(toParent: self)
view.addSubview(step1.view)
step1.didMove(toParent: self)
step1.view.anchor(top:mySegmentedControl.topAnchor,left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, paddingTop: 31)
delegate = step1
listOfSteps = [step1]
// second child of TripNavigation
let step2 = Step2(user: user, nextStep: -1)
self.addChild(step2)
step2.willMove(toParent: self)
view.addSubview(step2.view)
step2.didMove(toParent: self)
step2.view.anchor(top:mySegmentedControl.topAnchor,left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, paddingTop: 31)
delegate = step2
listOfSteps.append(step2)
mySegmentedControl.selectedSegmentIndex = 0
showView(num: 0)
}
// segmented control manage
@objc func segmentedValueChanged(_ sender:UISegmentedControl!){
switch sender.selectedSegmentIndex{
case 0:
showView(num: 0)
case 1:
showView(num: 1)
default:
break
}
}
// aux functions
func showView(num: Int){
hideRestofViews(actual: num)
let step = listOfSteps[num]
step.view.isHidden = false
}
func hideRestofViews(actual: Int){
if listOfSteps.count >= 1 {
for (index, v) in listOfSteps.enumerated() {
if index != actual{
v.view.isHidden = true
}
}
}
}
@objc func dissmisView() {
print("<W call to dismissview")
for v in listOfSteps {
v.willMove(toParent: nil)
v.view.removeFromSuperview()
v.removeFromParent()
print(v)
v.dismiss(animated: false, completion: nil)
print("1")
}
print("<W all deleted")
self.dismiss(animated: true, completion: nil)
}
// called in child, to change to other view child
@objc func changeView(notification: NSNotification){
if let next = notification.object as? Int {
if next == -1{
dissmisView()
}else{
mySegmentedControl.selectedSegmentIndex = next
showView(num: next)
}
}
}
}
Example of Step1 child code:
// Step1 and Step2 are practically the same
class Step1: UIViewController,TripProtocol {
var mapView = MKMapView()
var user: User?
var nextStep: Int
func userHasChanged(_ user: User?) {
self.user = user
}
init(user: User?, nextStep: Int) {
self.user = user
self.nextStep = nextStep
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
//configureMapView() ... configureUI... that show user data
}
@objc func nextStep(){
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "changeView"), object: nextStep)
}
}
Thanks so much everyone!!