Birisi iPhone'u salladığında tepki vermek istiyorum. Özellikle nasıl salladıkları umurumda değil, sadece bir saniye boyunca şiddetle sallandı. Bunu nasıl tespit edeceğini bilen var mı?
Birisi iPhone'u salladığında tepki vermek istiyorum. Özellikle nasıl salladıkları umurumda değil, sadece bir saniye boyunca şiddetle sallandı. Bunu nasıl tespit edeceğini bilen var mı?
Yanıtlar:
3.0'da artık daha kolay bir yol var - yeni hareket olaylarına bağlanın.
Ana hile, sallamak olay mesajlarını almak için firstResponder olarak istediğiniz bazı UIView (UIViewController değil) olması gerektiğidir. Sarsıntı olayları almak için herhangi bir UIView'de kullanabileceğiniz kod şunlardır:
@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
Herhangi bir UIView'i (sistem görünümlerini bile) yalnızca görünümü yalnızca bu yöntemlerle alt sınıflandırarak (ve ardından IB'deki temel tür yerine bu yeni türü seçerek veya bir görünüm).
Görünüm denetleyicisinde, bu görünümü ilk yanıtlayıcı olacak şekilde ayarlamak istiyorsunuz:
- (void) viewWillAppear:(BOOL)animated
{
[shakeView becomeFirstResponder];
[super viewWillAppear:animated];
}
- (void) viewWillDisappear:(BOOL)animated
{
[shakeView resignFirstResponder];
[super viewWillDisappear:animated];
}
Kullanıcı işlemlerinden (yanıt çubuğu veya metin giriş alanı gibi) ilk yanıtlayan diğer görünümleriniz varsa, diğer görünümden istifa ettiğinde sallayarak görünüm ilk yanıtlayıcı durumunu da geri yüklemeniz gerektiğini unutmayın!
Bu yöntem applicationSupportsShakeToEdit öğesini NO olarak ayarlasanız bile çalışır.
motionEnded
, sarsıntı gerçekten durmadan iPhone'un ateş etmeyi sevdiği için bu yaklaşım yararlı değildir . Yani bu yaklaşımı kullanarak uzun bir tane yerine ayrık bir dizi kısa sallama elde edersiniz. Diğer cevap bu durumda çok daha iyi çalışır.
[super respondsToSelector:
İstediğinizi yapmayacaktır, çünkü [self respondsToSelector:
geri dönecek olan çağrıya eşdeğerdir YES
. İhtiyacınız olan şey [[ShakingView superclass] instancesRespondToSelector:
.
Benim itibaren Diceshaker uygulaması:
// 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, kullanıcı sallamayı durdurana kadar sallama olayının birçok kez tetiklenmesini önler.
motionBegan
ve motionEnded
olaylar bir sarsıntıyı tam olarak başlatarak ve bitirmeyi tespit etmek açısından çok hassas veya doğru değil. Bu yaklaşım, istediğiniz kadar hassas olmanızı sağlar.
Sonunda bu Undo / Redo Manager Eğitimi kod örnekleri kullanarak çalışması yaptı .
Tam olarak yapmanız gerekenler:
- (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
DİR YES
.
[self resignFirstResponder];
öndesin [super viewWillDisappear:animated];
? Bu tuhaf görünüyor.
İlk olarak, Kendall'ın 10 Temmuz cevabı yerinde.
Şimdi ... Benzer bir şey yapmak istedim (iPhone OS 3.0+), sadece benim durumumda, uygulama genelinde istedim, böylece bir sarsıntı meydana geldiğinde uygulamanın çeşitli bölümlerini uyarabilirim . İşte sonunda bunu yaptım.
İlk olarak, UIWindow alt sınıfını . Bu çok kolay. Gibi bir arayüz ile yeni bir sınıf dosyası oluşturun MotionWindow : UIWindow
(kendiniz seçin, natch). Bunun gibi bir yöntem ekleyin:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) {
[[NSNotificationCenter defaultCenter] postNotificationName:@"DeviceShaken" object:self];
}
}
Değiştir @"DeviceShaken"
Seçtiğiniz bildirim adının. Dosya 'yı kaydet.
Bir MainWindow.xib (stok Xcode şablon şeyler) kullanmak Şimdi, eğer oraya gidip gelen Window nesnesinin sınıfını değiştirmek UIWindow için MotionWindow veya bunu denilen neyse. Xib'i kaydedin. Eğer kurarsanız UIWindow programlı yerine orada yeni Pencere sınıfını kullanın.
Şimdi uygulamanız özel UIWindow sınıfını kullanıyor. Bir sarsıntı hakkında bilgi almak istediğiniz her yerde, onlara bildirimler için kaydolun! Bunun gibi:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(deviceShaken) name:@"DeviceShaken" object:nil];
Kendinizi bir gözlemci olarak çıkarmak için:
[[NSNotificationCenter defaultCenter] removeObserver:self];
Benimkini viewWillAppear'a koydum : ve viewWillDisappear: Burada Görünüm Denetleyicileri söz konusu olduğunda . Sallama olayına verdiğiniz yanıtın "zaten sürüyor" olup olmadığını bildiğinden emin olun. Aksi takdirde, cihaz art arda iki kez çalkalanırsa, bir trafik sıkışıklığınız olur. Bu şekilde, orijinal bildirime gerçekten yanıt vermeyi bitirinceye kadar diğer bildirimleri yok sayabilirsiniz.
Ayrıca: motionBegan'a karşı motionEnded işaretini iptal etmeyi seçebilirsiniz . Sana kalmış. Benim durumumda, efekt her zaman cihaz dinlendikten sonra gerçekleşmelidir (titremeye başladığında vs.), bu yüzden motionEnded kullanıyorum . Her ikisini de deneyin ve hangisinin daha anlamlı olduğunu görün ... ya da her ikisini de tespit edin / bilgilendirin!
Burada bir (meraklı?) Gözlem daha var: Bu kodda ilk yanıtlayıcı yönetimi belirtisi olmadığına dikkat edin. Şimdiye kadar sadece Tablo Görünümü Kontrolörleri ile denedim ve her şey birlikte oldukça güzel çalışıyor gibi görünüyor! Yine de diğer senaryolar için kefil olamam.
Kendall, vd. al - UIWindow alt sınıfları için bunun neden böyle olabileceğini herkes söyleyebilir mi? Pencere besin zincirinin en üstünde olduğu için mi?
Bu yayına "titreyen" bir uygulama aradım. Ben tetiklemek için biraz daha "sallama eylemi" gerektiren bir şey arıyordum millenomi'nin yanıtı benim için iyi çalıştı. Boolean değeri int shakeCount ile değiştirdim. Ayrıca Objective-C L0AccelerationIsShaking () yöntemini yeniden uyguladı. ShakeCount'a eklenen miktarı ayarlayarak gerekli sarsıntı miktarını değiştirebilirsiniz. Henüz optimal değerleri bulduğumdan emin değilim, ancak şu ana kadar iyi çalışıyor gibi görünüyor. Umarım bu birine yardımcı olur:
- (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);
}
Not: Güncelleme aralığını saniyenin 1 / 15'ine ayarladım.
[[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / 15)];
İvmeölçeri ivmeölçer aracılığıyla kontrol etmeniz gerekir: didAccelerate: UIAccelerometerDelegate protokolünün bir parçası olan yöntem ve değerlerin bir sarsıntı için gereken hareket miktarı için bir eşiğin üzerinden geçip geçmediğini kontrol edin.
İvmeölçerde iyi bir örnek kod var: didAccelerate: yöntem, iPhone geliştirici sitesinde bulunan GLPaint örneğinde AppController.m'nin hemen altında.
Swift ile iOS 8.3'te (belki de daha önce) , görünüm denetleyicinizdeki motionBegan
veya motionEnded
yöntemlerini geçersiz kılmak kadar basit :
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!")
}
}
Bu, ihtiyacınız olan temel delege kodudur:
#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];
}
Ayrıca Arabirim'de uygun kodu girin. yani:
@interface MyViewController: UIViewController <UIPickerViewDelegate, UIPickerViewDataSource, UIAccelerometerDelegate>
GLPaint örneğine bakın.
http://developer.apple.com/library/ios/#samplecode/GLPaint/Introduction/Intro.html
ViewController.m dosyasına aşağıdaki yöntemleri ekleyin, düzgün çalışıyor
-(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
}
Bunu bir yorum yerine bir cevap olarak göndermek için üzgünüm ama gördüğünüz gibi Stack Overflow'da yeniyim ve bu yüzden henüz yorum gönderecek kadar saygın değilim!
Her neyse, görünüm görünüm hiyerarşisinin bir parçası olduğunda ilk yanıtlayıcı durumunu ayarladığınızdan emin olmak için ikinci saniye. Dolayısıyla, görünüm denetleyicileri viewDidLoad
yönteminizde ilk yanıtlayıcı durumunu ayarlamak örneğin çalışmaz. Ve çalışıp çalışmadığından [view becomeFirstResponder]
emin değilseniz, test edebileceğiniz bir boolean döndürür.
Başka bir nokta: Eğer yapabilirsiniz gereksiz yere bir UIView alt sınıfı oluşturmak istemiyorsanız sarsıntı olayı yakalamak için bir görünüm denetleyicisi kullanın. Çok fazla güçlük olmadığını biliyorum ama yine de seçenek var. Kendall'ın UIView alt sınıfına koyduğu kod snippet'lerini denetleyicinize taşıyın ve UIView alt sınıfı yerine becomeFirstResponder
ve resignFirstResponder
iletilerini gönderin self
.
Öncelikle, bunun eski bir yazı olduğunu biliyorum, ama yine de alakalı ve en yüksek oy alan iki cevabın sallamayı mümkün olduğunca erken tespit etmediğini buldum . Böyle yapılır:
ViewController'ınızda:
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake) {
// Shake detected.
}
}
En kolay çözüm, uygulamanız için yeni bir kök pencere türetmektir:
@implementation OMGWindow : UIWindow
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.type == UIEventTypeMotion && motion == UIEventSubtypeMotionShake) {
// via notification or something
}
}
@end
Sonra başvuru temsilcinizde:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[OMGWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
//…
}
Bir Storyboard kullanıyorsanız, bu daha zor olabilir, uygulama temsilcisinde ihtiyacınız olan kodu tam olarak bilmiyorum.
@implementation
Xcode 5. yana
Bunu yapmak için sadece bu üç yöntemi kullanın
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event{
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event{
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event{
Ayrıntılar için baştan tam bir örnek kodunu kontrol edebilir orada
İlk cevaba dayanan hızlı bir versiyon!
override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
if ( event?.subtype == .motionShake )
{
print("stop shaking me!")
}
}
Bu uygulamayı etkinleştirmek için UIWindow'da bir kategori oluşturdum:
@implementation UIWindow (Utils)
- (BOOL)canBecomeFirstResponder
{
return YES;
}
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake) {
// Do whatever you want here...
}
}
@end
viewDidAppear
yerine geçersiz kılmam gerekiyorduviewWillAppear
. Neden olduğundan emin değilim; belki de sarsıntı olaylarını almaya başlamak için ne gerekiyorsa yapabilmesi için görünümün görünür olması gerekir?