View controller property layout top of each other

52 Views Asked by At

I have two view controller . In first view controller I have table view with cell and search bar on top . The second view controller I have back button to go back to first view controller. The problem is when I enter the text into search bar and select the table view cell and then navigate to second view controller then when I click the back button at second view controller , the layout of the first view controller like each bar , navigation title is top of each other.

Here is the code for First view controller..

final class FirstViewController: UITableViewController {
    
    // MARK: - UI Search Components
    private let searchController = UISearchController(searchResultsController: nil)
    private let viewModel: ViewModel

    init(viewModel: MoviesViewModel) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        title = LocalizedString(key: "title")
        
        setupSearchController()
        configureTableView()
        updateFromViewModel()
        bindViewModel()
        viewModel.fetchData()
    }

    private func configureTableView() {
        tableView.dm_registerClassWithDefaultIdentifier(cellClass: MovieCell.self)
        tableView.rowHeight = UITableView.automaticDimension

        refreshControl = UIRefreshControl()
        refreshControl?.addTarget(self, action: #selector(refreshData), for: .valueChanged)
    }

    private func bindViewModel() {
        viewModel.updatedState = { [weak self] in
            guard let self else { return }
            DispatchQueue.main.async {
                self.updateFromViewModel()
            }
        }
    }

    private func updateFromViewModel() {
        switch viewModel.state {
        case .loading, .loaded:
            tableView.reloadData()
        case .error:
            showError()
        }
        refreshControl?.endRefreshing()
    }

    private func showError() {
        let alertController = UIAlertController(title: "", message: LocalizedString(key: "movies.load.error.body"), preferredStyle: .alert)
        let alertAction = UIAlertAction(title: LocalizedString(key: "movies.load.error.actionButton"), style: .default, handler: nil)
        alertController.addAction(alertAction)
        present(alertController, animated: true, completion: nil)
    }

    @objc private func refreshData() {
        viewModel.fetchData()
    }

    @objc private func textSizeChanged() {
        tableView.reloadData()
    }
    
    // MARK: Search Property.
    private func setupSearchController() {
        
        self.searchController.searchResultsUpdater = self
        self.searchController.obscuresBackgroundDuringPresentation = false
        self.searchController.hidesNavigationBarDuringPresentation = false
        self.searchController.searchBar.placeholder = "Search Movie"
        
        self.navigationItem.searchController = searchController
        self.definesPresentationContext = false
        self.navigationItem.hidesSearchBarWhenScrolling = false
        
        searchController.delegate = self
        searchController.searchBar.delegate = self
        searchController.searchBar.showsBookmarkButton = true
        searchController.searchBar.tintColor = .purple
        searchController.searchBar.setImage(UIImage(named: "Filter"), for: .bookmark, state: .normal)
        searchController.searchBar.setLeftImage(UIImage(named: "Search"))
        searchController.searchBar.showsBookmarkButton = true
    }
}

// MARK: - UITableViewDataSource
extension FirstViewController {

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        let inSearchMode = self.viewModel.inSearchMode(searchController)
        return inSearchMode ? self.viewModel.filteredData.count : self.viewModel.state.data.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell: DataCell = tableView.dm_dequeueReusableCellWithDefaultIdentifier()

        let inSearchMode = self.viewModel.inSearchMode(searchController)
        let data = inSearchMode ? self.viewModel.filteredData[indexPath.row] : self.viewModel.state.data[indexPath.row]
        cell.configure(movie)

        return cell
    }

}

// MARK: - UITableViewControllerDelegate
extension FirstViewController {

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let inSearchMode = self.viewModel.inSearchMode(searchController)
        let movie = inSearchMode ? self.viewModel.filteredData[indexPath.row] : self.viewModel.state.data[indexPath.row]
        let viewModel = DetailsViewModel(data: data, apiManager: APIManager())
        let viewController = SecondDetailsViewController(viewModel: viewModel)
        self.navigationController?.pushViewController(viewController, animated: true)
    }
}
// MARK: - Search Controller Delegate Functions.
extension FirstViewController: UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate {
    
    func updateSearchResults(for searchController: UISearchController) {
        self.viewModel.updateSearchController(searchBarText: searchController.searchBar.text)
        tableView.reloadData()
    }
}

Here is the back button code ..

extension UIBarButtonItem {
    static func backButton(target: Any?, action: Selector?) -> UIBarButtonItem {
        let backButton = UIBarButtonItem(image: UIImage(named: "ArrowLeft"), style: .plain, target: target, action: action)
        backButton.tintColor = UIColor.Brand.popsicle40
        return backButton
    }
}

Here is the code for second view controller.

final class SceondViewController: UIViewController {

    private let viewModel: DetailsViewModel
    private var currentViewController: UIViewController!

    init(viewModel: ViewModel) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
        navigationItem.largeTitleDisplayMode = .never
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.leftBarButtonItem = UIBarButtonItem.backButton(target: self, action: #selector(didTapBack(_:)))
        updateFromViewModel()
        bindViewModel()
        viewModel.fetchData()
    }

    private func bindViewModel() {
        viewModel.updatedState = { [weak self] in
            guard let self else { return }
            DispatchQueue.main.async {
                self.updateFromViewModel()
            }
        }
    }

    private func updateFromViewModel() {
        let state = viewModel.state
        title = LocalizedString(key: "title")
        switch state {
        case .loading(let data):
            self.showLoading(data)
        case .loaded(let details):
            self.showDetails(details)
        case .error:
            self.showError()
        case .pageLoaded(let page):
            self.showSimilarDetails(page)
        }
    }

    private func showLoading(_ data: data) {
       // show loading view controller content 
    }

    private func showDetails(_ Details: Details) {
         let displayViewController = DetailsDisplayViewController(Details: Details, viewModel: viewModel)
        addChild(displayViewController)
        displayViewController.view.frame = view.bounds
        displayViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        currentViewController?.willMove(toParent: nil)
        transition(
            from: currentViewController,
            to: displayViewController,
            duration: 0.25,
            options: [.transitionCrossDissolve],
            animations: nil
        ) { (_) in
            self.currentViewController.removeFromParent()
            self.currentViewController = displayViewController
            self.currentViewController.didMove(toParent: self)
        }
    }

    private func showSimilarDetails(_ similarDetails: Page<Details>) {
       let similarViewController = SimilariewController(viewModel: viewModel)
        addChild(similarViewController)
        similarViewController.view.frame = view.bounds
        similarViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        currentViewController?.willMove(toParent: nil)
        transition(
            from: currentViewController,
            to: similarViewController,
            duration: 0.25,
            options: [.transitionCrossDissolve],
            animations: nil
        ) { (_) in
            self.currentViewController.removeFromParent()
            self.currentViewController = similarViewController
            self.currentViewController.didMove(toParent: self)
        }
    }
    
    private func showError() {
       // code for error halding.
    }

    @objc private func didTapBack(_ sender: UIBarButtonItem) {
        navigationController?.popViewController(animated: true)
    }
}

I have tried that giving the constrains into table view controller but it did not fix the problem.

Here is the screenshot when I click the back button at second view controller and go back to first view controller , the layout of the view top of each other.

first view content layout error

here is the screenshot of the back button.

back button

0

There are 0 best solutions below