2008-11-07 16 views
140

क्या किसी ने एक सुविधा लागू की है जहां उपयोगकर्ता ने किसी निश्चित समय अवधि के लिए स्क्रीन को छुआ नहीं है, तो आप एक निश्चित कार्रवाई करते हैं? मैं ऐसा करने का सबसे अच्छा तरीका जानने की कोशिश कर रहा हूं।आईफोन: अंतिम स्क्रीन स्पर्श के बाद उपयोगकर्ता निष्क्रियता/निष्क्रिय समय का पता लगाना

वहाँ UIApplication में यह कुछ हद तक से संबंधित विधि है:

NSTimeInterval timeElapsed = [UIApplication sharedApplication].idleTimeElapsed; 

तो मैं एक टाइमर की स्थापना की और समय-समय पर जांच कर सकता है:

[UIApplication sharedApplication].idleTimerDisabled; 

यदि आप के बजाय कुछ इस तरह था अच्छा होगा यह मान, और जब यह सीमा से अधिक हो तो कुछ कार्रवाई करें।

उम्मीद है कि मैं जो खोज रहा हूं उसे समझाता हूं। क्या किसी ने पहले से ही इस मुद्दे का सामना किया है, या इस पर कोई विचार है कि आप इसे कैसे करेंगे? धन्यवाद।

+0

यह एक अच्छा सवाल है। विंडोज़ में ऑनडिल्ड इवेंट की अवधारणा है, लेकिन मुझे लगता है कि यह ऐप के बारे में अधिक है जो वर्तमान में आईओएस idleTimerDisabled संपत्ति बनाम संदेश संदेश पंप में कुछ भी संभाल नहीं रहा है जो केवल डिवाइस को लॉक करने के लिए चिंतित है। किसी को भी पता है कि आईओएस/मैकोज़क्स में विंडोज अवधारणा के करीब भी कुछ भी है? – stonedauwg

उत्तर

147

यहाँ जवाब मैं की तलाश में गया था है:

आपके आवेदन प्रतिनिधि उपवर्ग UIApplication है। इसलिए जैसे विधि: कार्यान्वयन फ़ाइल में sendEvent ओवरराइड

- (void)sendEvent:(UIEvent *)event { 
    [super sendEvent:event]; 

    // Only want to reset the timer on a Began touch or an Ended touch, to reduce the number of timer resets. 
    NSSet *allTouches = [event allTouches]; 
    if ([allTouches count] > 0) { 
     // allTouches count only ever seems to be 1, so anyObject works here. 
     UITouchPhase phase = ((UITouch *)[allTouches anyObject]).phase; 
     if (phase == UITouchPhaseBegan || phase == UITouchPhaseEnded) 
      [self resetIdleTimer]; 
    } 
} 

- (void)resetIdleTimer { 
    if (idleTimer) { 
     [idleTimer invalidate]; 
     [idleTimer release]; 
    } 

    idleTimer = [[NSTimer scheduledTimerWithTimeInterval:maxIdleTime target:self selector:@selector(idleTimerExceeded) userInfo:nil repeats:NO] retain]; 
} 

- (void)idleTimerExceeded { 
    NSLog(@"idle time exceeded"); 
} 

जहां maxIdleTime और idleTimer उदाहरण चर हैं।

आदेश में इस काम करने के लिए, आप भी अपने main.m संशोधित करने के लिए अपने प्रतिनिधि वर्ग (इस उदाहरण, AppDelegate में) प्रमुख वर्ग के रूप में उपयोग करने के लिए UIApplicationMain बताने की आवश्यकता में:

int retVal = UIApplicationMain(argc, argv, @"AppDelegate", @"AppDelegate"); 
+3

हाय माइक, मेरा ऐपडिलेगेट एनएसओब्जेक्ट से विरासत में है, इसलिए उपयोगकर्ता को निष्क्रिय होने का पता लगाने के लिए इसे उपरोक्त तरीकों को लागू किया गया है, लेकिन मुझे त्रुटि मिल रही है "अनसुलझा अपवाद के कारण ऐप को समाप्त करना" एनएसआईएननल इन्फॉन्सिस्टेंसी एक्सेप्शन ', कारण:' केवल एक यूआईपीप्लिकेशन इंस्टेंस हो सकता है '' .. .. मुझे कुछ और करने की ज़रूरत है ...? –

