hirbod / react-native-volume-manager

React Native module which adds the ability to change the system volume on iOS and Android, listen to volume changes and supress the native volume UI to build your own volume slider or UX. It can listen to iOS mute switch and ringer mode changes on Android (and let you set the ringer mode)
MIT License
216 stars 13 forks source link

iOS 16.4. showNativeVolumeUI - no effect #8

Closed olegderecha closed 1 year ago

olegderecha commented 1 year ago

I applied some actions on the volume button press and used this library for it. Before my iPhone has not been updated to 16.4 - everything worked well:

After iPhone has been updated to 16.4 await VolumeManager.showNativeVolumeUI({ enabled: false }); doesn't affect anymore and await VolumeManager.setVolume(0.5, { showUI: false }); too.

hunterfoulk commented 1 year ago

Same here on both IOS and Android, seems showNativeVolumeUI is not working properly.

Sewb21 commented 1 year ago

I've noticed that showNativeVolumeUI seems to break when react native Modals are open 🤔

hirbod commented 1 year ago

Hey guys, I am currently pretty caught up with work so I can't make any promises but I try to look into this soon.

olegderecha commented 1 year ago

My college, iOS developer, tried some variants to hide it on the native side, but all solutions below didn't work (I forwarded his investigation):

This approach did not work: applicationMusicPlayer volume notification

CGRect rect = CGRectMake(-500, -500, 0, 0);
MPVolumeView *volumeView = [[MPVolumeView alloc] initWithFrame:rect];
- (void)showVolumeUI:(BOOL)flag {
    if (!flag) {
        [[[[UIApplication sharedApplication] keyWindow] rootViewController].view addSubview:emptyVolumeView];
        // [volumeView removeFromSuperview];
    } else if (flag) {
        [emptyVolumeView removeFromSuperview];
        // [[[[UIApplication sharedApplication] keyWindow] rootViewController].view addSubview:volumeView];
    }
}

neither did this: applicationMusicPlayer volume notification

- (void)showVolumeUI:(BOOL)flag {
    if (!flag) {
        [[[[UIApplication sharedApplication] keyWindow] rootViewController].view addSubview:emptyVolumeView];
        [volumeView removeFromSuperview];
    } else if (flag) {
        [emptyVolumeView removeFromSuperview];
        [[[[UIApplication sharedApplication] keyWindow] rootViewController].view addSubview:volumeView];
    }
}

or this: (my idea)

- (void)showVolumeUI:(BOOL)flag {
  if (flag && [volumeSlider superview]) {
    [volumeSlider removeFromSuperview];
  } else if (!flag && ![volumeSlider superview]) {
    [[[[UIApplication sharedApplication] keyWindow] rootViewController].view addSubview:volumeSlider];
  }
}

or this: applicationMusicPlayer volume notification

- (void)showVolumeUI:(BOOL)flag {
    if (flag) { //} && [volumeView superview]) {
        volumeViewRect = volumeView.frame;
        [volumeView setFrame:CGRectMake(-1000,
                                        -1000,
                                        10,
                                        10)];
//    [volumeView removeFromSuperview];
    } else if (!flag) { //} && ![volumeView superview]) {
        [volumeView setFrame:volumeViewRect];
//    [[[[UIApplication sharedApplication] keyWindow] rootViewController].view addSubview:volumeView];
  }
}

or this: (simplified if() logic)

- (void)showVolumeUI:(BOOL)flag {
  if (!flag) {
    [volumeView removeFromSuperview];
  } else if (flag) {
    [[[[UIApplication sharedApplication] keyWindow] rootViewController].view addSubview:volumeView];
  }
}

This next approach didn’t work, but interestingly, it didn’t show he volume slider for several photos while using the shutter button. Once I used the volume button one time, it continued to show on each transition into and out of the camera.

- (void)showVolumeUI:(BOOL)flag {
  if (flag && [volumeView superview]) {
    [volumeView removeFromSuperview];
  } else if (!flag && ![volumeView superview]) {
    [[[[UIApplication sharedApplication] keyWindow] rootViewController].view addSubview:volumeView];
  } else if (!flag && [volumeView superview]) {
    [volumeView removeFromSuperview];
  } else if (flag && ![volumeView superview]) {
    [[[[UIApplication sharedApplication] keyWindow] rootViewController].view addSubview:volumeView];
  }
}

This too did not work:

- (instancetype)init {
  self = [super init];
  if (self) {
    [self addVolumeListener];
  }

  [self initVolumeView];
    volumeSlider.alpha = 0;
    volumeView.alpha = 0;

  return self;
}

I tried adding this to Appdelegate to see if I could disable the volume slider globally, NO GO: How to add the MPVolumeView via Xcode Designer?

  containerView = [[UIView alloc] init];
  MPVolumeView *volumeView = [[MPVolumeView alloc] init];

  [containerView addSubview:volumeView];
  [volumeView setShowsVolumeSlider:NO];
  [volumeView setHidden:YES];

@hirbod , maybe it can help somehow.

hirbod commented 1 year ago

Quick question: Are you actually playing any audio or video while adjusting the volume? If not, make sure to call setActive and use the ambient category. Otherwise, the HUD won't be suppressed irc.

Thanks for your investigations @olegderecha, we will have to watch out for API changes in 16.4

olegderecha commented 1 year ago

@hirbod , Seems it tried all combinations:

await VolumeManager.enable(true, true);
await VolumeManager.setActive(true, true);
await VolumeManager.setCategory('Ambient');
await VolumeManager.showNativeVolumeUI({ enabled: false });

plus:

await VolumeManager.setVolume(0.5, {
  showUI: false,
});

I think something changed in iOS layout and all solutions that could work before (they worked before!) - don't work anymore.

Sewb21 commented 1 year ago

Just to add, I am experiencing issues with showNativeVolumeUI on iOS and Android but only when Modals are open.

Are you using any visible modals too @olegderecha ?

olegderecha commented 1 year ago

@Sewb21 , yes, I had one open modal when using showNativeVolumeUI, I re-wrote the code without it and the issue is still here. By the way, we are using React Native Navigation as a navigation library.

olegderecha commented 1 year ago

Seems the issue has gone with iOS 16.4 (20E246). Could anybody confirm?

hirbod commented 1 year ago

Thanks. I could not reproduce this issue. Please re-open if you encounter this again.

hirbod commented 1 year ago

Hey everybody,

I was able to reproduce the issue related to modals. iOS is pretty smart in detecting whether a volume view is visible or not. That's also the reason I had issues hiding/moving the AirPlay Icon out of view. iOS would always detect this and show the native volume view. The same happens with modals since they are always mounted on top of everything.

I have made some changes to ensure that the invisible volume view is always mounted as the topmost view. The upcoming version 1.5.0 addresses this issue, and the fix is also backwards compatible.

I just need to make sure that this also works for Android.

olegderecha commented 1 year ago

@hirbod , are talking about this case?

image

Because we have AirPlay icon when await VolumeManager.showNativeVolumeUI({ enabled: false }); is called, but the interesting thing, is that it appears when the screen is open not in a modal, and doesn't appear when inside a modal. But for sure this specific case can be because of using not React Navigation but React Native Navigation package. It would be cool to be fixed in the next version. 🤞

hirbod commented 1 year ago

The Airplay Icon should actually be fixed in the latest version, the other issue I was referring to was that hiding the slider did not work in modals.

hirbod commented 1 year ago

I finally found a way to make this module work in modals for iOS and Android

hirbod commented 1 year ago

https://github.com/hirbod/react-native-volume-manager/releases/tag/v1.5.0 was released. I hope this one fixes all reported issues.