質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

2回答

3730閲覧

ランキング表示がうまくいきません。Swift,配列,Double型

roieisky

総合スコア6

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

2クリップ

投稿2018/04/27 08:50

前提・実現したいこと

Xcode(Swift)でiPhone向けのアプリを開発しております。
タイム「単位:秒(少数第2位までを文字列型で表示) 例: 03.48 秒」をランキング表示させる機能で以下のような問題が発生しました。

発生している問題

・整数部はタイムが昇順になっているが、少数部が昇順にならない
例:
00.23
01.55
01.23
03.45
05.05

該当のソースコード

↓TableViewController(ランキング表示に関わる部分です)

@IBOutlet weak var table: UITableView! //ForKey定義 let label2ArrayForKey:String = "label2Key" let label4ArrayForKey:String = "label4Key" let secArrayForKey:String = "secKey" // 配列定義 var label2Array:Array<String> = [] var label4Array:Array<String> = [] var secArray:Array<Double> = [] override func viewDidLoad() { super.viewDidLoad() //保存してある配列の取得 label2Array = readlabel2Array() label4Array = readlabel4Array() secArray = readSecArray() // Do any additional setup after loading the view. } //Table Viewのセルの数を指定 func tableView(_ table: UITableView, numberOfRowsInSection section: Int) -> Int { return label2Array.count } //各セルの要素を設定する func tableView(_ table: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // tableCell の ID で UITableViewCell のインスタンスを生成 let cell = table.dequeueReusableCell(withIdentifier: "bestTime1Cell", for: indexPath) // Tag番号 ごとに UILabel インスタンスを生成 let label1 = cell.viewWithTag(1) as! UILabel label1.text = "No." + String(indexPath.row + 1) let label2 = cell.viewWithTag(2) as! UILabel label2.text = String(describing: label2Array[indexPath.row]) let label3 = cell.viewWithTag(3) as! UILabel label3.text = "秒" let label4 = cell.viewWithTag(4) as! UILabel label4.text = String(describing: label4Array[indexPath.row]) return cell } // Cell の高さを10分割にする func tableView(_ table: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return table.bounds.height / 10 } // 保存する func saveArray(result: Array<Any>) { let defaults = UserDefaults.standard defaults.set(label2Array, forKey:label2ArrayForKey) defaults.set(label4Array, forKey:label4ArrayForKey) defaults.set(secArray, forKey:secArrayForKey) defaults.synchronize() } // label2Array取得する func readlabel2Array() -> Array<String> { let defaults = UserDefaults.standard if let aaa:Array = (defaults.object(forKey: label2ArrayForKey) as? Array<String>) { return aaa } else { return Array(repeating: "?", count: 10) } } // label4Array取得する func readlabel4Array() -> Array<String> { let defaults = UserDefaults.standard if let aaa:Array = (defaults.object(forKey: label4ArrayForKey) as? Array<String>) { return aaa } else { return Array(repeating: "?", count: 10) } } //secArray取得 func readSecArray() -> Array<Double> { let defaults = UserDefaults.standard if let aaa:Array<Double> = (defaults.object(forKey: secArrayForKey) as? Array<Double>) { return aaa } else { return Array(repeating: 99.99, count: 10) } } //reset func reset(){ //初期化 label2Array = Array(repeating: "?", count: 10) label4Array = Array(repeating: "?", count: 10) secArray = Array(repeating: 99.99, count: 10) } @IBAction func resetAction(_ sender: UIButton) { //初期化 reset() //配列を保存する。 saveArray(result: label2Array) saveArray(result: label4Array) saveArray(result: secArray) //再描画 loadView() viewDidLoad() }

↓ViewController2(タイムを計測し、ViewController3にタイムを渡しています)

