2009-04-21 6 views

Respuesta

21

Después de los recientes rechazos de Apple

No utilice este. Apple ahora usa algún parche que rechazaría su aplicación inmediatamente si usa cualquiera de las API privadas, aunque debe tener en cuenta que algunas aplicaciones en la App Store ya lo usan y siguen ahí.

La única manera de hacer esto ahora es tener un AVAudioPlayer preparado para jugar pero no para jugar ([player prepareToPlay]). Esto parece ocuparse de ajustar el volumen de la aplicación de acuerdo con los botones basculantes.

No hay otra forma publicada actualmente para manejar esto.

POR FAVOR LEA LO ANTERIOR NOTA

Sí, Usar la MPVolumeView

MPVolumeView *volume = [[[MPVolumeView alloc] initWithFrame:CGRectMake(18.0, 340.0, 284.0, 23.0)] autorelease]; 
    [[self view] addSubview:volume]; 

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(volumeChanged:) 
             name:@"AVSystemController_SystemVolumeDidChangeNotification" 
             object:nil];  
    for (UIView *view in [volume subviews]){ 
    if ([[[view class] description] isEqualToString:@"MPVolumeSlider"]) { 
     volumeViewSlider = view; //volumeViewSlider is a UIView * object 
    } 
    } 
    [volumeViewSlider _updateVolumeFromAVSystemController]; 

-(IBAction)volumeChanged:(id)sender{ 
    [volumeViewSlider _updateVolumeFromAVSystemController]; 
} 

Esto le dará un control deslizante (el mismo que se utilizó en el iPod) cuyo valor cambiará según el volumen de la teléfono

Recibirá una advertencia en tiempo de compilación de que la vista puede no responder a _updateVolumeFromAVSystemControl, pero simplemente ignórelo.

+0

Sí, estaba pensando en escuchar VolumeChanged, pero me pregunto si todavía recibirá un mensaje cuando no ocurra un cambio de volumen real (por ejemplo, el volumen está al máximo y lo estoy aumentando)? – COTOHA

+0

puede probar el código en un dispositivo y ver si se recibe el evento. – lostInTransit

+2

No puede enviar una aplicación con este código, usa una API privada. De eso se trata la advertencia de tiempo de compilación, y el prefijo del subrayado también es un regalo. – duncanwilcox

1

Si desea sumergir en la API privada, tengo a patch to Wolf3d que agrega exactamente la funcionalidad que está buscando. Se utiliza la clase privada AVSystemController y algunos métodos ocultos en UIApplication

17

Si lo que desea es obtener las notificaciones, creo que es así:

Por favor, corríjanme si estoy equivocado, pero no creo esto usa cualquier API interna.

[[NSNotificationCenter defaultCenter] addObserver:self 
     selector:@selector(volumeChanged:) 
     name:@"AVSystemController_SystemVolumeDidChangeNotification" 
     object:nil]; 

detalles de este evento está aquí: http://www.cocoadev.com/index.pl?AVSystemController

Las otras respuestas aquí parecen estar basadas en este truco: http://blog.stormyprods.com/2008/09/proper-usage-of-mpvolumeview-class.html que era una solución para un error ahora fijo.

Pero estoy bastante seguro de que si lo que quieres hacer es LLEGAR la notificación y no SET el volumen del sistema, sólo puede utilizar el centro de notificación al igual que con cualquier otro evento !!

tenga en cuenta: desde que Apple añadió la acción de volumen a la cámara, esta notificación es no publicado mientras que un UIImagePickerController es visible.

+0

Confirmar funciona para iOS 7. Esta debería ser la respuesta aceptada. –

+2

Tenga en cuenta que esta notificación * no * se envía cuando el 'UIImagePickerController' (por ejemplo, para la cámara) está visible. –

+0

si se verifica que debe ser editado en su respuesta. –

1

bien,

así que vi sus soluciones y no se sabe exactamente si Apple va a rechazar o aceptar el uso de AVSystemController_SystemVolumeDidChangeNotification. Pero tengo un trabajo alrededor.

Uso UISlider de MPVolumeView para el registro de los cambios en el volumen por el hardware del iPhone como esto

