0

How do I detect if the user has touched the screen in the past 5 seconds. If they have not I would like it to call my method to hide the navigation bars. I found a few answer but could not figure them out.

Thank You

.hhhh

   #import <UIKit/UIKit.h>
    #import "PageView.h"
    #import "SlideShowViewController.h"
    #import "PagesCollectionViewController.h"

    @interface PageFlipperAppDelegate : NSObject <UIApplicationDelegate> {}

    @property (nonatomic, retain) IBOutlet UIWindow *window;

    @property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
    @property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
    @property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;

    - (void)saveContext;
    - (NSURL *)applicationDocumentsDirectory;

    @end




    @protocol MyKindOfWindowDelegate;

    @interface MyKindOfWindow : UIWindow
    {

        NSTimer *tenthTimer;
        NSInteger tenthPast;
    }

    @property (nonatomic, assign) id <MyKindOfWindowDelegate> touchDelegate;

    -(void)startTimer;
    -(void)stopTimer;
    -(void)timerTick;

    @end

    @protocol MyKindOfWindowDelegate <NSObject>

    @required
    - (void) noTouchForFiveSeconds;
    @end

.mmmm

    #import "PageFlipperAppDelegate.h"

    @implementation PageFlipperAppDelegate

    @synthesize window;
    @synthesize managedObjectContext, managedObjectModel, persistentStoreCoordinator;
    - (void) noTouchForFiveSeconds  //!! your AppDelegate should implement this method
    {

        NSLog (@"No touch detected for over 5 seconds");

        //do your stuff

        //you can re-start timer immediately or somewhere later in the code
        [(MyKindOfWindow *)self.window startTimer];
    }


    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque];

        [NSThread sleepForTimeInterval:2.75];



        self.window = [[MyKindOfWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
     [(MyKindOfWindow *)self.window setTouchDelegate:self];

        //PagesTableViewController *pagesTableViewController = [[PagesTableViewController alloc] initWithManagedObjectContext:[self managedObjectContext]];
        //[(UINavigationController *)[[self window] rootViewController] pushViewController:pagesTableViewController animated:NO];

        PagesCollectionViewController *collectionViewController = [[PagesCollectionViewController alloc] initWithManagedObjectContext:[self managedObjectContext]];
        //self.window = (UIWindow*)[[SlideShowViewController alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        // [s setTouchDelegate:self];
        [(UINavigationController *)[[self window] rootViewController] pushViewController:collectionViewController animated:NO];

        [(UINavigationController *)[[self window] rootViewController] setToolbarHidden:NO animated:NO];
        //[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];

        //[pagesTableViewController release];
        [collectionViewController release];
        [[self window] makeKeyAndVisible];


        return YES;
    }

    - (void)applicationWillResignActive:(UIApplication *)application
    {
        //stopping timer since we're going to background
        [(MyKindOfWindow *)self.window stopTimer];
    }

    - (void)applicationDidBecomeActive:(UIApplication *)application
    {
        //you can start timer when app became active or somewhere later in the code
        [(MyKindOfWindow *)self.window startTimer];
    }


    - (void)applicationDidEnterBackground:(UIApplication *)application { [self saveContext]; }

    - (void)applicationWillEnterForeground:(UIApplication *)application
    {
        /*
         Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
         */
    }


    - (void)applicationWillTerminate:(UIApplication *)application { [self saveContext];
    }

    - (void)dealloc
    {
        [window release];
        [managedObjectContext release];
        [managedObjectModel release];
        [persistentStoreCoordinator release];
        [super dealloc];
    }

    - (void)awakeFromNib
    {
        /*
         Typically you should set up the Core Data stack here, usually by passing the managed object context to the first view controller.
         self..managedObjectContext = self.managedObjectContext;
         */
    }

    - (void)saveContext
    {
        NSError *error = nil;
        if ([self managedObjectContext])
        {
            if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error])
            {
                /*
                 Replace this implementation with code to handle the error appropriately.

                 abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
                 */
                //      NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
                abort();
            } 
        }
    }

    #pragma mark - Core Data stack

    /**
     Returns the managed object context for the application.
     If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
     */
    - (NSManagedObjectContext *)managedObjectContext
    {
        if (!managedObjectContext)
        {
            NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
            if (coordinator)
            {
                managedObjectContext = [[NSManagedObjectContext alloc] init];
                [managedObjectContext setPersistentStoreCoordinator:coordinator];
                [managedObjectContext setUndoManager:[[[NSUndoManager alloc] init] autorelease]];
            }
        }
        return managedObjectContext;
    }

    /**
     Returns the managed object model for the application.
     If the model doesn't already exist, it is created from the application's model.
     */
    - (NSManagedObjectModel *)managedObjectModel
    {
        if (!managedObjectModel)
        {
            NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"PageFlipper" withExtension:@"momd"];
            managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
        }    
        return managedObjectModel;
    }

    /**
     Returns the persistent store coordinator for the application.
     If the coordinator doesn't already exist, it is created and the application's store added to it.
     */
    - (NSPersistentStoreCoordinator *)persistentStoreCoordinator
    {
        if (persistentStoreCoordinator) return persistentStoreCoordinator;

        NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"PageFlipper.sqlite"];

        NSError *error = nil;
        persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
        if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
        {
            /*
             Replace this implementation with code to handle the error appropriately.

             abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.

             Typical reasons for an error here include:
             * The persistent store is not accessible;
             * The schema for the persistent store is incompatible with current managed object model.
             Check the error message to determine what the actual problem was.


             If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.

             If you encounter schema incompatibility errors during development, you can reduce their frequency by:
             * Simply deleting the existing store:
             [[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]

             * Performing automatic lightweight migration by passing the following dictionary as the options parameter: 
             [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];

             Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.

             */
            //  NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }    

        return persistentStoreCoordinator;
    }

    #pragma mark - Application's Documents directory

    /**
     Returns the URL to the application's Documents directory.
     */
    - (NSURL *)applicationDocumentsDirectory
    {
        return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    }

    /*- (IBAction)toggleSettingsView:(id)sender
     {
     UIView *from = ([[settingsViewController view] isHidden] ? [padViewController view] : [settingsViewController view]);
     UIView *to = ([[settingsViewController view] isHidden] ? [settingsViewController view] : [padViewController view]);

     const NSTimeInterval flipDuration = 1.0;

     [UIView transitionWithView:from duration:flipDuration options:UIViewAnimationOptionTransitionFlipFromLeft animations:^(void)
     {
     [from setHidden:YES];
     [to setHidden:NO];
     } 
     completion:^(BOOL finished) {}
     ];
     [UIView transitionWithView:to duration:flipDuration options:UIViewAnimationOptionTransitionFlipFromLeft animations:^(void)
     {
     [from setHidden:YES];
     [to setHidden:NO];
     } 
     completion:^(BOOL finished) {}
     ];
     }*/

    @end


    @implementation MyKindOfWindow

    @synthesize touchDelegate = _touchDelegate;

    - (id)initWithFrame:(CGRect)aRect
    {
        if ((self = [super initWithFrame:aRect])) {

            _touchDelegate = nil;

            tenthPast = 0;
            tenthTimer = nil;
        }
        return self;
    }

    -(void)startTimer
    {

        NSLog (@"starting timer");
        tenthPast = 0;

        if (tenthTimer) [self stopTimer];

        tenthTimer  = [NSTimer timerWithTimeInterval:0.1
                                              target:self
                                            selector:@selector(timerTick)
                                            userInfo:nil    
                                             repeats:YES];

        [[NSRunLoop currentRunLoop] addTimer:tenthTimer forMode:NSDefaultRunLoopMode];
    }

    -(void)stopTimer
    {
        NSLog (@"stopping timer");
        [tenthTimer invalidate];
        tenthTimer = nil;
    }

    -(void)timerTick
    {
        //NSLog (@"1/10th of second passed");

        if (++tenthPast > 50) {

            if ((_touchDelegate != nil) && ([_touchDelegate respondsToSelector:@selector(noTouchForFiveSeconds)])) {

                NSLog (@"sending info to delegate");

                [self stopTimer];
                [_touchDelegate noTouchForFiveSeconds];            
            }
        }
    }

    - (void)sendEvent:(UIEvent *)event
    {

        [super sendEvent: event];

        if (event.type == UIEventTypeTouches) {

            //NSLog (@"touches detected - resetting counter");
            tenthPast = 0;
        }
    }

    @end

