Contents
はじめに
前回こちらの記事でモデルを作成しapiから取得したデータからある程度の項目を抽出して使用できるようにしました。
今回は実際に取得した記事情報をXibファイル
を使って表示できるようにしましょう。
前回作成したモデル
前回ではこのようにモデルを作成しました。
struct Article {
let title: String
let body: String
let renderedBody: String
let urlString: String
let userId: String
let profileImageUrlString: String
init(json: [String: Any]) {
title = json["title"] as? String ?? ""
body = json["body"] as? String ?? ""
renderedBody = json["rendered_body"] as? String ?? ""
urlString = json["url"] as? String ?? ""
if let user = json["user"] as? [String: Any] {
userId = user["id"] as? String ?? ""
profileImageUrlString = user["profile_image_url"] as? String ?? ""
} else {
userId = ""
profileImageUrlString = ""
}
}
init() {
title = ""
body = ""
renderedBody = ""
urlString = ""
userId = ""
profileImageUrlString = ""
}
}
今回はこの中から、title(記事のタイトル)
,userId(ユーザー名)
,profileImageUrlString(サムネイルURL)
を使用して記事セルを作成していこうと思います。
現状のレイアウト
こちらの記事で作成したXibファイル
はタイトルをとりあえず中心に表示させただけのテキトーなレイアウトです。ここから修正を加えていきましょう。
現状このようになっています。
不要なオートレイアウトの設定を解除
まず前回設定したオートレイアウトの設定を削除していきます。
現状の設定はこのように設定を確認したいパーツを選択した状態で、右上の定規みたいなマークのところを選択すると確認することができます。
このEditの部分を押せば細かい数値の編集をすることができます。
削除する場合は、例えば先程のスクショでの「Align Center X」を消したい場合は青色の枠になっている状態でこれは選択されている状態なのでこの状態でDeleteボタンを押せば消えてくれます。
このようになっていれば完了です。
IBOutletのコネクションの削除
現在TableViewCell.xib
のLabel
と、TableViewCell.swift
の
@IBOutlet weak var label: UILabel!
の部分がコネクションでつながっています。
今回UILabel
を複数扱うのでこのようにlabel
という命名だとわかりずらくなってしまうので違う名前に変更したいのですが、このようにコネクションでつながっているので単純に名前をコードで書き換えてしまうだけでは実行時エラーになってしまいます。それを避けるためにコネクションを削除する方法を知っておきましょう。
先程のようにコネクションを切りたいパーツを選択した状態で、右上の右矢印のマークを押すとコネクション一覧の画面が出てきます。「Referencing Outlets」の部分にlabel
がTableViewCell
とつながってるよっていうのが出てきています。この部分の☓ボタンを押せば消すことができます。
これで削除できました。先程13行目の左の部分が白丸になってたと思いますが、コネクションを削除した場合に中の白い部分が消えたかと思います。これはコネクションがあるかどうかを表していて白丸のときはつながっている状態を表しています。ただし、このようにXibファイル
とswiftファイル
を横並びにしたときの画面じゃないとつながっていても黒丸になる場合もあるので注意です。
コネクションが切れたので、コードの@IBOutlet weak var label: UILabel!
の部分もあとで付け直すので一旦削除しておきましょう。
パーツを配置
今回使用するのは、
UILabel
を使用して記事タイトルを表示UILabel
を使用してユーザー名を表示UIImageView
を使用してユーザーのサムネ画像を表示
のようにしていきます。
イメージとしてはこのような配置にしようと思います。
この状態はまだオートレイアウトは設定していなくてただパーツをのせただけの状態です。
まずタイトル用のLabel
の設定をしていきます。
上と左と右の余白を20px
に設定しました。
赤い点線のマークが実線になった部分が適用されます。ひとつコツがあって、tabボタンを押すと次のフォームに移動できるので使えると速く設定できます。
あと、「Constrain to margin」のチェックが入っていると自動でマージンが入ってしまうのでチェックを外したほうがやりやすいので外すようにしました。
次にユーザー名用のLabel
の設定をしていきます。
上をタイトル用のLabel
から20px
、右と下の余白を20px
、高さを20px
に設定しました。
タイトルの方では高さを定義しませんでしたが、こちらでは高さを定義しています。高さを定義しない場合セルのサイズに合わせて大きさが可変になってくれますが、可変の部分が複数ある場合、他の設定から大きさが1通りに決まらない場合はエラーになってしまいます。タイトルは大きさがばらばらでそれに応じた高さにしたいので可変にして、一方ユーザー名のほうはそこまで長くなくて1行で収まりそうなので高さ固定にしました。
右と下の余白はセルの外枠に対して20px
あけるようにしていますが、上はタイトル用のLabel
から20px
あけるようにしました。このようにどのパーツに対して余白をあけるかどうかは
このように選択することができます。Content View
がこの場合で言うとセルの外枠を表します。
サムネ画像用のImageView
の設定もします。
上をタイトル用のLabel
から20px
、右はサムネ用のLabel
から20px
、下はセルから20px
、縦横比を固定に設定しました。
縦横比を設定するのは「Aspect ratio」にチェックを入れれば大丈夫ですが、現状の縦横比が自動で設定される形になるのでこの後編集する必要があります。
このように、テキトーにのせただけの状態のものが25:22
になっていたので自動ではいってしまっていたので1:1
に編集しました。
加えて、オートレイアウトの設定がここでは並んでいますが、どのパーツに対してどれぐらいあけるかというのは、この部分のSuperView
やLabel
のように書かれていてここで確認することができます。SuperView
は親のView
という意味なのでここではCell
を表します。Super
は親という意味で使われることが多いので覚えておきましょう。
これでオートレイアウトの設定ができました。
青色で位置関係が表されているのでこれはちゃんと設定ができていることを表します。赤色がでてる場合はエラーです。どのような場合にエラーが出るかというと、セルの大きさがどのような場合でも1通りに中身のレイアウトが決まらない場合はエラーになります。
よくあるのが全て高さを固定してしまって特定の大きさのセルでしかレイアウトがうまくいかないみたいなパターンでのエラーなので注意が必要です。
IBOutletの設定
パーツの配置が終わったので次はデータを当てはめるための準備としてIBOutlet
の設定をしていきます。詳しいやり方はTableViewを作成しように書いてあるのでわからない人はこちらを読んで下さい。
このように設定しました
import UIKit
class TableViewCell: UITableViewCell {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var userIdLabel: UILabel!
@IBOutlet weak var userThumbImageView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func bindData(article: Article) {
}
}
ポイントとしては命名を区別しやすいようにすることです。
僕は命名の際に、UILabel
の場合はLabel
、UIImageView
の場合はImageView
というように語尾につけるようにしています。
意識してないうちだとタイトル用のLabel
に対してtitle
と命名しちゃいがちだと思いますが、そうした場合に、このようにタイトルが渡ってきてそれをLabel
にセットする関数を作成したときにタイトルがかぶってややこしくなってしまいます。
func setTitle(title: String) {
title.text = title
}
それを避けるために語尾にLabel
やImageView
のようにつけるようにしています。
BindDataの実装
それではbindData
に各パーツに値を入れていく実装をしていきます。
このように書き換えました。
import UIKit
class TableViewCell: UITableViewCell {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var userIdLabel: UILabel!
@IBOutlet weak var userThumbImageView: UIImageView!
func bindData(article: Article) {
titleLabel.text = article.title
userIdLabel.text = article.userId
setUserThumbImageView(imageUrlString: article.profileImageUrlString)
}
private func setUserThumbImageView(imageUrlString: String) {
guard let profileImageUrl = URL(string: imageUrlString) else { return }
let session = URLSession(configuration: .default)
let downloadImageTask = session.dataTask(with: profileImageUrl) { (data, response, error) in
guard let imageData = data else { return }
let imageimage = UIImage(data: imageData)
DispatchQueue.main.async() { () -> Void in
self.userThumbImageView.image = imageimage
}
}
downloadImageTask.resume()
}
}
setUserThumbImageView
ではURL
から画像を取得してImageView
にセットしていますが、これはapiを叩いてTableViewに表示させるで使ったものとわりと似ていて同じ要領でできます。
このように1つのパーツに対して値を当てはめるコードが複数行に渡る場合はメソッドに切り分けて、bindData
ではそのメソッドを呼ぶようにしましょう。
あとawakeFromNib
とsetSelected
に関してはデフォルトで記述されていましたが不要なので削除しました。
これでXibファイル
の設定は完了です!
レイアウト確認
実際にビルドしてみて確認してみたいところですが、ViewController
の方でセルの高さを50
として設定していて、おそらく小さすぎるのでとりあえず100
で設定を変えてみてビルドしました。
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
このように設定しました。
ビルドした結果このようになりました。
いいかんじにできたと思いきや、よく見るとところどころタイトルが・・・で切れています。UILabel
のデフォルトの設定ではLabel
の大きさに対してはみ出てしまう量の文字列が来たときには最後を・・・となるようになっています。
タイトルのLabel
の高さは、セルの高さが決まってからオートレイアウトで計算されます。
今回で言うと、
(titleLabelの高さ) = (セルの高さ) - (titleLabelの上の余白) - (titleLabelの下の余白) - (userIdLabelの高さ) - (userIdLabelの下の余白)
なので
100 - 20 - 20 - 20 - 20 = 20
となります。
title
のフォントサイズはデフォルトのままで17px
なので1行分しか高さがありませんでした。
なのでセルの高さをとりあえず130
にしてみます。
もう一度ビルドしてみます。
このようにまだ直ってません。
しかし先程と比べて見るとタイトルの上下の余白が広くなったかなという印象です。
実際のLabel
のサイズがどうなっているかはこのように確認することができます。再生ボタンみたいなマークのボタンを押すとこのモードを解除できます。
これからわかるようにLabel
のサイズは2行分ぐらい入りそうな大きさになっていそうな雰囲気ですが1行しか表示してくれません。
これはtitleLabel
の行数が1で設定されているのが原因です。UILabel
ではデフォルトの設定で行数が1になっています。なのでこれを変更することで解決します。
この部分で行数で変更することができます。
コードで指定することもできて、
titleLabel.numberOfLines = 1
のように設定することができます。
もし両方設定した場合はコードのほうが優先されます。
ここで行数を2として設定してもよいのですが、0
を設定することができて、0
の場合はLabel
に渡された文字列の長さに応じて行数が可変になってくれます。なのでここでは0
を設定しました。
もう一度ビルドします。
これで無事タイトルがちゃんと表示されました。
おわりに
今回でXibファイル
のレイアウトを設定することができました。
簡単なレイアウトの表示でしたが、今回のような基本が出来るようになれば、パーツが増えたとしても基本の組み合わせでだいたい出来るので自分でいろいろいじってみてほしいです。
終盤にかけて、タイトルが2行になっても表示されるようにレイアウトを調整しましたが、まだ若干問題点が残っています。
- もしタイトルが3行以上になる場合にまた語尾が・・・になってしまう。
- タイトルが1行のときに余白が大きい気がする。
この2つの問題点が残っているのではないかなと思います。
なので次回はこれらの問題点を解決できるようにタイトルの長さに応じてセルの高さが変わるようにするというのを行いたいと思います。
今回のコードはこちらでも確認できるのでよかったらご確認ください。