+0

मैंने एक ही दृष्टिकोण का उपयोग किया है, यह ठीक काम करता है! ;) – SlowTree

+6

मैं जोड़ता हूं कि यूआईपीप्लिकेशन सबक्लास यूआईपीप्लिकेशन डिलेगेट सबक्लास – boliva

3

आखिरकार आपको उस चीज़ को परिभाषित करने की आवश्यकता है जिसे आप निष्क्रिय मानते हैं - उपयोगकर्ता का परिणाम स्क्रीन को छूने से नतीजा है या यह सिस्टम की स्थिति है यदि कोई कंप्यूटिंग संसाधनों का उपयोग नहीं किया जा रहा है? यह संभव है, कई अनुप्रयोगों में, उपयोगकर्ता को कुछ भी करने के लिए, भले ही टच स्क्रीन के माध्यम से डिवाइस के साथ सक्रिय रूप से बातचीत नहीं कर रहा हो। हालांकि उपयोगकर्ता शायद सोते समय डिवाइस की अवधारणा से परिचित है और नोटिस है कि यह स्क्रीन डमींग के माध्यम से होगा, यह आवश्यक नहीं है कि अगर वे निष्क्रिय हैं तो वे कुछ होने की उम्मीद करेंगे - आपको सावधान रहना होगा आप क्या करेंगे के बारे में। लेकिन मूल वक्तव्य पर वापस जा रहे हैं - यदि आप पहली बार अपनी परिभाषा मानते हैं, तो ऐसा करने का कोई आसान तरीका नहीं है। आपको प्रत्येक टच इवेंट प्राप्त करने की आवश्यकता होगी, इसे प्राप्त करने वाले समय को ध्यान में रखते हुए प्रतिक्रियाकर्ता श्रृंखला के साथ इसे पास करना होगा। इससे आपको निष्क्रिय गणना करने के लिए कुछ आधार मिलेगा। यदि आप दूसरे मामले को अपनी परिभाषा मानते हैं, तो आप उस समय अपने तर्क को करने और निष्पादित करने के लिए एनएसपीओस्टहेनआईडल अधिसूचना के साथ खेल सकते हैं।

+1

बस स्पष्ट करने के लिए, मैं स्क्रीन के साथ बातचीत के बारे में बात कर रहा हूं। मैं इसे प्रतिबिंबित करने के लिए प्रश्न अपडेट करूंगा। –

+1

फिर आप किसी भी समय स्पर्श कर सकते हैं जहां कोई स्पर्श होता है, आप एक वैल्यू को अपडेट करते हैं जिसे आप चेक करते हैं, या यहां तक ​​कि एक निष्क्रिय टाइमर को आग लगाने के लिए सेट (और रीसेट) भी सेट करते हैं, लेकिन आपको इसे स्वयं लागू करने की आवश्यकता होती है, क्योंकि वारक्वार्क ने कहा, निष्क्रिय क्या होता है विभिन्न ऐप्स के बीच। –

+1

मैं आखिरी बार स्क्रीन को छूने के बाद से "निष्क्रिय" को परिभाषित कर रहा हूं। मुझे लगता है कि मुझे इसे स्वयं लागू करने की आवश्यकता होगी, मैं बस सोच रहा था कि "सर्वश्रेष्ठ" तरीका क्या होगा, कहें, स्क्रीन स्पर्श को रोकें, या अगर कोई इसे निर्धारित करने के लिए वैकल्पिक विधि के बारे में जानता है। –

12

यह धागा था एक बड़ी मदद, और मैंने इसे UIWindow सबक्लास में लपेट लिया जो अधिसूचनाएं भेजता है। मैंने इसे एक वास्तविक ढीला युग्मन बनाने के लिए अधिसूचनाएं चुनी हैं, लेकिन आप आसानी से एक प्रतिनिधि को जोड़ सकते हैं।

