国旗の使い方2
これの記事です。12/21日分
前回作った255枚の国旗のPDFをCocoaアプリケーションから使えるようにします。
PDFのロード
適当にiOSアプリケーションプロジェクトを作ります。Single View Applicationで。
使う
全部の.pdfファイルをAssets.xcassets
にドロップします。私のMacBook Airはここで固まります。
pdfを配置できたので、ImageView
を適当にViewに配置しましょう
コントローラとImageViewを関連づけます。
とりあえずセントビンセント及びグレナディーン諸島でも出しておきます。
ViewControllerのviewDidLoad()
でImageViewにセントビンセント及びグレナディーン諸島を設定します。
Assetsにあるイメージはコード保管でサムネイル出てきます。素敵ですね。
ビルドしてSVGからPDFに変えたセントビンセント及びグレナディーン諸島がImageViewでちゃんと表示できるか確認します。 軽いとはいえAssetが255もあるのでビルドにそれなりに時間がかかります。
enumで扱う
大量の画像があるので、全部表示したい!みたいな時はAssetの名前指定ではなく、enumで扱うと楽です。
imageViewにセットする時はAssetの名前をname:
パラメータをString指定しなければならないので、enumの要素に対してStringが返ってくると楽です。
Swiftだとこんなふうにかけます
enum CountryKey: String { case JP = "Japan" } print(CountryKey.JP.rawValue) // "Japan\n"
いちいち文字列割り当てなくても、何も書かなければ要素のキーの文字列が返ってきます。 要素のキーは大文字開始が推奨されてます。
enum CountryKey: String { case JP } print(CountryKey.JP.rawValue) // "JP\n"
国旗が沢山あるのでこっちの方が楽そうでいいですね。
enumのcase文はPDFを作った時のrubyスクリプトgenerate.rb
で作ってもらうことにしましょう。
さらに、swiftのenumは全要素取得(Javaで言う所のvalues)もありません。こんなふうに実装します。
enum CountryKey: String{ case AD //国名全部 case ZW static func fromIndex(at: Int) -> CountryKey { switch at { case 0: return .AD case 1: //全部実装 default: return .ZW } } static func length() -> Int { return CountryKey.ZW.hashValue + 1 } static func values() -> [CountryKey] { var natural: [Int] = [] for i in 0...CountryKey.length() - 1 { natural.append(i) } var values: [CountryKey] = [] repeat { values.insert(fromIndex(at: natural.popLast()!), at:0) } while natural.count > 0 return values }
国旗が255もあるので、fromIndex(at:)
の中身もenumの要素のキーcase
と同じようにgenerate.rb
に作ってもらうことにします。
ただし、Swiftのenumの要素に-
ハイフンは使えません。
また、今生成できているPDFはSVGの元のファイル名に従って小文字の名前を持ってます。enum要素名の推奨は大文字始まりです。
ということで、今のPDFのAssetの名前を直接enumのcaseにするのはだめっぽいので、
rubyでAssetの名前を大文字、ハイフンをアンダースコアに変えます。 generate.rbをこんな風に変えました。ゲロみたいにネストしてますがAdventCalendarの投稿タイムリミットが迫っているのでご勘弁を
require "prawn" require "prawn-svg" src = "./node_modules/flag-icon-css/flags/4x3/" dist = "./dist/" swift = "./swift/" Dir.mkdir(dist) unless File.exists?(dist) Dir.mkdir(swift) unless File.exists?(swift) i = 0 open(swift + "enum.swift", "w") { |e| open(swift + "switch.swift", "w") { |s| Dir.entries(src).select { |f| if File.extname(src + f)==".svg" name = File.basename(src + f, ".svg").upcase.gsub(/-/,"_") e.puts("case " + name) s.puts("case " + i.to_s + ":") s.puts(" return ." + name) i += 1 Prawn::Document.generate(dist + name + ".pdf", page_size: [600, 800], page_layout: :landscape) do svg IO.read(src + f), position: :center, vposition: :center, width: 800, height: 600 end end } } }
綺麗に生成できました。真面目にswiftのenum生成してもいいのですが、投稿タイムリミットが迫ってるので普通にコピペします。
CollectionViewで見せる
投稿のタイムr ので適当にUICollectionViewをレイアウトします。
ViewControllerを書きます。CollectionViewのnumberOfItemsInSection
でCountryKey.length()
を返します。
cellForItemAt
でCountryKey.fromIndex
がいきてきます。
import UIKit class ViewController: UIViewController { @IBOutlet var collectionView: UICollectionView! override func viewDidLoad() { super.viewDidLoad() self.collectionView.dataSource = self } } extension ViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return CountryKey.length() } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) let imageView = cell.contentView.viewWithTag(1) as! UIImageView let image = UIImage(named: CountryKey.fromIndex(at: indexPath.row).rawValue) imageView.image = image return cell } } extension ViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let width = self.view.frame.width / 2 let height = width / 4 * 3 return CGSize(width: width, height: height) } }
collectionViewLayout
で国旗の比率に合わせたCGSizeを返すと良しです。
ああ間に合った。