Saya ingin bereaksi ketika seseorang mengguncang iPhone. Saya tidak terlalu peduli bagaimana mereka mengocoknya, hanya saja itu dilambaikan dengan penuh semangat selama sepersekian detik. Adakah yang tahu cara mendeteksi ini?
Saya ingin bereaksi ketika seseorang mengguncang iPhone. Saya tidak terlalu peduli bagaimana mereka mengocoknya, hanya saja itu dilambaikan dengan penuh semangat selama sepersekian detik. Adakah yang tahu cara mendeteksi ini?
Jawaban:
Di 3.0, sekarang ada cara yang lebih mudah - hubungkan ke acara gerak baru.
Trik utamanya adalah Anda harus memiliki beberapa UIView (bukan UIViewController) yang Anda inginkan sebagai firstResponder untuk menerima pesan acara shake. Berikut kode yang dapat Anda gunakan di UIView apa pun untuk mendapatkan acara 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
Anda dapat dengan mudah mengubah UIView (bahkan tampilan sistem) apa pun menjadi tampilan yang bisa mendapatkan peristiwa guncangan hanya dengan mensubklasifikasikan tampilan hanya dengan metode ini (dan kemudian memilih tipe baru ini, bukan tipe dasar dalam IB, atau menggunakannya saat mengalokasikan melihat).
Di pengontrol tampilan, Anda ingin mengatur tampilan ini menjadi responden pertama:
- (void) viewWillAppear:(BOOL)animated
{
[shakeView becomeFirstResponder];
[super viewWillAppear:animated];
}
- (void) viewWillDisappear:(BOOL)animated
{
[shakeView resignFirstResponder];
[super viewWillDisappear:animated];
}
Jangan lupa bahwa jika Anda memiliki tampilan lain yang menjadi responden pertama dari tindakan pengguna (seperti bilah pencarian atau bidang entri teks) Anda juga harus mengembalikan status guncangan tampilan responden pertama saat tampilan lain mengundurkan diri!
Metode ini berfungsi bahkan jika Anda mengatur applicationSupportsShakeToEdit ke NO.
motionEnded
sebelum goyang benar-benar berhenti. Jadi, dengan menggunakan pendekatan ini Anda mendapatkan serangkaian getar pendek yang terputus-putus, bukan satu yang panjang. Jawaban lain bekerja lebih baik dalam hal ini.
[super respondsToSelector:
tidak akan melakukan apa yang Anda inginkan, karena itu sama dengan menelepon [self respondsToSelector:
yang akan kembali YES
. Yang Anda butuhkan adalah [[ShakingView superclass] instancesRespondToSelector:
.
Dari aplikasi Diceshaker saya :
// 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
Histeresis mencegah peristiwa guncangan memicu beberapa kali hingga pengguna menghentikan guncangan.
motionBegan
dan motionEnded
acara tidak terlalu tepat atau akurat dalam hal mendeteksi awal dan akhir goyang. Pendekatan ini memungkinkan Anda untuk menjadi setepat yang Anda inginkan.
Saya akhirnya membuatnya bekerja menggunakan contoh kode dari Undo / Redo Manager Tutorial ini .
Inilah yang perlu Anda lakukan:
- (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
adalah YES
.
[self resignFirstResponder];
dulu [super viewWillDisappear:animated];
? Ini sepertinya aneh.
Pertama, jawaban Kendall tanggal 10 Juli tepat.
Sekarang ... Saya ingin melakukan sesuatu yang serupa (di iPhone OS 3.0+), hanya dalam kasus saya saya menginginkannya di seluruh aplikasi sehingga saya bisa mengingatkan berbagai bagian aplikasi ketika terjadi guncangan. Inilah yang akhirnya saya lakukan.
Pertama, saya subklasifikasi UIWindow . Ini peasy mudah. Buat file kelas baru dengan antarmuka seperti MotionWindow : UIWindow
(jangan ragu untuk memilih sendiri, natch). Tambahkan metode seperti ini:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) {
[[NSNotificationCenter defaultCenter] postNotificationName:@"DeviceShaken" object:self];
}
}
Ubah @"DeviceShaken"
ke nama notifikasi pilihan Anda. Simpan file.
Sekarang, jika Anda menggunakan MainWindow.xib (stok template kode Xcode), masuk ke sana dan ubah kelas objek Window Anda dari UIWindow ke MotionWindow atau apa pun namanya. Simpan xib. Jika Anda mengatur UIWindow terprogram, gunakan kelas Window baru Anda di sana.
Sekarang aplikasi Anda menggunakan kelas UIWindow khusus . Di mana pun Anda ingin diberi tahu tentang goyangan, daftar untuk pemberitahuan mereka! Seperti ini:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(deviceShaken) name:@"DeviceShaken" object:nil];
Untuk menghapus diri Anda sebagai pengamat:
[[NSNotificationCenter defaultCenter] removeObserver:self];
Saya menempatkan milik saya di viewWillAppear: dan viewWillDisappear: di mana View Controllers bersangkutan. Pastikan respons Anda terhadap acara goyang tahu apakah itu "sudah dalam proses" atau tidak. Jika tidak, jika perangkat diguncang dua kali berturut-turut, Anda akan mengalami kemacetan lalu lintas. Dengan cara ini Anda dapat mengabaikan notifikasi lain hingga Anda benar-benar selesai merespons notifikasi asli.
Juga: Anda dapat memilih untuk isyarat off dari motionBegan vs motionEnded . Terserah kamu. Dalam kasus saya, efeknya selalu perlu terjadi setelah perangkat dalam keadaan diam (vs. ketika mulai bergetar), jadi saya menggunakan motionEnded . Coba keduanya dan lihat mana yang lebih masuk akal ... atau deteksi / beri tahu untuk keduanya!
Satu lagi pengamatan (penasaran?) Di sini: Perhatikan tidak ada tanda-tanda manajemen responden pertama dalam kode ini. Saya hanya mencoba ini dengan Table View Controllers sejauh ini dan semuanya tampaknya bekerja dengan sangat baik bersama-sama! Saya tidak bisa menjamin skenario lain.
Kendall, et. al - adakah yang bisa berbicara mengapa ini mungkin terjadi untuk subkelas UIWindow ? Apakah karena jendelanya ada di puncak rantai makanan?
Saya menemukan posting ini mencari implementasi "gemetar". jawaban millenomi bekerja dengan baik untuk saya, walaupun saya sedang mencari sesuatu yang membutuhkan "aksi gemetar" untuk memicu. Saya telah mengganti ke nilai Boolean dengan shakeCount int. Saya juga mengimplementasikan kembali metode L0AccelerationIsShaking () di Objective-C. Anda dapat mengubah jumlah pengocokan yang diperlukan dengan mengubah jumlah yang ditambahkan ke shakeCount. Saya tidak yakin saya telah menemukan nilai optimal, tetapi tampaknya masih berfungsi dengan baik sejauh ini. Semoga ini bisa membantu seseorang:
- (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: Saya sudah mengatur interval pembaruan ke 1/15 detik.
[[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / 15)];
Anda perlu memeriksa accelerometer melalui accelerometer: didAccelerate: metode yang merupakan bagian dari protokol UIAccelerometerDelegate dan periksa apakah nilainya melampaui ambang batas untuk jumlah gerakan yang diperlukan untuk shake.
Ada contoh kode yang layak di accelerometer: didAccelerate: metode tepat di bagian bawah AppController.m dalam contoh GLPaint yang tersedia di situs pengembang iPhone.
Di iOS 8.3 (mungkin sebelumnya) dengan Swift, sesederhana mengganti metode motionBegan
atau motionEnded
di pengontrol tampilan Anda:
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!")
}
}
Ini adalah kode delegasi dasar yang Anda butuhkan:
#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];
}
Juga atur kode yang sesuai di Interface. yaitu:
@interface MyViewController: UIViewController <UIPickerViewDelegate, UIPickerViewDataSource, UIAccelerometerDelegate>
Lihatlah contoh GLPaint.
http://developer.apple.com/library/ios/#samplecode/GLPaint/Introduction/Intro.html
Tambahkan metode berikut dalam file ViewController.m, itu berfungsi dengan baik
-(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
}
Maaf memposting ini sebagai jawaban daripada komentar tetapi karena Anda dapat melihat saya baru di Stack Overflow dan jadi saya belum cukup terkemuka untuk mengirim komentar!
Pokoknya saya kedua @cire tentang memastikan untuk mengatur status responden pertama setelah tampilan adalah bagian dari hierarki tampilan. Jadi pengaturan status responden pertama dalam viewDidLoad
metode pengontrol tampilan Anda tidak akan berfungsi misalnya. Dan jika Anda tidak yakin apakah itu berfungsi [view becomeFirstResponder]
mengembalikan boolean yang dapat Anda uji.
Poin lain: Anda bisa menggunakan pengontrol tampilan untuk menangkap acara goyang jika Anda tidak ingin membuat subkelas UIView secara tidak perlu. Saya tahu ini tidak terlalu merepotkan tetapi masih ada opsi. Cukup pindahkan cuplikan kode yang dimasukkan Kendall ke dalam subkelas UIView ke dalam pengontrol Anda dan kirim pesan becomeFirstResponder
dan resignFirstResponder
ke self
alih-alih subkelas UIView.
Pertama, saya tahu ini adalah posting lama, tetapi masih relevan, dan saya menemukan bahwa dua jawaban pilihan tertinggi tidak mendeteksi guncangan sedini mungkin . Ini adalah cara untuk melakukannya:
Di ViewController Anda:
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake) {
// Shake detected.
}
}
Solusi termudah adalah mendapatkan jendela root baru untuk aplikasi Anda:
@implementation OMGWindow : UIWindow
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.type == UIEventTypeMotion && motion == UIEventSubtypeMotionShake) {
// via notification or something
}
}
@end
Kemudian dalam delegasi aplikasi Anda:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[OMGWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
//…
}
Jika Anda menggunakan Storyboard, ini mungkin lebih sulit, saya tidak tahu kode yang akan Anda butuhkan dalam delegasi aplikasi secara tepat.
@implementation
sejak Xcode 5.
Cukup gunakan tiga metode ini untuk melakukannya
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event{
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event{
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event{
untuk perincian Anda dapat memeriksa kode contoh lengkap di sana
Versi cepat berdasarkan pada jawaban pertama!
override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
if ( event?.subtype == .motionShake )
{
print("stop shaking me!")
}
}
Untuk mengaktifkan aplikasi ini, saya membuat kategori di 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
bukanviewWillAppear
. Saya tidak yakin mengapa; mungkin tampilan harus terlihat sebelum dapat melakukan apa pun untuk mulai menerima acara goyang?