[swift]挫折しながら覚えるiOS開発その6 レイアウト調整


前回はSDWebImageを用いて非同期画像表示処理行い、TableViewのスクロールが遅い問題の解消を行いました。

今回はレイアウト調整を行いつつ、細かい部分の対応をしたいと思います。

先にレイアウト調整前と調整後のキャプチャを載せておきます。

セクション分けに関しては長くなりそうなので次回にします。

Before:
ios4-3

After:
swift6-1

それっぽくなってきました。

主な変更点としては

  • 各項目の文字サイズ調整
  • 文字がはみ出さないようにAuto Layout設定
  • 偶数行の場合は背景色を変更
  • セル選択時の色を指定
  • ストック数表示部分の角丸表示
  • Newマーク表示
  • ディレクトリ構造の整理

となります。

1. 各項目のフォントサイズ調整

過去の週刊Qiitaのコードを見ながらフォントサイズを調整しました。

2. 文字がはみ出さないようにAuto Layout設定

第二回で設定したAuto Layoutだと文字がはみ出してしまっていたので、一度Auto Layout設定をリセットします。

Storyboardの右下にある△メニューを選択して、All Views in Link View Cellの「Clear Constraints」をクリックします。

swift6-2

これで現状のすべてのAuto Layout設定がリセットされました。

リセット後に「Add Missing Constraints」を使っておおまかにAuto Layout設定を行おうとしたのですが

Failed to automatically update constraints

というエラーが出て実行できませんでした。

色々試したところ、カスタムCellの部分を削除して「Add Missing Constraints」を再実行したら動いたので、カスタムCellには実行できないのかもしれません。

気を取り直して、カスタムCell部分のAuto Layout設定をひとつずつ設定していきます。

以下のようにtitle, icon, username, ストック数表示にそれぞれ設定しました。

swift6-3

swift6-4

また、レイアウト調整後にビルドした際に

Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want

という警告が出たのですが、これはどこかの制約とどこかの制約で矛盾しているとの警告らしいので、怪しそうな制約をリセットしては順番にビルドしながら試して調整しました。

もっとよい調整方法があれば知りたいです。

3. 偶数行の場合は背景色を変更

Bootstrapのテーブルでも用意されている「Striped rows」を実装しました。

tableViewメソッド内で偶数行の場合は背景色をグレーにする処理を追加します。

# ViewController.swift

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        var cell: LinkViewCell = tableView.dequeueReusableCellWithIdentifier("LinkViewCell") as! LinkViewCell

        // 偶数行の場合は色をグレーに
        let altCellColor = UIColor(red:243.0/255, green:243.0/255, blue:243.0/255, alpha:1.0)
        if (indexPath.row % 2 == 0) {
            cell.backgroundColor = altCellColor;
        } else {
            cell.backgroundColor = UIColor.whiteColor();
        }

        cell.setCell(links[indexPath.row])
        return cell
    }

4. セル選択時の色を指定

セル選択時の色がデフォルトだと灰色のため、緑色に変更できるようにします。

swift6-5

Qiitaの記事でExtensionの作成方法が書かれていたので、こちらをお借りします。

Swift – UITableViewCellの選択色をStoryboard上で設定する – Qiita

# UITableViewCellExtension.swift

import UIKit

extension UITableViewCell {

    @IBInspectable
    var selectedBackgroundColor: UIColor? {
        get {
            return selectedBackgroundView?.backgroundColor
        }
        set(color) {
            let background = UIView()
            background.backgroundColor = color
            selectedBackgroundView = background
        }
    }   
}

swift6-6

これで選択色を変更できるようになりました。

5. ストック数表示部分の角丸表示

角丸にしたい要素(ストック数表示View)の「Identity Inspector」メニューを開き、「User Defined Runtime Attributes」に以下を設定します。

layer.cornerRadius Number 10
layer.masksToBounds Boolean true

swift6-7

