¿Cómo puedo detectar cuando alguien sacude un iPhone?
Quiero reaccionar cuando alguien sacude el iPhone. No me importa en particular cómo lo sacudan, solo que se agitó vigorosamente durante una fracción de segundo. ¿Alguien sabe cómo detectar esto?
16 answers
En la versión 3.0, ahora hay una forma más fácil: engancharse a los nuevos eventos de movimiento.
El truco principal es que necesita tener algún UIView (no UIViewController) que desee como firstResponder para recibir los mensajes del evento shake. Aquí está el código que puede usar en cualquier UIView para obtener eventos shake:
@implementation ShakingView
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if ( event.subtype == UIEventSubtypeMotionShake )
{
// Put in code here to handle shake
}
if ( [super respondsToSelector:@selector(motionEnded:withEvent:)] )
[super motionEnded:motion withEvent:event];
}
- (BOOL)canBecomeFirstResponder
{ return YES; }
@end
Puede transformar fácilmente cualquier UIView (incluso vistas del sistema) en una vista que puede obtener el evento shake simplemente subclasificando la vista con solo estos métodos (y luego seleccionando este nuevo escriba en lugar del tipo base en IB, o usándolo al asignar una vista).
En el controlador de vista, desea configurar esta vista para que se convierta en el primer respondedor:
- (void) viewWillAppear:(BOOL)animated
{
[shakeView becomeFirstResponder];
[super viewWillAppear:animated];
}
- (void) viewWillDisappear:(BOOL)animated
{
[shakeView resignFirstResponder];
[super viewWillDisappear:animated];
}
No olvide que si tiene otras vistas que se convierten en el primer respondedor de las acciones del usuario (como una barra de búsqueda o un campo de entrada de texto), también deberá restaurar el estado del primer respondedor de la vista temblorosa cuando la otra vista renuncie.
Este método funciona incluso si establece applicationSupportsShakeToEdit en NO.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-28 20:42:14
De mi aplicación Diceshaker :
// Ensures the shake is strong enough on at least two axes before declaring it a shake.
// "Strong enough" means "greater than a client-supplied threshold" in G's.
static BOOL L0AccelerationIsShaking(UIAcceleration* last, UIAcceleration* current, double threshold) {
double
deltaX = fabs(last.x - current.x),
deltaY = fabs(last.y - current.y),
deltaZ = fabs(last.z - current.z);
return
(deltaX > threshold && deltaY > threshold) ||
(deltaX > threshold && deltaZ > threshold) ||
(deltaY > threshold && deltaZ > threshold);
}
@interface L0AppDelegate : NSObject <UIApplicationDelegate> {
BOOL histeresisExcited;
UIAcceleration* lastAcceleration;
}
@property(retain) UIAcceleration* lastAcceleration;
@end
@implementation L0AppDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[UIAccelerometer sharedAccelerometer].delegate = self;
}
- (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
if (self.lastAcceleration) {
if (!histeresisExcited && L0AccelerationIsShaking(self.lastAcceleration, acceleration, 0.7)) {
histeresisExcited = YES;
/* SHAKE DETECTED. DO HERE WHAT YOU WANT. */
} else if (histeresisExcited && !L0AccelerationIsShaking(self.lastAcceleration, acceleration, 0.2)) {
histeresisExcited = NO;
}
}
self.lastAcceleration = acceleration;
}
// and proper @synthesize and -dealloc boilerplate code
@end
La histéresis evita que el evento de sacudida se active varias veces hasta que el usuario detenga la sacudida.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-01 20:43:58
Finalmente lo hice funcionar usando ejemplos de código de este Tutorial del Administrador de Deshacer/Rehacer .
Esto es exactamente lo que necesitas hacer:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
application.applicationSupportsShakeToEdit = YES;
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
-(BOOL)canBecomeFirstResponder {
return YES;
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated {
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake)
{
// your code
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-28 20:43:05
Primero, la respuesta del 10 de julio de Kendall es acertada.
Ahora ... Quería hacer algo similar (en iPhone OS 3.0+), solo que en mi caso lo quería en toda la aplicación para poder alertar varias partes de la aplicación cuando se produjo una sacudida. Esto es lo que terminé haciendo.
Primero, subclase UIWindow. Esto es pan comido. Cree un nuevo archivo de clase con una interfaz como MotionWindow : UIWindow
(siéntase libre de elegir el suyo propio, natch). Añade un método como este:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) {
[[NSNotificationCenter defaultCenter] postNotificationName:@"DeviceShaken" object:self];
}
}
Cambiar @"DeviceShaken"
a la nombre de notificación de su elección. Guarde el archivo.
Ahora, si usa una ventana principal.xib (stock Xcode template stuff), entra ahí y cambia la clase de tu objeto Window de UIWindowa MotionWindow o como lo llames. Guarda el xib. Si configura UIWindow mediante programación, use su nueva clase Window allí en su lugar.
Ahora su aplicación está utilizando la clase especializada UIWindow. Donde quiera que le digan sobre un batido, regístrese para ellos notificaciones! Así:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(deviceShaken) name:@"DeviceShaken" object:nil];
Para quitarse a sí mismo como un observador:
[[NSNotificationCenter defaultCenter] removeObserver:self];
Pongo el mío en viewWillAppear:y viewWillDisappear: en lo que respecta a los Controladores de vista. Asegúrese de que su respuesta al evento shake sepa si está "ya en progreso" o no. De lo contrario, si el dispositivo se agita dos veces seguidas, tendrá un pequeño atasco de tráfico. De esta manera, puede ignorar otras notificaciones hasta que realmente haya terminado de responder a la original notificación.
También: Usted puede elegir cue fuera de motionBegan vs motionEnded. Depende de ti. En mi caso, el efecto siempre tiene que tener lugar después de el dispositivo está en reposo (vs.cuando comienza a temblar), por lo que uso motionEnded. Pruebe ambos y vea cuál tiene más sentido ... o detectar / notificar para ambos!
Uno más (curioso?) observación aquí: Observe que no hay señales de administración de primeros auxilios en este código. Sólo he intentado esto con Controladores de vista de tabla hasta ahora y todo parece funcionar muy bien juntos! Sin embargo, no puedo responder por otros escenarios.
Kendall, et. al - ¿puede alguien hablar de por qué esto podría ser así para UIWindow subclases? ¿Es porque la ventana está en la parte superior de la cadena alimentaria?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-29 14:36:20
Me encontré con este post en busca de una implementación "temblorosa". la respuesta de millenomi funcionó bien para mí, aunque estaba buscando algo que requiriera un poco más de "acción sacudida" para activarlo. He reemplazado a valor booleano con un int shakeCount. También reimplementé el método L0AccelerationIsShaking () en Objective-C. Puedes ajustar la cantidad de agitación requerida ajustando la cantidad de agitación agregada a shakeCount. No estoy seguro de haber encontrado los valores óptimos todavía, pero parece estar funcionando bueno, hasta ahora. Espero que esto ayude a alguien:
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
if (self.lastAcceleration) {
if ([self AccelerationIsShakingLast:self.lastAcceleration current:acceleration threshold:0.7] && shakeCount >= 9) {
//Shaking here, DO stuff.
shakeCount = 0;
} else if ([self AccelerationIsShakingLast:self.lastAcceleration current:acceleration threshold:0.7]) {
shakeCount = shakeCount + 5;
}else if (![self AccelerationIsShakingLast:self.lastAcceleration current:acceleration threshold:0.2]) {
if (shakeCount > 0) {
shakeCount--;
}
}
}
self.lastAcceleration = acceleration;
}
- (BOOL) AccelerationIsShakingLast:(UIAcceleration *)last current:(UIAcceleration *)current threshold:(double)threshold {
double
deltaX = fabs(last.x - current.x),
deltaY = fabs(last.y - current.y),
deltaZ = fabs(last.z - current.z);
return
(deltaX > threshold && deltaY > threshold) ||
(deltaX > threshold && deltaZ > threshold) ||
(deltaY > threshold && deltaZ > threshold);
}
PS: He establecido el intervalo de actualización a 1/15 de segundo.
[[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / 15)];
Debe verificar el acelerómetro a través del método accelerometer:didAccelerate: que forma parte del protocolo UIAccelerometerDelegate y verificar si los valores superan un umbral para la cantidad de movimiento necesario para una sacudida.
Hay un código de ejemplo decente en el método accelerometer:didAccelerate: justo en la parte inferior de AppController.m en el ejemplo de GLPaint que está disponible en el sitio de desarrolladores de iPhone.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-29 21:17:44
En iOS 8.3 (quizás anterior) con Swift, es tan simple como anular los métodos motionBegan
o motionEnded
en su controlador de vista:
class ViewController: UIViewController {
override func motionBegan(motion: UIEventSubtype, withEvent event: UIEvent) {
println("started shaking!")
}
override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent) {
println("ended shaking!")
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-20 22:39:56
Este es el código de delegado básico que necesita:
#define kAccelerationThreshold 2.2
#pragma mark -
#pragma mark UIAccelerometerDelegate Methods
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
if (fabsf(acceleration.x) > kAccelerationThreshold || fabsf(acceleration.y) > kAccelerationThreshold || fabsf(acceleration.z) > kAccelerationThreshold)
[self myShakeMethodGoesHere];
}
También establezca el código correspondiente en la Interfaz. i. e:
@interfaz MyViewController: UIViewController
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-04 07:00:53
Vea el ejemplo de GLPaint.
Http://developer.apple.com/library/ios/#samplecode/GLPaint/Introduction/Intro.html
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-28 20:44:09
Agregue los siguientes métodos en ViewController.m archivo, está funcionando correctamente
-(BOOL) canBecomeFirstResponder
{
/* Here, We want our view (not viewcontroller) as first responder
to receive shake event message */
return YES;
}
-(void) motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if(event.subtype==UIEventSubtypeMotionShake)
{
// Code at shake event
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"Motion" message:@"Phone Vibrate"delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alert show];
[alert release];
[self.view setBackgroundColor:[UIColor redColor]];
}
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self becomeFirstResponder]; // View as first responder
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-15 12:11:16
Lo sentimos para publicar esto como una respuesta en lugar de un comentario, pero como se puede ver soy nuevo en la pila de Desbordamiento y por lo que todavía no soy lo suficientemente buena reputación para publicar comentarios!
De todos modos, secundo @cire para asegurarme de establecer el estado del primer respondedor una vez que la vista sea parte de la jerarquía de la vista. Por lo tanto, establecer el estado del primer respondedor en su método view controllers viewDidLoad
no funcionará, por ejemplo. Y si no está seguro de si está funcionando [view becomeFirstResponder]
le devuelve un booleano que puede prueba.
Otro punto: puede usar un controlador de vista para capturar el evento shake si no desea crear una subclase UIView innecesariamente. Sé que no es mucha molestia, pero aún así la opción está ahí. Simplemente mueva los fragmentos de código que Kendall puso en la subclase UIView a su controlador y envíe los mensajes becomeFirstResponder
y resignFirstResponder
a self
en lugar de la subclase UIView.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-28 20:44:49
En primer lugar, sé que este es un post antiguo, pero sigue siendo relevante, y descubrí que las dos respuestas más votadas no detectaron el temblor lo antes posible . Así es como se hace:
- Enlaza CoreMotion con tu proyecto en las fases de compilación del objetivo:
-
En tu ViewController:
- (BOOL)canBecomeFirstResponder { return YES; } - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event { if (motion == UIEventSubtypeMotionShake) { // Shake detected. } }
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-13 14:59:13
La solución más fácil es derivar una nueva ventana raíz para su aplicación:
@implementation OMGWindow : UIWindow
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.type == UIEventTypeMotion && motion == UIEventSubtypeMotionShake) {
// via notification or something
}
}
@end
Luego en su delegado de solicitud:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[OMGWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
//…
}
Si está utilizando un guion gráfico, esto puede ser más complicado, no conozco el código que necesitará en el delegado de la aplicación precisamente.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-12 12:46:34
Simplemente use estos tres métodos para hacerlo
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event{
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event{
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event{
Para obtener más detalles, puede consultar un código de ejemplo completo en allí
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-21 09:30:47
Una versión swiftease basada en la primera respuesta!
override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
if ( event?.subtype == .motionShake )
{
print("stop shaking me!")
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-10 07:44:21
Para habilitar esta aplicación, creé una categoría en UIWindow:
@implementation UIWindow (Utils)
- (BOOL)canBecomeFirstResponder
{
return YES;
}
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake) {
// Do whatever you want here...
}
}
@end
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-15 19:45:23