enter image description here

BDGapps
  • 3,318
  • 10
  • 56
  • 75
  • See the answer below. You will have to subclass UIWindow. You'll also have to take another approach of creating/resetting NSTimer - your current approach would create a ton of timers per single finger-slide. I can help you with timer when i come from work if you don't get any response sooner. – Rok Jarc Feb 13 '12 at 09:39
  • you're very close. in the code below i added //!! comment at the lines that are missing or misused in your code - you didn't set PageFlipperAppDelegate as a delegate of MyKindOfWindow - for this you also have to implement MyKindOfWindowDelegate protocol and add noTouchForFiveSeconds in your PageFlipperAppDelegate.m – Rok Jarc Feb 14 '12 at 09:32
  • Ok It is very close I think the only thing is when I set delegate to self I have a warning because I declared it in the same .h.m [(MyKindOfWindow *)self.window setTouchDelegate:self]; – BDGapps Feb 14 '12 at 12:21
  • you probably get a warning because you forgot to declare PageFlipperAppDelegate as an implementor if MyKindOfWindowDelegate. See my version of AppDelegate.h below (line with @interface). You also have to put method noTouchForFiveSeconds inside PageFlipperAppDelegate.m - this is the method that gets called when the 5s alert fires. Also: don't you think PageFlipperAppDelegate should be a subclass of UIResponder? Yours is a subclass of NSObject. Is there a good reason for that? – Rok Jarc Feb 14 '12 at 13:18
  • The warning is "Sending 'PageFlipperAppDelegate*' to parameter of incompatible type 'id' I can't implement the delegate because its in the same file right???? – BDGapps Feb 14 '12 at 15:11
  • change '@interface PageFlipperAppDelegate : NSObject ' to '@interface PageFlipperAppDelegate : UIResponder ' – Rok Jarc Feb 14 '12 at 18:44
  • can't find protocol declaration for PageFlipperAppDelegate. Also my screen is still white... – BDGapps Feb 14 '12 at 19:55
  • You have to add something like self.window.rootViewController = collectionViewController in didFinishLaunching... And you'll probably have to declare collectionViewController as a property - you shouldn't be releasing it here. As for protocol name: sorry, my mistake. make it – Rok Jarc Feb 14 '12 at 20:07
  • Setting the delegate still doesn't work and has the same error. Regarding window.rootviewcontroller = ... It doesn't work. It just crashes...... – BDGapps Feb 14 '12 at 21:09
  • i see you're not using ARC - in this case you have to make touchDelegate a retained property. you also have to find a way yo define a window.rootViewController which should also have to be a retained property of PageFlipperAppDelegate. if your app doesn't have to run on iOS < 4.0 then consider using ARC. much easier to keep track of your memory management that way. – Rok Jarc Feb 14 '12 at 21:52
  • I fixed that my only problem now is their is no rootviewcontroller. I tried doing self.window.rootviewcontroller = collectonviewcontroler but when I put that in I get an error.... – BDGapps Feb 15 '12 at 00:27
  • What kind of error do you get? If you update your code (in your answer) i'll chek it out. – Rok Jarc Feb 15 '12 at 07:20

