سؤال

أريد الرد عندما يهز شخص ما جهاز iPhone.لا يهمني بشكل خاص كيف هزوها، فقط تم التلويح بها بقوة لجزء من الثانية.هل يعرف أحد كيفية اكتشاف هذا؟

هل كانت مفيدة؟

المحلول

في الإصدار 3.0، توجد الآن طريقة أسهل - استمتع بأحداث الحركة الجديدة.

الحيلة الرئيسية هي أنك تحتاج إلى بعض UIView (وليس UIViewController) الذي تريده كمستجيب أول لتلقي رسائل حدث الاهتزاز.إليك الكود الذي يمكنك استخدامه في أي UIView للحصول على أحداث الاهتزاز:

@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

يمكنك بسهولة تحويل أي UIView (حتى طرق عرض النظام) إلى عرض يمكنه الحصول على حدث الاهتزاز ببساطة عن طريق تصنيف العرض باستخدام هذه الطرق فقط (ثم تحديد هذا النوع الجديد بدلاً من النوع الأساسي في IB، أو استخدامه عند تخصيص منظر).

في وحدة التحكم في العرض، تريد تعيين هذا العرض ليصبح المستجيب الأول:

- (void) viewWillAppear:(BOOL)animated
{
    [shakeView becomeFirstResponder];
    [super viewWillAppear:animated];
}
- (void) viewWillDisappear:(BOOL)animated
{
    [shakeView resignFirstResponder];
    [super viewWillDisappear:animated];
}

لا تنس أنه إذا كان لديك طرق عرض أخرى تصبح المستجيب الأول من إجراءات المستخدم (مثل شريط البحث أو حقل إدخال النص)، فستحتاج أيضًا إلى استعادة حالة المستجيب الأول للعرض المهتز عندما يستقيل العرض الآخر!

تعمل هذه الطريقة حتى إذا قمت بتعيين applicationSupportsShakeToEdit على NO.

نصائح أخرى

من وجهة نظري شاكر النرد طلب:

// 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

يمنع التباطؤ حدوث حدث الاهتزاز عدة مرات حتى يوقف المستخدم الاهتزاز.

