Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove popup shadow on iOS 17 #1615

Merged
merged 9 commits into from
Mar 25, 2024
Merged

Conversation

cat0363
Copy link
Contributor

@cat0363 cat0363 commented Dec 21, 2023

This PR resolves the issue where Popup shadows are displayed on iOS 17.

Description of Change

In iOS 17, the hierarchy in which Popup shadows are drawn has changed.
The solution in PR #1463 doesn't work on iOS 17, so I modified it as follows:

[src\CommunityToolkit.Maui.Core\Views\Popup\MauiPopup.macios.cs]

public override void ViewDidLayoutSubviews()
{
    base.ViewDidLayoutSubviews();

    _ = View ?? throw new InvalidOperationException($"{nameof(View)} cannot be null.");

    View.Superview.Layer.CornerRadius = 0.0f;
    View.Superview.Layer.MasksToBounds = false;

    if (OperatingSystem.IsIOSVersionAtLeast(17))
    {
        if (VirtualView?.Color == Colors.Transparent)
        {
            View.Superview?.Superview?.Subviews.OfType<UIImageView>().FirstOrDefault()?.RemoveFromSuperview();
        }
        else
        {
            View.Superview?.Superview?.Superview?.Subviews.OfType<UIImageView>().FirstOrDefault()?.RemoveFromSuperview();
        }
    }
    else
    {
        PresentationController?.ContainerView?.Subviews.OfType<UIImageView>().FirstOrDefault()?.RemoveFromSuperview();
    }

    SetElementSize(new Size(View.Bounds.Width, View.Bounds.Height));
}

The hierarchy in which the shadow is drawn changes depending on whether the Popup color is transparent or not, so we will divide the cases as shown above.

Linked Issues

PR Checklist

  • Has a linked Issue, and the Issue has been approved(bug) or Championed (feature/proposal)
  • Has tests (if omitted, state reason in description)
  • Has samples (if omitted, state reason in description)
  • Rebased on top of main at time of PR
  • Changes adhere to coding standard
  • Documentation created or updated: https://github.com/MicrosoftDocs/CommunityToolkit/pulls

Additional information

Below are the verification results.

If Popup's Color is transparent

If Popup Color is not transparent

You can see that the Popup's shadow has been removed.

If the popup color is not transparent, I changed the popup color to white and the grid margin to 0 to test it.

@Toine-db
Copy link

Toine-db commented Jan 3, 2024

Please check if this solution still works when showing multiple (or just 2) popups one after another.
In my case the wrong background was removed ;-)

@cat0363
Copy link
Contributor Author

cat0363 commented Jan 4, 2024

@Toine-db , Isn't what looks like a shadow the Margin of the Popup's Content?
The overlay caused by displaying the popup nested is displayed above the margin of the popup's content, and it looks like a shadow.
If you set the Popup's Content Margin to 0, this shadow-like thing should disappear.

@Toine-db
Copy link

Toine-db commented Jan 4, 2024

@Toine-db , Isn't what looks like a shadow the Margin of the Popup's Content? The overlay caused by displaying the popup nested is displayed above the margin of the popup's content, and it looks like a shadow. If you set the Popup's Content Margin to 0, this shadow-like thing should disappear.

Im not sure what you mean, are you talking about the second popup or the first?

I noticed that when there was a short delay between the popups, the backgrounds were removed properly.

@cat0363
Copy link
Contributor Author

cat0363 commented Jan 4, 2024

@Toine-db , Something like the video below.

iPhone.15.iOS.17.0.2024-01-05.08-46-02.mp4

I mentioned that after displaying the second popup, the margin part of the previous popup appears like a shadow.
Is what you described similar to this?

@Toine-db
Copy link

Toine-db commented Jan 5, 2024

@Toine-db , Something like the video below.

iPhone.15.iOS.17.0.2024-01-05.08-46-02.mp4
I mentioned that after displaying the second popup, the margin part of the previous popup appears like a shadow. Is what you described similar to this?

This looks indeed like the issue I encountered, so this isn't the shadow we first removed?

@cat0363
Copy link
Contributor Author

cat0363 commented Jan 5, 2024

This looks indeed like the issue I encountered, so this isn't the shadow we first removed?

@Toine-db , This is not the shadow we removed earlier.
An overlay is displayed every time the Popup is displayed, but the overlay overlaps the Popup's Margin and looks like a shadow.
The margin area is transparent, but when the semi-transparent overlays overlap, the color of the overlapped area changes and looks like a shadow.
I tried various things, but I couldn't get rid of this shadow-like part.

Even in the code you wrote, this shadow-like area should appear by specifying the Margin for the Popup's content.

@Toine-db
Copy link

Toine-db commented Jan 11, 2024

