2018年5月30日水曜日

Xcode9.3.1と9.4

一週間くらいおいて続いてリリースされたのを書いておく。

Xcode9.3.1
  • Playground編集中にXcodeが遅くなる現象の修正
  • Apple IDの再入力が必要になる現象の修正
Xcode9.4
  • Swift 4.1、iOS 11.4、watchOS 4.3、tvOS 11.4、macOS High Sierra 10.13.4に対応
  • iOS 11.4のClassKit frameworkに対応
  • バグフィックスと安定性の向上
ClassKitってのはオブジェクト指向のクラスのことじゃなく、iPadを使った学校教育の管理SDKらしい。まあ当分使うことないな。

Xcode9.4の方は、App Storeアプリでアップデートをかけても何時間も待機中で待たされている。
その間にiPadとiPhoneをiOS11.4に上げちゃったもんだから、以前のXcodeで実機確認できなくなっちゃって、えらい迷惑😠
昼間から始めて、夜10時過ぎになってようやくインストール終わったよ。待たされたのは俺だけじゃなかったみたい。

2018年5月28日月曜日

複数のジェスチャーでViewをアフィン変換

やりたいこと

ピンチやローテートのジェスチャーを複数組み合わせてView(ImageView)の形を変更したい。
ジェスチャーは複数を一度に使えるように。

やり方

UIGestureRecognizerのdelegateを設定し、delegateメソッドで複数のGestureRecognizerの同時使用を許可。
複数のアフィン変換を組み合わせる処理をしてからViewに反映させる。
ジェスチャー(例ではpinchとrotate)使用中のtransformの状態と、終了時のtransformの状態をそれぞれ保存し、ジェスチャー開始時(再開時)は前回の続きから始めるようにする。

つまづいたとこ

Swift3あたりから書き方が変わったせいで、前回のtransformの状態から再開する方法、複数のアフィン変換を組み合わせるコードに迷った。

コード(Swift4)

class ViewController: UIViewController,UIGestureRecognizerDelegate {
    
    var myView = UIImageView()
    
    //ジェスチャーで変化中のtransformを保存
    var prevRotate = CGAffineTransform()
    var prevScale = CGAffineTransform()
    //ジェスチャー終了時のtransformを保存
    var prevEndRotate = CGAffineTransform()
    var prevEndScale = CGAffineTransform()
    