यहाँ सार है:

http://gist.github.com/365998

इसके अलावा, UIApplication उपवर्ग जारी करने के लिए कारण यह है कि एनआईबी तो 2 UIApplication वस्तुओं को बनाने के बाद से यह आवेदन और प्रतिनिधि शामिल सेटअप है। UIWindow subclass हालांकि महान काम करता है।

+0

आपके द्वारा शानदार काम! :) –

+1

क्या आप मुझे बता सकते हैं कि अपने कोड का उपयोग कैसे करें? मैं समझ नहीं पा रहा हूं कि इसे कैसे –

+1

कहा जाता है यह स्पर्श के लिए बहुत अच्छा काम करता है, लेकिन कीबोर्ड से इनपुट को संभालने में प्रतीत नहीं होता है। इसका मतलब यह है कि यदि उपयोगकर्ता गुई कीबोर्ड पर सामान टाइप कर रहा है तो यह समय समाप्त हो जाएगा। –

4

असल में सबक्लासिंग विचार बहुत अच्छा काम करता है। बस अपने प्रतिनिधि को UIApplication उप-वर्ग न बनाएं। UIApplication (उदा। MyApp) से प्राप्त एक और फ़ाइल बनाएं।आईबी में fileOwner ऑब्जेक्ट को myApp पर सेट करें और myApp.m में ऊपर के रूप में sendEvent विधि लागू करें। Main.m में:

int retVal = UIApplicationMain(argc,argv,@"myApp.m",@"myApp.m") 

et voilà!

+1

हां, एक स्टैंड-अलोन यूआईप्प्लिकेशन सबक्लास बनाना ठीक काम करता प्रतीत होता है। मैंने मुख्य रूप से दूसरा पार्म शून्य छोड़ा। –

+0

@Roby, एक नज़र डालें कि मेरी क्वेरी http://stackoverflow.com/questions/20088521/calling-method-from-principal-class-in-objective-c। – Tirth

86

मेरे पास निष्क्रिय टाइमर समाधान की एक भिन्नता है जिसे उप-वर्गीकरण UIAplplication की आवश्यकता नहीं है। यह एक विशिष्ट UIViewController सबक्लास पर काम करता है, इसलिए उपयोगी है यदि आपके पास केवल एक व्यू कंट्रोलर है (जैसे एक इंटरैक्टिव ऐप या गेम हो सकता है) या केवल एक विशिष्ट व्यू कंट्रोलर में निष्क्रिय टाइमआउट को संभालना चाहते हैं।

यह निष्क्रिय टाइमर रीसेट होने पर हर बार NSTimer ऑब्जेक्ट को फिर से नहीं बनाता है। टाइमर आग लगने पर यह केवल एक नया बनाता है।

आपका कोड किसी भी अन्य घटनाओं के लिए resetIdleTimer पर कॉल कर सकता है जिसे निष्क्रिय टाइमर (जैसे महत्वपूर्ण एक्सेलेरोमीटर इनपुट) को अमान्य करने की आवश्यकता हो सकती है।

@interface MainViewController : UIViewController 
{ 
    NSTimer *idleTimer; 
} 
@end 

#define kMaxIdleTimeSeconds 60.0 

@implementation MainViewController 

#pragma mark - 
#pragma mark Handling idle timeout 

- (void)resetIdleTimer { 
    if (!idleTimer) { 
     idleTimer = [[NSTimer scheduledTimerWithTimeInterval:kMaxIdleTimeSeconds 
                 target:self 
                selector:@selector(idleTimerExceeded) 
                userInfo:nil 
                repeats:NO] retain]; 
    } 
    else { 
     if (fabs([idleTimer.fireDate timeIntervalSinceNow]) < kMaxIdleTimeSeconds-1.0) { 
      [idleTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:kMaxIdleTimeSeconds]]; 
     } 
    } 
} 

