こんにちは!!!
手元からPCが消え去って1ヶ月。やっっっとパソコンが買えたので投稿。
今回はCircleMenuというオシャレなライブラリがあることを(今更)知ったのでこのアニメーションを利用してふわっと遷移しようという趣旨。
別にCircleMenuのオシャレなアニメーションに乗っかって遷移してるだけのハリボテです。
ブランクあるんや、、リハビリみたいなもんや、、
成果物
なんかgifだと残念な感じに円が残るけど実際には普通にスゥーっ!っと消えます!!!(血眼)
コード
import UIKit
import CircleMenu
class ViewController: UIViewController {
let menuButton: CircleMenu = {
let view = CircleMenu(frame: CGRect(x: UIScreen.main.bounds.width / 2 - 25, y: UIScreen.main.bounds.height / 2 - 25, width: 50, height: 50), normalIcon: "menu", selectedIcon: "error", buttonsCount: 5, duration: 0.3, distance: 150)
view.backgroundColor = .green
view.layer.cornerRadius = view.frame.size.width / 2.0
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
menuButton.delegate = self
self.view.addSubview(menuButton)
}
}
extension ViewController: CircleMenuDelegate {
func circleMenu(_ circleMenu: CircleMenu, buttonDidSelected button: UIButton, atIndex: Int) {
var vc: UIViewController?
switch atIndex {
case 0:
vc = NextViewController()
case 1:
vc = NextViewController()
case 2:
vc = NextViewController()
case 3:
vc = NextViewController()
case 4:
vc = NextViewController()
default:
print("no vc")
}
guard let VC = vc else { return }
VC.modalPresentationStyle = .overCurrentContext
self.present(VC, animated: false, completion: nil)
}
}
import UIKit
class NextViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .red
let tap = UITapGestureRecognizer(target: self, action: #selector(dismissAction))
self.view.addGestureRecognizer(tap)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.view.alpha = 0
UIView.animate(withDuration: 0.5) {
self.view.alpha = 1
}
}
@objc func dismissAction() {
self.dismiss(animated: false, completion: nil)
}
}
解説
import らへん
import CircleMenu
今回のメインであるライブラリのインポートですね。githubではbuildの項目がfailingになってましたけど普通に使えますね。なんででしょ。余裕があったら読みたいものですな。
view らへん
let menuButton: CircleMenu = {
let view = CircleMenu(frame: CGRect(x: UIScreen.main.bounds.width / 2 - 25, y: UIScreen.main.bounds.height / 2 - 25, width: 50, height: 50), normalIcon: "menu", selectedIcon: "error", buttonsCount: 5, duration: 0.3, distance: 150)
view.backgroundColor = .green
view.layer.cornerRadius = view.frame.size.width / 2.0
return view
}()
viewの宣言をクロージャで宣言してるんや。これは宣言してるviewが使われるときに一度だけ初期化されるヨ。
この形をとることでひとまとまりになってるし、viewDidload内が汚れないから見やすいね!
あとはあんまり良くないかもしれないけど、中で宣言するインスタンス名をviewにすることで他のviewを宣言するときコピペがとっても楽になるよ!笑
delegate らへん
delegateは移譲って言って、こいつがいるときは処理を他人任せにしてる証拠だ!!(そして大抵僕に丸投げされてるんだ。 これは私情。)
もし君が仕事を任されたとき自分なりのやり方でやるよね???(異論ナシ。)
delegateも任された側のやり方で処理することができるから便利なんだ。
delegateと一緒だね!!!!(歓喜)
あと、通知の役割も大きいけど今回は使ってないからパス _ (:3 」)_
menuButton.delegate = self
このコードからは、menuButton先輩が、self、つまり私(ViewController)に仕事をぶん投げるということを高らかに宣言しているのだ。(やめてくれよ)
逆に言えば、このように高らかに宣言してくれないとdelegateできない。つまり仕事を任せられていないので、
「お前これ頼んだよな?(威圧)」
「は?寝ぼけてんのか?(えっ、??いや、頼まれてないですよ、、?)」
なんてことが起こる。(特に良く僕のコード内で。うっかり。照)
Extension らへん
swiftに限らず継承はとても便利だ。
ここでは便利なextensionの継承の、書き方について触れておこうと思う。
extension ViewController: CircleMenuDelegate {
//省略
}
プロトコルを継承することで決まった形のクラスや構造体を作れるし、うまくかければ具象への依存かなり減らせてそれだけでとても変更しやすくなる。
しかし、ViewControllerの定義でついつい一度に継承するものを羅列してしまいがちだ。
class ViewController: UIViewController, UITableViewDelegate, CircleMenuDelegate {
//省略
}
これではどの関数がどのクラス・プロトコルから継承してきたものかかなり分かりづらいし、区切りのないコードが一気に肥大してしまう。
そのため、今回のように一つ一つ優しくextensionしてあげよう。これだけで見通しが良くなるし、「あっ、こいつイラネ!w」となった時もどこを消せばいいかすぐわかる。
追加もしやすい。
書き方をちょっと変えるだけでいいことづくめなのだ。
guard らへん
guardとif。ifが便利すぎでわざわざguardを使う意味がわからないという方もいるのではなかろうか。
ペーペーの僕がいうのもなんだが、僕なんかよりずっといいコードを書いている人でもguardを使わずifを使っていることがあるから頭に「?」が浮かぶ。
こんなに良い関数は他にないんじゃないか? それは無いか。
(ちなみに僕的、昨今の推しはmapです。)
guard let VC = vc else { return }
guardはなんと言っても一眼で役割が分かるのがいい。
- guardを使う場面はreturnされて本土(呼び出し元)に返されるか、fatalErrorでも呼ばれてアプリがクラッシュするかくらいだ。
「いやそれifでもできるやーん?」
そうなんだけど、あえてguardを使うことで自分や他の人がそこを読む時、一瞬で意図がわかる。
素敵だ。(イケボ)
そしてしばしばletとセットでオプショナル回避に用いられる。
let optional: Int? = 3
guard let Nakami = optional else { return }
// optional : Optional<3>
// Nakami : 3
このように中身を簡単かつ安全に、そして中身がない時は本土に返還するスグレモノなのだ。
lifecycle と animation らへん
animationを行うことで今回はそれっぽーく仕上げでいる。
透明度を表すalphaの値を事前に0にして完全に透明にしておく。
viewWillAppearが呼ばれたらアニメーションを行って、alphaを1すればふわっと素敵っぽい遷移が実現できた。(急に完了する。)
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.view.alpha = 0
UIView.animate(withDuration: 0.5) {
self.view.alpha = 1
}
}
ここで大事なのでviewWillAppearで呼ぶことだ。
これより前のviewDidloadで呼ぶと、そもそもアニメーションが行われない。
しかもvcを再利用していればviewDidloadは一回しか呼ばれない。
この後のviewWillLayoutSubviewsは名前の通りサブビューの描画準備のためにあるので役割として明らかにおかしい。
ここら辺に関してはもっと良い書き方があるんだろうなぁという感覚なので勉強します。
ここまで読んでいただきありがとうございました。
キーボードは配列が前のは海外仕様だったから逆に日本仕様に慣れない、、
書くつもりなかったのにPCきたの嬉しすぎて夢中になって書いてしまった、、
明日仕事なのに。もう寝なきゃ。