MPVolumeView *volumeView = [[MPVolumeView alloc] initWithFrame:CGRectZero]; 

for (UIView *view in [volumeView subviews]) { 
    if ([view.class.description isEqualToString:@"MPVolumeSlider"]){ 
     self.volume_slider = (UISlider*)view; 
     break; 
    } 
} 
[volumeView sizeToFit]; 
#THIS IS THE MAIN LINE. ADD YOUR CALLBACK TARGET HERE 
[self.volume_slider addTarget:self action:@selector(volumeListener:) forControlEvents:UIControlEventValueChanged]; 
[self addSubview:volumeView]; 
[volumeView setAlpha:0.0f]; 

-(void)volumeListener:(NSNotification*)notification { 
    #UPDATE YOUR UI ACCORDING OR DO WHATEVER YOU WANNA DO. 
    #YOU CAN ALSO GET THE SOUND STEP VALUE HERE FROM NOTIFICATION. 
} 

Avísame si esto ayuda a nadie.

+0

Entra dos veces al principio, no pude entender por qué. – ondermerol

1

La forma más fácil y funcionalmente completa de hacer esto que he encontrado al estudiar todas las fuentes mencionadas anteriormente y en otros hilos es: JPSVolumeButtonHandler (no estoy involucrado más que como usuario. Pero muchas gracias a la gente responsable!)

EDITAR: La versión 1.0.2 viene con algunos cambios/mejoras significativas. Dejaré mi respuesta anterior para 1.0.1 debajo de la tapa.

Puse una clase de contenedor de muestra que puede implementar tal como está, o usar para aprender el uso correcto de JPSVolumeButtonHandler en un separate Github repository real quick here.

Así es como se entiende el envoltorio para ser utilizado (Voy a añadir al repositorio tan pronto como llegue a él):

  1. clase singleton tiene dos banderas: isInUse y isOn. isInUse está destinado a establecerse en algún tipo de configuración general de la aplicación y activar y desactivar el botón de soporte en general. Entonces, no importa ningún otro valor en la clase, si esto es false, nada sucederá cuando el usuario presione un botón de volumen y la implementación se asegura tanto como sea posible para mantener las cosas limpias y no afectar el nivel de volumen del sistema innecesariamente. (Lea el problema mencionado en el README para saber qué puede suceder, cuando se activa la compatibilidad de botones por primera vez). isOn está destinado a ser true exactamente durante el tiempo que se necesita el botón. Puede activarlo y desactivarlo sin tener en cuenta el valor presente de isInUse.

  2. En cualquier vista que inicializar la acción que se supone que sucede cuando un botón de volumen se presiona, establecer la acción de esta manera:

    PhysicalButton.shared.action = {/ * hacer algo * /}

La acción tiene el tipo () -> Void. Hasta que inicie la acción, nada se romperá. Simplemente nada sucederá Esta funcionalidad defensiva era importante para mí, ya que la vista que utiliza la compatibilidad con botones de volumen solo se crearía después de configurar el soporte de botones.

Para ver cosas en acción, puede download the app que estoy usando esto de manera realmente rápida y gratuita. Las configuraciones manipulan "Soporte de botón físico" en general. La vista principal de Cronómetro es la que realmente activa el manejo de los botones al ingresar a la vista, y se apaga al salir de ella. Si encuentra que el tiempo, también encontrará una nota importante allí en Configuración> Guía del usuario> Opción: Soporte botón físico:

En circunstancias excepcionales, la aplicación puede no tener la oportunidad de botón de volumen adecuadamente conmutador manipulación fuera de la vista del cronómetro ...

Voy a agregar la nota completa al GITHUB README.md. Siéntase libre de adaptarlo y reutilizarlo, si es relevante en su caso.

Las circunstancias en realidad no son tan excepcionales y no he descubierto por completo cuál es el problema. Cuando el usuario cancela la aplicación (o simplemente detiene su aplicación desde Xcode) mientras los botones de volumen están activados, es posible que la compatibilidad con los botones físicos no se elimine del sistema operativo. Por lo tanto, puede terminar con dos instancias de controlador interno, solo una de las cuales tiene control. Entonces, cada vez que toca un botón, se producen dos o más llamadas a la rutina de acción. Mi envoltorio tiene un código de guardián para evitar una invocación demasiado rápida del botón. Pero eso es solo una solución parcial. La solución debe ir al controlador subyacente, que lamentablemente aún tengo muy poco conocimiento para tratar de arreglar las cosas por mí mismo.


