4

As we all know from the iOS 13+ modal presentation style for normal UIViewController will default to .pageSheet. If you want to change it you can change it to your's desired style when presenting it. I am using MFMailComposeViewController and MFMessageComposeViewController in my app to share content. In case of MFMailComposeViewController when i choose modalPresentationStyle = .fullScreen it works perfectly fine... but not in case of MFMessageComposeViewController. Please find the code snippet below

        if (MFMessageComposeViewController.canSendText()) {
            let controller = MFMessageComposeViewController()
            controller.body = “Message Body”
            controller.messageComposeDelegate = self
            controller.modalPresentationStyle = .fullScreen
            self.present(controller, animated: true, completion: nil)
            self.trackEvent(shareType: "SMS")
        }
    }
iGauravK
  • 91
  • 7
  • My answer didn't help you? – Alex.Pinhasov Jan 06 '20 at 11:49
  • @Alex.Pinhasov It was helpful but I was not looking for a custom implementation, I was just checking if I was doing it in the wrong way. But it seems to have a problem with MFMailComposeViewController itself. – iGauravK Jan 22 '20 at 06:37
  • @iGauravK can you please tell, did you resolve this issue? and how? – Usama Azam Jan 27 '20 at 07:36
  • @UsamaAzam Since modal presentation style is not working in this scenario, and I don't want any custom transition so the answer is No. – iGauravK Jan 29 '20 at 07:12

1 Answers1

0

While I can't answer your question on *why you cant use .fullScreen for MFMessageComposeViewController, I can tell you that if you implement your own custom transition you will get the desired behavior.

This is how you set custom transition for the view controller :

var customTransitionDelegate = CustomTransition(presentAnimation: CustomActionSheetPresentationTransition(), dismissAnimation: CustomActionSheetDismissalTransition())

 if (MFMessageComposeViewController.canSendText()) {
     let controller = MFMessageComposeViewController()
     controller.body = "Message Body"
     controller.messageComposeDelegate = self
     controller.modalPresentationStyle = .custom
     controller.transitioningDelegate = customTransitionDelegate
     self.present(controller, animated: true, completion: nil)
 }

You should also conform to the delegate and check the result to dismiss it after the user clicked cancel if he did :

    func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
    switch result {
    case .cancelled:
        self.presentedViewController?.dismiss(animated: true, completion: nil)
    default: break
    }
}

The presentation logic

class CustomActionSheetPresentationTransition: NSObject, UIViewControllerAnimatedTransitioning {
private let duration = 0.6

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    guard let fromView = transitionContext.viewController(forKey: .from) else { return }
    guard let toView   = transitionContext.viewController(forKey: .to) else { return }

    var screenOffUp = CGAffineTransform()
    let container = transitionContext.containerView

    screenOffUp = CGAffineTransform(translationX: 0, y: -fromView.view.frame.height)
    toView.view.frame = CGRect(x: 0, y: UIScreen.main.bounds.height, width: fromView.view.frame.width, height: fromView.view.frame.height)

    toView.view.center.x = container.center.x
    container.addSubview(toView.view)

    UIView.animate(withDuration: duration, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.8, options: [], animations: {
        toView.view.transform = screenOffUp
    }, completion: { (success) in
        transitionContext.completeTransition(success)
    })
}

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
    return duration
}
}

The dismissal logic

class CustomActionSheetDismissalTransition: NSObject, UIViewControllerAnimatedTransitioning {
private let duration = 0.8

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
    return duration
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    guard let fromView = transitionContext.viewController(forKey: .from) else { return }
    let screenOffDown = CGAffineTransform(translationX: 0, y: fromView.view.frame.height)

    UIView.animate(withDuration: duration, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.8, options: [], animations: {
        fromView.view.transform = screenOffDown
    }, completion: { (success) in
        transitionContext.completeTransition(success)
    })
}

}