5

I'm creating an overlay which will cover all displaying views on screen. This overlay always appears even in case rootViewController changes, pushing or presenting.

My idea is

  • Create CustomWindow which is a subclass of UIWindow. After that replacing default window of UIApplication with CustomWindow, create a new rootViewController for my new window.
  • In CustomWindow, I have an overlay (is an UIView). Overlay have light gray color with an alpha and every event on overlay will be pass through to below view.
  • Whenever CustomWindow add a new subview, i will bring overlay to front. It's make sure overlay will be on the top in every case.

CustomWindow

@implementation CustomWindow

- (instancetype)initWithFrame:(CGRect)frame {
  self = [super initWithFrame:frame];

  if (self) {
    _overlay = [[UIView alloc] initWithFrame:self.bounds];
    _overlay.userInteractionEnabled = NO;
    _overlay.backgroundColor = [UIColor lightGrayColor];
    _overlay.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    [self addSubview:_overlay];
  }

  return self;
}

- (void)didAddSubview:(UIView *)subview {
  [super didAddSubview:subview];

  [self bringSubviewToFront:_overlay];
}

@end

Everything works fine in every case even when pushing, presenting or changing rootViewController.

Problem

But when i show an UIActivityViewController, I can't click on any extensions which are displayed on UIActivityViewController.

Magically

  • When i click outside of UIActivityViewController or click on Cancel Button, UIActivityViewController is dismissed normally.

  • If i change color of overlay to clearColor, it works fine too.

My question is

  • How can i touch on extensions when i have overlay on window and overlay have a color ?

  • If i can't, can anyone tell me why it happens ? It's perfect when you can quote the reason from a document.

I'm pretty sure this doesn't relate to how i initialize UIActivityViewController or the way i show UIActivityViewController.

MORE

I found a problem quite similar to this problem on Android. But i'm not sure because i haven't seen any official document about it from Apple. One more thing is when changing color to clearColor can affect touch. So actually, i don't think they are same.

trungduc
  • 11,323
  • 4
  • 29
  • 51
  • I'm not on my mac to test your code but just got an idea. Did you try setOpaque method? – Naveed Abbas Nov 04 '17 at 11:15
  • @ToughGuy Thanks for your suggestion but `setOpaque` doesn't help me in this case. Slowly, I can wait for another question from you when you can test my code. – trungduc Nov 04 '17 at 11:20
  • I tried to make the Gray Box smaller in height making the buttons half visible. Basically touching any view other than ActivityViewController gives a responder event. – Naveed Abbas Nov 04 '17 at 12:56
  • Try to Apply a Gesture and cancel touches so it doesn't receive any touch at all. – Naveed Abbas Nov 04 '17 at 12:57
  • @ToughGuy did you try to click on `Cancel Button` or other things i wrote in **Magically**? – trungduc Nov 04 '17 at 12:58
  • Yes. Maybe, the cancel button seems to be at application level layer. Just like an Alert Box. But it looks more like the cancel button is ignoring all other touches. – Naveed Abbas Nov 04 '17 at 12:59
  • How about changing color of `overlay` to clear color? If you do it, you can click on everything. But window still has overlay on the top. I don't think color of view can affect touch – trungduc Nov 04 '17 at 13:01
  • I am experiencing it for several years. Making it's opacity to 0 doesn't receive touches. – Naveed Abbas Nov 04 '17 at 13:04
  • I tried to apply gesture on the custom view but it is not receiving any touches. I'm in a hurry right now. Good luck. – Naveed Abbas Nov 04 '17 at 13:21
  • @ToughGuy thank you very much ;) – trungduc Nov 04 '17 at 13:24

1 Answers1

3

This is due to a UIRemoveView (private) in the hierarchy. As best I can determine, your app cannot forward events directly to remote views. I suspect this is a security measure to prevent you from presenting the share dialog and automatically sending a touch event to it to do an external action the user didn't request. Remote views don't run in your application's process. The "Copy" button is interacted with across an XPC link.

This all means that if the remote view is covered by one of your views, there's no way (at least that I've found) to interact with it. You have to ensure that you're not covering it.

Actually doing that is simple. The thing that holds the remote view is called a UITransitionView and is used for other OS-level things that you probably shouldn't be covering either. So don't:

- (void)didAddSubview:(UIView *)subview {
    [super didAddSubview:subview];

    if ([subview isKindOfClass:NSClassFromString(@"UITransitionView")]) {
        // To raise it above your overlay;
        // otherwise it's immediately above the view controller (below the overlay)
        [self bringSubviewToFront:subview];
    } else {
        [self bringSubviewToFront:self.overlay];
    }
}

But.... This requires you to talk about UITransitionView in your code. This is both fragile, and possibly a forbidden use of private APIs.

Otherwise you'll have to wrap your UIActivityViewController requests with some call/notification that tells the window not to cover views until we're done (which you'll have to clear in the completion handler).

Rob Napier
  • 250,948
  • 34
  • 393
  • 528
  • As you said **if the remote view is covered by one of your views, there's no way (at least that I've found) to interact with it**, but when i change color of `overlay` to `clearColor`. I can touch extension items normally. Do you think we have something wrong here? – trungduc Nov 04 '17 at 15:31
  • Since invisible views do not capture touch events, I suspect that the touch is being forwarded directly to the remote view rather than going through your view. – Rob Napier Nov 04 '17 at 16:26
  • Totally agree. Seem like it's the answer i'm expecting. Once again thank you very much ;) – trungduc Nov 04 '17 at 16:58