Adding to a list of words 単語リストに追加する。ワードスクランブル:まとめ>
import SwiftUI struct ContentView: View { @State private var usedWords = [String]() @State private var rootWord = "" @State private var newWord = "" func addNewWord() { // lowercase and trim the word, to make sure we don't add duplicate words with case differences let answer = newWord.lowercased().trimmingCharacters(in: .whitespacesAndNewlines) // exit if the remaining string is empty guard answer.count > 0 else { return } // extra validation to come usedWords.insert(answer, at: 0) //usedWords.insert(answer, at:usedWords.endIndex) newWord = "" } var body: some View { NavigationView { VStack { TextField("Enter your word", text: $newWord, onCommit: addNewWord) .autocapitalization(.none) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding() List(usedWords, id: \.self) { Image(systemName: "\($0.count).circle") Text($0) } } .navigationBarTitle(rootWord) } } }
Running code when our app launches
アプリの起動時にコードを実行する
import SwiftUI struct ContentView: View { @State private var usedWords = [String]() @State private var rootWord = "" @State private var newWord = "" func startGame() { // 1. Find the URL for start.txt in our app bundle if let startWordsURL = Bundle.main.url(forResource: "start", withExtension: "txt") { // 2. Load start.txt into a string if let startWords = try? String(contentsOf: startWordsURL) { // 3. Split the string up into an array of strings, splitting on line breaks let allWords = startWords.components(separatedBy: "\n") // 4. Pick one random word, or use "silkworm" as a sensible default rootWord = allWords.randomElement() ?? "silkworm" // If we are here everything has worked, so we can exit return } } // If were are *here* then there was a problem – trigger a crash and report the error fatalError("Could not load start.txt from bundle.") } func addNewWord() { // lowercase and trim the word, to make sure we don't add duplicate words with case differences let answer = newWord.lowercased().trimmingCharacters(in: .whitespacesAndNewlines) // exit if the remaining string is empty guard answer.count > 0 else { return } // extra validation to come usedWords.insert(answer, at: 0) //usedWords.insert(answer, at:usedWords.endIndex) newWord = "" } var body: some View { NavigationView { VStack { TextField("Enter your word", text: $newWord, onCommit: addNewWord) .autocapitalization(.none) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding() List(usedWords, id: \.self) { Image(systemName: "\($0.count).circle") Text($0) } } .navigationBarTitle(rootWord) .onAppear(perform: startGame) } } }
Validating words with UITextChecker
UITextCheckerによる単語の検証
import SwiftUI struct ContentView: View { @State private var usedWords = [String]() @State private var rootWord = "" @State private var newWord = "" @State private var errorTitle = "" @State private var errorMessage = "" @State private var showingError = false //========================================================================== func startGame() { // 1. Find the URL for start.txt in our app bundle if let startWordsURL = Bundle.main.url(forResource: "start", withExtension: "txt") { // 2. Load start.txt into a string if let startWords = try? String(contentsOf: startWordsURL) { // 3. Split the string up into an array of strings, splitting on line breaks let allWords = startWords.components(separatedBy: "\n") // 4. Pick one random word, or use "silkworm" as a sensible default rootWord = allWords.randomElement() ?? "silkworm" // If we are here everything has worked, so we can exit return } } // If were are *here* then there was a problem – trigger a crash and report the error fatalError("Could not load start.txt from bundle.") } //========================================================================== func addNewWord() { // lowercase and trim the word, to make sure we don't add duplicate words with case differences let answer = newWord.lowercased().trimmingCharacters(in: .whitespacesAndNewlines) // exit if the remaining string is empty guard answer.count > 0 else { return } //================================= func isOriginal(word: String) -> Bool { !usedWords.contains(word) } //================================= func isPossible(word: String) -> Bool { var tempWord = rootWord for letter in word { if let pos = tempWord.firstIndex(of: letter) { tempWord.remove(at: pos) } else { return false } } return true } //================================= func isReal(word: String) -> Bool { let checker = UITextChecker() let range = NSRange(location: 0, length: word.utf16.count) let misspelledRange = checker.rangeOfMisspelledWord(in: word, range: range, startingAt: 0, wrap: false, language: "en") return misspelledRange.location == NSNotFound } // extra validation to come guard isOriginal(word: answer) else { wordError(title: "Word used already", message: "Be more original") return } guard isPossible(word: answer) else { wordError(title: "Word not recognized", message: "You can't just make them up, you know!") return } guard isReal(word: answer) else { wordError(title: "Word not possible", message: "That isn't a real word.") return } //================================= usedWords.insert(answer, at: 0) //usedWords.insert(answer, at:usedWords.endIndex) newWord = "" } //========================================================================== func wordError(title: String, message: String) { errorTitle = title errorMessage = message showingError = true } var body: some View { NavigationView { VStack { TextField("Enter your word", text: $newWord, onCommit: addNewWord) .autocapitalization(.none) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding() List(usedWords, id: \.self) { Image(systemName: "\($0.count).circle") Text($0) } } .navigationBarTitle(rootWord) .onAppear(perform: startGame) .alert(isPresented: $showingError) { Alert(title: Text(errorTitle), message: Text(errorMessage), dismissButton: .default(Text("OK"))) } } } }