Xcode 10.3
swift5
ios 12.4
swift5
ios 12.4
完成品
機能面を重視したのでデザインは特に意識していません
また、アラーム音もデフォルトしか設定していないので今回のサウンド設定は飾りです。
(ご要望があれば追加します。)
UI
実装の仕方はいろいろありますが、
今回はTableViewの上に独自Cellを乗せる設計にしました。
コード
AppDelegate
- import UIKit
- import UserNotifications //UserNotificationsをimport
- @UIApplicationMain
- class AppDelegate: UIResponder, UIApplicationDelegate {
- var window: UIWindow?
- var backgroundTaskID: UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier(rawValue: 0)
- func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
- // 通知許可の取得
- UNUserNotificationCenter.current().requestAuthorization(
- options: [.alert, .sound, .badge]){
- (granted, _) in
- if granted{
- UNUserNotificationCenter.current().delegate = self
- }
- }
- return true
- }
- //バックグラウンド遷移移行直前に呼ばれる
- func applicationWillResignActive(_ application: UIApplication) {
- // 新しいタスクを登録
- backgroundTaskID = application.beginBackgroundTask {
- [weak self] in
- application.endBackgroundTask((self?.backgroundTaskID)!)
- self?.backgroundTaskID = UIBackgroundTaskIdentifier.invalid
- }
- }
- //アプリがアクティブになる度に呼ばれる
- func applicationDidBecomeActive(_ application: UIApplication) {
- //タスクの解除
- application.endBackgroundTask(self.backgroundTaskID)
- }
- }
- extension AppDelegate: UNUserNotificationCenterDelegate{
- func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
- // アプリ起動中でもアラートと音で通知
- completionHandler([.alert, .sound])
- }
- func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
- // バックグラウンドで来た通知をタップしてアプリ起動したら呼ばれる
- completionHandler()
- }
- }
TimerVC
- import UIKit
- import UserNotifications //UserNotificationsをimport
- class TimerVC: UIViewController, UITableViewDelegate,UITableViewDataSource {
- @IBOutlet weak var tableView: UITableView!
- var timerOn:Bool = false
- var timerStart:Bool = false
- var time:Double = 0
- override func viewDidLoad() {
- super.viewDidLoad()
- //cellを登録
- registerCell(cellName: "TimerLabelCell")
- registerCell(cellName: "TimerSetCell")
- registerCell(cellName: "SoundSetCell")
- registerCell(cellName: "TimerButtonCell")
- }
- //cellを登録
- func registerCell(cellName:String){
- tableView.register(UINib(nibName: cellName, bundle: nil), forCellReuseIdentifier: cellName)
- }
- //セクションごとのセルの個数を指定
- func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- return 1
- }
- //セクション数
- func numberOfSections(in tableView: UITableView) -> Int {
- return 3
- }
- //セクションごとにセルの高さを設定
- func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
- switch indexPath.section {
- case 0:
- return 220
- case 1:
- return 44
- case 2:
- return 160
- default:
- return 0
- }
- }
- // セクションごとにセル生成
- func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- switch indexPath.section {
- case 0:
- //タイマーがスタートしているかどうか
- if timerStart {
- // タイマーがスタートしていた時TimerLabelCellを生成
- let cell = tableView.dequeueReusableCell(withIdentifier: "TimerLabelCell") as! TimerLabelCell
- cell.delegate = self
- // タイマー動いているか
- if timerOn {
- cell.time = time
- cell.startTimer()
- }else{
- cell.stopTimer()
- }
- return cell
- }else{
- // タイマーがスタートしていない時TimerSetCellを生成
- let cell = tableView.dequeueReusableCell(withIdentifier: "TimerSetCell") as! TimerSetCell
- cell.delegate = self
- cell.getTimer()
- return cell
- }
- case 1:
- let cell = tableView.dequeueReusableCell(withIdentifier: "SoundSetCell") as! SoundSetCell
- return cell
- case 2:
- let cell = tableView.dequeueReusableCell(withIdentifier: "TimerButtonCell") as! TimerButtonCell
- cell.delegate = self
- if !timerStart {
- cell.start()
- }
- return cell
- default:
- return UITableViewCell()
- }
- }
- }
- extension TimerVC:TimerLabelCellDelegate {
- func timerLabelCell(stopTimer: TimerLabelCell, remainingTime: Double) {
- time = remainingTime
- }
- func timerLabelCell(timerEnd alarmSet: TimerLabelCell) {
- timerStart = false
- timerOn = false
- tableView.reloadData()
- }
- }
- extension TimerVC:TimerSetCellDelegate {
- func timerSetCell(setTime: TimerSetCell, time: Double) {
- self.time = time
- }
- }
- extension TimerVC:TimerButtonCellDelegate {
- func timerButtonCell(startStopTime: TimerButtonCell) {
- //通知を設定
- let center = UNUserNotificationCenter.current()
- center.removePendingNotificationRequests(withIdentifiers: ["Timer"])
- if time != 0 {
- timerStart = true
- if timerOn {
- timerOn = false
- startStopTime.resume()
- }else{
- timerOn = true
- startStopTime.pause()
- let content = UNMutableNotificationContent()
- content.sound = UNNotificationSound.default
- content.title = "Timer Done"
- let trigger = UNTimeIntervalNotificationTrigger(timeInterval: TimeInterval(time), repeats: false)
- let identifier = "Timer"
- let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
- UNUserNotificationCenter.current().add(request){ (error : Error?) in
- if let error = error {
- print(error.localizedDescription)
- }
- }
- }
- }else{
- timerStart = false
- startStopTime.start()
- }
- tableView.reloadData()
- }
- func timerButtonCell(cancelTime: TimerButtonCell) {
- timerStart = false
- timerOn = false
- cancelTime.start()
- let center = UNUserNotificationCenter.current()
- center.removePendingNotificationRequests(withIdentifiers: ["Timer"])
- tableView.reloadData()
- }
- }
TimerLabelCell
- import UIKit
- protocol TimerLabelCellDelegate {
- func timerLabelCell(timerEnd:TimerLabelCell)
- func timerLabelCell(stopTimer:TimerLabelCell,remainingTime:Double)
- }
- class TimerLabelCell: UITableViewCell {
- @IBOutlet weak var timerLabel: UILabel!
- var timer = Timer()
- var time:Double = 0 {
- didSet {
- //タイマーをtimerLabelにセット
- timerLabel.text = timeString(time: time)
- }
- }
- var delegate:TimerLabelCellDelegate!
- //タイマースタート
- func startTimer(){
- //タイマーが動いているか
- if timer.isValid {
- timer.invalidate()
- }
- // タイマー生成、開始 1秒後の実行
- timer = Timer.scheduledTimer(
- timeInterval: 1.0, // 時間間隔
- target: self, // タイマーの実際の処理の場所
- selector: #selector(TimerLabelCell.tickTimer(_:)), // メソッド タイマーの実際の処理
- userInfo: nil,
- repeats: true)
- }
- //タイマー停止
- func stopTimer(){
- timer.invalidate()
- delegate.timerLabelCell(stopTimer: self, remainingTime: time)
- }
- // タイマー処理
- @objc func tickTimer(_ timer: Timer) {
- time -= 1
- timerLabel.text = timeString(time: time)
- //timeが-1になった時
- if time == -1 {
- // タイマーの停止
- timer.invalidate()
- delegate.timerLabelCell(timerEnd: self)
- }
- }
- //時分秒を取得
- func timeString(time:TimeInterval) -> String {
- let hours = Int(time) / 3600 //時
- let minutes = Int(time) / 60 % 60 //分
- let seconds = Int(time) % 60 //秒
- return String(format:"%02i:%02i:%02i", hours, minutes, seconds)
- }
- }
TimerSetCell
- import UIKit
- protocol TimerSetCellDelegate {
- func timerSetCell(setTime: TimerSetCell, time: Double)
- }
- class TimerSetCell: UITableViewCell,UIPickerViewDataSource, UIPickerViewDelegate {
- @IBOutlet weak var pickerView: UIPickerView!
- var delegate:TimerSetCellDelegate!
- var time:Double = 0
- var hour:Double = 0 //時
- var minutes:Double = 0 //分
- var seconds:Double = 0 //秒
- let labelText = ["hours", "min", "sec"]
- var labels :[UILabel] = []
- override func awakeFromNib() {
- super.awakeFromNib()
- pickerView.delegate = self
- pickerView.dataSource = self
- if time != 0 {
- delegate.timerSetCell(setTime: self, time: time)
- }
- }
- public override func layoutSubviews() {
- super.layoutSubviews()
- self.reloadAllComponentLabels()
- }
- //ピッカーに表示する列数
- func numberOfComponents(in pickerView: UIPickerView) -> Int {
- return 3
- }
- //ピッカーに表示する値(数字)
- func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
- switch component {
- case 0:
- return 24
- case 1,2:
- return 60
- default:
- return 0
- }
- }
- func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
- switch component {
- //秒に変換
- case 0:
- hour = Double(row * 3600)
- case 1:
- minutes = Double(row * 60)
- case 2:
- seconds = Double(row)
- default:
- break;
- }
- time = hour + minutes + seconds
- delegate.timerSetCell(setTime: self, time: time)
- }
- func timeString(time:TimeInterval) -> String {
- let hours = Int(time) / 3600
- let minutes = Int(time) / 60 % 60
- let seconds = Int(time) % 60
- return String(format:"%i:%02i:%02i", hours, minutes, seconds)
- }
- func getTimer(){
- delegate.timerSetCell(setTime: self, time: time)
- }
- //ピッカーに表示する値(文字列)
- func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
- return row.description
- }
- //列の幅
- func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
- return pickerView.frame.size.width/4
- }
- // ラベルの配置
- public func reloadAllComponentLabels() {
- let fontSize = UIFont.systemFontSize
- let labelTop = self.bounds.origin.y + self.bounds.height / 2 - fontSize
- let labelHeight = self.pickerView.rowSize(forComponent: 0).height
- var labelOffset = self.bounds.origin.x // Componentの右端
- for i in 0...(self.numberOfComponents(in: pickerView)) - 1 {
- if self.labels.count == i {
- let label = UILabel()
- label.text = labelText[i]
- label.backgroundColor = UIColor.clear
- label.font = UIFont.boldSystemFont(ofSize: fontSize)
- label.sizeToFit()
- self.addSubview(label)
- self.labels.append(label)
- }
- // ラベルの位置を決める
- let labelWidth = self.labels[i].frame.width
- labelOffset += pickerView.rowSize(forComponent: i).width
- self.labels[i].frame = CGRect(x: labelOffset - labelWidth + pickerView.rowSize(forComponent: i).width/2, y: labelTop, width: labelWidth, height: labelHeight)
- }
- }
- }
SoundSetCell
- import UIKit
- class SoundSetCell: UITableViewCell {
- @IBOutlet weak var soundNameLabel: UILabel!
- }
TimerButtonCell
githubはこちら
- import UIKit
- protocol TimerButtonCellDelegate {
- func timerButtonCell(startStopTime : TimerButtonCell)
- func timerButtonCell(cancelTime : TimerButtonCell)
- }
- class TimerButtonCell: UITableViewCell {
- @IBOutlet weak var startButton: UIButton!
- @IBOutlet weak var cancelButton: UIButton!
- var delegate:TimerButtonCellDelegate!
- override func awakeFromNib() {
- super.awakeFromNib()
- cancelButton.isEnabled = true
- }
- override func setSelected(_ selected: Bool, animated: Bool) {
- super.setSelected(selected, animated: animated)
- }
- //開始停止
- @IBAction func startstop(_ sender: Any) {
- delegate.timerButtonCell(startStopTime: self)
- }
- //キャンセル
- @IBAction func cancel(_ sender: Any) {
- delegate.timerButtonCell(cancelTime: self)
- cancelButton.isEnabled = true
- }
- //スタートボタンに変更
- func start(){
- startButton.setTitle("Start", for: .normal)
- startButton.backgroundColor = #colorLiteral(red: 0.1799043417, green: 0.6762347817, blue: 0.2553646266, alpha: 1)
- }
- //一時停止に変更
- func pause(){
- startButton.setTitle("Pause", for: .normal)
- startButton.backgroundColor = .orange
- }
- //再開に変更
- func resume(){
- startButton.setTitle("Resume", for: .normal)
- startButton.backgroundColor = #colorLiteral(red: 0.1799043417, green: 0.6762347817, blue: 0.2553646266, alpha: 1)
- }
- }
0 件のコメント:
コメントを投稿