@IBOutlet weak var tapBtn: UIButton! @IBOutlet weak var num: UILabel! @IBOutlet weak var timerSecond: UILabel! @IBOutlet weak var timerMSec: UILabel! var countNum = 3 var countTap = 1  //10の予定 var nowTime = 99.99 // タイマー var timer : Timer! var startTime = Date() let conWidth = UIScreen.main.bounds.size.width let conHeight = UIScreen.main.bounds.size.height override func viewDidLoad() { super.viewDidLoad() // カウントダウンタイマー作成 timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController2.onUpdate(timer:)), userInfo: nil, repeats: true) // Do any additional setup after loading the view. } // Segue 準備 override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if (segue.identifier == "ToViewController3") { let vc3: ViewController3 = (segue.destination as? ViewController3)! // ViewController3のタイム設定 vc3.textSec = timerSecond.text! vc3.textMSec = timerMSec.text! vc3.sec = nowTime } } //tapBtn押下 //ボタンをランダムな座標に出現させ、カウントが0になると結果画面に遷移する @IBAction func tapAction(_ sender: UIButton) { countTap -= 1 if(countTap == 0) { stopTimer() tapBtn.isHidden = true //画面遷移 self.performSegue(withIdentifier: "ToViewController3", sender: nil) } else { tapBtn.frame.origin = randPoint() tapBtn.setTitle(String(countTap), for: .normal) } } @objc func onUpdate(timer : Timer){ // カウント-1 countNum -= 1 if(countNum == 0){ timer.invalidate() num.isHidden = true tapBtn.frame.origin = randPoint() tapBtn.isHidden = false //計測タイマー作成 startTimer() } else{ // 桁数を指定して文字列を作る let str = String(countNum) // ラベルに表示 num.text = str } } //ランダムな座標を求める //tapBtn.frame.origin = randPoint() func randPoint() -> CGPoint{ let randWidth = CGFloat(arc4random_uniform(UInt32(conWidth)-80)) let randHeight = CGFloat(arc4random_uniform(UInt32(conHeight)-130)) let randPoint = CGPoint(x:randWidth,y:randHeight + 20) return randPoint } //計測タイマースタート @objc func startTimer() { if timer != nil{ // timerが起動中なら一旦破棄する timer.invalidate() } timer = Timer.scheduledTimer( timeInterval: 0.01, target: self, selector: #selector(self.timerCounter), userInfo: nil, repeats: true) startTime = Date() } //タイマーストップ @objc func stopTimer() { if timer != nil{ timer.invalidate() } } //計算タイマーのリピート処理 @objc func timerCounter() { // タイマー開始からのインターバル時間 let currentTime = Date().timeIntervalSince(startTime) // fmod() 余りを計算 //let minute = (Int)(fmod((currentTime/60), 60)) // currentTime/60 の余り let second = (Int)(fmod(currentTime, 60)) // floor 切り捨て、小数点以下を取り出して *100 let msec = (Int)((currentTime - floor(currentTime))*100) // %02d: 2桁表示、0で埋める //let sMinute = String(format:"%02d", minute) let sSecond = String(format:"%02d", second) let sMsec = String(format:"%02d", msec) //時間更新 timerSecond.text = sSecond timerMSec.text = sMsec nowTime = Double(second + (msec / 100)) //59秒以上で強制終了 if(nowTime >= 59.00){ stopTimer() tapBtn.isHidden = true //画面遷移 self.performSegue(withIdentifier: "ToViewController3", sender: nil) } }

↓ViewController3(タイムを配列と比較し昇順になるよう挿入します)

@IBOutlet weak var resultSecond: UILabel! @IBOutlet weak var resultMSec: UILabel! @IBOutlet weak var meritText: UILabel! var textSec = "99" var textMSec = "99" var sec = 99.99 //ForKey定義 let label2ArrayForKey:String = "label2Key" let label4ArrayForKey:String = "label4Key" let secArrayForKey:String = "secKey" // 配列定義 var label2Array:Array<String> = [] var label4Array:Array<String> = [] var secArray:Array<Double> = [] //let originalPasteboard: UIPasteboard = UIPasteboard(name: UIPasteboardName(rawValue: "bestTime1"), create: true)! override func viewDidLoad() { super.viewDidLoad() //保存してある配列の取得 label2Array = readlabel2Array() label4Array = readlabel4Array() secArray = readSecArray() resultSecond.text = textSec resultMSec.text = textMSec let strTime = textSec + "." + textMSec if(sec < 2.0){meritText.text = "神"} else if (sec < 3.0){meritText.text = "プロ"} else if (sec < 4.0){meritText.text = "若い!"} else if (sec < 6.0){meritText.text = "平凡..."} else if (sec < 9.0){meritText.text = "中年"} else if (sec < 15.0){meritText.text = "老人"} else{meritText.text = "遅い..."} //取得した配列要素とタイムを比較して挿入 let f = DateFormatter() f.timeStyle = .none f.dateStyle = .medium f.locale = Locale(identifier: "ja_JP") let now = Date() let strDate = f.string(from: now)      //配列と比較して挿入する処理 for i in 0 ... 9 { if secArray[i] > sec { label2Array.removeLast() label4Array.removeLast() secArray.removeLast() label2Array.insert(strTime, at: i) label4Array.insert(strDate, at: i) secArray.insert(sec, at: i) break } } //配列を保存する。 saveArray(result: label2Array) saveArray(result: label4Array) saveArray(result: secArray) // Do any additional setup after loading the view. } // 保存する 略 // label2Array取得する 略 // label4Array取得する 略 //secArray取得 略

試したこと

ランキング画面では配列10個をテーブルビューで表示しているので、
以下のようにsecArrayの0番から順番にsec(ViewController2から値が渡される)を比較し、
下回った場合に挿入しています。

