iOS Code Snippet Control general audio volume

JackKirk

Well-Known Member
Licensed User
I have to say I did a lot of head banging over this.

It appears that iPhones have 2 sets of audio, each with their own volume:
  • "General audio" - music, TTS etc
  • "Ringer" or "ringtones/alerts" - incoming phone call, SMS arrivals, other notifications AND pseudo camera shutter sound.
This link makes a reasonably good stab at explaining it all:

http://artoftheiphone.com/2012/02/13/basics-the-iphones-separate-volume-controls-for-ringtones-and-general-audio/

The following code snippet allows you to control the general audio volume and includes some Objective C:
  • That originated with this post of Erel's:
  • Was extensively modified by me, using bloody-minded determination and lots of googling as a substitute for any Objective C competence, I'm particularly indebted to:
  • Required a final nudge by JanPRO:
  • Allows you to change the general audio volume (0 = mute, 1= full).
  • Theoretically(**) allows you to get the current general audio volume.
  • Hides the stupid "general audio volume HUD" (the little grey square with a speaker icon in it) that would otherwise appear.
The Objective C comes wrapped in a simple B4I example whose usage is pretty self evident when you copy the snippet into a new B4I project then compile and run it.
B4X:
'Code module
#Region  Project Attributes
    #ApplicationLabel: Control General Audio Volume
    #Version: 1.0.0
    'Orientation possible values: Portrait, LandscapeLeft, LandscapeRight and PortraitUpsideDown
    #iPhoneOrientations: Portrait, LandscapeLeft, LandscapeRight
    #iPadOrientations: Portrait, LandscapeLeft, LandscapeRight, PortraitUpsideDown
    #Target: iPhone, iPad
    #MinVersion: 7
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'Public variables can be accessed from all modules.
    Public App As Application
    Public NavControl As NavigationController
    Private Page1 As Page

    Private xTTS As TTS
    Private label1 As Label
    Private volume_value As Float

End Sub

Private Sub Application_Start (Nav As NavigationController)
    NavControl = Nav
    Page1.Initialize("Page1")
    Page1.Title = "Page 1"
    Page1.RootPanel.Color = Colors.White
    NavControl.ShowPage(Page1)

    'Initialize TTS
    xTTS.Initialize("")

    Audiovolume_hide

End Sub

Private Sub Page1_Resize(Width As Int, Height As Int)

    Page1.RootPanel.RemoveAllViews

    label1.Initialize("")
    Page1.RootPanel.AddView(label1, 0, 0, Page1.RootPanel.Width, Page1.RootPanel.Height)
    label1.Multiline = True
    label1.Text = "Tap screen to change volume" & CRLF & "Volume set at: " & volume_value & CRLF & "Volume reported: " & Audiovolume_get

End Sub

Private Sub Page1_Click

    volume_value = volume_value - .25
    If volume_value < 0 Then volume_value = 1

    Audiovolume_set(volume_value)

    label1.Text = "Tap screen to change volume" & CRLF & "Volume set at: " & volume_value & CRLF & "Volume reported: " & Audiovolume_get

    'Speak away
    xTTS.Stop
    xTTS.Speak("testing testing 1 2 3 4 5 6 7 8 9 10", True)

End Sub

Private Sub Application_Background

End Sub

Sub Audiovolume_hide
    Dim no As NativeObject = Me
    no.RunMethod("audiovol_hide", Null)
End Sub

Sub Audiovolume_get As Float
    Dim no As NativeObject = Me
    Return no.RunMethod("audiovol_get", Null).AsNumber
End Sub

Sub Audiovolume_set(volumevalue As Float)
    Dim no As NativeObject = Me
    no.RunMethod("audiovol_set:", Array(volumevalue))
End Sub

#If OBJC
@import MediaPlayer;
- (void) audiovol_hide
{
    //following hides "general audio volume HUD" - see:
    //http://stackoverflow.com/questions/7430985/hide-grey-volume-overlay-when-using-mpvolumeview
    MPVolumeView *volumeView = [[MPVolumeView alloc] initWithFrame:CGRectMake(-2000., -2000., 0.f, 0.f)];
    NSArray *windows = [UIApplication sharedApplication].windows;
    volumeView.alpha = 0.1f;
    volumeView.userInteractionEnabled = NO;
    if (windows.count > 0) {
        [[windows objectAtIndex:0] addSubview:volumeView];
    }
}
- (float) audiovol_get
{
    MPVolumeView *volumeView = [[MPVolumeView alloc] init];
    //find general audio volumeViewSlider
    UISlider* volumeViewSlider = nil;
    for (UIView *view in [volumeView subviews]){
        if ([view.class.description isEqualToString:@"MPVolumeSlider"]){
            volumeViewSlider = (UISlider*)view;
            break;
        }
    }
    // get general audio volume here:
    static float volval = .123;
    // if comment next line out then this method returns .123 - proving that next
    // line actually always returns 0 (at least in iOS 9.3)
    volval = [volumeViewSlider value];
    return volval;
}
- (void) audiovol_set: (float) volval
{
    MPVolumeView *volumeView = [[MPVolumeView alloc] init];
    //find general audio volumeViewSlider
    UISlider* volumeViewSlider = nil;
    for (UIView *view in [volumeView subviews]){
        if ([view.class.description isEqualToString:@"MPVolumeSlider"]){
            volumeViewSlider = (UISlider*)view;
            break;
        }
    }
    // set general audio volume here:
    [volumeViewSlider setValue:volval animated:YES];
    [volumeViewSlider sendActionsForControlEvents:UIControlEventTouchUpInside];
}
#End If
A few notes:

(1) Re getting the current general audio volume:​

Above I say "Theoretically(**) ..." - if you look closely at the code at the bottom of the - (float) audiovol_get Objective C method you will see that I could only ever get a value of 0 returned.

I could only test on my iPhone 4s running iOS 9.3 and there are some google hits that suggest that [volumeViewSlider value] is broken in iOS 9, for example:

http://stackoverflow.com/questions/35214008/how-to-change-the-volume-in-ios9

I would be really interested to know if getting the current general audio volume with this snippet works in earlier iOS releases.
(2) This only gives control of general audio volume - for control of ringer volume look at the sister snippet here:

Any and all comments welcome...
 

Attachments

Last edited:

salvadoro

Member
Licensed User
Hi,

For newer versions of iOS you need to add a delay

B4X:
- (void) audiovol_set: (float) volval 

{

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

    //find general audio volumeViewSlider

    UISlider* volumeViewSlider = nil;

    for (UIView *view in [volumeView subviews]){

        if ([view.class.description isEqualToString:@"MPVolumeSlider"]){

            volumeViewSlider = (UISlider*)view;

            break;

        }

    }

    // set general audio volume here:

      //[volumeViewSlider setValue:volval animated:YES];

    //[volumeViewSlider sendActionsForControlEvents:UIControlEventTouchUpInside];

   

    // For newer version we need to add a delay

    // How to change volume programmatically on iOS 11.4

    // https://stackoverflow.com/questions/50737943/how-to-change-volume-programmatically-on-ios-11-4/50740074

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

        [volumeViewSlider setValue:volval animated:YES];

        [volumeViewSlider sendActionsForControlEvents:UIControlEventTouchUpInside];

      });

   

}
 
Top