初めてのSwift5(その2)
リストボックスをつけてみる
ボタンを押してテキストが変わっただけだと、正直つまらないよね。
ってことで今度はここにリストボックスをつけていきたいと思います。
いきなりできちゃってますが、途中経過取るの忘れてました。
夢中になってあーでもない、こーでもないっての繰り返しながらやってたので。
また、ただ初めてまだ2日目なんでこんなもの。
画像はいらすとやさんからお借りしたものを使用しています。
リストボックスにテキスト表示するだけじゃつまらないでしょ?
ということで、Unityでよく作る「リストアイテムに画像もテキストも表示するようなもの」を作ってみたいと思ったのです。
実はこれが結構大変。
知らないからとはいえ、苦労しました。
意地でも専門書買わずにインターネットで調べた知識でできないかが自分のテーマです(笑)
まず、好きな絵をアイコンにしたいと思います。
ここでは「animal_mitsubachi.png」と呼ばれるいらすとやさんからお借りしたものを前提にして話をします。
左側のプロジェクトナビゲーターから、Assets.xcassetsを選択します。
そうすると、右側に「AppIcon」ってのがでてきます。
ここに、用意したイメージファイルをFinderからドラッグアンドドロップを実施して放り込んでください。
こんな感じに。
こんな状態になっていればOK。
また、ここの名前が「リソースファイル名」になるので重要です。
ここの名前と、イメージファイルを扱うソースコードは一致していなければなりません。
ソースコード
今回はソースコードのコメント上に色々記載してあります。
//
// ContentView.swift
// HogeHoge
//
// Created by melon on 2020/05/05.
// Copyright © 2020 melon-group. All rights reserved.
//
import SwiftUI
// リストアイテム構造体
//--------------------------------------
// リストアイテムだけを管理する構造体です。
// Viewを継承しています。
// ここにリストアイテムで必要な情報を管理します。
// ※VB6のようなネーミング規則にしてみました
struct frm001ListItem: View {
// リストボックスClickイベント用
// リストボックス押したら、コールバックで呼び出す際の関数を保管
// する、変数。C言語でいう関数ポインタのようなもの。
// Windowsの例でスレッド開始する時の型宣言みたいなもんです。
// ----- VC++のイメージ -----
// typedef unsingd (WIN_API *TFUNC)( LPVOID );
//
var _action : ( (_ id:frm001ListItem) -> Void )?
// アイテムID。何が押されたか知りたいでしょ?
var Id: String
// リストボックスの表示用テキスト
var text: String
// 画像のリソース名
var icon: String
var body: some View{
// Horizon Stack。
// 水平の画面レイアウトを決めるためのオマジナイです。
HStack
{
// アイコンが指定されていればアイコンを表示するようにします。
if(icon.count > 0){
// アイコンの設定。
// ここでリソースのイメージファイル名をセットしています。
// 下のはおまけ。
// 画像丸めたりサイズ調整したりしているだけで調べて
// 貰えればこのあたりはわかるので説明は省略します。
Image(icon)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100)
.clipShape(Circle())
.overlay(
Circle().stroke(Color.orange, lineWidth: 4))
.shadow(radius: 10)
}
else{
// アイコンが指定されていない場合は無視します。
}
// リストアイテムにボタン機能をつけます。
// リストが選択されたら何かの処理したいよね?それです。
// 調べたんですがC#のようなリスナー追加のようなものがありません。
// ボタンをその代わり使うのが多いんだとか。
Button(
action:{
// ここでセットした関数を呼び出しています。
self._action?(self)
}
){
Text(text).frame(width: 200)
}
}.padding().aspectRatio(contentMode: .fit)
}
// リストアイテムを選択した時の処理をさせたい関数をセットする関数
// ややこしいことしているのは、呼び出し元の関数に紐付けたいから。
// ライブラリ前提で考えるとこうしたほうが使い勝手がいいのです。
mutating func SetAction( action: ( ( frm001ListItem ) -> Void )? = nil )
{
// 関数ポインタをセット(Swiftではなんて読んでるのかはまだ知らない)
_action = action
}
// Swift構造体の初期化処理
// コンストラクタのようなものかな。
init(
_id: String, // セットしたいId
_icon_name: String, // セットしたいアイコン名
_text: String, // セットしたいテキスト
_action: ( ( frm001ListItem ) -> Void )? = nil )
{
// 構造体のメンバーにセット
self.text = _text
self.icon = _icon_name
self.Id = _id
SetAction(action: _action)
}
}
// リストボックス本体管理構造体
// 上記で定義したリストアイテムをここで管理します。
struct frm001List: View {
// リストアイテムそれぞれ管理するための、配列
var lstItems: [frm001ListItem]
var body: some View{
// これは垂直に画面管理するおまじない
VStack
{
// SwiftUIのリストに追加します
List
{
// 繰り返し初期化時に作成したリストアイテムを画面にセット
ForEach( 0..<lstItems.count )
{
num in
self.lstItems[num]
}
}
.aspectRatio(contentMode: .fill)
.frame(width: 300.0, height: 100.0)
}
}
// 初期化処理
// ここでリストアイテムをセットします。
// 実際はDBやファイルから取り出したり、サーバから値取得してセットしたり
// することになるかと思います。
init(_action: ( ( frm001ListItem ) -> Void )? = nil){
// 空の実態配列を作成します
lstItems = [frm001ListItem]()
// 配列にリストアイテムを作成してセットします
lstItems.append(
frm001ListItem( _id: "001", _icon_name: "animal_mitsubachi",_text: "これは一行目です", _action: _action)
)
lstItems.append(
frm001ListItem( _id: "002", _icon_name: "animal_mitsubachi",_text: "これは二行目です", _action: _action)
)
lstItems.append(
frm001ListItem( _id: "003", _icon_name: "animal_mitsubachi",_text: "これは三行目です", _action: _action)
)
lstItems.append(
frm001ListItem( _id: "004", _icon_name: "animal_mitsubachi",_text: "これは四行目です", _action: _action)
)
lstItems.append(
frm001ListItem( _id: "005", _icon_name: "animal_mitsubachi",_text: "これは五行目です", _action: _action)
)
}
}
struct ContentView: View {
@State var textToUpdate = "こんにちは、世界!"
var body: some View {
// 垂直に画面管理するおまじない
VStack {
// リストボックスをセットします
frm001List(
// リストアイテムを選択するとOnClickが呼び出されます。
_action: OnClick
).frame(width: 300, height: 300)
// テキスト部分
Text(textToUpdate)
.padding()
// ボタンの部分
Button(action:
{
self.textToUpdate = "ボタンが押されました"
}
) {
Text(/*@START_MENU_TOKEN@*/"押してね!"/*@END_MENU_TOKEN@*/)
.frame(width: 200.0, height: 20.0)
}
}
.padding()
}
// リストアイテムが選択されたらこの関数が呼び出されます
func OnClick(item: frm001ListItem){
// テキストに表示します
self.textToUpdate = item.text
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
実行
実行直後はこんな感じ。
リストアイテムを選択するとこんな感じ。
きちんと機能していますね!
ということで、引き続き実施していこうと思います。