- (void)idleTimerExceeded { 
    [idleTimer release]; idleTimer = nil; 
    [self startScreenSaverOrSomethingInteresting]; 
    [self resetIdleTimer]; 
} 

- (UIResponder *)nextResponder { 
    [self resetIdleTimer]; 
    return [super nextResponder]; 
} 

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    [self resetIdleTimer]; 
} 

@end 

(स्मृति सफाई कोड संक्षिप्तता के लिए बाहर रखा गया।)

+0

+1 वास्तव में अच्छा जवाब मैं वास्तव में मदद की थी – Saawan

+1

बहुत अच्छा। यह जवाब चट्टानों! उत्तर को सही के रूप में चिह्नित किया गया है हालांकि मुझे पता है कि यह बहुत पहले था लेकिन यह अब एक बेहतर समाधान है। –

+0

अच्छा समाधान। बहुत बहुत धन्यवाद। – Prazi

4

मैं सिर्फ एक खेल है कि गति के नियंत्रण में है के साथ इस समस्या में पड़ गए अर्थात स्क्रीन विकलांग ताला लेकिन जब मेनू मोड में फिर से सक्षम करना है। एक टाइमर के बजाय मैं निम्न विधियों में उपलब्ध कराने के setIdleTimerDisabled के लिए सभी कॉल समझाया एक छोटा सा वर्ग के भीतर:

- (void) enableIdleTimerDelayed { 
    [self performSelector:@selector (enableIdleTimer) withObject:nil afterDelay:60]; 
} 

- (void) enableIdleTimer { 
    [NSObject cancelPreviousPerformRequestsWithTarget:self]; 
    [[UIApplication sharedApplication] setIdleTimerDisabled:NO]; 
} 

- (void) disableIdleTimer { 
    [NSObject cancelPreviousPerformRequestsWithTarget:self]; 
    [[UIApplication sharedApplication] setIdleTimerDisabled:YES]; 
} 

disableIdleTimer निष्क्रिय टाइमर, enableIdleTimerDelayed जब मेनू में प्रवेश करने या जो कुछ भी निष्क्रिय टाइमर सक्रिय और enableIdleTimer साथ चलाना चाहिए से कहा जाता है को निष्क्रिय कर अपने AppDelegate की applicationWillResignActive विधि यह सुनिश्चित करने के लिए कि आपके सभी परिवर्तन सिस्टम डिफ़ॉल्ट व्यवहार में ठीक से रीसेट हो जाएं।
मैं एक लेख लिखा था और IdleTimerManager Idle Timer Handling in iPhone Games

4

सिंगलटन वर्ग के लिए कोड प्रदान की यहाँ गतिविधि का पता लगाने के लिए एक और तरीका है:

टाइमर UITrackingRunLoopMode में जोड़ा जाता है, तो यह केवल आग कर सकते हैं, अगर वहाँ UITracking गतिविधि। यह सभी स्पर्श घटनाओं के लिए आपको स्पैमिंग नहीं करने का अच्छा लाभ भी है, इस प्रकार यह सूचित करता है कि पिछले ACTIVITY_DETECT_TIMER_RESOLUTION सेकंड में गतिविधि थी या नहीं। मैंने चयनकर्ता keepAlive नाम दिया क्योंकि यह इसके लिए उपयुक्त उपयोग केस लगता है। आप निश्चित रूप से ऐसी जानकारी के साथ जो कुछ भी चाहते हैं वह कर सकते हैं कि हाल ही में गतिविधि थी।

_touchesTimer = [NSTimer timerWithTimeInterval:ACTIVITY_DETECT_TIMER_RESOLUTION 
             target:self 
             selector:@selector(keepAlive) 
             userInfo:nil 
             repeats:YES]; 
[[NSRunLoop mainRunLoop] addTimer:_touchesTimer forMode:UITrackingRunLoopMode]; 
+0

यह कोड अधूरा है। – Jasper

+0