An overlay is displayed every time the Popup is displayed, but the overlay overlaps the Popup's Margin and looks like a shadow.
The margin area is transparent, but when the semi-transparent overlays overlap, the color of the overlapped area changes and looks like a shadow.

@cat0363 It took a while but I finally see what you are trying to say.

so if I understand correctly it happens because there is another popup (or a remnant of one) on the screen and the OS automatically (unintentionally) takes this into account with its built-in overlay.

I think this falls outside this issue, but since we are talking about it now, do you have a proposal on how to tackle this?

perhaps options such as:

  • ensure that all popups are closed before a new one appears, this can quickly become a hacky solution with delays
  • no longer use the OS overlay but make one yourself, it may be difficult to get it full screen due to safe areas

@cat0363
Copy link
Contributor Author

cat0363 commented Jan 11, 2024

@Toine-db , As you understand, at least the problem of erasing the original shadow has been resolved.
The remaining issue is that the overlay overlaps the popup's margin, making it look like a shadow, but this has not been resolved.
The current Popup overlay is not an overlay displayed on the OS side, but a self-made overlay.
There was a problem with the overlay on the OS side, so I applied my own overlay.

I tried various solutions, but none of them could make the part that looks like a shadow transparent.
I'll try to look into it as time permits, but it doesn't seem like I'll be able to resolve it any time soon.

@cat0363
Copy link
Contributor Author

cat0363 commented Jan 18, 2024

The shadow-like problem seemed to be resolved, but it was because I made the overlay View transparent.
I have deleted the previous post.

@cat0363
Copy link
Contributor Author

cat0363 commented Jan 19, 2024

@Toine-db , I finally found a solution to this problem.
The part that looks like a shadow is caused by the homemade overlay added in #1369.
I will upload the verification video below.

iPhone.13.iOS.15.5.2024-01-19.09-46-39.mp4

I will upload the modified code after refactoring the code.

@cat0363
Copy link
Contributor Author

cat0363 commented Jan 19, 2024

As I mentioned earlier, the part that looks like a shadow is due to the self-made overlay added in #1369.
The solution to this issue is listed below.

First, delete your homemade overlay.
The code enclosed by Remove Start and Remove End is not necessary.

[src\CommunityToolkit.Maui.Core\Views\Popup\MauiPopup.macios.cs]

public override void ViewWillTransitionToSize(CGSize toSize, IUIViewControllerTransitionCoordinator coordinator)
{
    coordinator.AnimateAlongsideTransition(_ =>
    {
        // Before screen rotate
        /* --- Remove Start ---
        if (ViewController?.View is UIView view)
        {
            var overlayView = GetOverlayView(view);
            overlayView.Frame = new CGRect(0, 0, view.Frame.Width, view.Frame.Height);
        }
        --- Remove End --- */
    }, _ =>
    {
        // After screen rotate
        if (VirtualView is not null)
        {
            PopupExtensions.SetSize(this, VirtualView);
            PopupExtensions.SetLayout(this, VirtualView);
        }
    });
    base.ViewWillTransitionToSize(toSize, coordinator);
}

public void SetElement(IPopup element)
{
#if MACCATALYST
    if (element.Parent?.Handler is not PageHandler)
    {
        throw new InvalidOperationException($"The {nameof(element.Parent)} must be of type {typeof(PageHandler)}.");
    }
#endif

    VirtualView = element;
    ModalPresentationStyle = UIModalPresentationStyle.Popover;

    _ = View ?? throw new InvalidOperationException($"{nameof(View)} cannot be null.");
    _ = VirtualView ?? throw new InvalidOperationException($"{nameof(VirtualView)} cannot be null.");

#if MACCATALYST
    var pageHandler = VirtualView.Parent.Handler as PageHandler;
    var rootViewController = pageHandler?.ViewController ?? WindowStateManager.Default.GetCurrentUIViewController() ?? throw new InvalidOperationException($"{nameof(PageHandler.ViewController)} cannot be null.");
#else
    var rootViewController = WindowStateManager.Default.GetCurrentUIViewController() ?? throw new InvalidOperationException($"{nameof(PageHandler.ViewController)} cannot be null.");
#endif

    ViewController ??= rootViewController;
    /* --- Remove Start ---
    SetDimmingBackgroundEffect();
    --- Remove End */
}

/* --- Remove Start ---
void SetDimmingBackgroundEffect()
{
    if (ViewController?.View is UIView view)
    {
        var overlayView = GetOverlayView(view);
        overlayView.Bounds = view.Bounds;
        overlayView.Layer.RemoveAllAnimations();
        overlayView.Frame = new CGRect(0, 0, view.Frame.Width, view.Frame.Height);
        overlayView.BackgroundColor = UIColor.Black.ColorWithAlpha(0.4f);
        view.AddSubview(overlayView);
    }
}

static UIView GetOverlayView(UIView view)
{
    const int overlayViewTagNumber = 38483;

    var overlayViewTag = new IntPtr(overlayViewTagNumber);
    var overlayView = view.Subviews
        .AsEnumerable()
        .FirstOrDefault(x => x.Tag == overlayViewTag);

    if (overlayView is null)
    {
        overlayView = new UIView();
        overlayView.Tag = overlayViewTag;
    }

    return overlayView;
}
--- Remove End --- */

