【徒然iOS】気ままにUIKit〜コラム 一旦、これまでの記事振り返り・まとめ〜
概要
で最後に軽く書いたんだけど、
💃UIViewControllerに組み込む各パーツを使った標準機能は網羅できた🕺
ので、ここまでの気ままにUIKitで身につけて知識や作法のまとめをカキコwww
⒈前処理→本処理→後処理を癖づける。
これまでの記事構成
①前準備(バックアップと新規のビューを追加)
↓
②本題(ビューに今回の機能を作成・追加)
↓
③後処理(シミュレータなどで実行、ブラッシュアップ)
て手順でAutoLayoutが出てきた以降はやってる手順なんだけど、どんな簡単な機能の追加、改修、削除などの変更を加える場合でも必ずやる理由は簡単で、
コンストラクションの基本
①前準備=バックアップを取る:問題が起きた時にすぐに元に戻せるようにしておくため。
↓
②本題=今回の機能を組み込む:ここで新しい機能なりを追加
↓
③後処理=機能がちゃんと期待値どおり動いているかを改修直後に確認・検証するため。
で、記事を、
実際に動かしながら読んで、自然に身につけて欲しいから
関連記事:
この基本を、ちょっとコードが書けて、ひととおりのアプリが作れたり、世間で持て囃される資格や学校を出たくらいで、
「俺クラス」とか「私レベル」とかって言ってる人
ほど、基本を大事にしないから疎かにしがち。そういう人にいざ、改修なんかをやらせると、
簡単だと思い込んでバックアップも
取っておらずにいざやったらバグだらけ、、、
↓
簡単なコードだと思って十分な検証もしてないので、
ちゃんと実機で動かない
↓
バックアップも取ってないから元に戻そうにも戻せない
↓
涙目😭
なんてことをやりがち。
実際にそんな人を今までの現場で山ほど見てきた💦
型なしと型破りは違う
💃本当の職人は、どこまでも基本に忠実🕺
*こういう人の詳細は、
で、面白おかしくまとめてます〜〜〜基本を疎かにしたり、開発哲学のない人がどういう末路を辿るのか
興味がある人は読んでみてね。
⒉暗記科目じゃない
今までの記事で、必ずサイト記事のコード
例(変更前)
//
// ViewController.swift
//
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var testLabel: UILabel!
//最初からあるメソッド
override func viewDidLoad() {
super.viewDidLoad()
}
//タップ時の呼び出しメソッド
@IBAction func tapLabel(sender: UITapGestureRecognizer) {
//ラベルの背景色をランダムな色に変更する。
testLabel.backgroundColor = UIColor(red: CGFloat(drand48()),
green: CGFloat(drand48()),
blue: CGFloat(drand48()),
alpha: 1.0)
}
//長押し時の呼び出しメソッド
@IBAction func pressLabel(sender: UILongPressGestureRecognizer) {
//呼び出されたタイミングを確認する。
if(sender.state == UIGestureRecognizerState.Began) {
testLabel.text = "長押し開始"
} else if (sender.state == UIGestureRecognizerState.Ended) {
testLabel.text = "長押し終了"
}
}
}
を最初に書いて、変更後のコード
class LongPressGestureViewController: UIViewController {
@IBOutlet weak var myLabel: UILabel!
//最初からあるメソッド
override func viewDidLoad() {
super.viewDidLoad()
}
//タップ時の呼び出しメソッド
@IBAction func myTap(_ sender: UITapGestureRecognizer) {
//ラベルの背景色をランダムな色に変更する。
myLabel.backgroundColor = UIColor(
red: CGFloat(drand48()),
green: CGFloat(drand48()),
blue: CGFloat(drand48()),
alpha: 1.0)
}
//長押し時の呼び出しメソッド
@IBAction func myLongPress(_ sender: UILongPressGestureRecognizer) {
//呼び出されたタイミングを確認する。
if(sender.state == UIGestureRecognizer.State.began) {
myLabel.text = "長押し開始"
} else if (sender.state == UIGestureRecognizer.State.ended) {
myLabel.text = "長押し終了"
}
}
}
を書くって構成でやってるんだけど、Swiftも比較的新しい言語で、
コードの書き方自体が常に進化し続けているから、
「このメソッドの時はこう書く」
「この色を表示したいときはこうだ」
みたいな感じで、1問1答形式で紋切り調に丸暗記しても、コード自体が変更される可能性が高いので、コード自体を暗記科目みたいに覚えようとしても意味はないし、覚えたところで自分が作りたいものや顧客の要求に応じたものが作れないと一切意味はない。
オイラたちはアクションリサーチをやってるのであって、
Swiftのコード自体を研究してる訳ではない人が殆どだからね。
まずは、
💃最低限の標準機能を動かせるようになること🕺
実際、オイラなんて、
現在メインで開発してるのはSwiftUIでやってるから、UIKitの書き振りなんて全然覚えてもない状態で、このマガジンを気ままに始めて、以降も全然、
コード自体を覚えても意味はない
ってスタンスで一貫して記事を書き続けてきてるし。
例えば、
「プログラミング=全部空でコードがかけて当たり前だし、それがプロ」とか思い込んでる人とかから、
「MapKitの中心座標を取得するコードは何でしょう?」
なんて、言われても
「さあ?なんだろう。とりあえず、MapKitのフレームワークをプロジェクトに追加して、ソースコードにMapKit自体をインポートして、CoreLocation系のなんかのメソッドを予測変換で緯度経度取得すれば行けんじゃね?」
くらいしか瞬間では、思い浮かばないね💦
実際、暗記と勘違いしてる人で、
「俺はコードを全部、空で覚えてるから何も見ずに書ける、ノート買ってくれば、1冊分くらいすぐに書けるよ。」と豪語してる人に居酒屋で会ったこともあったが、内心、
動かして検証して本当に期待どおりに動くかの確認が一番大事なのに、
ノートに書いてどうやってそのコードが正確かなんて検証するんだ?
ノートからデジタルに書き起こすだけ手間じゃねーか?
こいつ頭沸いてんな?🤔
と思った話は内緒にしとく〜〜〜〜🕺
とりあえず、
コードなんて覚えてなくても、作り方の大枠を理解しておけばそれでOK
普段の作業 ≠ 学校の試験
普段の作業で行うプログラミングはカンニングOKだし、
カンニングが当たり前の世界
サンプルコードをいきなり動かしながら、
生きたコードとコードの文法を理解する
⒊0からコードを書こうとするより、まずは多くのサンプルに触れて、各オブジェクトのつながりを理解=設計
階段型の勉強で、
①コードの書き方を理解
↓
②作り方を身に付ける
みたいな学校のお勉強風な考え方で、一生懸命、
的なオイラたちが辞書がわりに普段、
「あれ?この$0って何だったっけ?」
みたいな時に使う本を、何ヶ月も何年もかけて丸暗記しようとして、結果、挫折する道を最初から選んでる人は多い。
それは完全に、
木を見て、森を見ず
な話。英語を話せるようになるために、辞書を丸暗記しようとしてるみたいなもの。
👉正直、動くモノを作りながら文法が繋がらないと意味がない。
なので、
このマガジンでは、いきなりUIKitのラベルの作り方から入って、
文法については、
で、SwiftUIとUIKitでSwiftの基本文法は共通なので別マガジンで書いてるんだよね〜〜〜。
多くのサンプルの中で、
class(クラス)
func (関数)
enum(列挙型)
struct(構造体)
delegate(デリゲート)
protocol(プロトコル)
なんかに、標準機能の作り方を通じて、生きたコードの繋ぎ方を実はサンプルを通じて触れてきたつもり。なので、
💃コードを暗記するよりも、まずはたくさんのサンプルに触れる、動かす
コードの書き方よりも、各コードブロック=オブジェクトの繋がり=設計を意識する🕺
⒋命名規則やコードの書きぶりを意識=コード管理
コードがある程度書けてくると、
必ずいるのが、
独自性=オリジナリティ=自分らしさと勘違いする人
で、必ず、数日〜数ヶ月前に自分が書いた意味不明なコード自体にハマってバグ改修やコードの解析に時間がかかる。
教科書どおりに打つ必要はないんだけど、コードにはある一定の規則や法則があることまで理解してないんだよね。
あたりに書いてるから参考にして欲しいんだけど、
サイト記事のコードでは、
test○○って感じで頭小文字のtestを先頭につけてる
オイラのコードでは、殆ど、
my○○って感じで頭小文字のmyを先頭につけてる
んでさらに、ここはサイト記事と共通なんだけど、
CocoaTouchClassにしろ、新規のクラスを前準備で追加する時にしろ、
class LongPressGestureViewController: UIViewController {
て感じで、class(予約語)の後は、
大文字始まりで、意味がわかる単語で長くなりすぎない名前
をクラス名につけてる。こいつらは実は、
Swiftのコーディング基本作法
⒈引数と関数名は小文字始まりの名前
⒉⒈以外は大文字始まりの名前
⒊そのクラスや引数の対象は何で何をしてるかを一目でわかる名前にしとく
って作法に単純に従ってるだけ。
こうしないと、コード管理がぐちゃぐちゃで読みにくくなるからね〜〜〜〜
ちなみに、
といったVBAやGASのコードに関しては、関数名を日本語にしてるんだけど、それは、
VBAやGASの開発環境は日本語名を許容してる
Xcodeでは日本語名を許容していない
って違いがあるから!!!!
開発環境に応じて、
<より読みやすく直感で理解しやすい書き方は何か>
だけの違い。
まあ、気になった人は、実際にXcodeでCocoaTouchClassファイルなんかを追加する時に日本語でやってみて。
おかしなことになるから藁🤣
⒌管理しやすいファイル構成を考える=ファイル管理
たしか、
でしれっと、普段はCocoaTouchClassをサイト記事では使うところを、
単純にclassファイルを追加するだけだから〜〜〜
と、ひとつのファイルにまとめたやり方を紹介した思うし、ファイルが現在でも多いと軽く話したと思うんだけど、、、
実際、レガシー言語の現場で、パラメータ化やオブジェクト指向言語の本質に慣れてる人であれば、実は、
<同一プロジェクトファイル内であれば、
ブロック化してない限りclassはどこからでも呼び出せる>
ってことを知ってるので、よりファイルを少なくまとめると、、、
こうすると、
アクションやアウトレットな接続が絡んだ場合に、どこで何を管理してるのかをコード検索しないといけない
サイト記事の構成に忠実にできなくなる
どちらにしてもXIBファイルまで要るヤツはXIBファイルを個別で作る必要がある
んで実は、
敢えてこれまでの構成にしてる感じ
CocoaTouchClassで作ったコードをまとめると、実は、
import Foundation
import UIKit
//デリゲート先に適用してもらうプロトコル
protocol TableKeyboardDelegate {
func textFieldDidEndEditing(cell:TableViewKeyboardCell, value:String)
}
class TableViewKeyboardCell: UITableViewCell,UITextFieldDelegate {
@IBOutlet weak var tableTextField: UITextField!
var delegate:TableKeyboardDelegate! = nil
override func awakeFromNib() {
super.awakeFromNib()
//テキストフィールドのデリゲート先を自分に設定する。
tableTextField.delegate = self
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
//デリゲートメソッド
func textFieldShouldReturn(_ textField: UITextField) -> Bool
{
//キーボードを閉じる。
textField.resignFirstResponder()
return true
}
//デリゲートメソッド
func textFieldDidEndEditing(_ textField: UITextField) {
//テキストフィールドから受けた通知をデリゲート先に流す。
self.delegate.textFieldDidEndEditing(cell: self, value: textField.text!)
}
}
class TableViewCell: UITableViewCell {
@IBOutlet weak var redTablecellLabel: UILabel!
@IBOutlet weak var blueTablecellLabel: UILabel!
@IBOutlet weak var blackTablecellLabel: UILabel!
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
}
}
class myCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var customLabel: UILabel!
}
class MyCollectionReusableView: UICollectionReusableView {
@IBOutlet weak var myCollectiinSectionLabel: UILabel!
}
class MtCollectionLayoutViewCell: UICollectionViewCell {
@IBOutlet weak var myCollectionLayoutLabel:UILabel!
}
class MyUICollectionViewLayout: UICollectionViewLayout {
let numberColumns = 2 //列数
let height:CGFloat = 50 //セルの高さ
//レイアウト配列
private var layoutData = [UICollectionViewLayoutAttributes]()
//レイアウトを準備するメソッド
override func prepare() {
//全体の幅
let allWidth = CGRectGetWidth(collectionView!.bounds) - collectionView!.contentInset.left - collectionView!.contentInset.right
//列の幅
let columnWidth = allWidth / CGFloat(numberColumns)
//座標
var y:CGFloat = 0
var x:CGFloat = 0
//要素数ぶんループ
for count in 0 ..< collectionView!.numberOfItems(inSection: 0) {
let indexPath = NSIndexPath(item:count, section:0)
//レイアウトの配列に位置とサイズを登録する。
let frame = CGRect(x:x, y:y, width:columnWidth, height: height)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath as IndexPath)
attributes.frame = frame
layoutData.append(attributes)
//X座標を更新
if(count % 2 == 0) {
x = columnWidth
} else {
x = 0
}
//Y座標を更新
y = y + height
}
}
//レイアウトを返すメソッド
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return layoutData
}
//全体サイズを返すメソッド
override var collectionViewContentSize: CGSize {
//全体の幅
let allWidth = CGRectGetWidth(collectionView!.bounds) - collectionView!.contentInset.left - collectionView!.contentInset.right
//全体の高さ
let allHeight = CGFloat(collectionView!.numberOfItems(inSection: 0)) * height
return CGSize(width:allWidth, height:allHeight)
}
}
class MyCollectionDelegateCell: UICollectionViewCell {
@IBOutlet weak var myDelegateLabel: UILabel!
}
class MyCollectionDelegateLayout: UICollectionViewLayout {
let numberColumns = 2 //列数
let height:CGFloat = 50 //セルの高さ
//レイアウト
private var layoutData = [UICollectionViewLayoutAttributes]()
//レイアウトを準備するメソッド
override func prepare() {
//全体の幅
let allWidth = CGRectGetWidth(collectionView!.bounds) - collectionView!.contentInset.left - collectionView!.contentInset.right
//列の幅
let columnWidth = allWidth / CGFloat(numberColumns)
//座標
var y:CGFloat = 0
var x:CGFloat = 0
//要素数ぶんループ
for count in 0 ..< collectionView!.numberOfItems(inSection: 0) {
let indexPath = NSIndexPath(item:count, section:0)
//レイアウトの配列に位置とサイズを登録する。
let frame = CGRect(x:x, y:y, width:columnWidth, height: height)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath as IndexPath)
attributes.frame = frame
layoutData.append(attributes)
//X座標を更新
if(count % 2 == 0) {
x = columnWidth
} else {
x = 0
//Y座標を更新
y = y + height
}
}
}
//レイアウトを返すメソッド
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return layoutData
}
//全体サイズを返すメソッド
override var collectionViewContentSize: CGSize {
//全体の幅
let allWidth = CGRectGetWidth(collectionView!.bounds) - collectionView!.contentInset.left - collectionView!.contentInset.right
//全体の高さ
let allHeight = CGFloat(collectionView!.numberOfItems(inSection: 0)) * height
return CGSize(width:allWidth, height:allHeight)
}
}
class MyMKPointAnnotation: MKPointAnnotation {
//ピンの色
var pinColor:UIColor!
//ピンの画像
var pinImage:String!
}
たったこれだけのコードになるんだけど、UIKitの場合、パラメータ化や再利用以上に、
アクション接続やアウトレット接続を意識しないといけない
ってのがあるから、
<ファイルをどう管理するのが一番管理しやすいか>
👉結局、自分次第
ただ、動きはどちらでも同じ
でも、意識するとしないとでは全然違う🕺
⒍エラーは出て当たり前。Xcodeの機能を理解=利便性、効率性
これまでに何回も
を皮切りに、
であったり、各記事でコード組み込んだ後の、
赤や黄色警告エラーの修正方法
(Fixやstubsの追加)
なんかで触れてきたとおりなんだけど、この記事の⒉や⒊で書いてるとおり、Swiftは進化し続けてる言語なんで、急に書き方が変わってて赤エラーが出たり、非推奨で黄色エラーが出るなんてザラだし、ある意味それが普通。
別に、
コードを書いてエラー警告が出るのが悪いのではなくて、
エラーのところは、Swiftみたいな
静的プログラミング言語
👉プログラムを実行しなくても、
予め間違いを教えてくれるありがたい機能を備えた言語
だと、後はそのエラーがなぜ出てるのかを調べて、
よりお利口さんになればいい
だけの話。
エラー拒否症候群
みたいな人も多いみたいだけど、コード書いたのにエラーが出るのを怖がるってのは、Swiftに限らずある意味、
静的プログラミング言語の醍醐味を理解していない
ってことを自白してるようなもの。
エラーは怖がるものではなく、
💃直せばいいだけのもの🕺
その調べ方や直し方、改修後のコードについては、
これまでの記事で基本は全て網羅してるつもり。
オイラはコードの森に入らずに、
直し方の地図を持ってるから、
UIKitで自由気ままに遊べてます。
_ senderと#selectorの違い
AnyObjectとAnyの違い
func 関数名()-> Bool{ }と func 関数名:Bool{ }の違い
なんかもこれまでの記事で示してるしね💦
そもそも世の中に、
完璧なコード=正解なんて存在しない
最適解なコード=ベストプラクティスを調べる
👉アクションリサーチ
丸暗記型で完璧なコードとか意識してる
時点で素人なんだが 笑笑🤣
⒎<動くか動かないか>、<簡単か難しいか>より、<使いやすいか使いにくいか>を意識=ユーザービリティ
大体のことについては書いたので、
標準機能について、ある程度動かせるようになったなら、
でまとめた記事を参考に、
<自分ならどんなアプリを作りたいか>
👉そのアイデアが、公開規約に違反していないか
を意識しよう。
アプリらしいアプリなんて、人によって千差万別🤔
Appleが大事にしてることは、
法律や他者の権利を侵害しないコンプライアンスについては、さることながら、
で、
ユーザーがいかに使いやすい、面白い、使ってワクワクするもの
=ユーザービリティを重視
してることがわかるとおもう。
ま、アプリを個人で開発しようと思う人
の最初の動機なんて過去の自分もそうだったし、
お金儲け
だろうけど、お金儲けばかり考えてアプリを作っても、結局最後は、
ユーザービリティを意識しないといけなくなるし。
そう思って、いずれ気ままにUIKitを書こうと思っていた時期に、気ままにUIKitに入る前に、
iOS開発に必要な前提知識 = 入り口であり、出口
に触れてるんだよね〜〜〜🕺
ユーザービリティを意識せず、
「AutoLayoutなんか簡単そうだし、面倒だし、デザインもグチャグチャなままだけどコードは完璧だから機能は出来た!」
公開申請してみたところ、公開拒否を喰らった
👉簡単だし、意味がないと思ってたAutoLayoutやUI/UXデザインをやり直す羽目になり、、、、なんて、昔は結構ザラにいたとネット記事なんかにも載ってるしね。
⒏【徒然iOS】マガジンを1回通して読み返す=前提と標準機能を知る
まとめは以上なんだけど、
上記を読んで、iOS開発を自分でもやってみたいって思った人は、
まず、
から読み返してみてね。
⒐【気ままにUIKit】関連記事を1回通して、自分で動かしてみる=会得
実際に動かしながら同じものを作ってみる
と、実際に動かさないとわからない勘とかが自然に身につくし、動かさないと閃かなかったアイデアなんかを思い付くだろし。
⒑自分の作りたいアプリをサンプルコードを繋いで作ってみる=体得
アイデアが閃いたなら、
後は、今までの機能やサンプルコードで必要なものだけを何個かのビューに組み込んで自分だけのアプリを作っちゃおう🕺他の記事でも書いてるけど、
どんな大きなシステムやアプリも所詮は小さな機能やコードの塊
👉要は、
💃組み合わせ🕺
アイデアを形にして、正確に動かすのが、どのくらい
簡単 or 難しい
がわかると思う。
自分にとって、
思い込んでるほど簡単でもなければ、思ったより難しいものではないことは、自分でやってみた人にしかわからない
💃アプリ開発が楽しいかどうかもね🕺
まとめのまとめ
このマガジンでは、あくまでも
知ってさえいれば誰でも作れて当たり前
=本当に標準的な機能
知らないと標準機能が何かさえわからない
👉まずは当たり前=標準
を書いてるだけだし、すでにSwiftUIの開発が最新なので、あくまでも
UIKit=SwiftUIをより早く理解する程度
(いきなりSwiftUIから始めてもOKなんだが💦)
と思って、気楽にやってみてね〜〜〜〜🕺
とか、言いつつ日本の企業では、今やっとこObjective-CからUIKitへの移行し始めなんてプロジェクトも多いらしいが。そら、世界より10年遅れだから取り残されるし、ヒットするアプリなんて出てこんわ 藁🤣
CoreData以外だと残り33記事くらいなのと、来週は色々忙しいので。週末だけど記事にしてみたw
それでは皆さま今週も、、、
💃良い週末を🕺
また、気が向いたら来週以降にでも記事を書きます。
やっぱり、また下から上に順番どおりに書こうかな🤔