viejo, para 1.0.1:

En particular, mi interés estaba en una solución rápida. El código está en Objective-C. Para salvar a alguien un poco de investigación, esto es todo lo que hice usando Cocoapods (para principiantes como yo):

  1. Añadir pod 'JPSVolumeButtonHandler' a la PODFILE
  2. Run pod install en la línea de comandos
  3. Añadir #import <JPSVolumeButtonHandler.h> en el fichero de cabecera de puente
  4. Establecer devoluciones de llamada para el volumen botones arriba y abajo, así:

    let volumeButtonHandler = JPSVolumeButtonHandler(
        upBlock: { 
         log.debug("Volume up button pressed...") 
         // Do something when the volume up button is pressed... 
        }, downBlock: { 
         log.debug("Volume down button pressed...") 
         // Do something else for volume down... 
        }) 
    

Eso es todo. El resto es opcional.


En mi caso, quería para permitir la superposición de botón físico empuja con botones virtuales en la pantalla solo para ciertos puntos de vista, mientras se asegura de bloquear la menor de las funciones normales del botón como sea posible (de modo que el usuario puede ejecutar música en segundo plano y ajustar su volumen en el resto de la aplicación). Terminé con una clase Singleton sobre todo de la siguiente manera:

class OptionalButtonHandler { 

    static var sharedInstance: OptionalButtonHandler? 

    private var volumeButtonHandler: JPSVolumeButtonHandler? = nil 
    private let action:() ->() 

    var enabled: Bool { 
    set { 
     if !enabled && newValue { 
      // Switching from disabled to enabled... 
      assert(volumeButtonHandler == nil, "No leftover volume button handlers") 
      volumeButtonHandler = JPSVolumeButtonHandler(upBlock: { 
       log.debug("Volume up button pressed...") 
       self.action() 
       }, downBlock: { 
        log.debug("Volume down button pressed...") 
        self.action() 
      }) 
     } else if enabled && !newValue { 
      log.debug("Disabling physical button...") 
      // The other way around: Switching from enabled to disabled... 
      volumeButtonHandler = nil 
     } 
    } 
    get { return (volumeButtonHandler != nil) } 
    } 

    /// For one-time initialization of this otherwise singleton class. 
    static func initSharedInstance(action:() ->()) { 
     sharedInstance = OptionalButtonHandler(action: action) 
    } 

    private init(action:() ->()) { 
     self.action = action 
    } 
} 

Sólo hay una acción común para tanto hacia arriba y hacia abajo los botones de volumen aquí. El initSharedInstance() era necesario, porque mi acción incluía referencias a un elemento de IU (una vista) que solo se configuraría en algún punto dependiente del usuario después del lanzamiento de la aplicación.

Una vez establecido este modo:

OptionalButtonHandler.initSharedInstance({ 
    // ...some UI action 
}) 

activar/desactivar selectivamente simplemente así:

OptionalButtonHandler.sharedInstance!.enabled = true // (false) 

(Nótese que mi lógica del código se asegura de que .enabled Nunca se accede antes initSharedInstance().)

Estoy ejecutando Xcode 7.3 y iOS 9.3.2 en el dispositivo de prueba (¡requerido!).

Estamos deseando saber cómo se siente Apple por sobrecargar sus preciosos botones de volumen. Al menos mi aplicación se asegura de ser mínimamente invasiva y el uso del botón realmente tiene sentido. No es una aplicación de cámara, pero las aplicaciones comparables han usado botones de volumen físico antes (incluso menos).

+0

Probé esto en el último iOS 10.2 y no funciona – ShayanK

+0

Actualicé la respuesta muy rápido (o realmente no tan rápido). Espero eso ayude. Paso bastante tiempo con la nueva versión 1.0.2 para que mi código funcione. Déjame saber dónde más explicaciones pueden ayudar. – marco

Cuestiones relacionadas