3 Answers3

5

Create a timer that's set to fire in 5 seconds. Every time the user interacts with UI invalidate the timer and reset it. If the timer ever fires then you know that the user has not interacted in the last 5 seconds.

jaminguy
  • 25,840
  • 2
  • 23
  • 21
3

As an addition to jaminguy's answer you could probably subclass UIWindow overriding - (void)sendEvent:(UIEvent *)event to:

- (void)sendEvent:(UIEvent *)event {

    [super sendEvent: event];

    if (event.type == UIEventTypeTouches) {


    //here you can invalidate & restart your 5s timer   
    }
}

You should then use this subclass as you main window for application (the one that is set as key in appDelegate).

Some literature:

UIWindow Class Reference

UIEvent Class Reference

To achieve this you have to add another class to your project (MyKindOfWindow).

file MyKindOfWindow.h

#import <UIKit/UIKit.h>

@interface MyKindOfWindow : UIWindow

@end

file MyKindOfWindow.m

#import "MyKindOfWindow.h"

@implementation MyKindOfWindow

- (void)sendEvent:(UIEvent *)event {

    [super sendEvent: event];

    if (event.type == UIEventTypeTouches) {

        NSLog (@"touch detected");

        //here you can invalidate & restart your 5s timer   
    }
}

@end

You will also have to modify your AppDelegate.m. at the begining you add

#import "MyKindOfWindow.h"

in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions you replace the line

self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

with line

