ios SideMenu ライブラリの使い方

アプリの左から開くサイドメニューを作りたくライブラリを探したらけっこう出てきた。こちらの使い方を調べたので簡単にまとめておく
github.com

今回やるながれはこんな感じ
1. メニュー化するViewControllerの作成
2. AppDelegateで1.で作成したViewControllerをメニューとしてセットアップ
3. メニューを開く
4. メニューを閉じて値を返す

cocoapodsまたはcarthageで他のライブラリとおなじように入れられるのでそれは省略

1. メニュー化するViewControllerの作成

今回はMenuViewControllerとかにする

MainViewController.swift

import UIKit
import SideMenu

class LeftMenuViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

	// 背景色は白にする
	view.backgroundColor = .white

	// メニューとするViewControllerはナビゲーションをもつので、見た目とか調整
	self.navigationController?.navigationBar.tintColor = .clear
	self.navigationController?.navigationBar.barStyle = UIBarStyle.black
	self.navigationController?.navigationBar.isTranslucent = true
	self.navigationController?.navigationBar.backgroundColor = .green
    }
}
2. AppDelegateで1.で作成したViewControllerをメニューとしてセットアップ

AppDelegate.swift

import UIKit
import SideMenu

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
	// アプリ起動時に表示するViewControllerを設定
	let viewController = ViewController()
	let navigationController = UINavigationController(rootViewController: viewController)
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.backgroundColor = UIColor.white
        window?.rootViewController = navigationController
        window?.makeKeyAndVisible()

        // さきほど作ったメニュー用のMenuViewControllerをSideMenuにセットする
        let menuViewController = MenuViewController()
        let menuNavigationController = SideMenuNavigationController(rootViewController: menuViewController)
        SideMenuManager.default.leftMenuNavigationController = menuNavigationController
        SideMenuManager.default.addPanGestureToPresent(toView: navigationController.navigationBar)
        SideMenuManager.default.addScreenEdgePanGesturesToPresent(toView: navigationController.view)

        // Readmeに書かれているオプションもここで指定できる
        leftMenuNavigationController.statusBarEndAlpha = 0
        leftMenuNavigationController.menuWidth = 300
        leftMenuNavigationController.enableSwipeToDismissGesture = false
        return true
    }

    ・・・
}
3. メニューを開く

ViewController.swift

import UIKit
import SideMenu

class ViewController: UIViewController {

   ・・・

   @objc func showMenu(_ sender: UITapGestureRecognizer){
     // ここでメニューが開く
     let menu = SideMenuManager.default.leftMenuNavigationController!
     present(menu, animated: true, completion: nil)
   }
}
4. メニューを閉じて値を返す

メニュー側の画面にメニューを閉じる処理と呼び出し元に値を返してやる処理を追加

class MenuViewController: UIViewController {

   ・・・

    @objc func onTap(_ sender: UITapGestureRecognizer){
        dismiss(animated: true, completion: nil)

        NotificationCenter.default.post(
          name: Notification.Name("SelectMenuNotification"),
          object: nil,
          userInfo: ["hoge":"fuga"] // 返したいデータをセットするよ
        )
    }

呼び出し側でメニューから値を受け取る

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // メニューからの通知を受け取れるようにするよ
        NotificationCenter.default.addObserver(
          self,
          selector: #selector(catchSelectMenuNotification(notification:)),
          name: Notification.Name("SelectMenuNotification"),
          object: nil
        )

        ・・・
    }

    @objc func catchSelectMenuNotification(notification: Notification) -> Void {
	// notification.userInfo にメニューからの返り値を取得できるよ
    }
}

SideMenuのライブラリは色々あるけど今回試したのはこんな感じでした。以上です

ionic Platform.readyについて

ionicのPlatform.readyはネイティブ側で必要な初期化処理が完了したときに発火される。ネイティブの機能を使うプラグインを使用する場合、Platform.readyが終わってないと使用できないというわけだ。console.logも実機で動かす場合はcordovaがラップしてObjectivecでログ出力する処理を置き換えてるはずなのでネイティブの機能と言える(たぶん)。例えば、以下のコード。ionViewWillEnterとionViewDidEnterでプラグインを呼び出そうとしている。

import { Component } from '@angular/core';
import { Platform } from '@ionic/angular';

@Component({
  selector: 'app-tab1',
  templateUrl: 'tab1.page.html',
  styleUrls: ['tab1.page.scss']
})
export class Tab1Page {

  constructor(public platform: Platform) {
    platform.ready().then(() => {
      window.hoge_plugin.fuga();
    });
  }

  ionViewWillEnter() {
    window.hoge_plugin.fuga();
  }

  ionViewDidEnter() {
    window.hoge_plugin.fuga();
  }
}