لقد نجحت أخيرًا في العمل باستخدام أمثلة التعليمات البرمجية من هذا التراجع/الإعادة مدير البرنامج التعليمي.
هذا هو بالضبط ما عليك القيام به:

  • تعيين التطبيق يدعم ShakeToEdit الخاصية في مندوب التطبيق:
  • 
        - (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
        }
    }
    

    أولاً، كانت إجابة كيندال في العاشر من تموز (يوليو) في محلها.

    الآن ...كنت أرغب في القيام بشيء مماثل (في نظام التشغيل iPhone OS 3.0+)، فقط في حالتي كنت أرغب في ذلك على مستوى التطبيق حتى أتمكن من التنبيه متنوع أجزاء من التطبيق عند حدوث اهتزاز.وإليكم ما انتهى بي الأمر بفعله.

    أولا، أنا صنفت فرعيا UIWindow.هذا أمر سهل.قم بإنشاء ملف فئة جديد بواجهة مثل MotionWindow : UIWindow (لا تتردد في اختيار بنفسك، ناتش).أضف طريقة مثل ذلك:

    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
        if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) {
            [[NSNotificationCenter defaultCenter] postNotificationName:@"DeviceShaken" object:self];
        }
    }
    

    يتغير @"DeviceShaken" إلى اسم الإشعار الذي تختاره.حفظ الملف.

    الآن، إذا كنت تستخدم MainWindow.xib (عناصر قالب Xcode)، فانتقل إلى هناك وقم بتغيير فئة كائن Window الخاص بك من UIWindow ل MotionWindow أو كما سميته.احفظ xib.إذا قمت بإعداد UIWindow برمجياً، استخدم فئة Window الجديدة هناك بدلاً من ذلك.

    الآن يستخدم تطبيقك التخصص UIWindow فصل.أينما تريد أن يتم إخبارك عن الاهتزاز، قم بالتسجيل للحصول على إشعارات!مثله:

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

    لإزالة نفسك كمراقب:

    [[NSNotificationCenter defaultCenter] removeObserver:self];
    

    أنا وضعت الألغام في عرض سوف يظهر: و عرض سوف تختفي: حيث يتعلق الأمر بوحدات التحكم في العرض.تأكد من أن استجابتك لحدث الاهتزاز تعرف ما إذا كان "قيد التقدم بالفعل" أم لا.وإلا، إذا تم هز الجهاز مرتين متتاليتين، فسوف يكون لديك ازدحام مروري بسيط.وبهذه الطريقة يمكنك تجاهل الإشعارات الأخرى حتى تنتهي من الرد على الإشعار الأصلي.

    أيضًا:يمكنك اختيار الإشارة إلى الخروج motionBegan ضد. انتهت الحركة.الأمر متروك لك.في حالتي، يجب أن يحدث التأثير دائمًا بعد الجهاز في حالة راحة (مقابل.عندما يبدأ بالاهتزاز)، لذلك أستخدمه انتهت الحركة.جرب كلاهما وانظر أيهما أكثر منطقية ...أو كشف/إخطار لكليهما!

    ملاحظة أخرى (غريبة؟) هنا:لاحظ أنه لا توجد علامة على إدارة المستجيب الأول في هذا الرمز.لقد جربت هذا فقط مع وحدات تحكم عرض الجدول حتى الآن ويبدو أن كل شيء يعمل بشكل جيد معًا!لا أستطيع أن أضمن سيناريوهات أخرى بالرغم من ذلك.

    كيندال وآخرون.آل - هل يمكن لأي شخص أن يتحدث عن سبب حدوث ذلك UIWindow فئات فرعية؟هل لأن النافذة تقع في أعلى السلسلة الغذائية؟

    لقد صادفت هذا المنشور أبحث عن تنفيذ "مهتز".لقد نجحت إجابة ميلينومي بشكل جيد بالنسبة لي، على الرغم من أنني كنت أبحث عن شيء يتطلب المزيد من "إجراء الاهتزاز" لتشغيله.لقد استبدلت القيمة المنطقية بـ int ShakCount.لقد قمت أيضًا بإعادة تنفيذ طريقة L0AccelerationIsShaking() في Objective-C.يمكنك تعديل مقدار الاهتزاز المطلوب عن طريق تعديل مقدار الاهتزاز المضاف إلى ShakeCount.لست متأكدًا من أنني وجدت القيم المثالية حتى الآن، ولكن يبدو أنها تعمل بشكل جيد حتى الآن.أمل أن هذا يساعد شخصاما:

    - (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);
    }
    

    ملاحظة:لقد قمت بتعيين الفاصل الزمني للتحديث على 1/15 من الثانية.

    [[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / 15)];
    

    أنت بحاجة إلى التحقق من مقياس التسارع عبر مقياس التسارع:didAccelerate:الطريقة التي تعد جزءًا من بروتوكول UIAccelerometerDelegate وتحقق مما إذا كانت القيم تتجاوز عتبة مقدار الحركة اللازمة للاهتزاز.

    يوجد نموذج تعليمي لائق في مقياس التسارع:didAccelerate:الطريقة مباشرة في الجزء السفلي من AppController.m في مثال GLPaint المتوفر على موقع مطور iPhone.

    في نظام التشغيل iOS 8.3 (وربما الأقدم) مع Swift، يكون الأمر بسيطًا مثل تجاوز ملف motionBegan أو motionEnded الطرق في وحدة تحكم العرض الخاصة بك:

    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!")
        }
    }
    

    هذا هو رمز المفوض الأساسي الذي تحتاجه:

    #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];   
        }
    

    قم أيضًا بتعيين الكود المناسب في الواجهة.أي:

    @interface MyViewController :UIViewController <UIPickerViewDelegate، UIPickerViewDataSource، UIAccelerometerDelegate>

    أضف الطرق التالية في ملف ViewController.m، فهو يعمل بشكل صحيح

        -(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 
         }
    

    آسف لنشر هذا كإجابة بدلاً من تعليق ولكن كما ترون أنا جديد على Stack Overflow ولذا فأنا لا أتمتع بسمعة طيبة بعد بما يكفي لنشر التعليقات!

    على أي حال، أنا أؤيدcire بشأن التأكد من تعيين حالة المستجيب الأول بمجرد أن يصبح العرض جزءًا من التسلسل الهرمي للعرض.لذلك قم بتعيين حالة المستجيب الأول في وحدات التحكم في طريقة العرض الخاصة بك viewDidLoad لن تعمل الطريقة على سبيل المثال.وإذا لم تكن متأكدًا مما إذا كان يعمل أم لا [view becomeFirstResponder] ترجع لك قيمة منطقية يمكنك اختبارها.

    نقطة اخرى:أنت يستطيع استخدم وحدة تحكم العرض لالتقاط حدث الاهتزاز إذا كنت لا ترغب في إنشاء فئة فرعية UIView دون داع.أعلم أن الأمر ليس بهذه المتاعب ولكن لا يزال الخيار موجودًا.ما عليك سوى نقل مقتطفات التعليمات البرمجية التي وضعها Kendall في فئة UIView الفرعية إلى وحدة التحكم الخاصة بك وإرسال ملف becomeFirstResponder و resignFirstResponder رسائل الى self بدلاً من الفئة الفرعية UIView.

    أولاً، أعرف أن هذا منشور قديم، لكنه لا يزال ذا صلة، وقد وجدت أن الإجابتين الأعلى تصويتًا لم تكونا كذلك كشف الاهتزاز في أقرب وقت ممكن.هذه هي الطريقة للقيام بذلك:

    1. قم بربط CoreMotion بمشروعك في مراحل إنشاء الهدف:CoreMotion
    2. في ViewController الخاص بك:

      - (BOOL)canBecomeFirstResponder {
          return YES;
      }
      
      - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
      {
          if (motion == UIEventSubtypeMotionShake) {
              // Shake detected.
          }
      }
      

    الحل الأسهل هو اشتقاق نافذة جذر جديدة لتطبيقك:

    @implementation OMGWindow : UIWindow
    
    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
        if (event.type == UIEventTypeMotion && motion == UIEventSubtypeMotionShake) {
            // via notification or something   
        }
    }
    @end
    

    ثم في مندوب التطبيق الخاص بك:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        self.window = [[OMGWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        //…
    }
    

    إذا كنت تستخدم Storyboard، فقد يكون ذلك أكثر صعوبة، لا أعرف الرمز الذي ستحتاجه في مندوب التطبيق على وجه التحديد.

    ما عليك سوى استخدام هذه الطرق الثلاث للقيام بذلك

    - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event{
    - (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event{
    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event{
    

    للحصول على التفاصيل يمكنك التحقق من رمز المثال الكامل هناك

    نسخة Swiftease تعتمد على الإجابة الأولى!

    override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
        if ( event?.subtype == .motionShake )
        {
            print("stop shaking me!")
        }
    }
    

    لتمكين هذا على مستوى التطبيق، قمت بإنشاء فئة على UIWindow:

    @implementation UIWindow (Utils)
    
    - (BOOL)canBecomeFirstResponder
    {
        return YES;
    }
    
    - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
    {
        if (motion == UIEventSubtypeMotionShake) {
            // Do whatever you want here...
        }
    }
    
    @end
    
    مرخصة بموجب: CC-BY-SA مع الإسناد
    لا تنتمي إلى StackOverflow
    scroll top