LeoNatan / LNPopupController

A framework for presenting view controllers as popups of other view controllers, much like the Apple Music and Podcasts apps.
MIT License
3.04k stars 340 forks source link

Question #384

Closed iDevelopper closed 4 years ago

iDevelopper commented 4 years ago

@LeoNatan , I hope you are well despite these circumstances. This is just a question please? Do you know why snapshotViewAfterScreenUpdates: method does not capture a toolbar view correctly ? The capture view show only items and everything else is clear. I saw that your framework version 3, you use the private UIView _UIPortalView. Is it for a workaround? Thanks,

LeoNatan commented 4 years ago

Sadly, it seems like a limitation of Apple. Snapshotting visual effect views is not recommended. It used to look better in the past, but in recent years, it looks bad.

Portal views are different (and private!). The allow rendering parts of the hierarchy in different places, without ripping them from their actual position in hierarchy. But it’s very limited. For example, not possible to render the same view in two different places, unlike snapshots. Apple uses it in interaction previews, for example.

LeoNatan commented 4 years ago

Hope you are well too.

iDevelopper commented 4 years ago

Thanks a lot for your answer. Can _UIPortalView support a sourceRect?? instead of sourceView? Be well!

LeoNatan commented 4 years ago

No possible unfortunately:

https://github.com/LeoNatan/Apple-Runtime-Headers/blob/master/iOS/PrivateFrameworks/UIKitCore.framework/_UIPortalView.h

iDevelopper commented 4 years ago

Thanks,

And unfortunately, Apple cannot provide a reliable API for these screenshots, then! Even more strange, if I capture a toolbar from a navigation controller with snapshotViewAfterScreenUpdates, it works, but if I capture a toolbar that I added myself on a view, it does not work, it is completely transparent, except if I set a background color to the toolbar I added. However, looking in debug view hierarchy these two toolbar seem to be similar.

iDevelopper commented 4 years ago

They do not say everything to us ! They are not transparent, them! :-)

LeoNatan commented 4 years ago

You can fake it by adding a visual effect view under your snapshot.

iDevelopper commented 4 years ago

Yes but with what effect? In the debug view hierarchy, if I look at the visualEffectView of the toolbar, effect = none.

LeoNatan commented 4 years ago

On iOS 13, it’s easy. Set the effect to UIBlurEffectStyleSystemChromeMaterial. On previous systems, it’s harder to find a match, and may even be impossible.

iDevelopper commented 4 years ago

I will test, fortunately, the snapshot is bugged only on iOS 13 and I need it because of the new modal presentation style...

iDevelopper commented 4 years ago

Thanks, it works, I don't understand why (did you work at Apple?). But no for tabBar of UITabBarController. So, this is what I did:

    private func setupBottomBarForPresentation() -> UIView? {
        #if targetEnvironment(macCatalyst)
            return nil
        #else
        if UIDevice.current.userInterfaceIdiom == .pad {
            if self.dropShadowViewFor(presentingVC.view) != nil {
                return nil
            }
        }
        if ProcessInfo.processInfo.operatingSystemVersion.majorVersion < 13 {
            return nil
        }

        var imageRect = self.presentingVC.defaultFrameForBottomBar()
        imageRect.origin.y = 0
        imageRect.size.height += self.presentingVC.insetsForBottomBar().bottom

        //for debug
        //let image = presentingVC.bottomBar.makeSnapshot(from: imageRect)

        if let view = self.presentingVC.bottomBar.resizableSnapshotView(from: imageRect, afterScreenUpdates: false, withCapInsets: .zero) {

            if self.presentingVC is UITabBarController {
                return view
            }
            if #available(iOS 13.0, *) {
                let blurEffect = UIBlurEffect(style: .systemChromeMaterial)
                let effectView = UIVisualEffectView(effect: blurEffect)
                effectView.frame = view.bounds
                effectView.contentView.addSubview(view)
                return effectView
            }
            return view
        }
        return nil
        #endif
    }