これでシミュレータを起動すると角丸表示になります。

ただし、Storyboard上には反映されないので注意が必要です。

また、masksToBoundsを設定しておくとView内の文字が枠をはみ出すのを防げるそうです。

6. Newマーク表示

APIには新着記事かどうかを判定するフラグがあるので、新着記事だったらNewマークを表示するようにします。

まずは、ResourcesディレクトリにNewアイコンを追加し、Storyboard上にImageViewを追加します。

swift6-8

続いてLinkモデルにisNewプロパティを追加します。

# Link.swift

import Foundation

class Link : NSObject {
    var title:String
    var username:String
    var userIconUrl:String?
    var stockCount:Int
    var isNew:Bool  # 追加

    init(title: String, username: String, userIconUrl: String?, stockCount: Int, isNew: Bool){
        self.title = title
        self.username = username
        self.userIconUrl = userIconUrl
        self.stockCount = stockCount
        self.isNew = isNew # 追加
    }
}

LinkViewCellではisNewの状態に応じてNewマークの表示/非表示を切り替えるようにします。

# LinkViewCell.swift

import UIKit

class LinkViewCell: UITableViewCell {

    @IBOutlet weak var title: UILabel!
    @IBOutlet weak var username: UILabel!
    @IBOutlet weak var userIcon: UIImageView!
    @IBOutlet weak var stockCount: UILabel!
    @IBOutlet weak var newIcon: UIImageView!

    func setCell(link: Link) {
        self.title.text = link.title
        self.username.text = link.username
        self.stockCount.text = String(link.stockCount)

        if link.userIconUrl != nil {
            self.userIcon.sd_setImageWithURL(NSURL(string: link.userIconUrl!))
        }

        // isNewに応じてアイコンの表示切り替え
        if link.isNew == true {
            self.newIcon.hidden = false
        } else {
            self.newIcon.hidden = true
        }
    }
}

ViewControllerのsetupLinksメソッド内でAPIからデータを取得した際に、isNewも取得するようにします。

# ViewController.swift

    func setupLinks() {
        Alamofire.request(.GET, "http://qiita-stock.info/api.json")
            .responseJSON { _, _, data, _ in
                var json = JSON(data!)
                for (i, topic) in json {
                    var links = [Link]()
                    for (k, link) in topic["links"] {
                        links.append(Link(
                            title: link["title"].asString!,
                            username: link["author"].asString!,
                            userIconUrl: link["icon"].asString!,
                            stockCount: link["stock"].asInt!,
                            isNew: link["is_new"].asBool!   # 追加
                        ))
                    }
                    self.sections.append(topic["send_date"].asString!)
                    self.linkList.append(links)
                }
                self.tableView.reloadData()
        }
    }

これで新着記事の場合はNewマークが表示されるようになりました。

7. ディレクトリ構造の整理

最後に、ディレクトリ構造の整理をします。

現状だと、Xcode上では以下のようにディレクトリ分けされているのですが、Finderで見ると全ファイルが同一階層に保存されています。

swift6-9

一個ずつディレクトリを調整していくのは面倒だなと思って調べたところ、synxというライブラリを使うとコマンド一発でディレクトリの整理ができました。

事前にプロジェクトのバックアップを取っておいて、

gem install synx

でsynxをインストール。

その後、プロジェクトのルートディレクトリで

synx weeqiita.xcodeproj

を実行したら、Xcode上のディレクトリ構造に合わせて、Finder上にもディレクトリが作成されました。

ただ、ルートディレクトリに置いていた「weeqiita-Bridging-Header.h」が消えてしまっていたので、バックアップからコピーしてLib以下に移動し、Objective-C Bridging Headerのパスも変更する必要がありました。

ルートディレクトリ直下に置いているファイルがある場合は注意が必要です。

長くなってしまいましたが、これでレイアウト調整がひと通りできました。

次回はTableViewのセクション分けの対応を行います。

参考