ฉันต้องการที่จะตอบสนองเมื่อมีคนเขย่า iPhone ฉันไม่สนใจว่าพวกเขาจะสั่นอย่างไรโดยเฉพาะว่ามันโบกสะบัดอย่างแรงประมาณเสี้ยววินาที ไม่มีใครรู้วิธีการตรวจสอบนี้
ฉันต้องการที่จะตอบสนองเมื่อมีคนเขย่า iPhone ฉันไม่สนใจว่าพวกเขาจะสั่นอย่างไรโดยเฉพาะว่ามันโบกสะบัดอย่างแรงประมาณเสี้ยววินาที ไม่มีใครรู้วิธีการตรวจสอบนี้
คำตอบ:
ใน 3.0 ตอนนี้มีวิธีที่ง่ายกว่า - ขอเข้าร่วมกิจกรรมการเคลื่อนไหวใหม่
เคล็ดลับหลักคือคุณต้องมี UIView (ไม่ใช่ UIViewController) บางอย่างที่คุณต้องการเป็น firstResponder เพื่อรับข้อความสั่นเหตุการณ์ นี่คือรหัสที่คุณสามารถใช้ใน 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 ใด ๆ (แม้แต่มุมมองของระบบ) ให้เป็นมุมมองที่สามารถรับเหตุการณ์การสั่นโดยเพียงแค่ทำการ subclassing มุมมองด้วยวิธีเหล่านี้เท่านั้น (จากนั้นเลือกชนิดใหม่นี้แทนประเภทฐานใน IB หรือใช้เมื่อทำการจัดสรร ดู).
ในตัวควบคุมมุมมองคุณต้องการตั้งค่ามุมมองนี้ให้เป็นการตอบกลับครั้งแรก:
- (void) viewWillAppear:(BOOL)animated
{
[shakeView becomeFirstResponder];
[super viewWillAppear:animated];
}
- (void) viewWillDisappear:(BOOL)animated
{
[shakeView resignFirstResponder];
[super viewWillDisappear:animated];
}
อย่าลืมว่าหากคุณมีมุมมองอื่น ๆ ที่กลายเป็นการตอบกลับครั้งแรกจากการกระทำของผู้ใช้ (เช่นแถบค้นหาหรือช่องป้อนข้อความ) คุณจะต้องเรียกคืนสถานะมุมมองการสั่นครั้งแรกเมื่อมุมมองอื่น ๆ ลาออก!
วิธีนี้ใช้ได้แม้ว่าคุณจะตั้งค่า applicationSupportsShakeToEdit เป็น NO
motionEnded
ก่อนที่การสั่นจะหยุดลงจริง ๆ ดังนั้นเมื่อใช้วิธีนี้คุณจะได้รับชุดเขย่าสั้น ๆ ที่แยกจากกันแทนที่จะเป็นแบบยาว คำตอบอื่น ๆ ทำงานได้ดีขึ้นมากในกรณีนี้
[super respondsToSelector:
จะไม่ทำสิ่งที่คุณต้องการเพราะมันเทียบเท่ากับการโทรที่จะกลับมา[self respondsToSelector:
สิ่งที่คุณต้องการYES
[[ShakingView superclass] instancesRespondToSelector:
จากแอปพลิเคชัน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
การทำซ้ำของเขาจะป้องกันเหตุการณ์สั่นไม่ให้ทำงานหลายครั้งจนกว่าผู้ใช้จะหยุดการสั่นไหว
motionBegan
และmotionEnded
เหตุการณ์ไม่แม่นยำหรือแม่นยำมากในแง่ของการตรวจจับการเริ่มต้นและสิ้นสุดของการสั่น วิธีนี้ช่วยให้คุณมีความแม่นยำเท่าที่คุณต้องการ
ในที่สุดฉันก็ทำให้มันทำงานโดยใช้ตัวอย่างโค้ดจากบทช่วยสอน Undo / Redo Managerนี้
นี่คือสิ่งที่คุณต้องทำ:
- (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
}
}
applicationSupportsShakeToEdit
YES
[self resignFirstResponder];
ล่วงหน้า[super viewWillDisappear:animated];
ทำไม นี่มันแปลกประหลาด
ก่อนอื่นคำตอบที่ 10 กรกฎาคมของ Kendall เป็นจุด ๆ
ตอนนี้ ... ฉันต้องการทำสิ่งที่คล้ายกัน (ใน iPhone OS 3.0+) เฉพาะในกรณีของฉันฉันต้องการให้แอพกว้างดังนั้นฉันจึงสามารถแจ้งเตือนส่วนต่างๆ ของแอปเมื่อเกิดการสั่นไหว นี่คือสิ่งที่ฉันทำ
ครั้งแรกผม subclassed UIWindow นี่คือ peasy ง่าย สร้างไฟล์คลาสใหม่ด้วยอินเทอร์เฟซเช่นMotionWindow : UIWindow
(อย่าลังเลที่จะเลือกของคุณเอง natch) เพิ่มวิธีการดังนี้:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) {
[[NSNotificationCenter defaultCenter] postNotificationName:@"DeviceShaken" object:self];
}
}
เปลี่ยน@"DeviceShaken"
เป็นชื่อการแจ้งเตือนที่คุณเลือก บันทึกไฟล์
ตอนนี้ถ้าคุณใช้ MainWindow.xib (สิ่งเทมเพลต Xcode หุ้น) ไปที่นั่นและเปลี่ยนคลาสของวัตถุหน้าต่างของคุณจากUIWindowเป็นMotionWindowหรืออะไรก็ตามที่คุณเรียกมัน บันทึก xib หากคุณตั้งค่าUIWindow โดยทางโปรแกรมให้ใช้คลาส Window ใหม่ของคุณที่นั่นแทน
ตอนนี้แอปของคุณกำลังใช้คลาสUIWindow เฉพาะ ไม่ว่าคุณต้องการได้รับการแจ้งเตือนเกี่ยวกับการสั่นไหวเมื่อใดก็ตามลงทะเบียนเพื่อรับการแจ้งเตือน! แบบนี้:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(deviceShaken) name:@"DeviceShaken" object:nil];
ในการลบตัวคุณเองออกจากการเป็นผู้สังเกต:
[[NSNotificationCenter defaultCenter] removeObserver:self];
ฉันใส่ของฉันลงในviewWillAppear:และviewWillDisappear:โดยที่ตัวควบคุมมุมมองเกี่ยวข้อง ตรวจสอบให้แน่ใจว่าการตอบสนองของคุณต่อเหตุการณ์สั่นทราบว่าเป็น "กำลังดำเนินการอยู่" หรือไม่ มิฉะนั้นหากอุปกรณ์ถูกเขย่าอย่างต่อเนื่องสองครั้งคุณจะประสบปัญหาการจราจรติดขัด วิธีนี้คุณสามารถเพิกเฉยต่อการแจ้งเตือนอื่น ๆ จนกว่าคุณจะตอบกลับการแจ้งเตือนดั้งเดิมอย่างแท้จริง
นอกจากนี้คุณอาจเลือกที่จะออกจากคิวmotionBeganกับmotionEnded มันขึ้นอยู่กับคุณ. ในกรณีของผลที่ออกมามักจะต้องการที่จะใช้สถานที่หลังจากที่อุปกรณ์ที่เหลือ (เทียบกับเมื่อมันเริ่มสั่น) ดังนั้นฉันใช้motionEnded ลองทั้งคู่และดูว่าอันไหนที่เหมาะสมกว่า ... หรือตรวจจับ / แจ้งเตือนสำหรับทั้งคู่!
อีกข้อสังเกตหนึ่ง (อยากรู้อยากเห็น) ที่นี่: สังเกตว่าไม่มีสัญญาณของการจัดการการตอบกลับครั้งแรกในรหัสนี้ ฉันลองทำสิ่งนี้กับ Table View Controllers แล้วและทุกอย่างดูเหมือนจะทำงานร่วมกันได้ดีมาก! ฉันไม่สามารถรับรองสถานการณ์อื่น ๆ ได้
เคนดัลและอื่น ๆ al - ใคร ๆ ก็พูดได้ว่าทำไมถึงเป็นเช่นนั้นสำหรับUIWindow subclasses เป็นเพราะหน้าต่างอยู่ด้านบนสุดของห่วงโซ่อาหารหรือไม่
ฉันเจอโพสต์นี้เพื่อหาการใช้งานแบบ "เขย่า" คำตอบของ millenomi นั้นใช้ได้ดีสำหรับฉันแม้ว่าฉันกำลังมองหาบางสิ่งที่ต้องใช้ "การกระทำเขย่า" อีกเล็กน้อยเพื่อกระตุ้น ฉันได้เปลี่ยนเป็นค่าบูลีนด้วย int shakeCount ฉันยังใช้วิธี 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);
}
PS: ฉันได้ตั้งช่วงเวลาการอัปเดตเป็น 1/15 ของวินาที
[[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / 15)];
คุณต้องตรวจสอบ accelerometer ผ่าน accelerometer: didAccelerate: วิธีการซึ่งเป็นส่วนหนึ่งของโปรโตคอล UIAccelerometerDelegate และตรวจสอบว่าค่าผ่านเกณฑ์สำหรับปริมาณการเคลื่อนไหวที่จำเป็นสำหรับการสั่นหรือไม่
accelerometer: 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>
ลองดูตัวอย่าง GLPaint
http://developer.apple.com/library/ios/#samplecode/GLPaint/Introduction/Intro.html
เพิ่มวิธีการต่อไปนี้ในไฟล์ 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
ปิดครั้งแรกผมรู้ว่านี่เป็นโพสต์เก่า แต่มันก็ยังมีความเกี่ยวข้องและฉันพบว่าทั้งสองได้รับการโหวตมากที่สุดคำตอบไม่ได้ตรวจสอบการสั่นเร็วที่สุดเท่าที่เป็นไปได้ นี่คือวิธีที่จะทำ:
ใน 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];
//…
}
หากคุณใช้สตอรีบอร์ดนี่อาจเป็นเรื่องที่ยากกว่านั้นฉันไม่รู้รหัสที่คุณจะต้องใช้ในการมอบหมายแอปพลิเคชันอย่างแม่นยำ
@implementation
ตั้งแต่ Xcode 5
เพียงใช้สามวิธีนี้ในการทำ
- (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
viewDidAppear
viewWillAppear
ฉันไม่แน่ใจว่าทำไม บางทีมุมมองต้องมองเห็นได้ก่อนที่จะสามารถทำอะไรก็ได้เพื่อเริ่มรับเหตุการณ์สั่น