初めてのSwift5 その3

TypeAliasを知る

前回のソースコードで、関数ポインタ部分が毎回フル定義していると長くなるので短くする方法があるかなと少し調べました。

すると、C言語で言う「typedef」に当たる「typealias」といったもので定義しておけば、それを使って型を宣言できるみたい。

まあC言語で困れば「voidのポインタ」にして型キャストすればなんでもOKな気楽さはないけども、ね。

前回のソースコードをtypealiasで書き直してみたのが下記の通り。

//  クリックイベント処理
typealias onClick = ( (_ id:frm001ListItem) -> Void )?

こんな感じで型として宣言することができるようです。
使い方は通常の変数と同じように定義してあげるだけ。

    mutating func SetAction( action: onClick = nil )
   {
       //  関数ポインタをセット(Swiftではなんて読んでるのかはまだ知らない)
       _action = action
   }

関数の引数部分がすっきりしましたね。
でも謎なのは、メンバー変数として定義するとエラーがでてしまう・・・。
原因は不明。
だって理論上はフルスペルで定義してもエイリアスで定義しても、コンパイル時にエイリアスコードを展開してするはずだから元のソースコードとかわりないはずなんだけどね。

このあたりがよくわからない。┐(´д`)┌ヤレヤレ
メンバー変数以外をエイリアスに置き換えてコンパイルし実行すると正しく動作しました。
全ソースコードはこれ。

まだ全然なれてません。

//
//  ContentView.swift
//  HogeHoge
//
//  Created by melon on 2020/05/05.
//  Copyright © 2020 melon-group. All rights reserved.
//

import SwiftUI

//  クリックイベント処理
typealias onClick = ( (_ id:frm001ListItem) -> Void )?

//  リストアイテム構造体
//--------------------------------------
//  リストアイテムだけを管理する構造体です。
//  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: onClick = nil )
   {
       //  関数ポインタをセット(Swiftではなんて読んでるのかはまだ知らない)
       _action = action
   }
   //  Swift構造体の初期化処理
   //  コンストラクタのようなものかな。
   init(
       _id: String,            //  セットしたいId
       _icon_name: String,     //  セットしたいアイコン名
       _text: String,          //  セットしたいテキスト
       _action: onClick = 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: onClick = 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()
   }
}

この記事が気に入ったらサポートをしてみませんか?