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
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)
}
}githubはこちら


0 件のコメント:
コメントを投稿