Zachary Smouse
Zachary Smouse

Reputation: 143

Show UIView When Popping Back on a Navigation Controller

So I'm trying to show an UIView in a stack view when I click continue on a controller and pop back to the previous controller. However, I'm having trouble showing the view when I pop. It will show if I present the controller, but I need it to show when I pop. How should I go about this? Thank you.

// ServiceDetailController

// MARK: - Properties

lazy var dateContainer: ShadowCardView = {
    let view = ShadowCardView()
    view.backgroundColor = .white
    view.addShadow()
    view.setHeight(height: 40)
        
    let stack = UIStackView(arrangedSubviews: [calendarIcon, dateLabel, timeOfDayLabel])
    stack.axis = .horizontal
    stack.distribution = .fillProportionally
    stack.spacing = 8
        
    view.addSubview(stack)
    stack.centerY(inView: view)
    stack.anchor(left: view.leftAnchor, right: view.rightAnchor, paddingLeft: 12, paddingRight: 70)
        
    view.addSubview(closeButton)
    closeButton.centerY(inView: view)
    closeButton.anchor(right: view.rightAnchor, paddingRight: 12)
        
    return view
}()

lazy var dateStack = UIStackView(arrangedSubviews: [dateLabelStack, dateContainer, dateContainerView])

// MARK: - Lifecycle

override func viewDidLoad() {
    super.viewDidLoad()
        
    configureUI()
}

// MARK: - Selectors

@objc func handleDateCreationTapped() {
    let controller = DateCreationController()
    controller.jobService = self.jobService
    navigationController?.pushViewController(controller, animated: true)
}

// MARK: - Helper Functions

fileprivate func configureUI() {
        
     setupNavigationBar()
        
     view.backgroundColor = .groupTableViewBackground
        
     setupServiceInfoView()
        
     setupFormView()
        
}

fileprivate func setupFormView() {
        
    showDataContainer(shouldShow: false)
    setupTapGestureRecognizers()
        
}

fileprivate func setupTapGestureRecognizers() {
        
     let dateTap = UITapGestureRecognizer(target: self, action: #selector(handleDateCreationTapped))
     dateTap.numberOfTapsRequired = 1
     dateTextField.addGestureRecognizer(dateTap)
        
}

func showDateContainer(shouldShow: Bool) {
            
       if shouldShow {
          dateContainer.isHidden = false
          dateStack.spacing = 5
       } else {
          dateContainer.isHidden = true
          dateStack.spacing = -18
       }
            
 }

// DateCreationController

// MARK: - Properties

private let continueButton: UIButton = {
     let button = UIButton(type: .system)
     button.setTitle("Continue", for: .normal)
     button.setTitleColor(.white, for: .normal)
     button.titleLabel?.font = UIFont(name: "AvenirNext-Medium", size: 14)
     button.backgroundColor = .darkGray
     button.setHeight(height: 50)
     button.layer.cornerRadius = 8
     button.addTarget(self, action: #selector(handleContinue), for: .touchUpInside)
     return button
}()

// MARK: - Selectors

@objc func handleContinue() {
        
    let controller = ServiceDetailController()
    controller.showDateContainer(shouldShow: true)
        
    navigationController?.popViewController(animated: true)

}

Upvotes: 0

Views: 196

Answers (1)

DonMag
DonMag

Reputation: 77682

The main problem is this func in your DateCreationController:

@objc func handleContinue() {
        
    // here, you are creating a NEW instance of ServiceDetailController
    let controller = ServiceDetailController()
    controller.showDateContainer(shouldShow: true)
        
    // here, you are popping back to where you came from... the EXISTING instance of ServiceDetailController
    navigationController?.popViewController(animated: true)

}

You need to use either delegate/protocol pattern or a closure.

Here's an example using a closure...

Add this var to your DateCreationController class:

var continueCallback: ((Bool)->())?

In your ServiceDetailController class, when you instantiate and push your DateCreationController, you'll also setup that closure:

@objc func handleDateCreationTapped() {
    let controller = DateCreationController()
    controller.jobService = self.jobService
    
    // add the closure
    controller.continueCallback = { [weak self] shouldShow in
        guard let self = self else { return }
        self.showDateContainer(shouldShow: shouldShow)
        self.navigationController?.popViewController(animated: true)
    }

    navigationController?.pushViewController(controller, animated: true)
}

Then, in your button action in DateCreationController, you "call back" using that closure:

@objc func handleContinue() {
    
    continueCallback?(true)
    
}

Here's a runnable example. It creates a simple yellow view as the dateContainer view, but it is hidden on load. Tapping anywhere will push to DateCreationController, which has a single "Continue" button. Tapping that button will execute the closure, where the dateContainer view will be set to visible and we'll pop back:

class ServiceDetailController: UIViewController {
    
    var jobService: String = "abc"

    // plain yellow view
    let dateContainer: UIView = {
        let view = UIView()
        view.backgroundColor = .yellow
        return view
    }()
    
    // MARK: - Lifecycle
    
    override func viewDidLoad() {
        super.viewDidLoad()

        let infoLabel = UILabel()
        infoLabel.text = "Tap anywhere"
        view.addSubview(infoLabel)
        view.addSubview(dateContainer)
        
        infoLabel.translatesAutoresizingMaskIntoConstraints = false
        dateContainer.translatesAutoresizingMaskIntoConstraints = false
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            infoLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
            infoLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            
            dateContainer.topAnchor.constraint(equalTo: infoLabel.bottomAnchor, constant: 20.0),
            dateContainer.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            dateContainer.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            dateContainer.heightAnchor.constraint(equalToConstant: 100.0),
        ])

        setupTapGestureRecognizers()
        
        // hide dateContainer on load
        showDateContainer(shouldShow: false)
    }
    
    // MARK: - Selectors
    
    @objc func handleDateCreationTapped() {
        let controller = DateCreationController()
        controller.jobService = self.jobService
        
        // add the closure
        controller.continueCallback = { [weak self] shouldShow in
            guard let self = self else { return }
            self.showDateContainer(shouldShow: shouldShow)
            self.navigationController?.popViewController(animated: true)
        }

        navigationController?.pushViewController(controller, animated: true)
    }
    
    // MARK: - Helper Functions
    
    fileprivate func setupTapGestureRecognizers() {
        
        let dateTap = UITapGestureRecognizer(target: self, action: #selector(handleDateCreationTapped))
        dateTap.numberOfTapsRequired = 1
        view.addGestureRecognizer(dateTap)
        
    }
    
    func showDateContainer(shouldShow: Bool) {
        
        if shouldShow {
            dateContainer.isHidden = false
        } else {
            dateContainer.isHidden = true
        }
        
    }
}
class DateCreationController: UIViewController {
    var jobService: String = "abc"
    
    var continueCallback: ((Bool)->())?
    
    lazy var continueButton: UIButton = {
        let button = UIButton(type: .system)
        button.setTitle("Continue", for: .normal)
        button.setTitleColor(.white, for: .normal)
        button.titleLabel?.font = UIFont(name: "AvenirNext-Medium", size: 14)
        button.backgroundColor = .darkGray
        button.layer.cornerRadius = 8
        button.addTarget(self, action: #selector(handleContinue), for: .touchUpInside)
        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .green
        view.addSubview(continueButton)
        continueButton.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            continueButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            continueButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            continueButton.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.75),
            continueButton.heightAnchor.constraint(equalToConstant: 50.0),
        ])
    }
    
    // MARK: - Selectors
    
    @objc func handleContinue() {
        
        continueCallback?(true)
        
    }
}

Upvotes: 0

Related Questions