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

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

ただいまの
回答率

87.34%

[Swift]辞書をソートしてTableViewに値を棒グラフのように表したい

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 843

score 25

アプリの概要

月別の出費額をソートして棒グラフみたいに表したい.

これまでの流れ

写真1のように,CellLabel(青色のやつ)を配置して,コード2TableViewCellに接続した.
そして,コード1のようにコードを書き足し,実行すると写真2のようになった.
(この時,写真4のようにCellの高さを高くして,全てのCellが1度に画面に入りきらないようになっている.)
(ソートされているので,上から12月,11月,・・・,2月,1月)

わからないこと

実行した後に,TableViewをスライドさせたり更新ボタンを何回か押すとログ1がログに表示され,写真3のようにLabelの長さが正しく表示されない.
(ソートはされていて,上から12月,11月,・・・,2月,1月 になっている)

考えられること

コード3の部分が多分悪さをしていると思うが,解決方法がわからない.
力をお貸しください.よろしくお願いします.
(実際に試していただければわかりやすいかと思います.)

写真1

イメージ説明

写真2

イメージ説明

写真3

イメージ説明

写真4

イメージ説明

コード1

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!

    //月別の出費額
    var array1: [String: Int] = ["1月":10,"2月":20,"3月":30,"4月":40,"5月":50,"6月":60,"7月":70,"8月":80,"9月":90,"10月":100,"11月":110,"12月":120]
    //ソートした後の月別の出費額
    var rank = [(key: String, value: Int)]()
    //最も出費が多かった月の出費額
    var rank1Value = Int()


    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
        tableView.dataSource = self
        reloadData()
    }

    //TableViewを更新する
    func reloadData() {
        //ソート
        self.rank = self.array1.sorted {$0.value > $1.value}
        //最も出費が多かった月の出費額を取得
        self.rank1Value = self.rank[0].value
        self.tableView.reloadData()
    }


    @IBAction func refreshButton(_ sender: Any) {
        reloadData()
    }


    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.rank.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? TableViewCell else {
            return UITableViewCell()
        }
        //月の出費額を取得
        let value: Int = self.rank[indexPath.row].value
        //最も出費が多かった月の出費額で割ることで,それに対する割合を求める
        let ratio = Double(value) / Double(self.rank1Value)
        let multiplier = CGFloat(ratio)
        //TabelViewにLabelが収まるように幅を調節
        let constant = CGFloat(ratio * -70.0)

        DispatchQueue.main.async {
            //Labelの幅に関する制約(multiplierとconstant)を設定する
            cell.label.widthAnchor.constraint(equalTo: self.tableView.widthAnchor, multiplier: multiplier, constant: constant).isActive = true
        }
        return cell
    }
}

コード2

import UIKit

class TableViewCell: UITableViewCell {

    @IBOutlet weak var label: UILabel!


    override func awakeFromNib() {
        super.awakeFromNib()
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }
}

ログ1

2019-09-08 19:45:42.235064+0900 a[10964:357303] [LayoutConstraints] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x600003929090 UITableView:0x7fe57f86ee00.leading == UILayoutGuide:0x600002376d80'UIViewSafeAreaLayoutGuide'.leading   (active)>",
    "<NSLayoutConstraint:0x600003929270 UITableView:0x7fe57f86ee00.trailing == UILayoutGuide:0x600002376d80'UIViewSafeAreaLayoutGuide'.trailing   (active)>",
    "<NSLayoutConstraint:0x6000039058b0 UILabel:0x7fe57ec17540.width == 0.0833333*UITableView:0x7fe57f86ee00.width - 5.83333   (active)>",
    "<NSLayoutConstraint:0x60000392cff0 UILabel:0x7fe57ec17540.width == 0.333333*UITableView:0x7fe57f86ee00.width - 23.3333   (active)>",
    "<NSLayoutConstraint:0x60000392b070 'UIView-Encapsulated-Layout-Width' UIView:0x7fe57ed27f00.width == 320   (active)>",
    "<NSLayoutConstraint:0x600003929130 'UIViewSafeAreaLayoutGuide-left' H:|-(0)-[UILayoutGuide:0x600002376d80'UIViewSafeAreaLayoutGuide'](LTR)   (active, names: '|':UIView:0x7fe57ed27f00 )>",
    "<NSLayoutConstraint:0x6000039291d0 'UIViewSafeAreaLayoutGuide-right' H:[UILayoutGuide:0x600002376d80'UIViewSafeAreaLayoutGuide']-(0)-|(LTR)   (active, names: '|':UIView:0x7fe57ed27f00 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x6000039058b0 UILabel:0x7fe57ec17540.width == 0.0833333*UITableView:0x7fe57f86ee00.width - 5.83333   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.

