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

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

ただいまの
回答率

90.83%

  • Swift

    6514questions

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

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

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 166

roieisky

score 2

 前提・実現したいこと

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)

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+2

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

// ↓ 例にあった時間の配列
var timeArray = ["03.45", "01.55", "05.05", "01.23", "00.23"]

timeArray.sort()
// timeArray => ["00.23", "01.23", "01.55", "03.45", "05.05"]

以下回答追記

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

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let item1 = Item(time: "01.55", date: "2018/05/05", compareTime: 01.55)
        let item2 = Item(time: "01.23", date: "2018/05/04", compareTime: 1.23)
        let item3 = Item(time: "00.23", date: "2018/05/06", compareTime: 0.23)
        let saveItems = [item1, item2, item3]

        // 保存
        let userDefaults = UserDefaults.standard
        let archivedObject = NSKeyedArchiver.archivedData(withRootObject: saveItems)
        userDefaults.set(archivedObject, forKey: "ItemSaveKey")
        userDefaults.synchronize()

        // 取得
        var resultItems: [Item] = []
        if let itemData = userDefaults.object(forKey: "ItemSaveKey") as? Data,
           let getItems = NSKeyedUnarchiver.unarchiveObject(with: itemData) as? [Item] {

            // ソート
            resultItems = getItems.sorted{ $0.time < $1.time }
        }

        // resultItems => [Item(time: "00.23", date: "2018/05/06", compareTime: 0.23),
        //                 Item(time: "01.23", date: "2018/05/04", compareTime: 1.23),
        //                 Item(time: "01.55", date: "2018/05/05", compareTime: 01.55)]
    }
}


class Item: NSObject, NSCoding {

    enum ItemKey: String {
        case itemTimeKey
        case itemDateKey
        case itemCompareTimeKey
    }

    /// タイム表示用
    var time: String
    /// 日付
    var date: String
    /// タイム比較用
    var compareTime: Double

    init(time: String, date: String, compareTime: Double) {
        self.time = time
        self.date = date
        self.compareTime = compareTime
    }

    required init?(coder aDecoder: NSCoder) {
        guard let time = aDecoder.decodeObject(forKey: Item.ItemKey.itemTimeKey.rawValue) as? String,
            let date = aDecoder.decodeObject(forKey: Item.ItemKey.itemDateKey.rawValue) as? String else {
                return nil
        }
        self.time = time
        self.date = date
        self.compareTime = aDecoder.decodeDouble(forKey: Item.ItemKey.itemCompareTimeKey.rawValue)
    }

    func encode(with aCoder: NSCoder) {
        aCoder.encode(time, forKey: Item.ItemKey.itemTimeKey.rawValue)
        aCoder.encode(date, forKey: Item.ItemKey.itemDateKey.rawValue)
        aCoder.encode(compareTime, forKey: Item.ItemKey.itemCompareTimeKey.rawValue)
    }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/05 07:19

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

    キャンセル

check解決した方法

0

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 90.83%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Swift

    6514questions

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