Xcode 10.3
ios 12.4
swit5
ios 12.4
swit5
完成品
UI
今回はViewが多いですがなるべく標準に似せて設置しました。
コード
AppDelegate
import UIKit import UserNotifications @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. // 通知許可の取得 UNUserNotificationCenter.current().requestAuthorization( options: [.alert, .sound, .badge]){ (granted, _) in if granted{ UNUserNotificationCenter.current().delegate = self } } return true } func applicationWillEnterForeground(_ application: UIApplication) { // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. let center = UNUserNotificationCenter.current() center.getDeliveredNotifications { (notifications: [UNNotification]) in for notification in notifications { _ = AlarmVC.shared.getAlarm(from: notification.request.identifier) NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: nil) } } } } extension AppDelegate: UNUserNotificationCenterDelegate{ func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { // アプリ起動中でもアラートと音で通知 completionHandler([.alert, .sound]) let uuid = notification.request.identifier _ = AlarmVC.shared.getAlarm(from: uuid) NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: nil) } func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { let identifier = response.actionIdentifier if identifier == "snooze"{ let snoozeAction = UNNotificationAction( identifier: "snooze", title: "Snooze 5 Minutes", options: [] ) let noAction = UNNotificationAction( identifier: "stop", title: "stop", options: [] ) let alarmCategory = UNNotificationCategory( identifier: "alarmCategory", actions: [snoozeAction, noAction], intentIdentifiers: [], options: []) UNUserNotificationCenter.current().setNotificationCategories([alarmCategory]) let content = UNMutableNotificationContent() content.title = "Snooze" content.sound = UNNotificationSound.default content.categoryIdentifier = "alarmCategory" let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5 , repeats: false) let request = UNNotificationRequest(identifier: "Snooze", content: content, trigger: trigger) UNUserNotificationCenter.current().add(request) { (error) in if let error = error { print(error.localizedDescription) } } } let uuid = response.notification.request.identifier _ = AlarmVC.shared.getAlarm(from: uuid) NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: nil) completionHandler() } }
AlarmVC
import UIKit import UserNotifications class AlarmVC: UIViewController,UITableViewDelegate,UITableViewDataSource { static let shared = AlarmVC() var appDelegate = UIApplication.shared @IBOutlet weak var tableView: UITableView! var userDefaults = UserDefaults.standard var index:Int! var timeArray:[AlarmTimeArray] = [] override func viewDidLoad() { super.viewDidLoad() tableView.allowsSelectionDuringEditing = true tableView.allowsSelection = false tableView.register(UINib(nibName: "AlarmTimeCell", bundle: nil), forCellReuseIdentifier: "AlarmTimeCell") self.navigationItem.setLeftBarButton(self.editButtonItem, animated: true) timeLoad() tableView.reloadData() NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil) } @objc func methodOfReceivedNotification(notification: Notification) { timeLoad() DispatchQueue.main.async { self.tableView.reloadData() } } func timeLoad(){ if let timeArrayData = UserDefaults.standard.object(forKey: "timeArray") as? Data { if let getTimeArray = try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(timeArrayData) as? [AlarmTimeArray] { timeArray = getTimeArray } } } override func setEditing(_ editing: Bool, animated: Bool) { super.setEditing(editing, animated: animated) tableView.setEditing(editing, animated: animated) } func setCellLabel(index:Int) -> String{ if timeArray[index].repeatLabel == "Never" { return timeArray[index].label }else{ return timeArray[index].label+","+timeArray[index].repeatLabel } } func getAlarm(from uuid: String){ timeLoad() guard let alarm = timeArray.first(where: { $0.uuidString == uuid }) else {return } if alarm.week.isEmpty { alarm.onOff = false } saveDate() let center = UNUserNotificationCenter.current() center.removeAllDeliveredNotifications() } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return timeArray.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "AlarmTimeCell") as! AlarmTimeCell cell.timeLabel.text = getTime(date: timeArray[indexPath.row].date) cell.label.text = setCellLabel(index: indexPath.row) cell.sw.isOn = timeArray[indexPath.row].onOff cell.editingAccessoryType = .disclosureIndicator return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { // 画面遷移の準備 if tableView.isEditing { index = indexPath.row performSegue(withIdentifier: "showAlarmAdd", sender: nil) } } func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete { UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [timeArray[indexPath.row].uuidString]) timeArray.remove(at: indexPath.row) tableView.deleteRows(at: [indexPath], with: .fade) saveDate() } } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 72 } @IBAction func addButton(_ sender: Any) { self.performSegue(withIdentifier: "showAlarmAdd", sender: nil) } func getTime(date:Date) -> String { let f = DateFormatter() f.timeStyle = .short f.locale = Locale(identifier: "ja_JP") return f.string(from: date) } func saveDate(){ let timeArrayData = try! NSKeyedArchiver.archivedData(withRootObject: timeArray, requiringSecureCoding: false) userDefaults.set(timeArrayData, forKey: "timeArray") userDefaults.synchronize() } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "showAlarmAdd"{ guard let nvc = segue.destination as? UINavigationController else {return} guard let vc = nvc.topViewController as? AlarmAddVC else {return} vc.delegate = self vc.isEdit = tableView.isEditing if tableView.isEditing { vc.alarmTime = timeArray[index] } } } } extension AlarmVC:AlarmAddDelegate{ func AlarmAddVC(alarmAdd: AlarmAddVC, alarmTime: AlarmTimeArray) { if tableView.isEditing { timeArray[index] = alarmTime }else{ timeArray.append(alarmTime) } timeArray.sort(){$0.date < $1.date} saveDate() self.setEditing(false, animated: false) tableView.reloadData() } func AlarmAddVC(alarmDelete: AlarmAddVC, alarmTime: AlarmTimeArray) { self.setEditing(false, animated: false) timeArray.remove(at: index) saveDate() UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [timeArray[index].uuidString]) } func AlarmAddVC(alarmCancel:AlarmAddVC){ self.setEditing(false, animated: false) } }
AlarmTimeCell
import UIKit protocol AlarmTimeCellDelegate { func alarmTimeCell(switchTappe:UITableViewCell,isOn:Bool) } class AlarmTimeCell: UITableViewCell { @IBOutlet weak var timeLabel: UILabel! @IBOutlet weak var label: UILabel! @IBOutlet weak var sw: UISwitch! override func awakeFromNib() { super.awakeFromNib() accessoryView = sw } }
AlarmAddVC
import UIKit import UserNotifications protocol AlarmAddDelegate { func AlarmAddVC(alarmAdd:AlarmAddVC,alarmTime:AlarmTimeArray) func AlarmAddVC(alarmDelete:AlarmAddVC,alarmTime:AlarmTimeArray) func AlarmAddVC(alarmCancel:AlarmAddVC) } class AlarmAddVC: UIViewController ,UITableViewDelegate,UITableViewDataSource{ @IBOutlet weak var datePicker: UIDatePicker! @IBOutlet weak var tableView: UITableView! var delegate:AlarmAddDelegate! var alarmTime:AlarmTimeArray = AlarmTimeArray() var isEdit: Bool = false var titleText = ["Repeat","Label","Sound"] override func viewDidLoad() { super.viewDidLoad() datePicker.date = alarmTime.date registerCell(cellName: "AlarmSnoozeCell") registerCell(cellName: "AlarmAddCell") registerCell(cellName: "AlarmDeleteCell") tableView.tableFooterView = UIView() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) if let indexPathForSelectedRow = tableView.indexPathForSelectedRow { tableView.deselectRow(at: indexPathForSelectedRow, animated: true) } } //cell登録 func registerCell(cellName:String){ tableView.register(UINib(nibName: cellName, bundle: nil), forCellReuseIdentifier: cellName) } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { switch section { case 0: return 4 case 1: return 1 default: return 0 } } func numberOfSections(in tableView: UITableView) -> Int { return isEdit ? 2:1 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { switch indexPath.section { case 0: switch indexPath.row{ case 0: let cell = tableView.dequeueReusableCell(withIdentifier: "AlarmAddCell") as! AlarmAddCell cell.titleLabel.text = titleText[indexPath.row] cell.subTitleLabel.text = alarmTime.repeatLabel return cell case 1: let cell = tableView.dequeueReusableCell(withIdentifier: "AlarmAddCell") as! AlarmAddCell cell.titleLabel.text = titleText[indexPath.row] cell.subTitleLabel.text = alarmTime.label return cell case 2: let cell = tableView.dequeueReusableCell(withIdentifier: "AlarmAddCell") as! AlarmAddCell cell.titleLabel.text = titleText[indexPath.row] cell.subTitleLabel.text = "Default" cell.selectionStyle = .none return cell case 3: let cell = tableView.dequeueReusableCell(withIdentifier: "AlarmSnoozeCell") as! AlarmSnoozeCell cell.delegate = self cell.snoozeSwitch.isOn = alarmTime.snooze return cell default: break } case 1: let cell = tableView.dequeueReusableCell(withIdentifier: "AlarmDeleteCell") as! AlarmDeleteCell cell.delegate = self return cell default: break } return UITableViewCell() } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { switch indexPath.section { case 0: switch indexPath.row { case 0: self.performSegue(withIdentifier: "showRepeat", sender: nil) case 1: performSegue(withIdentifier: "showLabel",sender: nil) break case 2:break default:break } default: break } } public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { if section == 1 { return 50 }else{ return 0 } } //アラーム設定時間を保存 @IBAction func saveButton(_ sender: Any) { alarmSet() delegate.AlarmAddVC(alarmAdd: self, alarmTime: alarmTime) dismiss(animated: true, completion: nil) } //スヌーズ設定 func setCategories(){ let snoozeAction = UNNotificationAction( identifier: "snooze", title: "Snooze 5 Minutes", options: [] ) let noAction = UNNotificationAction( identifier: "stop", title: "stop", options: [] ) var alarmCategory:UNNotificationCategory! if alarmTime.snooze { alarmCategory = UNNotificationCategory( identifier: "alarmCategory", actions: [snoozeAction, noAction], intentIdentifiers: [], options: []) }else{ alarmCategory = UNNotificationCategory( identifier: "alarmCategory", actions: [], intentIdentifiers: [], options: []) } UNUserNotificationCenter.current().setNotificationCategories([alarmCategory]) } //通知設定 func setNotificationC(day:String, repeats:Bool){ let content = UNMutableNotificationContent() content.title = alarmTime.label content.sound = UNNotificationSound.default content.categoryIdentifier = "alarmCategory" var dateComponents = DateComponents() if !day.isEmpty { dateComponents.weekday = weekDay(day: day) } dateComponents.hour = Calendar.current.component(.hour, from: datePicker.date) dateComponents.minute = Calendar.current.component(.minute, from: datePicker.date) let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: repeats) let request = UNNotificationRequest(identifier: alarmTime.uuidString+day, content: content, trigger: trigger) UNUserNotificationCenter.current().add(request) { (error) in if let error = error { print(error.localizedDescription) } } alarmTime.date = datePicker.date } //アラート設定 func alarmSet(){ removeAlarm(identifiers: alarmTime.uuidString) let shortWeekday = DateFormatter().shortWeekdaySymbols! for i in shortWeekday { removeAlarm(identifiers: alarmTime.uuidString+i) } if alarmTime.week.isEmpty { setCategories() setNotificationC(day:"", repeats: false) }else{ for i in alarmTime.week { setCategories() setNotificationC(day: i, repeats: true) } } } //アラート設定削除 func removeAlarm(identifiers:String){ UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [identifiers]) } //曜日 func weekDay(day:String) -> Int{ var week = DateFormatter().weekdaySymbols! switch day { case week[0]: return 1 case week[1]: return 2 case week[2]: return 3 case week[3]: return 4 case week[4]: return 5 case week[5]: return 6 case week[6]: return 7 default: return Int() } } //キャンセル @IBAction func cancelButton(_ sender: Any) { delegate.AlarmAddVC(alarmCancel: self) dismiss(animated: true, completion: nil) } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { switch segue.identifier { case "showRepeat": guard let nextVC:AlarmRepeatVC = segue.destination as? AlarmRepeatVC else {return} nextVC.delegate = self nextVC.selectDay = alarmTime.week case "showLabel": guard let nextVC:AlarmAddLabelVC = segue.destination as? AlarmAddLabelVC else {return} nextVC.delegate = self nextVC.text = alarmTime.label default: return } } } extension AlarmAddVC:AlarmRepeatVCDelegate { func AlarmRepeatVC(addRepeat: AlarmRepeatVC, week: [String]) { alarmTime.week = [] alarmTime.repeatLabel = "" alarmTime.week += week if alarmTime.week.count == 1 { alarmTime.repeatLabel = "Every"+alarmTime.week[0] }else if alarmTime.week.isEmpty { alarmTime.repeatLabel = "Never" }else if alarmTime.week.count == 7{ alarmTime.repeatLabel = "Every day" }else{ let shortWeekday = DateFormatter().shortWeekdaySymbols! for i in alarmTime.week { if alarmTime.repeatLabel != "" { alarmTime.repeatLabel += "," } alarmTime.repeatLabel += shortWeekday[weekDay(day: i)] } } tableView.reloadData() } } extension AlarmAddVC:AlarmAddLabelDelegate { func alarmAddLabel(labelText: AlarmAddLabelVC, text: String) { alarmTime.label = text tableView.reloadData() } } extension AlarmAddVC:AlarmSnoozeCellDelegte{ func alarmSnoozeCell(swichOn: AlarmSnoozeCell, On: Bool) { alarmTime.snooze = On } } extension AlarmAddVC:AlarmDeleteCellDelegate{ func alarmDeleteCell(delete: UITableViewCell) { delegate.AlarmAddVC(alarmDelete: self,alarmTime:alarmTime) dismiss(animated: true, completion: nil) } }
AlarmAddCell
import UIKit class AlarmAddCell: UITableViewCell { @IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var subTitleLabel: UILabel! }
AlarmSnoozeCell
import UIKit protocol AlarmSnoozeCellDelegte { func alarmSnoozeCell(swichOn:AlarmSnoozeCell,On:Bool) } class AlarmSnoozeCell: UITableViewCell { @IBOutlet weak var snoozeSwitch: UISwitch! var delegate:AlarmSnoozeCellDelegte! @IBAction func switchChanged(_ sender: UISwitch) { delegate.alarmSnoozeCell(swichOn: self, On: sender.isOn) } }
AlarmRepeatVC
import UIKit protocol AlarmRepeatVCDelegate { func AlarmRepeatVC(addRepeat:AlarmRepeatVC,week:[String]) } class AlarmRepeatVC: UIViewController ,UITableViewDelegate,UITableViewDataSource{ @IBOutlet weak var tableView: UITableView! var delegate:AlarmRepeatVCDelegate! var week:[String] = [] var selectDay:[String] = [] override func viewDidLoad() { super.viewDidLoad() // 複数選択可にする tableView.allowsMultipleSelection = true week = DateFormatter().weekdaySymbols! } override func viewWillDisappear(_ animated: Bool) { delegate.AlarmRepeatVC(addRepeat: self, week:sortWeek(selectDays: selectDay)) } func sortWeek(selectDays: [String]) -> [String]{ var week = DateFormatter().weekdaySymbols! var dayDictionary: [String: Int] = [:] for i in 0...6 { dayDictionary[week[i]] = i } var daysOfWeek: [String] = selectDays daysOfWeek.sort { (dayDictionary[$0] ?? 7) < (dayDictionary[$1] ?? 7)} return daysOfWeek } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return week.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // セルを取得する let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "weekCell", for: indexPath) cell.textLabel!.text = "Every"+week[indexPath.row] cell.selectionStyle = .none for i in selectDay { if week[indexPath.row] == i { cell.accessoryType = .checkmark break }else{ cell.accessoryType = .none } } return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let cell = tableView.cellForRow(at:indexPath) // チェックマークを入れる cell?.accessoryType = .checkmark selectDay.append(week[indexPath.row]) } // セルの選択が外れた時に呼び出される func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { let cell = tableView.cellForRow(at:indexPath) // チェックマークを外す cell?.accessoryType = .none selectDay = selectDay.filter { $0 != week[indexPath.row] } } }
AlarmAddLabelVC
import UIKit protocol AlarmAddLabelDelegate { func alarmAddLabel(labelText:AlarmAddLabelVC,text:String) } class AlarmAddLabelVC: UIViewController,UITextFieldDelegate { var text:String! @IBOutlet weak var textField: UITextField! var delegate:AlarmAddLabelDelegate! override func viewDidLoad() { super.viewDidLoad() // テキストを全消去するボタンを表示 textField.clearButtonMode = .always // 改行ボタンの種類を設定 textField.returnKeyType = .done // UITextFieldを追加 textField.delegate = self //キーボードを表示する textField.becomeFirstResponder() textField.text = text } override func viewDidDisappear(_ animated: Bool) { //textFieldの中身が空でない時 if textField.text != "", let text = textField.text{ delegate.alarmAddLabel(labelText: self, text:text) } } // 完了ボタンを押した時の処理 func textFieldShouldReturn(_ textField: UITextField) -> Bool { //textFieldの中身が空でない時 if textField.text != "", let text = textField.text{ delegate.alarmAddLabel(labelText: self, text:text) self.navigationController?.popViewController(animated: true) } return true } }
AlarmTimeArray
import UIKit class AlarmTimeArray: NSObject,NSCoding { var date:Date var uuidString:String var label:String var sound:Bool var snooze:Bool var onOff:Bool var repeatLabel:String var week:[String] override init() { self.date = Date() self.uuidString = UUID().uuidString self.label = "Alarm" self.sound = true self.snooze = true self.onOff = true self.week = [] self.repeatLabel = "Never" } func encode(with aCoder: NSCoder) { aCoder.encode(self.date, forKey: "date") aCoder.encode(self.uuidString, forKey: "uuidString") aCoder.encode(self.label, forKey: "label") aCoder.encode(self.sound, forKey: "sound") aCoder.encode(self.snooze, forKey: "snooze") aCoder.encode(self.onOff, forKey: "onOff") aCoder.encode(self.week, forKey: "week") aCoder.encode(self.repeatLabel, forKey: "repeatLabel") } required init?(coder aDecoder: NSCoder) { date = aDecoder.decodeObject(forKey: "date") as! Date uuidString = aDecoder.decodeObject(forKey: "uuidString") as! String label = aDecoder.decodeObject(forKey: "label") as! String sound = aDecoder.decodeBool(forKey: "sound") snooze = aDecoder.decodeBool(forKey: "snooze") onOff = aDecoder.decodeBool(forKey: "onOff") week = aDecoder.decodeObject(forKey: "week") as! [String] repeatLabel = aDecoder.decodeObject(forKey: "repeatLabel") as! String } }githubはこちら
参考
UITableViewの編集モードを使ってCellの削除を実装するまでUITableViewのセルを選択不可にする方法
【Swift4】配列の中身(数値・文字・日付)を比較してソートする方法【Xcode9】
UITableViewでセルを複数選択する
【Swift】UserDefaultsに自作クラスのデータを保存する方法(iOS12対応)[Swift][Obj-C]UUIDStringの使い分け
0 件のコメント:
コメントを投稿