ايفون:اكتشاف عدم نشاط المستخدم/وقت الخمول منذ آخر لمسة للشاشة

StackOverflow https://stackoverflow.com/questions/273450

سؤال

هل قام أي شخص بتطبيق ميزة حيث إذا لم يلمس المستخدم الشاشة لفترة زمنية معينة، يمكنك اتخاذ إجراء معين؟أحاول معرفة أفضل طريقة للقيام بذلك.

هناك هذه الطريقة ذات الصلة إلى حد ما في UIApplication:

[UIApplication sharedApplication].idleTimerDisabled;

سيكون من الرائع لو كان لديك بدلاً من ذلك شيء مثل هذا:

NSTimeInterval timeElapsed = [UIApplication sharedApplication].idleTimeElapsed;

ثم يمكنني إعداد مؤقت والتحقق من هذه القيمة بشكل دوري واتخاذ بعض الإجراءات عندما تتجاوز الحد الأدنى.

نأمل أن يفسر هذا ما أبحث عنه.هل تناول أي شخص هذه المشكلة بالفعل، أو لديه أي أفكار حول كيفية القيام بذلك؟شكرًا.

هل كانت مفيدة؟

المحلول

وهنا يكمن الجواب الأول كان يبحث عن:

هل لديك التطبيق الخاص بك مندوب فرعية 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 لإعلام UIApplicationMain استخدام الطبقة مندوب (في هذا المثال، AppDelegate) كطبقة أساسية:

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

نصائح أخرى

ولدي الاختلاف من حل مؤقت الخمول والتي لا تتطلب شاء subclasses ترث UIApplication. وهي تعمل على فئة فرعية 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

و(الذاكرة رمز تنظيف استبعاد الإيجاز.)

للسويفت v 3.1

لا تنس التعليق على هذا السطر في 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)

وكان هذا موضوع مساعدة كبيرة، وأنا ملفوفة أنه حتى في فئة فرعية UIWindow التي ترسل الإخطارات. اخترت الإخطارات لجعله اقتران فضفاض الحقيقي، ولكن يمكنك إضافة مفوض بسهولة بما فيه الكفاية.

وهنا يكمن جوهر:

http://gist.github.com/365998

وأيضا، والسبب في قضية فرعية UIApplication هو أن بنك نور الإسلامي هو الإعداد ثم لخلق الكائنات 2 UIApplication لأنه يحتوي على التطبيق ومندوب. UIWindow فرعية يعمل كبيرة بالرغم من ذلك.

وركضت للتو في هذه المشكلة مع اللعبة التي تسيطر عليها حركات أي لديها شاشة قفل المعوقات ولكن ينبغي أن تمكن مرة أخرى عندما تكون في وضع القائمة. بدلا من توقيت I مغلفة جميع المكالمات إلى 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 يتم استدعاؤها من طريقة applicationWillResignActive AppDelegate الخاص بك لضمان إعادة تعيين كافة التغييرات بشكل صحيح إلى سلوك النظام الافتراضي.
كتبت مقالا وقدمت رمز للطبقة مفردة IdleTimerManager <لأ href = "http://www.scio.de/en/blog-a-news/scio-development-blog-en/entry/idle-timer- التعامل مع في واي فون الألعاب "يختلط =" نوفولو "> التعامل مع الموقت الخمول في اي فون ألعاب

وهنا هو طريقة أخرى للكشف عن النشاط:

ويتم إضافة الموقت في UITrackingRunLoopMode، لذلك ما في وسعها النار إلا إذا كان هناك نشاط UITracking. كما أن لديها ميزة لطيفة من إغراق لكم على كل الأحداث التي تعمل باللمس، وبالتالي إبلاغ إذا كان هناك نشاط في ACTIVITY_DETECT_TIMER_RESOLUTION الثواني الأخيرة. I اسمه keepAlive محدد كما يبدو حالة استخدام المناسبة لذلك. يمكنك بالطبع أن تفعل ما تريدها مع المعلومات التي كان هناك نشاط في الآونة الأخيرة.

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

والواقع أن فكرة إن شاء subclasses ترث يعمل كبيرة. فقط لا تجعل مندوب بك فئة فرعية UIApplication. إنشاء ملف آخر أن يرث من UIApplication (على سبيل المثال اسم التطبيق). في IB تعيين فئة من الكائن fileOwner إلى myApp وفي myApp.m تنفيذ طريقة sendEvent على النحو الوارد أعلاه. في main.m القيام به:

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

وآخرون فويلا!

وفي نهاية المطاف تحتاج إلى تعريف ما عليك أن تنظر في وضع الخمول - هو الخمول نتيجة المستخدم لا لمس الشاشة أو هو حالة النظام إذا كان يتم استخدام أي موارد الحوسبة؟ ومن الممكن، في العديد من التطبيقات، للمستخدم أن تفعل شيئا حتى لو لم يكن التفاعل النشط مع الجهاز من خلال الشاشة التي تعمل باللمس. في حين أن المستخدم هو على الارجح مطلع على مفهوم الجهاز النوم ولاحظ أن ذلك سيحدث عبر يعتم الشاشة، فإنه ليس بالضرورة أنها سوف نتوقع حدوث شيء ما إذا كانت الخمول - عليك أن تكون حذرا حول ما سيفعله. ولكن بالعودة إلى البيان الأصلي - إذا كنت تنظر في قضية 1ST أن يكون التعريف الخاص بك، لا توجد وسيلة سهلة حقا للقيام بذلك. كنت بحاجة للحصول على كل حدث اتصال، ويمر على طول على سلسلة الرد حسب الحاجة مع ملاحظة الوقت الذي وردت فيه. وهذا يعطيك بعض الأسس لجعل حساب الخمول. إذا كنت تنظر في الحالة الثانية أن يكون التعريف الخاص بك، يمكنك ان تلعب مع إشعار NSPostWhenIdle في محاولة لأداء المنطق الخاص في ذلك الوقت.

وهناك طريقة للقيام هذا التطبيق على نطاق واسع دون تحكم الفردية الحاجة إلى القيام بأي شيء. فقط إضافة التعرف على لفتة أن لا يلغي اللمسات. بهذه الطريقة، سيتم تتبع كل اللمسات للتوقيت، واللمسات والحركات الأخرى لا تتأثر على الإطلاق لذلك لا أحد آخر لديه لمعرفة ذلك.

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 دون ذلك منع وظائف أخرى.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top