Swift中如何在特定时间执行代码?日夜模式切换需求咨询
Got it, let's work through this problem. You need to switch day/night mode at 6 AM and 6 PM only when your app is in the foreground, no background execution required, and make sure other app functions keep running smoothly. Here's a clean, native Swift approach that fits all your requirements:
Core Concept
We'll use a combination of:
NotificationCenterto listen for app foreground/background state changesCalendarto calculate the exact next trigger time (6 AM or 6 PM)Timerto fire the mode toggle at the right time (only when the app is active)
This way, we don't waste resources in the background, and we ensure the timer only runs when the app is in use.
Step 1: Create a Mode Toggle Manager
We'll encapsulate all logic in a dedicated class to keep your code organized and avoid cluttering view controllers or app delegates.
import UIKit class ModeToggleManager { static let shared = ModeToggleManager() private var toggleTimer: Timer? private init() { // Listen for app state changes NotificationCenter.default.addObserver(self, selector: #selector(appDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(appWillResignActive), name: UIApplication.willResignActiveNotification, object: nil) } @objc private func appDidBecomeActive() { // When app comes to foreground, start or reset the timer scheduleNextToggle() } @objc private func appWillResignActive() { // When app goes to background, invalidate the timer to save resources toggleTimer?.invalidate() toggleTimer = nil } private func scheduleNextToggle() { // Invalidate existing timer first toggleTimer?.invalidate() let calendar = Calendar.current let now = Date() // Calculate next 6 AM and 6 PM guard let nextMorning6 = calendar.nextDate(after: now, matching: DateComponents(hour: 6, minute: 0, second: 0), matchingPolicy: .strict), let nextEvening6 = calendar.nextDate(after: now, matching: DateComponents(hour: 18, minute: 0, second: 0), matchingPolicy: .strict) else { return } // Pick the sooner of the two let nextTriggerTime = min(nextMorning6, nextEvening6) // Check if we've already passed the trigger time (e.g., app opened at 7 AM) if nextTriggerTime <= now { // Trigger immediately, then schedule the next one toggleMode() scheduleNextToggle() return } // Calculate time interval until next trigger let timeInterval = nextTriggerTime.timeIntervalSince(now) // Schedule the timer toggleTimer = Timer.scheduledTimer(withTimeInterval: timeInterval, repeats: false) { [weak self] _ in self?.toggleMode() // Schedule the next toggle after this one fires self?.scheduleNextToggle() } } private func toggleMode() { // Replace this with your actual day/night mode switching logic DispatchQueue.main.async { let currentStyle = UIApplication.shared.keyWindow?.overrideUserInterfaceStyle UIApplication.shared.keyWindow?.overrideUserInterfaceStyle = currentStyle == .dark ? .light : .dark // Optional: Post a notification if other parts of the app need to react to the mode change NotificationCenter.default.post(name: NSNotification.Name("ModeDidToggle"), object: nil) } } }
Step 2: Initialize the Manager
You just need to initialize the manager once when your app launches. Add this to your SceneDelegate (for iOS 13+) or AppDelegate:
// In SceneDelegate.swift func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // Initialize the toggle manager _ = ModeToggleManager.shared // ... rest of your code }
Key Notes
- Foreground-only execution: The timer is only active when the app is in the foreground. We invalidate it as soon as the app moves to the background, so no background resources are used.
- Timezone awareness: Uses
Calendar.current, which respects the user's device timezone, so triggers happen at local 6 AM/PM. - Non-intrusive: The manager is self-contained, so it won't interfere with other app functionality. If you need other parts of the app to react to mode changes, use the optional notification we added.
- Edge case handling: If the app is opened after a trigger time (e.g., 7 AM), it will immediately toggle the mode and schedule the next trigger (6 PM that day).
Troubleshooting Tips
- If the toggle doesn't fire, double-check that the manager is initialized correctly at app launch.
- Ensure your mode switching logic runs on the main thread (we added
DispatchQueue.main.asyncintoggleMode()to handle this, since UI changes must happen on the main thread). - Test timezone changes to make sure triggers adjust correctly with the user's location.
内容的提问来源于stack exchange,提问作者Bubba