I added a self-made overlay in #1369 because I didn't think there was an overlay provided by the OS.
However, the overlay provided by the OS was present.
I didn't notice it was there because the overlay was transparent.

The overlay provided by the OS existed under the class name "_UIPopoverDimmingView" when searching the hierarchy below ContainerView of PresentationController. This is the overlay provided by the OS.

You need to reset the transparency of this overlay.

Next, I added the code below to erase the shadow of the Popup provided by the OS, but I will make this part general.

[src\CommunityToolkit.Maui.Core\Views\Popup\MauiPopup.macios.cs]

public override void ViewDidLayoutSubviews()
{
    base.ViewDidLayoutSubviews();

    _ = View ?? throw new InvalidOperationException($"{nameof(View)} cannot be null.");

    View.Superview.Layer.CornerRadius = 0.0f;
    View.Superview.Layer.MasksToBounds = false;

    /* --- Remove Start ---
    if (OperatingSystem.IsIOSVersionAtLeast(17))
    {
        if (VirtualView?.Color == Colors.Transparent)
        {
            View.Superview?.Superview?.Subviews.OfType<UIImageView>().FirstOrDefault()?.RemoveFromSuperview();
        }
        else
        {
            View.Superview?.Superview?.Superview?.Subviews.OfType<UIImageView>().FirstOrDefault()?.RemoveFromSuperview();
        }
    }
    else
    {
        PresentationController?.ContainerView?.Subviews.OfType<UIImageView>().FirstOrDefault()?.RemoveFromSuperview();
    }
    --- Remove End --- */

    SetElementSize(new Size(View.Bounds.Width, View.Bounds.Height));
}

The Popup shadow added by the OS exists with the class name "_UICutoutShadowView" when you search the hierarchy below ContainerView of PresentationController. This is the first shadow I removed in this PR.

Based on the above, here's what we should do.

public override void ViewDidLayoutSubviews()
{
    base.ViewDidLayoutSubviews();

    _ = View ?? throw new InvalidOperationException($"{nameof(View)} cannot be null.");

    View.Superview.Layer.CornerRadius = 0.0f;
    View.Superview.Layer.MasksToBounds = false;

    SetShadowView(PresentationController!.ContainerView);

    SetElementSize(new Size(View.Bounds.Width, View.Bounds.Height));
}

void SetShadowView(UIView target)
{
    if (target.Class.Name == "_UICutoutShadowView")
    {
        target.RemoveFromSuperview();
    }
    if (target.Class.Name == "_UIPopoverDimmingView")
    {
        target.BackgroundColor = UIColor.Black.ColorWithAlpha(0.4f);
    }

    foreach (UIView view in target.Subviews)
    {
        SetShadowView(view);
    }
}

Add the SetShadowView method to remove the shadow added by the OS and change the transparency of the overlay.
This allows you to remove shadows and change the transparency of overlays provided by the OS.

Below is the verification video.

iPhone.13.iOS.15.5.2024-01-19.09-46-39.mp4

The shadow that was displayed on the Popup disappears, and the overlay gets darker as you add more Popups, so you can see that it is displayed as intended.

@Toine-db
Copy link

@cat0363 you have been busy, great job finding the issue. I wasn't able to find time to help you with this issue, sorry for that.

Your solution looks promising, hopefully it will be released soon.

@cat0363
Copy link
Contributor Author

cat0363 commented Jan 19, 2024

@Toine-db , Thank you for your reply.
Since I am a contributor, I would appreciate if a Community Toolkit member would review this PR.
It would have been nice if the handler could handle this, but it seems better to wait for release.

@maonaoda
Copy link

thank you for your investigation

@Toine-db
Copy link

Toine-db commented Mar 4, 2024

Hi Guys,
How is it going with finalising this issue ?

@leonedev
Copy link

Hi guys, when it will be ready? Many thanks!

Copy link
Collaborator

@brminnick brminnick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @cat0363! Apologies this took so long to merge.

@brminnick brminnick enabled auto-merge (squash) March 25, 2024 22:30
@brminnick brminnick merged commit c4074c5 into CommunityToolkit:main Mar 25, 2024
8 checks passed
@cat0363 cat0363 mentioned this pull request May 14, 2024
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[BUG] Popup Background Is Not Transparent on iOS for .Net8
6 participants