【问题标题】:Problem Using a Switch to Set A TimeInterval使用开关设置时间间隔的问题
【发布时间】:2022-01-23 02:32:39
【问题描述】:

我很难创建用户设置选项。我希望用户自定义计时器的频率以接收本地通知。我使用 SystemSettingsVC 上的开关让用户选择和设置用户默认值,并且我在 MainVC 中使用用户默认设置作为 TimerInterval。我的应用程序运行但时间没有改变。我知道开关正在工作,因为我也在测试背景颜色的变化。

这是我的 SystemSettingsVC 代码: ...

import UIKit
import SwiftUI
import CoreData

class SettingsViewController: UIViewController {


@IBOutlet weak var timeSelection: UISegmentedControl!


let userDefaults = UserDefaults.standard

let TIME_KEY = "TIME_KEY"
let ONE_HOUR_KEY = 60.0
let THREE_HOUR_KEY = 120.0
let SIX_HOUR_KEY = 300.0


override func viewDidLoad() {
    super.viewDidLoad()

    
    updateTime()
    
}


func updateTime() {
    let time = userDefaults.object(forKey: "TIME_KEY")
    if(time as? Double == ONE_HOUR_KEY) {
        timeSelection.selectedSegmentIndex = 0
        let userDefaults = UserDefaults.standard
        userDefaults.set(60.0, forKey: "TIME_KEY")
        view.backgroundColor = UIColor.white
       save()
    }
    else if(time as? Double == THREE_HOUR_KEY) {
        timeSelection.selectedSegmentIndex = 1
        let userDefaults = UserDefaults.standard
        userDefaults.set(120.0, forKey: "TIME_KEY")
        view.backgroundColor = UIColor.gray
       save()
    }
    else if(time as? Double == SIX_HOUR_KEY) {
        timeSelection.selectedSegmentIndex = 2
        let userDefaults = UserDefaults.standard
        userDefaults.set(300.0, forKey: "TIME_KEY")
        view.backgroundColor = UIColor.darkGray
        
        save()
    }
}

func save() {
    if let savedData = try? NSKeyedArchiver.archivedData(withRootObject: clock, requiringSecureCoding: false){
let defaults = UserDefaults.standard
        defaults.set(savedData, forKey: "TIME_KEY")
     }
}




@IBAction func selectTimeOfQuotes(_ sender: Any) {
    
    
    switch timeSelection.selectedSegmentIndex
    {
    case 0:
        userDefaults.set(60.0, forKey: "TIME_KEY")
        save()
    case 1:
        userDefaults.set(120.0, forKey: "TIME_KEY")
        save()
    case 2:
        userDefaults.set(300.0, forKey: "TIME_KEY")
     save()
    default:
        userDefaults.set(60.0, forKey: "TIME_KEY")
        save()
    }
    updateTime()
}
}

...