コード3

cell.label.widthAnchor.constraint(equalTo: self.tableView.widthAnchor, multiplier: multiplier, constant: constant).isActive = true

コード4

cell.widthConstraint.constant = cell.bounds.size.width * multiplier + constant

解決方法

k2moonsのアンサーにより解決することができました.

Labelwidthに関する制約を設け(Main.Stroybord上で),@IBOutletTableViewCellクラスに接続し,コード3コード4に書き換えることで解決しました.

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+2

Xcode10.3, Swift5.1の環境で試してみました。

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self       // 抜けてました
        tableView.dataSource = self     // 抜けてました
        reloadData()
    }

上記の設定が抜けているくらいで、特に問題はないようでした。ログも出ませんでした。

イメージ説明

func dequeueReusableCell(withIdentifier identifier: String, for indexPath: IndexPath) -> UITableViewCell

func dequeueReusableCell(withIdentifier identifier: String) -> UITableViewCell?

であれば起こりそうですが、ソースコードはそうはなっていません。

問題は2つあって、並び替え順の問題と、ログ1の問題は別のように思われますが、再現しませんでしたので、下記は参考までに書きます。

ログ1は、一度付加したConstraintを何度も上書きしようとして出ているように思います。
例えば、事前にwidthのConstraintを設定し、Constraintの値のみを変更するやり方であれば、ログ1のようにはならないと思います。

        DispatchQueue.main.async {
            //Labelの幅に関する制約(multiplierとconstant)を設定する
            cell.widthConstraint.constant = cell.bounds.size.width * multiplier
        }

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/09/09 23:13 編集

    再現しました。
    ```
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? TableViewCell else {
    return UITableViewCell()
    }
    if cell.tag == 0 {
    cell.tag = indexPath.row + 100
    }
    print("index.row=\(indexPath.row), tag=\(cell.tag)")
    ```
    というようなコードを入れてみると何が起こっているのかが分かりやすいです。

    func dequeueReusableCell(withIdentifier identifier: String, for indexPath: IndexPath) -> UITableViewCell

    func dequeueReusableCell(withIdentifier identifier: String) -> UITableViewCell?

    同じ仕様なので、やはり最初に書いたようにConstraintと上記の仕様で発生すると思われます。

    UITableViewは隠れたセルを使い回しています。これはメモリーをセル分だけ確保することでテーブルリストが巨大になった場合のメモリー消費を抑制するためです。
    print文を見ると消えたセルを使い回していることが分かります。でもなぜ使い回したセルの設定がうまくいかないか。これも最初に書いたように、既に昔のセルにはConstraintがセットされていてそれが有効なためです。
    お知らせた方法を使うと、使い回したセルに対して、必要な情報を再セットできるので、うまく行くはずです。

    キャンセル

  • 2019/09/11 21:38

    k2moonsさん
    貴方の方法を試したところ解決することができました!
    最後までお付き合いありがとうございました.
    今後ともよろしくお願いします.

    キャンセル

  • 2019/09/11 22:02

    コード3をコード4に書き換えることで解決できました.

    キャンセル

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

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

関連した質問

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