कैसे? मुझे विश्वास है कि यह स्पष्ट है कि आपको अपने पास जो कुछ भी चाहिए, उसे "keepAlive" चयनकर्ता स्वयं बनाना चाहिए। शायद मैं तुम्हारा दृष्टिकोण देख रहा हूँ? –

+0

आप कहते हैं कि यह गतिविधि का पता लगाने का एक और तरीका है, हालांकि, यह केवल एक आईवीआर को चालू करता है जो एक एनएसटीमर है। मैं नहीं देखता कि यह ओपी के सवाल का जवाब कैसे देता है। – Jasper

6

तेज वी के लिए 3.1

dont't भूल AppDelegate में इस लाइन टिप्पणी // @ UIApplicationMain

extension NSNotification.Name { 
    public static let TimeOutUserInteraction: NSNotification.Name = NSNotification.Name(rawValue: "TimeOutUserInteraction") 
} 


class InterractionUIApplication: UIApplication { 

static let ApplicationDidTimoutNotification = "AppTimout" 

// The timeout in seconds for when to fire the idle timer. 
let timeoutInSeconds: TimeInterval = 15 * 60 

var idleTimer: Timer? 

// Listen for any touch. If the screen receives a touch, the timer is reset. 
override func sendEvent(_ event: UIEvent) { 
    super.sendEvent(event) 

    if idleTimer != nil { 
     self.resetIdleTimer() 
    } 

    if let touches = event.allTouches { 
     for touch in touches { 
      if touch.phase == UITouchPhase.began { 
       self.resetIdleTimer() 
      } 
     } 
    } 
} 

// Resent the timer because there was user interaction. 
func resetIdleTimer() { 
    if let idleTimer = idleTimer { 
     idleTimer.invalidate() 
    } 

    idleTimer = Timer.scheduledTimer(timeInterval: timeoutInSeconds, target: self, selector: #selector(self.idleTimerExceeded), userInfo: nil, repeats: false) 
} 

// If the timer reaches the limit as defined in timeoutInSeconds, post this notification. 
func idleTimerExceeded() { 
    NotificationCenter.default.post(name:Notification.Name.TimeOutUserInteraction, object: nil) 
    } 
} 

main.swif फ़ाइल बनाने और इस ऐड (नाम है महत्वपूर्ण)

CommandLine.unsafeArgv.withMemoryRebound(to: UnsafeMutablePointer<Int8>.self, capacity: Int(CommandLine.argc)) {argv in 
_ = UIApplicationMain(CommandLine.argc, argv, NSStringFromClass(InterractionUIApplication.self), NSStringFromClass(AppDelegate.self)) 
} 

एक किसी अन्य वर्ग

NotificationCenter.default.addObserver(self, selector: #selector(someFuncitonName), name: Notification.Name.TimeOutUserInteraction, object: nil) 
+0

सही! धन्यवाद दोस्त! – BrunoVillanova

0

इन सभी में अधिसूचना अवलोकन बहुत जटिल लगते हैं। मेरा मानना ​​है कि यह आपके लिए आवश्यक सभी स्पर्शों को प्रॉक्सी करने का एक क्लीनर तरीका है।

fileprivate var timer ... //timer logic here 

@objc public class CatchAllGesture : UIGestureRecognizer { 
    override public func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) { 
     super.touchesBegan(touches, with: event) 
    } 
    override public func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) { 
     //reset your timer here 
     state = .failed 
     super.touchesEnded(touches, with: event) 
    } 
    override public func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) { 
     super.touchesMoved(touches, with: event) 
    } 
} 

@objc extension YOURAPPAppDelegate { 

    func addGesture() { 
     let aGesture = CatchAllGesture(target: nil, action: nil) 
     aGesture.cancelsTouchesInView = false 
     self.window.addGestureRecognizer(aGesture) 
    } 
} 

अपने ऐप प्रतिनिधि के पूर्ण लॉन्च विधि में, बस addGesture पर कॉल करें और आप सभी सेट हैं। सभी स्पर्श CatchAllGesture के तरीकों के माध्यम से दूसरों की कार्यक्षमता को रोकने के बिना जायेंगे।

संबंधित मुद्दे