おおよそこちらのコードは問題なく動作するはずだが期待どおりに動作しない場合がある。そう、Platform.readyが完了する前にionViewWillEnterやionViewDidEnterが呼ばれた場合だ。アプリ起動後の最初のページなどにこのような処理を書くとエラーになる場合があるだろう。そこで以下のようにPlatform.readyを使うように修正する

export class Tab1Page {

  constructor(public platform: Platform) {
    platform.ready().then(() => {
      window.hoge_plugin.fuga();
    });
  }

  ionViewWillEnter() {
    this.platform.ready().then(() => {
      window.hoge_plugin.fuga();
    });
  }

  ionViewDidEnter() {
    this.platform.ready().then(() => {
      window.hoge_plugin.fuga();
    });
  }
}

当初私はPlatform.readyはアプリ起動後に1度だけ呼ばれると勘違いしていたのでこのコードの意味がよくわからなかった理解するポイントは、

・Platform.readyが完了済みなら即時実行される
・Platform.readyが未完了ならPlatform.readyが完了してから実行される

といったところだろうか。まとめるとプラグインを呼び出す際はPlatform.readyが完了している前提なのでPlatform.readyで囲んでおけば間違いないのだろう

AngularjsでIME入力を完了しないとng-modelに反映されなくてハマった

またAngularjsとか今さらな話だけど...

https://qiita.com/yaegaki/items/c9cf111ef9d0c541a194
こちらを参考に自分のアプリに試してみたところ、動くところと動かないところが出てきた

こんな感じのコードが動かなかった

<div ng-if="show()">
  <input type="text" ng-mode="input.hoge" jp-input />
</div>

ng-showにしたら動いたよ

<div ng-show="show()">
  <input type="text" ng-mode="input.hoge" jp-input />
</div>

以上です

iOS Share Extensionで保存したuserDefaultsの値が取得できなくてハマったよ

https://qiita.com/KosukeQiita/items/994693da551a7101cc9c
こちらを参考にほぼ同じコードで試していたが上手くいかないこと数時間...

保存した値をuserDefaultsから取得しようとしたらこんなエラーぽいメッセージが出てた

[User Defaults] Couldn't read values in CFPrefsPlistSource<0x2805fd380> (Domain: group.xxxxx.yyyyy.zzzzz, User: kCFPreferencesAnyUser, ByHost: Yes, Container: (null), Contents Need Refresh: Yes): Using kCFPreferencesAnyUser with a contai\
ner is only allowed for System Containers, detaching from cfprefsd

が、どうやら関係なかったXcodeのCapabilitiesから設定するApp GroupsをOnにする設定がメインのアプリの方にだけしかされておらず、Extensionの方に設定されていないのが原因でした。つらい...

ionic(cordova) での動画のトリミング

はじめに

ionic(cordova)で動画を時間でトリミングすることができるUIを実装したくてプラグインを探した。Instagramの動画投稿のようなものをイメージしている。

https://github.com/jbavari/cordova-plugin-video-editor
最初に見つかったのがこちらのプラグイン。READMEによると機能は
・Transcode a video
・Trim a Video (iOS only)
・Create a JPEG thumbnail from a video
で、UIは自前で実装することになりそう

(1)最初にサムネイルを生成してそのキャプチャを横に並べて横スクロール可能なUIの作成
(2)動画はvideoタグで表示して(1)をスクロールするとスクロール位置に合わせてseekさせる
(3)選択した範囲から開始と終了時間を取得してこのプラグインでトリミング(iOSのみだが)

こんなイメージになるだろうか...けっこう大変そう...あと、以前、ローカルのファイルをvideoタグで表示させるときに容量とか大きくなると苦労した記憶が...

cordova-plugin-video-trimming-editor プラグイン

https://github.com/nrikiji/cordova-plugin-video-trimming-editor
やりたいことはまさにこれでした

f:id:yoppy0066:20190820113034p:plain

// フォトライブラリから動画ファイルを選択
camera.getPicture(function(path) {
  // 選択された動画ファイルのパスをcordova-plugin-video-trimming-editorに渡す
  VideoTrimmingEditor.open({
    input_path:	path,
    video_max_time: 10, // 範囲指定可能な最大秒数
  }, function(result) {
    console.log(result); // { output_path: "/path/to/zzz.mp4" }
  }, function(error) {
  }
);
}, function(error) {
}, {
  sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
  mediaType: Camera.MediaType.VIDEO
});

以上です

flutter Widget rebuild statsを確認

Android Studioの話

1. 画面右下の「Flutter Performance」をクリック

f:id:yoppy0066:20190723224545p:plain

2. Widget rebuild stats」を選択して、「Show Widget rebuild information」のチェックを外す

f:id:yoppy0066:20190723224623p:plain

3. ビルドボタンする

f:id:yoppy0066:20190723224649p:plain

4.「Show Widget rebuild information」をチェックする

f:id:yoppy0066:20190723224709p:plain

5. Widgetがリビルドされると該当の箇所がグルグル回るのでわかる

f:id:yoppy0066:20190723224735g:plain

以上です