我正在尝试创建一个长按手势,一旦按下按下3秒钟,就会出现第二个视图控制器。但是,我只希望第二个视图控制器出现,如果设备在整个3秒内处于某个加速度计方向。也就是说,如果手势持续时间不够或设备倾斜太多,手势将被解除,用户必须再次尝试。
//在FirstViewController.h中
#import <UIKit/UIKit.h>
#import <CoreMotion/CoreMotion.h>
@interface FirstViewController : UIViewController
@property (nonatomic, strong) CMMotionManager *motionManager;
@end
//在FirstViewController.m中
#import "FirstViewController"
#import "SecondViewController"
@implementation motionManager;
- (void) viewDidLoad
{
[super viewDidLoad];
self.motionManager = [[CMMotionManager alloc]init];
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(handleLongPress:)];
longPress.minimumPressDuration = 3.0;
[self.view addGestureRecognizer:longPress];
}
- (void) handleLongPress: (UILongPressGestureRecognizer *)sender
{
// Not sure what to do here
}
我之前在最后一种方法中尝试过代码块,但它看起来很讨厌而且不正确。相反,我已经在下面列出了几行代码,我知道这些代码是单独工作的,但我需要帮助才能使它们全部协同工作。
//加速度计
if ([self.motionManager isAccelerometerAvailable])
{
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[self.motionManager startAccelerometerUpdatesToQueue:queue withHandler:^(CMAccelerometerData *accelerometerData,NSError *error)
{
if (ABS(accelerometerData.acceleration.x) < 0.3 && ABS(accelerometerData.acceleration.y) < 0.30 && ABS(accelerometerData.acceleration.z) > 0.70) // Phone is flat and screen faces up
{
NSLog(@"Correct Orientation!!!");
[self.motionManager stopAccelerometerUpdates];
}
else
{
NSLog(@"Incorrect orientation!!!");
[self.motionManager stopAccelerometerUpdates];
}];
}
else
{
NSLog(@"Accelerometer is not available.");
}
//转到第二个视图控制器
if (sender.state == UIGestureRecognizerStateBegan)
{
SecondViewController *svc = [self.storyboard instantiateViewControllerWithIdentifier:@"SecondViewController"];
[self presentViewController:svc animated:YES completion:nil];
}
有什么想法吗?或者甚至更一般的方法来取消手势,除非满足条件将是非常有帮助的。
答案 0 :(得分:0)
你可以通过subclassing UIGestureRecognizer做你想做的事情来制作你自己的手势识别器,类似于UILongPressGestureRecognizer,除了按下至少3秒的持续时间外,还可以监听加速度计数据。
答案 1 :(得分:0)
我可能会覆盖onTouchesBegan和onTouchesEnded方法,而不是使用手势识别器。
然后我在视图控制器中创建一个NSTimer对象,一个NSTimeInterval var和一个BOOL;为了它,我会称它们为touchTimer,initialTouchTimeStamp和touchValid。
为了复杂起见,假设viewControllers视图不是多点触控。
假设repeatTime = 0.25f; longPressTimeRequired = 3;
定时器选择器将合并你的加速度计方法,如果加速度计方法中的数据无效,我将touchValid设置为false(并使定时器无效),否则我将其设置为true检查加速度计后,我会检查我的initialTouchTimeStamp var是否为longPressTimeRequired或[touchTimer fireDate] - repeatTime
之前的更多秒,如果是,并且touchValid为true,那么我将转到我的第二个控制器。
onTouchesBegan,我将touchTimer无效并创建一个新的重复每秒repearTime秒,并持续y秒。将touchValid设置为NO,并将initialTouchTimeStamp设置为touch.timestamp。
onTouchesEnded,我将touchTimer无效,并检查我的initialTouchTimeStamp var是否为long [touchTimer fireDate] - repeatTime
之前的更长时间,如果是,并且touchValid为true,那么我将转到我的第二个控制器。< / p>
这里有许多共同的元素,它可能不是最优雅的做事方式,但它应该有效。希望这会有所帮助。
答案 2 :(得分:0)
我希望代码足够冗长,你可以阅读,它非常自我解释 - 我坚持你的加速度计代码,并使用相同的变量名称。
#import "ViewController.h"
#import <CoreMotion/CoreMotion.h>
@interface ViewController () {
NSOperationQueue *motionQueue;
NSTimer *touchTimer;
NSTimeInterval initialTimeStamp;
BOOL touchValid;
float timerPollInSeconds;
float longPressTimeRequired;
}
@property (strong, nonatomic) NSTimer *touchTimer;
@property (assign, nonatomic) NSTimeInterval initialTimeStamp;
@property (assign, nonatomic) BOOL touchValid;
@property (assign, nonatomic) float timerPollInSeconds;
@property (assign, nonatomic) float longPressTimeRequired;
@property (strong, nonatomic) CMMotionManager *motionManager;
@property (strong, nonatomic) NSOperationQueue *motionQueue;
@end
@implementation ViewController
@synthesize touchTimer = _touchTimer, initialTimeStamp, touchValid, motionQueue = _motionQueue;
@synthesize timerPollInSeconds, longPressTimeRequired, motionManager = _motionManager;
- (void)viewDidLoad
{
self.timerPollInSeconds = 0.25f;
self.longPressTimeRequired = 3.0f;
self.touchTimer = nil;
self.touchValid = NO;
self.initialTimeStamp = NSTimeIntervalSince1970;
self.motionManager = [[CMMotionManager alloc] init];
self.motionQueue = [[NSOperationQueue alloc] init];
[_motionQueue setName:@"MotionQueue"];
[_motionQueue setMaxConcurrentOperationCount:NSOperationQueueDefaultMaxConcurrentOperationCount];
[super viewDidLoad];
self.view.multipleTouchEnabled = NO;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Operations
-(void) startLongPressMonitorWithTimeStamp:(NSTimeInterval) timeStamp {
NSLog(@"Starting monitoring - %g", timeStamp);
if( self.touchTimer ) {
if( [_touchTimer isValid] ) {
[_touchTimer invalidate];
}
}
self.touchTimer = [NSTimer timerWithTimeInterval:self.timerPollInSeconds target:self selector:@selector(timerPolled:) userInfo:nil repeats:YES];
if( [_motionManager isAccelerometerAvailable] ) {
NSLog(@"Accelerometer Available");
if( ![_motionManager isAccelerometerActive] ) {
NSLog(@"Starting Accelerometer");
[_motionManager startAccelerometerUpdatesToQueue:self.motionQueue withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
if (ABS(accelerometerData.acceleration.x) < 0.3 && ABS(accelerometerData.acceleration.y) < 0.30 && ABS(accelerometerData.acceleration.z) > 0.70) // Phone is flat and screen faces up
{
dispatch_sync(dispatch_get_main_queue(), ^{
self.touchValid = YES;
});
}
else
{
dispatch_sync(dispatch_get_main_queue(), ^{
self.touchValid = NO;
[self stopLongPressMonitoring:YES];
});
};
}];
}
else {
NSLog(@"Accelerometer already active");
}
}
else {
NSLog(@"Accelerometer not available");
}
self.initialTimeStamp = timeStamp;
self.touchValid = YES;
[_touchTimer fire];
[[NSRunLoop mainRunLoop] addTimer:self.touchTimer forMode:NSRunLoopCommonModes];
}
-(void) stopLongPressMonitoring:(BOOL) touchSuccessful {
[_motionManager stopAccelerometerUpdates];
[_touchTimer invalidate];
self.touchValid = NO;
if( touchSuccessful ) {
NSLog(@"Yes");
}
else {
NSLog(@"No");
}
}
#pragma mark - User Interaction
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//We're using the current times, interval since the touches timestamp refers to system boot up
// it is more than feasible to use this boot up time, but for simplicity, I'm just using this
NSTimeInterval timestamp = [NSDate timeIntervalSinceReferenceDate];
[self startLongPressMonitorWithTimeStamp:timestamp];
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if( self.touchValid && [NSDate timeIntervalSinceReferenceDate] - self.initialTimeStamp == self.longPressTimeRequired ) {
[self stopLongPressMonitoring:YES];
}
else {
[self stopLongPressMonitoring:NO];
}
}
#pragma mark - Timer Call back
-(void) timerPolled:(NSTimer *) timer {
NSTimeInterval firedTimeStamp = [NSDate timeIntervalSinceReferenceDate];
NSLog(@"Timer polled - %g", firedTimeStamp);
if( self.touchValid ) {
NSLog(@"Time elapsed: %d", (int)(firedTimeStamp - self.initialTimeStamp));
if( firedTimeStamp - self.initialTimeStamp >= self.longPressTimeRequired ) {
NSLog(@"Required time has elapsed");
[self stopLongPressMonitoring:YES];
}
}
else {
NSLog(@"Touch invalidated");
[self stopLongPressMonitoring:NO];
}
}
@end