0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【SwiftUI】カウントダウン+TODOアプリを作ってみた(初)

Last updated at Posted at 2021-08-31

はじめに

Qiita初投稿です。
勉強中のSwiftUIで自分の為にAPPを作りたい!
そんな感じです。

何を作ったの?

TODOリストにタイマーをくっつけたもの。
初めてだから無理せずシンプルに!

開発環境

Swift version 5.3.1
Xcode Version 12.5.1

code

    import SwiftUI

    struct TodoModel: Codable, Identifiable {
        var id = UUID()
        let todo: String
        var imcomplete: Bool
        
        init(todo: String, imcomplete: Bool) {
            self.todo = todo
            self.imcomplete = imcomplete
        }
    }

    class TimerModel: ObservableObject {
        @Published var deadLine : Date {
            didSet {
                UserDefaults.standard.set(deadLine, forKey: "deadLine")
            }
        }
        init() {
            deadLine = UserDefaults.standard.object(forKey: "deadLine") as? Date ?? Date()
        }
    }

    struct ContentView: View {
        
        init() {
                UITableView.appearance().backgroundColor = UIColor.clear
            }

        
        @State private var newTodo = ""
        @State private var allTodos: [TodoModel] = []
        @EnvironmentObject var timerModel: TimerModel
        @State var nowDate: Date = Date()
        
        var timer: Timer {
            Timer.scheduledTimer(withTimeInterval: 1, repeats: true) {_ in
                self.nowDate = Date()
            }
        }
        
        var body: some View {
            NavigationView {
                VStack {
                    VStack {
                        HStack {
                            DatePicker("", selection: $timerModel.deadLine, in: Date()...).labelsHidden()
                        }
                        Text("期限まであと")
                            .frame(maxWidth: .infinity, alignment: .center)
                        let timerFunc = TimerFunc(self.timerModel.deadLine)
                        HStack {
                            if timerFunc.day <= 0,
                               timerFunc.hour <= 0,
                               timerFunc.minute <= 0,
                               timerFunc.second <= 0 {
                                Text("Time Up!")
                                    .fontWeight(.bold)

                            } else if timerFunc.day <= 0,
                                      timerFunc.hour <= 0,
                                      timerFunc.minute <= 0 {
                                Text("\(timerFunc.second)")
                                    .fontWeight(.bold)
                            } else if timerFunc.day <= 0,
                                      timerFunc.hour <= 0 {
                                Text("\(timerFunc.minute):")
                                    .fontWeight(.bold)
                                Text("\(timerFunc.second)")
                                    .fontWeight(.bold)
                            } else if timerFunc.day <= 0 {
                                Text("\(timerFunc.hour):")
                                    .fontWeight(.bold)
                                Text("\(timerFunc.minute):")
                                    .fontWeight(.bold)
                                Text("\(timerFunc.second)")
                                    .fontWeight(.bold)
                            } else {
                                VStack {
                                    Text("\(timerFunc.day) day")
                                        .font(.largeTitle)
                                        .fontWeight(.bold)
                                    HStack {
                                        Text("\(timerFunc.hour):")
                                            .fontWeight(.bold)
                                        Text("\(timerFunc.minute):")
                                            .fontWeight(.bold)
                                        Text("\(timerFunc.second)")
                                            .fontWeight(.bold)

                                    }
                                }
                            }
                        }.onAppear(perform: {_ = self.timer}).font(.title)
                    }
                    
                    List {
                        ForEach (0..<allTodos.count, id: \.self) { index in
                            HStack {
                                Image(systemName: allTodos[index].imcomplete ? "square" : "checkmark.square.fill")
                                    .foregroundColor(.blue)
                                Text(self.allTodos[index].todo)
                                Spacer()
                                if allTodos[index].imcomplete {
                                    Text("")
                                } else {
                                    Text("complete!")
                                        .foregroundColor(.blue)
                                }
                            }.onTapGesture {
                                allTodos[index].imcomplete.toggle()
                                self.saveTodos()
                            }
                        }.onDelete(perform: deleteTodos)
                        HStack {
                            TextField("Add todo", text: $newTodo)
                            Button(action: {
                                guard !self.newTodo.isEmpty else { return } 
                                self.allTodos.append(TodoModel(todo: self.newTodo, imcomplete: true))
                                self.newTodo = ""
                                self.saveTodos()
                            }) {
                                Image(systemName: "plus")
                                    .foregroundColor(.blue)
                            }
                        }
                    }
                }
            }
            .onAppear(perform: loadTodos)
        }
        
        func TimerFunc(_ date:Date) -> (day: Int, hour: Int, minute: Int, second: Int) {
            let cal = Calendar(identifier: .japanese)
            
            let timeVal = cal.dateComponents([.day, .hour, .minute, .second], from: nowDate, to: timerModel.deadLine)
            
                return (Int(timeVal.day ?? 0),
                        Int(timeVal.hour ?? 0),
                        Int(timeVal.minute ?? 0),
                        Int(timeVal.second ?? 0))
        }
        
        private func saveTodos() {
            UserDefaults.standard.set(try? PropertyListEncoder().encode(self.allTodos), forKey: "todosKey")
        }
        
        private func loadTodos() {
            if let todosData = UserDefaults.standard.value(forKey: "todosKey") as? Data {
                if let todosList = try? PropertyListDecoder().decode(Array<TodoModel>.self, from: todosData) {
                    self.allTodos = todosList
                }
            }
        }
        
        private func deleteTodos(at offsets: IndexSet) {
            self.allTodos.remove(atOffsets: offsets)
            saveTodos()
        }
    }

    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
                .environmentObject(TimerModel())
        }
    }

つまずいた!

userdefaltsに保存できない
 いろいろ調べた結果、構造体及びタプル型は保存出来ないらしいのでCodableを使って処理
https://ichi.pro/swiftui-chu-toriaru-saisho-no-ios-apuri-o-sakuseisuru-212038845663015

チェックボックスの動きと処理
 .onTapGestureで解決

タイマーの処理と表示
参考にしたページを真似すると「0day0:0:1」とかになり「0」が邪魔なので改造した。
しかしながら、それが原因で冗長に……
https://betterprogramming.pub/make-a-simple-countdown-with-timer-and-swiftui-3ce355b54986
https://qiita.com/yuujioka/items/53f555ccba5e97bedb59
https://mt312.com/785

今後はどうするの?

せっかくなので元グラフィックデザイナーを生かして見た目もイケてるものにupdateしていこうと思う。
機能的には十分だが、タイマーを複数個別に走らせて複数のタスクリストをもてるようにしたいかな〜

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?