self.window = [[MyKindOfWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

Now you're on half of the way. In your debug console you should see 'touch detected' whenever you touch the screen. You have to decide where you'll put your timer (jaminguy's answer) in: MyKindOfWindow, AppDelegate, seperate class...

Rok Jarc
  • 18,765
  • 9
  • 69
  • 124
  • i've ammanded my answer with more explanation on how to implement touch detection. code is tested :) if you will need help to implement timer feel free to ask. but first try to implement this code (as a result you should see 'touch detected' in xcode debug console when you touch the screen. – Rok Jarc Feb 13 '12 at 09:23
  • I have a navigation controller -> my main page -> pushed to a view off of that How do I get the touch to be in the pushed view??? – BDGapps Feb 13 '12 at 20:06
  • //self.window = (UIWindow*)[[SlideShowViewController alloc] initWithFrame:[[UIScreen mainScreen] bounds]];????? – BDGapps Feb 13 '12 at 20:21
  • have you tried the code below? this navController is still in the main windows so it should work – Rok Jarc Feb 13 '12 at 20:21
  • MyKindOfWindow is my view???? so do i put this in self.window = [[MyKindOfWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; this doesn't work because my MyKindOfWindow is a uiviewcontroller???? What is MyKindOfWindow??? – BDGapps Feb 13 '12 at 21:02
  • MyKindOfWindow is not a UIViewController, it's a subclass of UIWindow. If you want to detected (non)touches on a screen that would be (one of many possible) a way to do it. Have you even tried this code? – Rok Jarc Feb 13 '12 at 21:41
  • Ok I have posted my implementation above. Do you see anything that could be causing a problem??? Thanks – BDGapps Feb 14 '12 at 00:13
1

I've adopted your code so that it should work.

Main changes are: pagesTableViewController and collectionViewController should be retained properties of your application delegate.

self.window.rootViewController is now defined in didFinishLaunchingWithOptions:, both viewControllers are released in dealloc.

Note that you really should consider placing class definitions in separate files and possibly start using ARC for new projects.

PageFlipperAppDelegate.h

    #import <UIKit/UIKit.h>
    #import "PageView.h"
    #import "SlideShowViewController.h"
    #import "PagesCollectionViewController.h"

    @protocol MyKindOfWindowDelegate;

    @interface PageFlipperAppDelegate : NSObject <UIApplicationDelegate,MyKindOfWindowDelegate> {}

    @property (nonatomic, retain) IBOutlet UIWindow *window;
    @property (nonatomic, retain) PagesTableViewController* pagesTableViewController;
    @property (nonatomic, retain) SlideShowViewController* collectionViewController;

    @property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
    @property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
    @property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;

    - (void)saveContext;
    - (NSURL *)applicationDocumentsDirectory;

    @end

    @interface MyKindOfWindow : UIWindow
    {

        NSTimer *tenthTimer;
        NSInteger tenthPast;
    }

    @property (nonatomic, retain) id <MyKindOfWindowDelegate> touchDelegate;

    -(void)startTimer;
    -(void)stopTimer;
    -(void)timerTick;

    @end

    @protocol MyKindOfWindowDelegate <NSObject>

    @required
    - (void) noTouchForFiveSeconds;
    @end

PageFlipperAppDelegate.m

 #import "PageFlipperAppDelegate.h"

    @implementation PageFlipperAppDelegate

    @synthesize window,pagesTableViewController,collectionViewController;
    @synthesize managedObjectContext, managedObjectModel, persistentStoreCoordinator;
    - (void) noTouchForFiveSeconds  //!! your AppDelegate should implement this method
    {

        NSLog (@"No touch detected for over 5 seconds");

        //do your stuff

        //you can re-start timer immediately or somewhere later in the code
        [(MyKindOfWindow *)self.window startTimer];
    }


    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque];

        [NSThread sleepForTimeInterval:2.75]; 

        self.window = [[MyKindOfWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        [(MyKindOfWindow *)self.window setTouchDelegate:self];

        self.pagesTableViewController = [[PagesTableViewController alloc] initWithManagedObjectContext:[self managedObjectContext]];
        self.collectionViewController = [[PagesCollectionViewController alloc] initWithManagedObjectContext:[self managedObjectContext]];        

        self.window.rootViewController = self.pagesTableViewController;

        [self.pagesTableViewController pushViewController:pagesTableViewController animated:NO];        

        [self.pagesTableViewController setToolbarHidden:NO animated:NO];

        [[self window] makeKeyAndVisible];

        return YES;
    }

    - (void)dealloc
    {
        [pagesTableViewController release];
        [collectionViewController release];
        [window release];
        [managedObjectContext release];
        [managedObjectModel release];
        [persistentStoreCoordinator release];
        [super dealloc];
    }

I made the changes in text editor so there might be some typos. Hopfully not.

EDIT:

If you want a certain viewController to start/stop timer with it's appearing/disappearing you should remove all startTimer accourancies in the appDelegate and put the following lines in that viewControllers .m file:

#import "PageFlipperAppDelegate.h"

- (void)viewDidAppear:(BOOL)animated {

  [super viewDidAppear: animated];

  PageFlipperAppDelegate *appDelegate = (PageFlipperAppDelegate *)[[UIApplication sharedApplication] delegate];
  [(MyKindOfWindow *)appDelegate.window startTimer];
}

- (void)viewDidDisappear:(BOOL)animated {

  [super viewDidDisappear: animated];

  PageFlipperAppDelegate *appDelegate = (PageFlipperAppDelegate *)[[UIApplication sharedApplication] delegate];
  [(MyKindOfWindow *)appDelegate.window stopTimer];
}
Rok Jarc
  • 18,765
  • 9
  • 69
  • 124
  • all working!!! is there any way to have them animate slower [[self navigationController]setNavigationBarHidden:YES animated:YES]; [[self navigationController]setToolbarHidden:YES animated:YES]; – BDGapps Feb 16 '12 at 12:19
  • Congratulations! As for making animations slower: there is no direct or by the book way to do it but i've heard of people finding workarounds. Post another question (or search in one exists). Oh, if this is working please make sure to accept one answer so the topic is closed. – Rok Jarc Feb 17 '12 at 08:31