intuit/CardParts

Auto Resizing Issue on Modal Views Presented in formSheet Style

rborquezdiaz opened this issue · 4 comments

Describe the bug

There seems to be an issue with Autolayout behavior when the CardsViewController is presented modally using a modalPresentationStyle of types: formSheet or actionSheet. I haven't tried it on iPhone but it's really evident on iPads running iOS13 (Since formSheet is the new automatic behavior). Also worth noting, if the device is rotated, Autolayout will then rearrange the elements to another incorrect state that will remain until the controller is dismissed.

If the modal view controller is presented on fullScreen, there is no issue whatsoever.

To Reproduce
Steps to reproduce the behavior:

  1. Create a CardsViewController
  2. Create and initialize multiple CardPartsViewControllers instances.
  3. Load CardPartsViewControllers into the CardsViewController.
  4. Present CardsViewController modally with a modal presentation style of: .formSheet or .pageSheet.

Expected behavior
The elements should be displayed in the same way as if they were presented in .fullscreen.

Screenshots
Fullscreen Presentation Portrait
IMG_0011

Fullscreen Presentation Landscape
IMG_0012

Formsheet Presentation Portrait Before Rotation
IMG_0006

Formsheet Presentation Landscape After Rotation
IMG_0007

Formsheet Presentation Portait After Rotation
IMG_0008

Info (please complete the following information):

  • Device: iPad
  • OS: iOS 13
  • Pod Version: 2.23.1
  • Xcode Version: 11.2.1

Additional context
Will answer through responese.

@rborquezdiaz Thank you for the very detailed bug description - we will try to take a look into this! Would be interesting to understand if you see the same issue if trying on an iPhone

Thank you for answeing so fast and being so dilligent with updating the framework, hopefully it's just a mistake on my part of the implementation. I don't know if it's relevant, but it's being presented from a non RxSwift module (In other words, a classic presentation).

It's happening on iPhone as well. I'll attach a Screenshot.

File

@rborquezdiaz Do you mind sharing the code used to present and the code of the CardsViewController as well as the CardPartsViewController? This can help us debug the issue.

@croossin Sure thing. One thing I noticed is that if I present the controller embeded in a Navigation Controller, it fixes the issue. But I seem to be having issues presenting the CardsViewController inside a ContainerView.

MainViewController (CardsViewController)

class MainViewController: CardsViewController {
    var isModallyPresented = false
    
    var productInfoCardController: ProductInfoCardController!
    var existenciaInfoCardController: ExistenciaInfoCardController!
    var ofertasCardController: OfertasCardController!
    
    let barcodeScannerController = BarcodeScannerController()
    
    @IBOutlet weak var barcodeButton: LGButton?
    
    let disposeBag = DisposeBag()
    
    let viewModel = MainViewModel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        barcodeScannerController.configureScanner()
        
        productInfoCardController = ProductInfoCardController()
        existenciaInfoCardController = ExistenciaInfoCardController()
        ofertasCardController = OfertasCardController()
        
        
        self.setupBindings()
        self.setupUI()
        
        let cards =  [productInfoCardController, existenciaInfoCardController, ofertasCardController]
        

        
        loadCards(cards: cards as! [CardController])
    }

    
    private func setupUI() {
        if let barcodeButton = barcodeButton {
            self.view.bringSubviewToFront(barcodeButton)
            barcodeButton.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
        }
        
        if !isModallyPresented {
            let logo = UIImage(named: "Proscai_logo")
            let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 219, height: 60))
            imageView.image = logo
            imageView.contentMode = .scaleAspectFit
            self.navigationItem.titleView = imageView
        }
    }
    
    private func setupBindings() {
        
        viewModel.ean.asObservable()
            .bind(to: productInfoCardController.viewModel.setCurrentEAN)
            .disposed(by: disposeBag)
        
        viewModel.icod.asObservable()
            .bind(to: existenciaInfoCardController.viewModel.setCurrentIcod)
            .disposed(by: disposeBag)
        
        viewModel.icod.asObservable()
            .bind(to: ofertasCardController.viewModel.setCurrentIcod)
            .disposed(by: disposeBag)
        
        barcodeScannerController.viewModel.didSelectEAN.asObservable()
        .bind(to: viewModel.setCurrentEAN)
        .disposed(by: disposeBag)
        
        productInfoCardController.viewModel.didSelectIcod.asObservable()
        .bind(to: viewModel.setCurrentIcod)
        .disposed(by: disposeBag)
        
    }
    
    @objc func buttonTapped() {
        
        barcodeScannerController.startScanning()
        // Show the scanner.
        if let picker = barcodeScannerController.picker {
            picker.modalPresentationStyle = .overFullScreen
            present(picker, animated: true, completion: nil)
        }
        
    }
    
     @objc func dismissSelf() {
        self.dismiss(animated: true, completion: nil)
    }
}

MainContainerViewController


class MainContainerViewController: UIViewController {
    @IBOutlet weak var closeBarButton: UIBarButtonItem!
    @IBOutlet weak var containerView: UIView!
    
    var containedViewController : UIViewController?
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func viewDidAppear(_ animated: Bool) {
        addChild(containedViewController!)
        containerView.addSubview(containedViewController!.view)
        containedViewController?.didMove(toParent: self)
        self.view.layoutIfNeeded()
    }
    

    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destination.
        // Pass the selected object to the new view controller.
    }
    */
    @IBAction func didPressClose(_ sender: Any) {
        self.dismiss(animated: true, completion: nil)
    }
    
}

Presentation

let mventoryStoryboard = UIStoryboard(name: "Mventory", bundle: nil)
let mventoryVC = MainViewController()
let mainContainerVC = mventoryStoryboard.instantiateViewController(withIdentifier: "MainContainerViewController") as? MainContainerViewController
mainContainerVC?.containedViewController = mventoryVC
mainContainerVC?.modalPresentationStyle = .formSheet
            

self.present(mainContainerVC, animated: true) {
    mventoryVC.barcodeScannerController.barcodeScanner(didScan: barcode)
}

Unfortunately, the CardPartsViewControllers shows more business logic than I'm allowed to share. But I reproduced the issue by presenting the CardsViewController from your Demo. Therefore, it seems that the CardPartsViewController itself is irrelevant to the issue.
You can actually reproduce it by making a MainContainerViewController on your demo, that's how I tested it.

Ps. The MainContainerViewController view is just a NavigationBar, with a right bar button item for closing and a ContainerView with constraints (0-top, 0-trailing, 0-bottom, 0-leading).