サンプル勉強会 1-2 実装
はじめに
前回、ForceSketchを題材にソースコードリーディングをしたお話をしました。今回は、そのソースコードリーディングの結果を踏まえて、サンプルと同じ機能の実装に挑戦しました。
勉強会の進め方
実装フェーズ(1h)と共有フェーズ(0.5h)の次回配分で進めます。各フェーズで行うことは下記の通りです。
- ソースコードリーディングフェーズ
- 黙々実装
- 共有フェーズ
- どういう方針で作ったか
- どこでつまずいたか
結果
結論としては、ちゃんと動かせるところまで実装できた人はいませんでした。理由は、サンプルが古く、swift2からswift4に対応させる必要があったことに加えて、なぜか imageAccumulator.setImage()
で落ちるという現象があったからです。
他のメンバーの共有フェーズ時の議論内容
- Fix祭りだった
- swift2からswift4に直すのをXcodeがサポートしてくれる機能を使いまくったという話です
- selector利用時に、対象のメソッドに@objcをつける必要がある
- delegateメソッドの第一引数に
_
が入る - NSを使わない
UIGraphicsGetCurrentContext
の扱い方が変わったUIGraphicsGetCurrentContext
のインスタンスをメソッドに渡してたのが、このインスタンスのインスタンスメソッドを使うようになった- オブジェクト指向っぽくなった
- swift2からswift4に直すのをXcodeがサポートしてくれる機能を使いまくったという話です
- 実行してしばらくすると落ちる
Thread 3: EXC_BAD_ACCESS (code=1, address=0x28)
imageAccumulator.setImage(gaussianBlur.valueForKey(kCIOutputImageKey) as! CIImage)
どうもここのようだ
私の共有フェーズ時の議論内容
- 詰まったところは同上
- 設計方針
- タッチと描画を管理するロジックをViewControllerからカスタムViewに分離
- Github
後日調査結果
- iOS11だと落ちる、iOS10やiOS9だと落ちない
- iOS10やiOS9のシミュレーターでの操作はForceSketchにあるgifのように綺麗にならない
- 実機の確認はしていない
終わりに
今回は、ForceSketchを題材にソースコードリーディングをした結果を元に、実装に挑戦した内容を記しました。ターゲットとしたサンプルコードが古かったため、動作させるところまで到達できませんでした。次回以降は、なるべく最近のサンプルを使おうと思います。ただし、Swift3から関数引数が省略されなくなったはなしなど、swift2からswift3への大幅な変更に対して手を動かしてみるという経験ができたのはよかったと思います。
サンプル勉強会 1-1 ソースコードリーディング
はじめに
前回お話ししたiPhoneアプリのサンプル集を用いた勉強会について記します。今回は、シンプルでコード量の少なそうなForceSketchを題材にソースコードリーディングをします。
勉強会の進め方
ソースコードリーディングフェーズ(1h)と共有フェーズ(0.5h)の次回配分で進めます。各フェーズで行うことは下記の通りです。
- ソースコードリーディングフェーズ
- 下記の項目を意識して読む
- 実装のキモ
- いいねっていう実装
- 微妙だと思う実装
- 知らなかったという実装
- 下記の項目を意識して読む
- 共有フェーズ
- ソースコードリーディングでまとめたものを共有
結果
今回のサンプルは、swiftのバージョンが古かったため、動かしながら確認ができず、皆苦戦していた。適当にサンプル集のgifだけで選んでしまったのがよくなかった。
ただ、ViewController.swiftのみで完結しているので、初めての試みとしてはちょうどよかった。自分で実装するときにどう分離するかという練習にできそうなので。
他のメンバーの共有フェーズ時の議論内容
- 15行目などの
!
(暗黙的アンラップ型:Implicitly Unwrapped Optional)ってどうなの?- 固定の引数のため一度動作確認取れていれば動くということか?
- 仮に、この絶対性がなかったとしてもサンプルということで、アプリが落ちればいいだけとも考えられる
- インスタンス利用時に必ず初期化されることが保証できる場合なら使うと便利らしい
- 固定の引数のため一度動作確認取れていれば動くということか?
- 54行目の
UIEvent?
でなぜ?(オプショナル型)- UIKitの仕様
- 昔からの名残があるんじゃないかな?
- 56行目のような
guard
の使い方いいね - 75行目の
coalescedTouch.force != 0
の意味がぱっと見でわかりにくい- extensionでメソッド作るといいかも
- 75行目の三項演算子が長くてわかりにくい
touchesEnded(_:with:)
だけでなくtouchesCancelled(_:with:)
も実装したほうがいいのでは?- ちゃんとしたアプリであれば、仕様の検討したほうがいい
- 51行目などの
touches.first?
は、このスコープではtouchって絶対あるのだろうから?
じゃないのでは?Set
クラスの仕様- 空だったらnilが返ってくる
step()
の使われ方がよくわからない- CADisplayLinkでUnityのUpdate()みたいなことしてるだけ
- "CADisplayLinkは画面のリフレッシュレートと同期して描画させるタイマーオブジェクトです。"
- CADisplayLinkでUnityのUpdate()みたいなことしてるだけ
私の共有フェーズ時の議論内容
実装のキモ
locationInView(view)
で画面(UIViewControllerのview)での座標を掴んでるlet lineWidth = coalescedTouch.force != 0 ? (coalescedTouch.force / coalescedTouch.maximumPossibleForce) * 20 :10
で圧力から線の太さ決めているのだろうimageView.image = UIImage(CIImage: imageAccumulator.image())
- CIImageは忘れたが、UIImageに変換できて、CIFilterとかは画像処理になるんだろうな。直接画像をプログラムで処理してるんだろう
CADisplayLink
でUnityのUpdateみたいになるんだろう
知らなかったもの
- 画像処理系(昔使ったけど忘れたやつ)
- CIFilter
- CIImageAccumulator
- UIGraphicsBeginImageContext
- UIGraphicsGetCurrentContext
- CGContextSetLineCap
- その他(初めて見た)
- CADisplayLink
- coalescedTouchesForTouch
問題点
- IB使ってない
let imageView = UIImageView()
- autolayoutをちゃんと使ったほうがいい
- let lineWidth = coalescedTouch.force != 0 ? (coalescedTouch.force / coalescedTouch.maximumPossibleForce) * 20 :10
- マジックナンバーたくさんでわかりにくい
- classに分離したい
- CIFilleterのname引数に文字列指定がタイポとか怖い
その他
- 描画処理系で、Begin, Endで囲むというのはよくみる気がする。
- CGContextRef型のcgContextを取得し、それを関数の引数に渡して実行する感じがc言語っぽさを感じる
終わりに
今回は、ForceSketchを題材にソースコードリーディングをした際に、皆で議論した内容を記しました。一部、推測で書いている部分もあるため、間違っている情報もあるかもしれません。もしご存知の方がいらしたらご指摘いただけたら幸いです。
次回は、今回のソースコードリーディングを元に自身らで実装します。
ViewDidLayoutがいつ呼ばれるのか知りたい
みなさんこんにちはtakenoteです。 この二日三日で突然暖かくなって、家から出た時に幸せを感じるようになってきました。
朝明るくなるまでの時間も徐々に早くなってきて、冬の終わりを肌で感じます。良いですよね、春。花粉がなければ。
今日のお話
StoryBoard上で入れ子構造のレイアウトを作った時、どれがどの順番で呼ばれるのかを調べてみました
前提
僕が今作っているレイアウトはこんな感じです。
ViewController AはStoryBoard上に直接CustomViewを配置しています。 ViewController BはGameViewControllerのViewDidLoad上でViewControllerCを中味に実装します。 (ほんとは他にも細かいUIPartsがありますが、ここでは簡略化のため記載を省略しています)
絵で示すとこんな感じです。
結論
呼ばれるのは以下のような順番でした。
1. GameViewControllerのinit
1.1. ViewControllerAのinit
- 1.1.1. CustomViewのxibファイルからの生成
1.2. ViewControllerAのviewDidLoad()
1.3. ViewControllerBのinitとviewDidLoad()
2. GameViewControllerのviewDidLoad()
2.1. ViewControllerCのinit
- 2.1.1. CustomViewのxibファイルからの生成
2.2. ViewControllerCのviewDidLoad()
3. GameViewControllerのViewDidLayoutSubViews()
3.1 ViewControllerBのViewDidLayoutSubViews()
3.2 ViewControllerBのViewDidLayoutSubViews()
詳細
今回整理してみて、大切だと思ったのは以下の二つだと考えています。
1. UIViewControllerのviewDidLoadはUIViewControllerのインスタンスが生成された後に呼ばれる
2. もしそのViewControllerが内部にViewを持つ場合は、自身のインスタンスを生成する過程でその子Viewを生成する
こうして記事をまとめていると至極当たり前のことを書いているような気がしてしまいます。 なんで view did loadな関数なわけですから、自分の持っているviewは全てLoadされているわけですよね、 なので呼び出されている順番自体は大きい枠から順番なのですが、 実際に完了していく順番(viewDidLoad()が呼ばれる順番)は中に入っている子Viewからになるわけなんですね。納得したなぁ。
終わりに
この順番を把握していなかったおかげで、Viewをアップデートしたはずなのに毎回初期化されてしまって大変な目にあってしまいました。 初期化のmethodをどこに入れるべきか、とかこの数値を参照するためにはこういう順番だからここできちんと定義しておかなきゃ、とか、呼ばれている順番をきちんと理解するのは非常に大切なことだなーと実感しました。 次回はこの順番を把握するために調べたdebugの方法、効率のよいLog出力についてまとめられれば良いなと考えています。
iPhoneアプリ開発初心者の友人たちとサンプル集を使った勉強会をしてみます
はじめに
こんにちは。toosaaです。今日は、最近アプリ開発に興味ある友人たちとサンプル集を使った勉強会をやり始めたので、この勉強会についてお話しします。今回は、勉強会の進め方や目的について話し、次回以降で各会の様子について書こうと思います。
メンバーについて
- アプリ開発経験者
- toosaaを含めて二人
- 未経験者
- 三人
未経験者の三人は、プログラミングの経験はあるが大規模な開発の経験はあまりないという感じです。なお、Swiftについては、皆で「Swift実践入門」を輪講形式で読みあったので、皆最低限のことは理解しています。
勉強会の進め方
毎週1.5hほど集まって、サンプルアプリのソースコードを読むか実装をします。サンプルに関してはTeckAcademyさんがまとめてくれているものから選びます。
今回の勉強会では、いろんな人のいろんな実装をみたり、とにかくたくさん作ってみるという経験を優先したいので、次に記述するように1週目にソースコードを読み、2週目に実装するのを繰り返します。
1週目
やり方
- サンプルのソースコードを読む(1h)
- 以下の点についてまとめる
- 実装のキモ
- 勉強になった実装
- 知らなかった機能やClass
- よくない・直すべき点
- 以下の点についてまとめる
- 読んだ結果を共有する(0.5h)
- どのような順で読んだか
- まとめた個所の共有
ねらい
- 人のコードを読む練習する
- 単純に勉強になる
- 今後、あとからプロジェクトに参加するというときに役立つ
- 精読ではなく多読で経験を積む
- いろんな人がいろんな書き方をするんだなーというのを実感する
- 精読に関しては、この勉強会ののちに各自で行えば良い
- 他の人のコードの読み進め方を知る
- 普通だと聞かない知識なので、私が聞いてみたいだけです笑
- 他の人が重要だと思うことの差異や反対に一致する点を知る
- 他人が気にする点と自分が気にする点をはっきりとさせる
2週目
やり方
- 1週目で読んだソースと同じ機能を実装する(1h)
- 参考にしつつ自分なりに書き換えられるとなおよし
- 実装した結果を共有する(0.5h)
- どこまで書いたか
- どやりポイントはどこか
- 苦労したところはどこか
ねらい
- 実装の訓練
- 短い時間という追い詰められた状態でも書く訓練をする
そういう力が求められることもあるだろう
- とりあえず作ってみることを経験する
- アプリ作りたくなっても、リリースできるようなものは、いきなり作れないし、そもそも思い付かない
- サンプルアプリを真似ることで、とりあえず作ってみたという経験を積む
- 自分で組んでみると思わぬところで躓くのをたくさん経験する
- 短い時間という追い詰められた状態でも書く訓練をする
終わりに
今回は、勉強会の進め方についてだけ触れました。次回は、1回目の様子についてお話ししたいと思います。
kituraのコードをIBM Cloud(Bluemix)のDevOps経由でdeployする
はじめに
こんにちは。toosaaです。 今回は、Kituraで書いたコードをIBM Cloud(Bluemix)上のホスティングサーバーに、同じくIBM Cloud(Bluemix)のDevOpsを利用して自動deployするやり方について解説します。 ただし、今回はチュートリアルなど公式のドキュメントを元に学んだものではなく、いろいろ試した結果うまくいった方法なので、ご注意ください。
IBM Cloud(Bluemix)にログインする
- sign upする
- sign upに必要なものは頑張ってください
ツールチェーンを作成する
- 左上のハンバーガーメニューでDevOpsを選択する
- 地域を適当な場所にする
- 右上のツールチェーンボの作成ボタンを押す
- Cloud Foundryアプリの開発を選択する
- Delivery Pipelineを選択する
GitリポジトリをGitLabに指定する
- ツールの追加でGitLabを追加する
Delivery Pipelineを作成し、自動デプロイできるようにする
- ツールの追加でDelivery Pipelineを追加する
- 作成したDelivery Pipelineを選択する
- ビルドステージを作成する
- 入力を自身のgitリポジトリにする
- ジョブを追加する
- ビルダータイプはシンプル
- デプロイステージを作成する
- 入力をビルド成果物にする
- ジョブをデプロイにする
以上の流れでGitLabにKituraで書いたコードをpushしたら自動でサーバーにデプロイされるようになりました。 urlはデプロイステージの作業完了後にリンクが貼られるので、それを参照してください。
おわりに
今回は、Kituraで書いたコードをIBM Cloud(Bluemix)上に自動デプロイする方法を書きました。基本、用意されたツールを使うだけなのでポチポチすればできます。ただ、GitLabとかに変えようとするとデフォルトでできるリポジトリを消したりと手間がかかります。次回は一旦kituraから離れようかと思います。
Viewを出したり消したりしたい
ゲームの設定画面を作りたい
はじめに
こんにちは、takenoteです。 今回もボードゲームのアプリを作成してみながらSwiftの勉強をしていきます。
どこから手をつけていいものかわかりませんが、 とりあえずゲームプレイ前の初期設定画面の作成を試みます。
今回の内容
以下のような画面の作成を目指します。
この画面の実現のために以下の3つの項目を今回の記事でお話しします。
UIのゴール
・人数を決めるためのギミックを用意する
・名前を入力するためのCustomViewを作成する
・人数に応じてCustomVIewを出したり消したりする
またこの画面において期待される機能は以下のようなものです。
動作のゴール
・上にあるPickerViewを回すと画面下部のPlayerName入力用のCustomViewが出たり消えたりする
・下にあるGameStart!のボタンを押すと"Player人数", "Playerの名前"の情報が次のViewControllerに引き継がれる
今回は画面遷移のところまではいかないため、動作のゴールとしては一つめのみを記載します。 では本題にいきましょう。
人数を決めるためのギミックを用意する
今回はPlayer人数が2~6人だったこともあり、選択するとプルダウンメニューが出て、人数を選ぶUIがよいのかなーとか思ってました。 ちなみに普段はAndroidユーザーです。
人数を選択するためのUIには様々な種類が存在しているかと思います。僕がパッと思いついたのは以下の3つでした。
1. プルダウン型の選択UI
2. ラジオボタン
3. ロールみたいになってるやつ
あれなんですね、、、iOSって標準ライブラリでプルダウン型のUIもラジオボタンも用意されてないんですね。。。 実現することは普通に可能なようで、少し調べて見たら以下のような記事が出てきました。
一方でロールみたいにして選択するあれはPickerViewというもので標準的に搭載されているみたいです。
使い方は調べてみるとたくさん出てきますね。
という訳で今回は人数選択にこいつを用いることにします。 人数選択のUIにこだわるのは一周回ってきてデザイン面のブラッシュアップをしたくなった時にすることにします。
ちなみにこのPickerView、本来はいくつか列を持って組み合わせで選択ができるようです。 今現在は人数しか入力させていませんが、特殊ルールやモード選択などができるようになれば、その時にはもっと有効に使える日がくるかもしれません。
名前を入力するためのCustomViewを作成する
以下の方法でLabelとTextViewが横に並んだだけのCustomViewを作成しました。
1. レイアウトを決めたxibファイルを作成する
2. 対応するswiftファイルを作成する
3. StoryBoard上に配置する
基本的には"X code CustomView"で調べるとすぐに実現方法がわかるのですが、 一点だけ見落としてしまい、なかなか前に進むことができないことがありました。
Viewを選択した時に出てくるIdentity Inspectorではなく、File's Ownerを選択した時に出てくるIdentity InspectorのところにあるClassに対応するのところにswiftファイルの名前を入力してください。
やらしいことにViewを選んでも全く同じような画面が表示されてしまうので、できたできた、と思って次に行ってしまうんですよね。
swiftファイルの中身はこんな感じでとてもシンプルです。
import UIKit class TestCustomView: UIView { @IBOutlet weak var leftLabel: UILabel! @IBOutlet weak var textView: UITextField! //コードから生成したときに通る初期化処理 override init(frame: CGRect) { super.init(frame: frame) self.commonInit() } //InterfaceBulderで配置した場合に通る初期化処理 required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.commonInit() } fileprivate func commonInit() { //MyCustomView.xibファイルからViewを生成する。 //File's OwnerはMyCustomViewなのでselfとする。 guard let view = UINib(nibName: "TestCustomView", bundle: nil).instantiate(withOwner: self, options: nil).first as? UIView else { return } view.frame = self.bounds view.autoresizingMask = [.flexibleHeight, .flexibleWidth] self.addSubview(view) } }
人数に応じてCustomVIewを出したり消したりする
「数字を設定するためのPicker」と「出したり消したりするCustomView」の用意が終わりました。 今から用意したCustomViewを出したり消したりします。
customViewの表示を出したり消したりするためには、Viewのもつプロパティであるalphaをいじるようです。 今回はPickerViewの値に応じて数を変更させたかったので、PickerViewのメソッド内に処理を入れています。
class InitialSettingViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource{ //上のドラムと、その上下に表示する文字Labelです @IBOutlet weak var pickerView: UIPickerView! @IBOutlet weak var onPickerLabel: UILabel! @IBOutlet weak var belowPickerLabel: UILabel! @IBOutlet weak var goButton: UIButton! //名前を入力するためのTextViewです。左にあるLabelとセットです。 @IBOutlet weak var nameInputView0: TestCustomView! @IBOutlet weak var nameInputView1: TestCustomView! @IBOutlet weak var nameInputView2: TestCustomView! @IBOutlet weak var nameInputView3: TestCustomView! @IBOutlet weak var nameInputView4: TestCustomView! @IBOutlet weak var nameInputView5: TestCustomView! let numOfPlayer: [[Int]] = [[2,3,4,5,6]] //ドラムに表示する数字です。ドラムは列を増やせるため今後ちょっとしたルールを追加するのにも使えます。 var inputNameViews: [TestCustomView] = [] //TextViewを一括操作するための配列です var defaultNumOfPlayer: Int = 2 //表示するTextVIewの数の初期値 override func viewDidLoad() { super.viewDidLoad() goButton.setTitle("Game Start!!", for: .normal) onPickerLabel.text = "プレイ人数を選択してください" belowPickerLabel.text = "人でプレイ!!" inputNameViews = [nameInputView0, nameInputView1, nameInputView2, nameInputView3, nameInputView4, nameInputView5] var i = 1 for view in inputNameViews{ if(i > defaultNumOfPlayer){ //ドラムで選択された数値以上のTextViewは表示されない。初期設定は2人にしたのでそれ以上のPlayerの入力窓は非表示にした。 view.alpha = 0 } view.leftLabel.text = "Player" + i.description + ": " view.textView.placeholder = "Input your name" i += 1 } } //コンポーネントの個数を返すメソッド @IBOutlet weak var numOfPlayersPicker: UIPickerView! func numberOfComponents(in pickerView: UIPickerView) -> Int { return numOfPlayer.count } //コンポーネントに含まれるデータの個数を返すメソッド func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return numOfPlayer[component].count } //データを返すメソッド func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return numOfPlayer[component][row].description } //データ選択時の呼び出しメソッド func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { defaultNumOfPlayer= numOfPlayer[component][row] //ここで取れた値を元に、CustomViewのalpha値を変更する。 showOrHideNameInputView(component: component, row: row) } //CustomViewを出したり消したりするmethod func showOrHideNameInputView(component: Int, row: Int){ for i in 0 ..< numOfPlayer[component][row]{ inputNameViews[i].alpha = 1 } for i in numOfPlayer[component][row] ..< inputNameViews.count{ inputNameViews[i].alpha = 0 } } }
これで実装完了です。 ViewControllerを超えた値の持ち越しは次回に回します。
読んでくださってありがとうございました。
ゴキブリポーカーでSwiftの練習
とりあえず挨拶回です
はじめに
こんにちは、takenoteです。toosaaのマブダチです。
最近toosaaと一緒(?)にSwiftの勉強のためにボードゲームを作っています。基本的にProgramingは初心者、かつSwiftも初めて触っているため作成にあたっては何度もつまづいてしまっています。 このブログでは「せっかくなのでやったことをアウトプットしつつ振り返っていこう」ということを目的に、備忘録や自己レビューのために詰まってしまったポイントの「こんな風にしたら解決したよ」を共有させてただきます。
なお作成するボードゲームはオリジナルではなく、既存のものを模倣させていただきます。Swiftの基本文法ですらおぼつかないのに新ゲームなんて作っていたらしっちゃかめっちゃかになってしまうからです。
今回勉強のために作成してみるボードゲームはこちらです。
・誰でも理解できるシンプルなルール
・登場するアイテムがカードだけ(カウンターやボードなどがない)
・ちょうど最近みんなでやって盛り上がった
など、いくつかの点でプログラミングの練習題材としてもってこいだと考えてました。結構有名なゲームなので存在を知っている方も多いのではないでしょうか。 ブログの本題からずれてしまうためゲームの詳細な説明などはしませんが、記事が無事進んでいけば、ルールについて触れることもあるかと思います。 なのでゲームの全貌についてはじわじわ知っていただけるかと思います。すぐに知りたい人は友達を探してLet's Play
ブログの進め方的
平日の朝晩にちょっとずつ作業して、休日にそれをブログに起こせればいいなーと思っています。 なので1週間に一本の記事が目下目標かなというところです。
技術的な記載はほとんどリンクを貼って参考ページを元にしながら、 実際にこんな風に書いて見ました、をどんどん載せていければよいなーと思っています。
初回である今回は「こんな感じに進めていくよ!」を書いて終わりにします。
全体的な構成イメージとか
今回は技術的なことは全く触れずに、これからどんなアプリを作成していこうかの考えだけを書いていこうと思います。 UIはこんな風にしたいよ、とかこういうポリシーで作るよ、とかそんなところです。
全体のUIイメージ
ザクっと以下のような画面構成でアプリを作成しようと考えています。
この画面イメージは、アプリ作成にあたっては以下のような項目に気をつけたいと考えたからです。
・常に情報が画面に表示されていて、選択/決断の時にいちいち確認に画面を閉じたり戻ったりしない
・一つのスマートフォンにゲームを入れておけばみんなで楽しめる
一番目を達成するために、真ん中の部分に「他のPlayerとかの情報」を表示する土地を大きくとりました。 また二番目と達成するために「今今どういう状況で何が起こったか」を表示しておく「ゲーム状況のLog」表示用の土地を用意しました。
画面遷移について
ゲーム全体を通して「オープニング画面」⇨「初期設定画面」⇨「ゲーム画面」で終わりにしようと考えています。 またゲームプレイ中の画面の遷移は0にしたいなーと考えています。その心は「常に情報が画面に表示されていて、選択/決断の時にいちいち確認に画面を閉じたり戻ったりしない」に基づいています。
どうやって実現するのかは、またおいおい考えます。
アプリ設計について
設計指針については次回に回そうと思います。 というか考え方がいまいちわかっていません。 あまりこの辺に詳しくないため、使いまわせないクラス構造とか見たことないデザインパターンとかFat ViewControllerとか多発すると思います。
できるだけ考えたことは素直にアウトプットして、後日新しく学んだことは追記する形で自己レビューしていければいいなーと考えています。
以上、とりあえずこんな感じで作りたいな、という項目でした。
終わりに
今までは一人でこそっとアプリの勉強とかしていたので、こんな風に人に公開しながら進めたり 友人と一緒にやったりという体験にとてもワクワクしています。
賢いやり方とかいっぱい勉強できるでしょうか。 自分の成長のために何かためになることを得られればと思っています。
ついでにどなたか皆様のためにもなればこれ幸いです。
とりあえず継続を目標にやっていくのでどうぞよろしくお願いいたします。