Ios 仅在满足特定条件时使用长按手势

Ios 仅在满足特定条件时使用长按手势,ios,uigesturerecognizer,Ios,Uigesturerecognizer,我正在尝试创建一个长按手势,在按住按钮3秒钟后,显示第二个视图控制器。但是,如果设备在整个3秒钟内处于某个加速计方向,我只希望显示第二个视图控制器。也就是说,如果手势保持时间不够长或设备倾斜过大,则该手势将被取消,用户必须重试 //在FirstViewController.h中 #import <UIKit/UIKit.h> #import <CoreMotion/CoreMotion.h> @interface FirstViewController : UIView

我正在尝试创建一个长按手势,在按住按钮3秒钟后,显示第二个视图控制器。但是,如果设备在整个3秒钟内处于某个加速计方向,我只希望显示第二个视图控制器。也就是说,如果手势保持时间不够长或设备倾斜过大,则该手势将被取消,用户必须重试

//在FirstViewController.h中

#import <UIKit/UIKit.h>
#import <CoreMotion/CoreMotion.h>

@interface FirstViewController : UIViewController

@property (nonatomic, strong) CMMotionManager *motionManager;

@end
我以前在最后一个方法中尝试过代码块,但它看起来令人讨厌,而且不正确。相反,我在下面列出了几行代码,我知道它们可以单独工作,但我需要帮助使它们一起工作

//加速计

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.");
}

有什么想法吗?或者,除非满足某个条件,否则用一种更通用的方法取消手势会非常有帮助。

您可以做任何您想做的事情,使自己的手势识别器类似于UILongPress手势识别器,除了持续至少3秒的按压外,还可以监听加速计数据。

我可能会覆盖该选项onTouchesBegan和OnTouchSend方法,而不是使用手势识别器

然后在视图控制器中创建一个NSTimer对象、一个NSTimeInterval变量和一个BOOL;为了方便起见,我将调用它们,touchTimer、initialTouchTimeStamp和touchValid

出于复杂性的考虑,假设ViewController视图不是多点触摸

假设重复时间=0.25f; 所需长按时间=3

计时器选择器将包含加速计方法,如果加速计方法中的数据无效,我会将touchValid设置为false(并使计时器无效),否则我会在检查加速计后将其设置为true,我会检查我的初始TouchTimeStamp var是否为longPressTimeRequired或在
[touchTimer fireDate]-repeatTime
,如果是,并且touchValid为true,则我将转到我的第二个控制器

在TouchsBegan上,我将使touchTimer无效,并创建一个新的touchTimer,它将每重复一秒重复一次,持续y秒。将touchValid设置为NO,并将initialTouchTimeStamp设置为touch.timestamp

在TouchSend上,我将使touchTimer无效,并检查我的initialTouchTimeStamp var是否为longPressTimeRequired或在
[touchTimer fireDate]-repeatTime
之前的秒数,如果是,并且touchValid为true,则我将转到第二个控制器


这里有许多常见元素,这可能不是最优雅的处理方式,但它应该会起作用。希望这能有所帮助。

我希望代码足够详细,您可以阅读,这是非常不言自明的-我坚持使用您的加速计代码,并使用相同的变量名称

#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
#导入“ViewController.h”
#进口
@界面视图控制器(){
NSOperationQueue*运动队列;
NSTimer*触摸计时器;
NSTimeInterval初始时间戳;
布尔触摸有效;
浮动时间(秒);
所需的浮动长按时间;
}
@属性(强,非原子)NSTimer*touchTimer;
@属性(赋值,非原子)NSTimeInterval初始时间戳;
@属性(赋值,非原子)BOOL touchValid;
@属性(赋值,非原子)浮点timerPollInSeconds;
@属性(赋值,非原子)float longPressTimeRequired;
@属性(强、非原子)CMMotionManager*motionManager;
@属性(强,非原子)NSOperationQueue*motionQueue;
@结束
@实现视图控制器
@合成touchTimer=\u touchTimer,initialTimeStamp,touchValid,motionQueue=\u motionQueue;
@合成timerPollInSeconds,长按timerequired,motionManager=\u motionManager;
-(无效)viewDidLoad
{
self.timerPollInSeconds=0.25f;
self.longPressTimeRequired=3.0f;
self.touchTimer=nil;
self.touchValid=否;
self.initialTimeStamp=NSTimeIntervalSince1970;
self.motionManager=[[CMMotionManager alloc]init];
self.motionQueue=[[NSOperationQueue alloc]init];
[_motionqueuesetname:@“motionQueue”];
[\u motionQueue setMaxConcurrentOperationCount:NSOperationQueueDefaultMaxConcurrentOperationCount];
[超级视图下载];
self.view.multipleTouchEnabled=否;
}
-(无效)未收到记忆警告
{
[超级记忆警告];
//处置所有可以重新创建的资源。
}
#pragma标记-操作
-(void)带时间戳的StartOngPress监视器:(NSTimeInterval)时间戳{
NSLog(@“开始监视-%g”,时间戳);
if(self.touchTimer){
如果([\u touchTimer有效]){
[_touchtimerinvalidate];
}
}
self.touchTimer=[NSTimer timer WithTimeInterval:self.timerPollInSeconds目标:self选择器:@selector(timerPolled:)userInfo:nil repeats:YES];
如果([\u motionManager是AccelerometerAvailable]){
NSLog(“加速度计可用”);
如果(![\u motionManager是AccelerMeteractive]){
NSLog(“启动加速计”);
[\u motionManager startAccelerometerUpdatesToQueue:self.motionQueue with Handler:^(CMAccelerometerData*accelerometerData,NSError*错误){
如果(ABS(accelerometerData.acceleration.x)<0.3和ABS(accelerometerData.acceleration.y)<0.30和ABS(accelerometerData.acceleration.z)>0.70)//手机是平的,屏幕朝上
{
调度同步(调度获取主队列()^{
self.touchValid=是;
});
}
其他的
{
调度同步(调度获取主队列()^{
self.touchValid=否;
[自动停止长按监控:是];
});
};
}];
if (sender.state == UIGestureRecognizerStateBegan)
{
    SecondViewController *svc = [self.storyboard instantiateViewControllerWithIdentifier:@"SecondViewController"];
    [self presentViewController:svc animated:YES completion:nil];
}
#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