Swiftはプロパティもオーバーライドできる
バージョン
- Swift 3.0.2
- Xcode 8.2.1
クラスのプロパティ
知らなかっただけなのですが、Swiftはプロパティもオーバーライドできます。
使い方次第で便利に使えそう。
サンプルコード
プロパティのオーバーライドとプロパティ監視を組み合わせて、ボタンが無効化されたらアルファ値を変えるコードです。
import UIKit class CustomButton: UIButton { override var isEnabled: Bool { didSet { super.isEnabled = isEnabled self.alpha = isEnabled ? 1.0 : 0.4 } } } let customButton: CustomButton = CustomButton() print(customButton.isEnabled) // true print(customButton.alpha) // 1.0 customButton.isEnabled = !customButton.isEnabled print(customButton.isEnabled) // false print(customButton.alpha) // 0.4
UIViewからUITableViewのカスタムセルを作るとボタンタップイベントが取れない
現象
UITableVIewのカスタムセルにボタンを置いて、タップ用の処理を書いたがタップイベントを検知できない。
原因
ボタンの上のレイヤーに、UITableViewCellContentView
というビューが描画されており、このビューに邪魔されてタップイベントが検知できていなかったため。
根本原因
カスタムセルのxibファイルを、UITableViewCell
ではなく、 UIView
を使って作成していたため。
UITableViewCell
で作ったxibは、UITableViewCellContentView
の上に自分の配置したパーツが描画されています。
ちなみに、xibファイルの新規作成すると、デフォルトで UIView
が置いてあるというXCodeのおせっかい機能のがほんとの原因かもしれません。
対策
カスタムセルを作る際には、xibファイルのデフォルトで置いてある UIView
を消して、UITableViewCell
を置くと良いです。
または、新規ファイルを作成する際に、クラスファイルから作成して、 Cocoa Touch Class
を選択して、次の画面で Also create XIB file
にチェックを入れると、勝手に UITableViewCell
を配置したxibファイルを生成してくれます。
AutoLayoutに慣れてる人には当たり前なのかもしれないですが、ちょっとわかりにくいなーと思いました。
2016年の振り返り
今年もあんまりブログ書けなかったけど、振り返りはしておく。
お仕事全般
こまごまPHPもやったけど、ほぼSwiftでiOSアプリ開発ばっかりだった。
チームでのアプリ開発を経験できたり、RxSwift触ってみたり楽しかった。
プチSCMBCとかを開催して、ちょっとずつプルリクエスト方式での開発ができるようになってきた。
Qiita:Team
去年に引き続き、積極的に投稿するようにしていた。
だんだん書いてくれる人が増えてきて嬉しい。
少しは役に立っているのかも。
社内勉強会
Bitbucketを使うことが増えてきたので、プチSCMBCみたいなことをした。 コンフリクト祭りが楽しかった。
全職種向けにバージョン管理についての勉強会を開催して日付zipがなぜダメなのかを伝えた。
社外勉強会など
ほぼ参加できなかったけど、まあ、これは仕方ない。
プライベート
第二子である次男が生まれて家の中がさらに賑やかになった。
長男が保育園に通うようになって、より時間を効率的に使う必要が出てきた。
早めに出社して勉強してたのが、子どもを寝かしつけてからの時間に変わった。
たまに一緒に寝落ちしちゃうのどうにかしたい。
来年の目標
- Qiita:Teamをディレクターさん、デザイナーさんも使うようになってきたのでそっち向けの記事も書く
- Swift環境での自動テストを導入する
- iOSアプリ開発に偏った1年だったので、Web開発もうちょっとがんばる
- 何かしらアウトプットする
配列の要素をオプショナルバインディング
はじめに
コードレビューで、ちょっと不思議なコードがあったのでメモ。
結論
配列の添字アクセスをオプショナルバインディングしてもビルドエラーにならないけど、わかりにくいのでやめたほうがいい。
コード
var hoge:[Int] = [] // hogeを更新する処理など if let n: Int = hoge[0] { //ランタイムエラー print(n) } else { print("nothing") }
一見すると良さそうですが、配列が空っぽなのでコメントした行で fatal error: Index out of range
になります。
当たり前といえば当たり前ですね。
改善案
配列でこういった判定を行うなら、空配列の判定したほうがわかりやすい気がします。
if hoge.isEmpty { print("nothing") } else { print(hoge[0]) }
そもそも、配列アクセスでnil
が返る可能性を想定したコードになっていますが、hoge
はInt
型の配列であって、Optional<Int>
型の配列ではないので、オプショナルバインディングは適切ではないです。
なんでビルドエラーにならないのかは謎です。
どうしてもオプショナルバインディングしたい場合
if let n: Int = hoge.first { print(n) } else { print("nothing") }
今回の場合は、配列の最初の要素にアクセスしたいので、Array
クラスの first
プロパティでOK。
first
はオプショナル型なので問題なし。
似たようなプロパティとして最後の要素を参照する last
もあります。
どちらも正確には Array
クラスに適用している CollectionType
プロトコルの特徴ですが。
PHPで画像やcssの404エラーでセッション切れになる現象
はじめに
FuelPHPを使っていて、セッションが切れることがあり調査していました。
ググるとドキュメントルートにファビコンが存在していないと発生するなどの記事がヒットするのですが、ファビコンだけでなく読み込んでいる画像やcssがなくても同じ事象が発生することがわかったのでまとめておきます。
結局FuelPHPだけの話ではなかったです。
原因
FuelPHPに限らず、PHPのWebアプリケーションフレームワークだと、たいてい.htaccess
にこんな感じの設定があります。
リクエストされたパスがファイルやディレクトリじゃなかったら、フロントコントローラーであるindex.php
に処理を投げるというものです。
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ /index.php$1 [L]
通常、画像やcssは最初の条件に該当するのでindex.php
で処理しないのですが、ファイルが存在しない場合、どちらの条件にも該当せずindex.php
で処理することになります。
コントローラーには画像やcssファイル名のアクションはないので、最終的に404に流れます。
index.php
内(というか、その先のコントローラーなど)で、FuelPHPで言うフラッシュセッションのような次のリクエスト時に消えるセッションを使用していると、404リクエストで消えてしまうためセッション切れが発生します。
生存期間の長い通常のセッション使っても解決するのですが、フォームデータの削除し忘れとか嫌ですし。
対策
コントローラのアクション以外をindex.php
で処理しなければいいので、.htaccess
の設定をこんな感じにすれば良いです。
特定の拡張子(この例ではcss/jpeg/png)の場合、index.php
で処理しないようにしています。
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_URI} !^.*\.(css|jp?g|png)$ RewriteRule ^(.*)$ /index.php$1 [L]
Sketchで透過オブジェクトを含むグループごとスライスする
はじめに
最近、PhotoShopの代わりにSketchを使うことも増えてきました。
SketchはMake Exportableで@2xや@3xもまとめて簡単に切り出せていいなーとか思ってたら、よくあるアイコンの下に固定サイズの透過オブジェクトがあるパターンで、グループごと切り出しても透過オブジェクトのサイズに切り出せずアイコン自体のサイズになってしまって困りました。
プラグインとかいれないで解決できたのでメモ。
確認バージョン
Version 3.6.1 (26313)
解決策
グループ化する
ヘルプアイコンと24x24の矩形をグループ化。
アイコンと透過レイヤーを選択
スライスツールを使ってオブジェクトを選択
スライスツールはここ。
外側を選ばないと、アイコンだけ切り出すことになるので注意。
アイコンだけ選んだ場合
選択すると切り出した結果が右側に表示されるが、背景も含んでしまっている。
Export Group Contents Onlyにチェック
グループ以外のオブジェクトが除外されるので透過になる。
あとは必要に応じて@2や@3を追加してExportShapeでOK。
iOSで表示要素を動的に消して親ビューの高さを変える
はじめに
ログイン前後で表示要素の数を動的にし、ビューの高さを変えるという仕様があり、実現方法を調べました。
意外とめんどくさかった。もっと効率の良いやり方もありそうです。
バージョン
XCode Version 7.3.1 (7D1014)
サンプル
ラベル2つとボタン1つを持つフッターがあり、状態によって2つ目のラベルを非表示にして、その分ボタンを上に詰めるという仕様で考えます。
やりかた
制約
添付のような状態にします。
ポイントはボタンのconstraintsにLabel2を使わないこと。
ここでは高さと左右下のマージンだけ設定しています。
コード
調べるまで知らなかったのですがconstraintsもIBOutletでViewControllerに接続できます。
フッターの高さとLabel2の上マージンを接続して、viewDidLoadでLabel2を非表示にするため高さを0に、空いた隙間を詰めるためフッターの高さを調整しています。
確認用にボタンタップで表示するようにしています。
もちろん、他のタイミングでもできるのでやりたいことに合わせて読み替えましょう。
ちなみに、Label2自体をIBOutletで接続してhidden
にtrue
で非表示にしても、htmlと違って高さを維持するのでうまくいかないです。
import UIKit class ViewController: UIViewController { @IBOutlet weak var footerHeight: NSLayoutConstraint! @IBOutlet weak var label2TopMargin: NSLayoutConstraint! override func viewDidLoad() { super.viewDidLoad() self.label2TopMargin.constant = 0 self.footerHeight.constant = 120 } @IBAction func tapButton(sender: AnyObject) { self.label2TopMargin.constant = 4 self.footerHeight.constant = 150 } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } }
実行結果
初期ロード時
Label2が非表示でLabel1とボタンの間が詰まっています。
ボタンタップ時
フッターの高さが高くなり、Label2を表示しています。