2025.08.29
Laravelで空判定にif文を使うとハマる理由
2014.06.19
プログラミング自作のツールバーがiOSのSafariっぽく動くようにする

こんにちは、ZYです。
今回は、
「その動き、君がいいねと言ったら、Safari記念日」
ということで、「なんかiOSのSafariっぽい挙動をする自作ツールバー」を紹介します。
と、いっても、今回再現するのは、「スクロール時やタップ時に、ツールバーが出たり引っ込んだりする動き」です。中のボタンの動きなんかは再現しないので、あしからず。
さて。まずは、iOSのSafariで使われているToolBarの挙動を確認しておきましょう。
出てくるイベント
1.勢いよく上にスクロール(指は下にpan)した時
(ゆっくりした時は、出てこない)
2.一番下までスクロールした時
3.画面下部をタップした時
隠れるイベント
4.下にスクロールした時
(スクロールにあわせて、途中までゆっくり降りて、ある程度まで行くとアニメーションで隠れる)
以上の動作を、各イベントにあわせて自作のツールバーが動くように実装していきます。
以下が、そのコードです。
@interface ImitateSafariToolBarViewController : UIViewController<UIScrollViewDelegate> // スクロールビュー @property UIScrollView *scrollView; // 自作ツールバー 縦幅44 @property UIView *toolBar; // 制御用の変数たち。 @property BOOL showReplyView; @property BOOL animetionReplyView; @end
// 自作ツールバーを動かすイベントを取るUIScrollViewデリゲート群。
// スクロールが始まった時に呼ばれるメソッド
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
// 今回は特に何もなし。
}
// スクロール中に呼ばれる
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// velocityInViewは、panの「速さ」を取得します
CGPoint v = [scrollView.panGestureRecognizer velocityInView:scrollView];
if(v.y > 800){
// 勢いよく上にスクロールした場合。800は、割と反応しやすい方。
// SafariのToolBarに近づけるなら、1100以上でもいいかも?
[self showToolBar];
}
// translationInViewは、pan開始時から現在地までの「距離」を取得
CGPoint p = [scrollView.panGestureRecognizer translationInView:scrollView];
if(p.y <= -1){
if (_animetionReplyView) return;
if (! _showReplyView) return;
// 上にスクロールした場合
// パンの動きに連動して、閉じる方向に少しずつ動く
CGRect frame = self.toolBar.frame;
frame.origin.y = self.view.frame.size.height - self.toolBar.frame.size.height + (p.y * -1);
self.toolBar.frame = frame;
// 一定の位置まで行くと閉じる。
if (p.y < -22) {
[self hideToolBar];
}
}
}
// ドラッグが終了した時に呼ばれる
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
// 一番下まで行くと、ツールバーを出す。
if (scrollView.contentOffset.y > scrollView.contentSize.height - scrollView.frame.size.height) {
[self showToolBar];
}
// ツールバーがゆっくり下がっている途中で離した際には、ちょっとずれてるので、戻す
if (_showReplyView && !_animetionReplyView) {
if (self.toolBar.frame.origin.y != self.view.frame.size.height - self.toolBar.frame.size.height) {
_showReplyView = NO;
[self showToolBar];
}
}
}
// 慣性で動くスクロールが静止したときに呼ばれる。基本的にはドラッグを離した時と同じような動作。
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
// 一番下ならツールバーを出す
if (scrollView.contentOffset.y > scrollView.contentSize.height - scrollView.frame.size.height) {
_showReplyView = NO;
[self showToolBar];
}
if (_showReplyView && !_animetionReplyView) {
if (self.toolBar.frame.origin.y != self.view.frame.size.height - self.toolBar.frame.size.height) {
[self showToolBar];
}
}
}
// 自作ツールバーの閉じたり出たりのアニメーション。
-(void)showToolBar{
// ツールバーを出す
// アニメーションの途中や、もうすでに開いてる時は動作しない。
//if (_animetionReplyView) return;
if (_showReplyView) return;
_animetionReplyView = YES;
CGRect frame = self.toolBar.frame;
frame.origin.y = self.view.frame.size.height - self.toolBar.frame.size.height;
[UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^{
self.toolBar.frame = frame;
} completion:^(BOOL finished){
_animetionReplyView = NO;
_showReplyView = YES;
}];
}
-(void)hideToolBar{
// ツールバーを隠す
NSLog(@"end");
//if (_animetionReplyView) return;
if (!_showReplyView) return;
_animetionReplyView = YES;
[UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^{
CGRect frame = self.toolBar.frame;
frame.origin.y = self.view.frame.size.height;
self.toolBar.frame = frame;
} completion:^(BOOL finished){
_animetionReplyView = NO;
_showReplyView = NO;
}];
}
UIScrollViewのデリゲートメソッドを使っているので、UIScrollViewもしくは、その継承Class(UITableViewとか、UITextViewとか)が存在しているUIViewControllerでお使いください。
UITableViewでも、<UITableViewDelegate>を指定してやれば、ついでにUIScrollViewDelegateが呼び出されます。
今回は自作のツールバーを使いましたが、UINavigationControllerのtoolBarを使うことも可能。
上記のself.toolBarを、self.navigationController.toolbarに変えれば、基本的にはOK。
ふつーにself.navigationController.toolbar.frameをいじれば、動きます。
一応、
-(void)showToolBar;
-(void)hideToolBar;
を呼び出しているところを
[self.navigationController setToolbarHidden:YES animated:YES];
[self.navigationController setToolbarHidden:NO animated:YES];
に変えても動きますが・・・私がやった時は、たまにエラー落ちしてたので、あんまりおすすめしないです。
ま、でも、たぶん、ある程度条件つけて、呼び出すタイミングを制御してやれば、大丈夫ですよ、たぶん。
その他、一番下までスクロールしなくても出るようにするなど、カスタムしてお使いください。
【記事への感想募集中!】
記事への感想・ご意見がありましたら、ぜひフォームからご投稿ください!【テクノデジタルではエンジニア/デザイナーを積極採用中です!】
下記項目に1つでも当てはまる方は是非、詳細ページへ!Qangaroo(カンガルー)
【テクノデジタルのインフラサービス】
当社では、多数のサービスの開発実績を活かし、
アプリケーションのパフォーマンスを最大限に引き出すインフラ設計・構築を行います。
AWSなどへのクラウド移行、既存インフラの監視・運用保守も承りますので、ぜひご相談ください。
詳細は下記ページをご覧ください。
最近の記事
1
2025.08.29
Laravelで空判定にif文を使うとハマる理由
2

2025.08.29
Git SSH接続でfetch/pullができなくなった時の対処法
3

2025.08.28
【体験談】Amazon Linux 2でMySQLインストール時のOpenSSL・GPG key エラーにハマった話
4
2025.08.28
【AWS】SSMポートフォワーディングとInstance Connectを利用したプライベートEC2へのSSH接続手順
5
2025.07.24
Log::info()が使えない!?Laravel.logのPermission denied エラーを解決する(Docker環境)
タグ検索