    override func viewDidLoad() {
        super.viewDidLoad()
        
        myView = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
        myView.image = UIImage(named: "image")
        myView.transform = CGAffineTransform.identity //念のため変形を初期化
        myView.isUserInteractionEnabled = true //ユーザーインタラクションを許可
        myView.center = CGPoint(x: self.view.frame.midX, y: self.view.frame.midY)
        self.view.addSubview(myView)
        
        let rotate = UIRotationGestureRecognizer(target: self, action: #selector(ViewController.rotate))
        rotate.delegate = self
        myView.addGestureRecognizer(rotate)
        
        let pinch = UIPinchGestureRecognizer(target: self, action: #selector(ViewController.pinch))
        pinch.delegate = self
        myView.addGestureRecognizer(pinch)
        
        //アフィン変換の初期値設定しとかないとダメ
        prevRotate = myView.transform
        prevScale = myView.transform
        prevEndRotate = myView.transform
        prevEndScale = myView.transform
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @objc func rotate(sender:UIRotationGestureRecognizer) {
        //前回のrotateの続きからrotateする
        let nowRotate = prevEndRotate.rotated(by: sender.rotation)
        //複数のアフィン変換を組み合わせる
        myView.transform = nowRotate.concatenating(prevScale)
        prevRotate = nowRotate

        //rotate終了時のtransform状態を保存
        if sender.state == .ended {
            prevEndRotate = nowRotate
        }
    }

    @objc func pinch(sender:UIPinchGestureRecognizer) {
        //前回のpinchの続きからscaleを変える
        let nowScale = prevEndScale.scaledBy(x: sender.scale, y: sender.scale)
        //複数のアフィン変換を組み合わせる
        myView.transform = nowScale.concatenating(prevRotate)
        prevScale = nowScale

        //pinch終了時のtransform状態を保存
        if sender.state == UIGestureRecognizerState.ended {
            prevEndScale = nowScale
        }
    }
    
    //複数のrecognizerの同時使用許可
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
}

参考サイト

2018年5月11日金曜日

Twitter Kit SDKのサポートが終わる!?

情報ソース:ツイッター開発者ブログ

唐突だが「2018年10月31日をもって、GitHub上でiOS、 AndroidとUnityを対象にした本オープンソースに関する質問や問題を受け付けることを終了します」と公式発表された。

「Twitter Kitはみんなが簡単にツイッターコンテンツを作れるように用意したもので、去年12月にオープンソースにしてより一層盛り上げるようにしたんだけど、開発者ニーズや開発環境が大きく変わるからサポートやめたよ」という理由だという。

これって文章通りに見るなら「GitHub上での質問や問題受付のみ終了する」んだけど、まさかTwitter Kit SDKの提供や使い方の解説なんかまでやめちゃわないよな?
だってその後に、Twitter Kitを使わないでアプリにツイートを表示する方法を紹介してるんだもん。

もしアプリからツイートできなくなったりすると、かなり不便になるからなあ。

実機確認でiPhoneのつなぎっぱなしに注意

こないだようやくiPhone8買ったんだけど(今まではiPadとシミュレータで開発してた)、実機確認のためにiPhoneをUSB接続すると、USB経由のテザリングと認識されちゃう。
なぜかiPhoneの設定で「インターネット共有」をOFFにしておいてもだ。Macでテザリング相手としてiPhoneを選んでないのに。

そのため、つないだままMacでYouTubeなんて見てると、どんどんSIM容量が消費されてしまうという悲惨な結果に。
充電のためにMacにつないだまま寝たら(Macはスリープ状態)、朝になってYouTube見た時ほどじゃないけど数MB消費されてた。Macがスリープ中に何か通信したんだろうけど。

というわけで、iPhoneで実機確認のためにUSB接続する際は、容量を消費しない低速モード(そういう設定があるキャリアなら)に設定しておいた方がいいだろう。

つーか、テザリング(インターネット共有)切ってあるのになんで勝手に接続しちゃうかな…。
うちの設定が悪いんだろうか?
モバイル通信できるiPadにSIM入れて使ってた時はそんなことなかったのに。

2018年5月10日木曜日

MenuControllerを表示

この「複製/削除」ね
Text関係をタップした時なんかにコピー/カット/ペースト/ユーザ辞書…とか表示される横長のメニューあるでしょ? あれを使いたいの。

お約束としてやらなきゃならないことはあるけど、難しくないんでコードだけ書いとく。
UIViewのサブクラスとして作っておかないとcanPerformAction()とかが使えないので注意。
タップしてメニューを出したいUIをFirstResponderにするのがキモ。TextFieldとかなら、編集始めると自動でFirstResponderになってくれるからその辺の処理がいらないかも?


//Swift4
//メニュー表示
func makeMenu() -> Void {
    //先にFirstResponderにしとかないとメニューは表示されない
    becomeFirstResponder()
    //複製、削除メニュー
    let menu = UIMenuController.shared
    menu.isMenuVisible = true
    menu.arrowDirection = .up //メニューの矢印(ふきだしのしっぽみたいなの)の向き
    menu.setTargetRect(self.bounds, in: self)
    //メニューアイテム
    let menuItem1 = UIMenuItem(title: "複製", action: #selector(Fukidasi.onDuplicate(sender:)))
    let menuItem2 = UIMenuItem(title: "削除", action: #selector(Fukidasi.onDelete(sender:)))
    let menuItems = [menuItem1, menuItem2]
    menu.menuItems = menuItems
    menu.setMenuVisible(true, animated: true)

}


//ふきだしのUIViewがFirstResponderにならないとメニューは表示されない
//以前はfuncだったものが最近readOnlyでfalseを返すvarに変わったので、overrideしてtrueを返させる
override var canBecomeFirstResponder: Bool {
    return true
}

//これmenu表示に必要
override func canPerformAction(_ action: Selector, withSender sender: Any!) -> Bool {
    if action == #selector(Fukidasi.onDuplicate(sender:)) ||
        action == #selector(Fukidasi.onDelete(sender:)) {
        return true
    }
    return false
}

@objc func onDuplicate(sender: UIMenuItem) {
    print("複製された")
}

@objc func onDelete(sender: UIMenuItem) {
    print("削除された")
}