这是我的视图控制器的代码,用于调用用户默认值,我将 let userDefaults = UserDefaults.standard 放在 ViewDidLoad 中: '''代码''' ```

func configureAlerts() {
    let center = UNUserNotificationCenter.current()
    
     center.removeAllDeliveredNotifications()
     center.removeAllPendingNotificationRequests()


    let listQuotes = quotes
    
    
    let i = 1
                  
       let content = UNMutableNotificationContent()
        content.title = “Inspire”
        content.body = listQuotes[i].shareMessage
       content.sound = UNNotificationSound.default
  
        let alertDate = Date().byAdding(days: i)
  
        var alertComponents = Calendar.current.dateComponents([.day, .month, .year], from:     alertDate)
        alertComponents.hour = 8
        

    let userDefaults = UserDefaults.standard
   
   typealias NSTimeInterval = Double

   let thisTime:TimeInterval = userDefaults.double(forKey: "TIME_KEY")
      

        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: thisTime, repeats: true)
        let uuidString = UUID().uuidString
        let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: trigger)

        center.add(request) { error in
           if let error = error {
           print(error.localizedDescription)
            }
        }

【问题讨论】:

    标签: notifications switch-statement userdefaults nstimeinterval


    【解决方案1】:

    你没有展示你的模型是如何连接的,所以我们无法判断错误沟通发生在哪里,或者这可能是问题所在。它们没有连接。

    但乍一看,您并没有重新安排通知。 selectTimeOfQuotessaveupdateTime 不要打电话给configureAlerts

    需要注意的是,您有很多重复代码和硬编码值,这可能是造成混乱的根源。

    BTW 120 是 2 小时而不是 3 idk,如果这是故意的,但它突出了我的下一点。

    当你改变一个值时,你只想在一个地方做;如果可能的话;因此集中模型将帮助您避免在多个地方进行更改。

    对于您的选择器的选项,enum 可以容纳所有内容。

    enum NotificationInterval: Double, CaseIterable, Codable{
        case ONE_HOUR_KEY = 3660 //TimeInterval == seconds
        case THREE_HOUR_KEY = 10800 //TimeInterval == seconds
        case SIX_HOUR_KEY = 21600 //TimeInterval == seconds
        
        func label() -> String{
            var result = ""
            switch self {
            case .ONE_HOUR_KEY:
                result = "1 hour"
            case .THREE_HOUR_KEY:
                result = "3 hours"
            case .SIX_HOUR_KEY:
                result = "6 hours"
            }
            return result
        }
        func color() -> UIColor{
            var result = UIColor.label
            switch self {
            case .ONE_HOUR_KEY:
                result = UIColor.white
            case .THREE_HOUR_KEY:
                result = UIColor.gray
            case .SIX_HOUR_KEY:
                result = UIColor.darkGray
            }
            return result
        }
        ///Key for storage of user selected interval
        static var userDefaultKey: String{
            "TIME_KEY"
        }
        ///Saves value to store using the `userDefaultKey`
        func saveToStore(){
            var mgr = UserDefaultManager()
            mgr.intervalTime = self
        }
        ///Gets value from store using the `userDefaultKey`
        static func getFromStore() -> NotificationInterval{
            let raw = UserDefaultManager().intervalTime
            return raw
        }
        ///Gets the index for the object in the `allCases` array
        func getAllCasesIndex() -> Int?{
            NotificationInterval.allCases.firstIndex(where: {
                self == $0
            })
        }
        ///Gets the index for the `userDefaultKey` stored object in the `allCases` array
        static func getStoredIndex() -> Int?{
            NotificationInterval.getFromStore().getAllCasesIndex()
        }
    }
    

    那么,由于您至少有 2 个不相关的类在用户默认值中使用值存储,因此您也可以集中该工作

    ///This stores and retreives userdefaults to a predetermined store
    struct UserDefaultManager{
        //Having a single location for this will simplify  UserDefault storage
        //A use case would be switching to an App Group store when you decide to support watch in the future or if you want to add Widgets
        private let store = UserDefaults.standard
        ///User selected interval for the notifications
        var intervalTime: NotificationInterval{
            get{
                getObject(forKey: NotificationInterval.userDefaultKey, type: NotificationInterval.self) ?? NotificationInterval.ONE_HOUR_KEY
            }
            set{
                save(newValue, forKey: NotificationInterval.userDefaultKey)
            }
        }
        ///Saves any Codable to UserDefaults
        func save<T: Codable>(_ object: T, forKey: String){
            
            let encoder = JSONEncoder()
            do{
                let encoded = try encoder.encode(object)
                store.set(encoded, forKey: forKey)
            }catch{
                print(error)
            }
        }
    
        //Gets any Codable from UserDefaults
        func getObject<T: Codable>(forKey: String, type: T.Type) -> T?{
            guard let saved = store.object(forKey: forKey) as? Data else {
                return nil
            }
            let decoder = JSONDecoder()
            do{
                let loaded = try decoder.decode(T.self, from: saved)
                return loaded
            }catch{
                print(error)
                return nil
            }
            
        }
        
    }
    

    那么您的SettingsViewController 将如下所示

    class SettingsViewController: UIViewController {
        ///Programatic use of IBOutlet
        var timeSelection: UISegmentedControl!
        
        private let quoteManager = QuoteManager()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            //Create control PS I dont have a storyboard setup but you can replace this with your IBOutlet and IBAction
            timeSelection = UISegmentedControl(items: NotificationInterval.allCases.map({
                $0.label()
            }))
            timeSelection.addTarget(self, action: #selector(selectTimeOfQuotes), for: .allEvents)
            //Set the initial value from storage
            timeSelection.selectedSegmentIndex =  NotificationInterval.getStoredIndex() ?? 0
            self.view.addSubview(timeSelection)
            timeSelection.translatesAutoresizingMaskIntoConstraints = false
            timeSelection.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
            timeSelection.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
            //End of programatic setup
            
            //Set the color from storage
            view.backgroundColor = NotificationInterval.getFromStore().color()
            
        }
        ///Programatic use of IBAction
        @objc
        func selectTimeOfQuotes() {
            //Identify the selected interval
            let interval = NotificationInterval.allCases[timeSelection.selectedSegmentIndex]
            //Save it
            interval.saveToStore()
            //Change the color
            view.backgroundColor = interval.color()
            //Change the notification
            quoteManager.rescheduleQuotes()
        }
    }
    

    正如最后一行代码所示,一旦所有工作完成,您应该重新安排引号。

    我创建了一个 mini-QuoteManager,因为您没有显示此连接。任何视图控制器都可以使用此管理器来获取报价,甚至可以通过调用提供的方法在报价更改时重新安排。

    //Adapt this to your use case this is just a sample
    ///Liason for quote Storege
    struct QuoteManager{
        var listQuotes = ["one", "two", "three"]
        private let notificationManager = NotificationManager.shared
        private let userDefaultsManager = UserDefaultManager()
        ///Reschedules quotes
        func rescheduleQuotes(count: Int = 10){
            let title = "Inspire"
            notificationManager.deleteNotifications()
            
            print(#function)
            for n in 1..<count+1{
                print(n)
                let newDate = userDefaultsManager.intervalTime.rawValue*Double(n)
                //Idenfier must be unique so I added the n
                notificationManager.scheduleUNTimeIntervalNotificationTrigger(title: title, body: listQuotes.randomElement()!, timeInterval: newDate, identifier: "com.yourCompany.AppName.\(title)_\(n.description)")
            }
        }
    }
    

    QuoteManager 调用NotificationManager。我在下面创建了一个小版本。

    class NotificationManager: NSObject, UNUserNotificationCenterDelegate{
        //Singleton is requierd because of delegate
        static let shared: NotificationManager = NotificationManager()
        let notificationCenter = UNUserNotificationCenter.current()
        
        private override init(){
            super.init()
            //This assigns the delegate
            notificationCenter.delegate = self
            requestAuthorization()
        }
        func scheduleUNTimeIntervalNotificationTrigger(title: String, body: String, timeInterval: TimeInterval, identifier: String, repeats: Bool = false){
            print(#function)
            let content = UNMutableNotificationContent()
            content.title = title
            content.body = body
            content.sound = .default
            
            let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval, repeats: repeats)
            let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
            notificationCenter.add(request) { (error) in
                if error != nil {
                    print(error!)
                }
                self.printNotifications()
            }
        }
        func requestAuthorization() {
            print(#function)
            notificationCenter.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
                if granted {
                    print("Access Granted!")
                } else {
                    print("Access Not Granted")
                }
            }
        }
        func deleteNotifications(){
            print(#function)
            notificationCenter.removeAllPendingNotificationRequests()
            notificationCenter.removeAllDeliveredNotifications()
        }
        
        ///Prints to console schduled notifications
        func printNotifications(){
            print(#function)
            notificationCenter.getPendingNotificationRequests { request in
                print("UNTimeIntervalNotificationTrigger Pending Notification")
                for req in request{
                    if req.trigger is UNTimeIntervalNotificationTrigger{
                        print((req.trigger as! UNTimeIntervalNotificationTrigger).nextTriggerDate()?.description ?? "invalid next trigger date")
                        print(req.content.body)
                    }
                }
                print("UNCalendarNotificationTrigger Pending Notification")
                for req in request{
                    if req.trigger is UNCalendarNotificationTrigger{
                        print((req.trigger as! UNCalendarNotificationTrigger).nextTriggerDate()?.description ?? "invalid next trigger date")
                    }
                }
            }
        }
        
        //MARK: UNUserNotificationCenterDelegate
        func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
            
            completionHandler(.banner)
        }
    }
    

    这可能看起来很多,但如果你专注于SettingsViewController,你会发现整个事情变得多么简单。

    所有这些都是工作代码。只需复制并粘贴到 .swift 文件中即可。

    您可能需要更改 UISegmentedControl,因为我是通过编程方式创建的,但如果您将 SettingsViewController 放在空白情节提要中,它应该可以正常工作。

    【讨论】:

    • 感谢您的 cmets 和您的帮助,我真的很感激......你可以看到我在这方面很新。我将听取您的建议并更好地组织此代码。
    • @Sharlene 如果我回答了您的问题,我很高兴它对您有所帮助,请通过选择上面的绿色复选标记来接受答案。
    • For:enum NotificationInterval: Double, CaseIterable{ case ONE_HOUR_KEY = 60 case THREE_HOUR_KEY = 180 case SIX_HOUR_KEY = 300 如何为案例提供标签,而不是在主情节提要上显示 60、180、300 ?
    • 只需像我为color() 所做的那样添加一个label() 并将$0.rawValue.description 更改为$0.label()viewDidLoadUISegmentedControlitems。见上文。
    • @Sharlene 另外,如果您想访问整个NotificationInterval,那么您可以在其他地方使用labelcolorrawValue,您可以将UserDefaultManager 更改为保存对象而不是几秒钟
    猜你喜欢
    • 1970-01-01
    • 2020-06-21
    • 2013-08-08
    • 2014-04-04
    • 1970-01-01
    • 2018-06-26
    • 1970-01-01
    • 1970-01-01
    • 2011-11-09
    相关资源
    最近更新 更多