//配列と比較して挿入する処理 for i in 0 ... 9 { if secArray[i] > sec { label2Array.removeLast() label4Array.removeLast() secArray.removeLast() label2Array.insert(strTime, at: i) label4Array.insert(strDate, at: i) secArray.insert(sec, at: i) break } }

補足情報(FW/ツールのバージョンなど)

macOS Seirra Version 10.12.6(16G1314)
Xcode Version 9.2 (9C40b)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

文字列(タイム)を配列で保持していると思いますので、配列を以下の用にソートしてみてください。

swift

1// ↓ 例にあった時間の配列 2var timeArray = ["03.45", "01.55", "05.05", "01.23", "00.23"] 3 4timeArray.sort() 5// timeArray => ["00.23", "01.23", "01.55", "03.45", "05.05"]

以下回答追記

情報を分けた配列で持つのは大変なので、一つの案としてUserDefaultsに保存できるクラスにしてみるのはどうでしょう?
※ 数が多くなるようであればDBの使用なども考えた方がよいと思いますが。
古いですが参考URL: Swiftオブジェクトの配列をNSUserDefaultsに保存する

swift

1import UIKit 2 3class ViewController: UIViewController { 4 5 override func viewDidLoad() { 6 super.viewDidLoad() 7 8 let item1 = Item(time: "01.55", date: "2018/05/05", compareTime: 01.55) 9 let item2 = Item(time: "01.23", date: "2018/05/04", compareTime: 1.23) 10 let item3 = Item(time: "00.23", date: "2018/05/06", compareTime: 0.23) 11 let saveItems = [item1, item2, item3] 12 13 // 保存 14 let userDefaults = UserDefaults.standard 15 let archivedObject = NSKeyedArchiver.archivedData(withRootObject: saveItems) 16 userDefaults.set(archivedObject, forKey: "ItemSaveKey") 17 userDefaults.synchronize() 18 19 // 取得 20 var resultItems: [Item] = [] 21 if let itemData = userDefaults.object(forKey: "ItemSaveKey") as? Data, 22 let getItems = NSKeyedUnarchiver.unarchiveObject(with: itemData) as? [Item] { 23 24 // ソート 25 resultItems = getItems.sorted{ $0.time < $1.time } 26 } 27 28 // resultItems => [Item(time: "00.23", date: "2018/05/06", compareTime: 0.23), 29 // Item(time: "01.23", date: "2018/05/04", compareTime: 1.23), 30 // Item(time: "01.55", date: "2018/05/05", compareTime: 01.55)] 31 } 32} 33 34 35class Item: NSObject, NSCoding { 36 37 enum ItemKey: String { 38 case itemTimeKey 39 case itemDateKey 40 case itemCompareTimeKey 41 } 42 43 /// タイム表示用 44 var time: String 45 /// 日付 46 var date: String 47 /// タイム比較用 48 var compareTime: Double 49 50 init(time: String, date: String, compareTime: Double) { 51 self.time = time 52 self.date = date 53 self.compareTime = compareTime 54 } 55 56 required init?(coder aDecoder: NSCoder) { 57 guard let time = aDecoder.decodeObject(forKey: Item.ItemKey.itemTimeKey.rawValue) as? String, 58 let date = aDecoder.decodeObject(forKey: Item.ItemKey.itemDateKey.rawValue) as? String else { 59 return nil 60 } 61 self.time = time 62 self.date = date 63 self.compareTime = aDecoder.decodeDouble(forKey: Item.ItemKey.itemCompareTimeKey.rawValue) 64 } 65 66 func encode(with aCoder: NSCoder) { 67 aCoder.encode(time, forKey: Item.ItemKey.itemTimeKey.rawValue) 68 aCoder.encode(date, forKey: Item.ItemKey.itemDateKey.rawValue) 69 aCoder.encode(compareTime, forKey: Item.ItemKey.itemCompareTimeKey.rawValue) 70 } 71} 72 73

投稿2018/05/04 07:06

編集2018/05/05 01:45
_Kentarou

総合スコア8490

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

roieisky

2018/05/04 22:19

ご回答ありがとうございます。 言葉足らずで申し訳ありませんでした、コードには記載があるのですが、実は配列を3つ同時に保存しており(タイム[文字列、表示用]、日付[文字列、表示用]、タイム[数値、比較用])、タイムとチャレンジした日付がセットでランキングに表示されています。 1つの配列をソートした際に他の2つの配列に同じ動きをさせることができるのでしょうか?
guest

0

自己解決

ほとんどコードを書き換えずに自己解決することができました。
配列に挿入するか判断する条件分岐でDouble型(例:5.32)の代わりにInt型(例:532)を用いることで、
正確に挿入することができました。

投稿2018/05/07 05:01

roieisky

総合スコア6

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問