- 投稿日:2020-09-17T23:26:17+09:00
対象(ImageView)の回転について
はじめに
iPhoneアプリ開発における目標物の回転についての記事を書こうと思います。
先日、私事ではありますが自身初のiPhoneアプリであるルーレットアプリをAppStoreの方に公開させていただきました。
その際にImageViewの回転について実装したので、自分で思い出す意味でも記事にしておこうと思います。実装
回転を実装するにはanimateプロパティにおいて、"CGAffineTransform"を用います。CGAffineTransform(rotationAngle:CGFloat.回転角)とすることで実装できます。
viewController.swiftimport UIKit class ViewController: UIViewController { @IBOutlet weak var rouletteView: UIImageView! @IBOutlet weak var button: UIButton! func rotation() { UIView.animate(withDuration: 1, animations: { self.rouletteView.transform = CGAffineTransform(rotationAngle: CGFloat.pi) }) } @IBAction func go(_ sender: Any) { rotation() } }以上のように記述すると、回転角が pi = 180°となっているので半回転します。
※ここで注意したいのが、角度の指定にはpiを用いなければならないので、viewController.swiftself.rouletteView.transform = CGAffineTransform(rotationAngle: CGFloat.45)などとするとエラーが返されます。
なので、細かい角度まで調整したいときは、viewController.swiftself.rouletteView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/4) self.rouletteView.transform = CGAffineTransform(rotationAngle: 45*CGFloat.pi/180)//などでも正常に動作します。もう一つ問題があります。
例えば、以下のように240°回転させるような記述をしたとします。viewController.swiftself.rouletteView.transform = CGAffineTransform(rotationAngle: 240*CGFloat.pi/180)すると...
このように、120°逆回転しています。
この記述は、確かに240°回転するように書かれています。ただ、機械からすると240°回転したときの"到達点"への回転移動を行っていることになります。したがって、240°右回転するよりも120°左に回ってしまった方が、速く処理できますから左回転してしまうわけです。ということはもちろん以下の記述をしたときは...
viewController.swiftself.rouletteView.transform = CGAffineTransform(rotationAngle: 360*CGFloat.pi/180)解決方法
では、181°~360°での右回転はどのようにしたらいいのかというと...
viewController.swiftself.rouletteView.transform = CGAffineTransform(rotationAngle: 180*CGFloat.pi/180) self.rouletteView.transform = CGAffineTransform(rotationAngle: 240*CGFloat.pi/180)このように、2回に分けて処理を行えば動作します。1行目の
self.rouletteView.transform = CGAffineTransform(rotationAngle: 180*CGFloat.pi/180)で
半回転させてしまえば、そこ(180°)の地点から181°~360°の地点は右回転が最短ルートになるので、そのまま右回転を続けるというわけです。
また、ルーレットのように複数回回転するような場合は、以下のように制御構文を用いてあげれば問題ないです。
viewController.swiftfunc rotation() { UIView.animate(withDuration: 1.5, animations: { let number = Int.random(in: 1..<360) for _ in 0...2{ self.rouletteView.transform = CGAffineTransform(rotationAngle: 180*CGFloat.pi/180) self.rouletteView.transform = CGAffineTransform(rotationAngle: 360*CGFloat.pi/180)//360で1回転 } self.rouletteView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/180*CGFloat(number)) }) }まとめ
今回、初めてのQiita投稿になったので思っていたよりも時間がかかってしまいましたが、なんとか投稿できてよかったです。文章やコードなど、おかしい点などありましたらコメントいただけると助かります。
また、iOSは14.0にアップデートされXcodeも12.0へバージョンアップしたのでできるだけ速く順応できるようにがんばりたいです!
- 投稿日:2020-09-17T19:25:20+09:00
Flutter で Swift のライブラリがリンクされずにビルド失敗する
問題
Flutter でプラグイン(ネイティブ実装)を含む新しいパッケージを使うようにしたら、突然 Swift 関係のリンクエラーでビルド失敗することがあります。
ld: symbol(s) not found for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation) ld: warning: Could not find or use auto-linked library 'swiftCoreLocation' ld: warning: Could not find or use auto-linked library 'swiftCoreFoundation' ld: warning: Could not find or use auto-linked library 'swiftCompatibility50' ld: warning: Could not find or use auto-linked library 'swiftFoundation' ...原因
flutter create -i objc で作成した Flutter アプリから、Swift コードを含むパッケージを参照すると、この問題が発生します。Swift 関係のライブラリの参照設定が全く行われていないからです。
flutter create 時に Swift (デフォルト)が選択されていればこの問題は発生しません。
解決
Flutter の Runner ワークスペース(ios/Runner.xcworkspace) を開いて、何か1つ Swift のコードを追加してやれば治ります。
- Xcode を使って、ios/Runner.xcworkspace を開きます。
- New File... メニューを使って何か1つダミーの Swift ファイルを追加します。
- 「Swift - ObjC 間の Briding-Header.h を作るか」と尋ねられるので、「YES」を選択してヘッダを生成させます。
- 投稿日:2020-09-17T18:43:24+09:00
Cocoapodsのアンインストール方法
(1)Macのコマンドを使って
自分が作成しているアプリのファイルの場所まで進む(2)以下を入力してEnter
「gem list --local| grep cocoapods」Cocoapodsのバージョンやファイルの一覧がでてきます。
(写真参照願います)(3)cocoapodsのバージョンやファイル名が確認できたら以下を入力してEnter
sudo gem uninstall 「削除したいcocoapodsのファイル名」
僕の場合は8つのファイルをuninstallしました
(写真に赤線を引っぱっておきました)以上です。
※「青」で塗りつぶしてあるところは
自分のmacの名前と 現在作成しているアプリのファイル名になります。
- 投稿日:2020-09-17T14:24:32+09:00
【iOS 14】SKOverlay を使ってオススメ/関連アプリを紹介する UI をお手軽実装
はじめに
時事雑談
新しい iPhone の発売が遅れるというアナウンスもあって,
iOS 14 のリリースの仕方が気になっていましたが,
9/15 のイベントの中でかなり急な感じでリリース発表がありました。
(えっ明日!? Xcode 12 GM 出てたっけ?)
iOS 14 リリース日に合わせてアプリの配信を。と
考えていたデベロッパも多いはずで間に合うことを祈りたいですね・・・iOS 14 で別のアプリをユーザーに薦め,すぐに App Store でインストール可能にする SKOverlay が使えるようになりました。
実装自体とても簡単で,UIKit,SwiftUI 両方で使えます。
自分の作った別のアプリを紹介するという形で組み込んでみたので
実装していて気づいた点を含め紹介します。
全体の動きはこんな感じです。SKOverlay とは
Developer サイト1 には下記のように説明があります。
By displaying an overlay, you can recommend another app to users and enable them to download it immediately.
オススメしたいアプリやコンテンツに関連するアプリなどを表示させ,
App Store へ遷移させることでユーザがすぐにアプリ情報にアクセス,
アプリインストールが可能になります。今まで App Store へのリンクや UI を用意していたのが,
ほんの少しの実装で代わりになってくれます。
iOS 14 以上対応なので,iOS 13 以下は別途対応が必要です。
(私はあえて iOS 13 以下は何もしないことにしました。)アプリのレビュー促進(
SKStoreReviewController
2)など
簡単な実装で実現できる便利で Good な機能あるので StoreKit いいですよね。SwiftUI
StoreKit
をimport
します。
指定された条件がtrue
の場合に表示されます。
例えばボタンタップで表示させたりができます。import SwiftUI import StoreKit // 追加 struct ContentView: View { // SKOverlayを表示するかの状態 @State private var showSKOverlay = false var body: some View { Button("オススメアプリを表示") { self.showSKOverlay.toggle() // 状態切替 } .appStoreOverlay(isPresented: $showSKOverlay) { SKOverlay.AppConfiguration(appIdentifier: "1234567890", position: .bottom) } } }
appIdentifier
は App Store のアプリページのリンクの
末尾のid
を除いた数字部分を文字列にしたもので,
https://apps.apple.com/jp/app/id1234567890
だったら "1234567890" です。表示位置は 2パターンあり,
.bottom
と.bottomRaised
です。
後者は少し上に上がって表示されます。TabBar とか ToolBar を意識しているのかな?
.bottom
.bottomRaised
Run した場合ビルド自体は通るのですが,プレビューを見ようとしたらできないです。
いつか治るといいのですが・・・・(リリース版でもダメ?)UIKit
私は UIKit の方で対応したので,
このような関数を用意してコールしてみました。
scene に対して present するあたりが特徴でしょうか。import StoreKit // 追加 @available(iOS 14.0, *) private func displayOverlay() { guard let scene = view.window?.windowScene else { return } let config = SKOverlay.AppConfiguration(appIdentifier: self.myAppInfo.appIdentifier, position: .bottom) let overlay = SKOverlay(configuration: config) overlay.present(in: scene) }表示位置は同じで,
.bottom
と.bottomRaised
の2パターンです。ダークモードも対応しているので色の設定等の対応は不要です。
OPEN のところはローカライズされています。(私が英語環境のため)
インストール済みだったら該当のアプリが開きます。インストールされていない場合やボタン以外のビューをタップすると
App Store に遷移します。実装していての気づき
シミュレータでは確認できない
実装してみてシミュレータで動作確認しようとしたところ,
アプリ情報が表示されなくて少し時間を無駄にしました。
iOS 14 の実機で確認すればちゃんと表示されます。スクロール量の調整
表示は画面の下からコンテンツに被さる形となるため,
position を.bottomRaised
にすることで事足りる場合はいいのですが,
コンテンツが隠れてしまうことが多いと思います。
UIScrollView
(もちろんUITableView
やUICollectionView
も)を
使っている場合はスクロール量を少し調整してあげたら良いと思います。
使っていない場合は制約を調整すれば良さそうです。私のアプリは
UIScrollView
を使っていたので
下記のように iOS 14 以上ではコンテンツサイズを調整させました。override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) if #available(iOS 14.0, *) { self.baseScrollView.contentSize.height += 100.0 self.displayOverlay() } }一応表示は下にスワイプすると隠れてくれます。
表示/非表示が完了するタイミング,ロードが失敗した際に呼ばれる,
Delegate メソッドが用意されているので,
レイアウトをしっかりしたい場合等はこちらで調整しても良さそうです(SKOverlayDelegate
)。3例えば,表示されたらコンテンツのサイズ変更し,
非表示になったらアニメーションつけてサイズを戻してあげたりできますね。
お手軽感は薄れますがこっちの方が良さそうです。// overlay.delegate = self @available(iOS 14.0, *) extension HogeViewController: SKOverlayDelegate { // SKOverlayの表示が終わったときに呼ばれる func storeOverlayDidFinishPresentation(_ overlay: SKOverlay, transitionContext: SKOverlay.TransitionContext) { self.baseScrollView.contentSize.height += 100.0 } // SKOverlayが非表示になり始めたときに呼ばれる func storeOverlayWillStartDismissal(_ overlay: SKOverlay, transitionContext: SKOverlay.TransitionContext) { UIView.animate(withDuration: 1.0) { self.baseScrollView.contentSize.height -= 100.0 } } }適切なタイミングで非表示に
よく仕様を確認せず表示されたことに満足していたところ,
画面遷移などによって表示させたい画面を離れても
表示されたままになってしまい,UX を損なうことがありました。VC に対しての present ではなく,scene に対しての present のためです。
/// Attempts to present an app overlay in a `UIWindowScene`. @available(iOS 14.0, *) open func present(in scene: UIWindowScene)そのため,非表示にする実装もしっかりしないといけないです。
画面を離れる際に非表示にするように対応しました。override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) if #available(iOS 14.0, *) { self.dismissOverlay() } } @available(iOS 14.0, *) private func dismissOverlay() { guard let scene = view.window?.windowScene else { return } SKOverlay.dismiss(in: scene) }おわりに
iOS 14 から使えるようになった SKOverlay について書きました。
お手軽に実装できるので,
もし関連アプリやオススメしたいアプリがあったらぜひ実装したいですね。強調しすぎるのも良くないので適切な画面,タイミングで表示し,
同じく非表示にしてあげるのも大事だと思いました。ご覧いただきありがとうございました?♂️
App Clip でも使えるらしいけどキャッチアップがまだ・・・4
https://developer.apple.com/documentation/storekit/skoverlay ↩
https://developer.apple.com/documentation/storekit/skstorereviewcontroller ↩
https://developer.apple.com/documentation/storekit/skoverlaydelegate ↩
https://developer.apple.com/documentation/app_clips/recommending_an_app_clip_s_corresponding_app ↩
- 投稿日:2020-09-17T06:07:13+09:00
Device Orientationで迷ったときに見る記事
向きの一覧
デバイスの向き一覧はこちらの記事にあります。
Orientationのパラメータのまとめ(UIDeviceOrientation, UIInterfaceOrientation)
起動してすぐはデバイスの向きが取れない
UIDevice.current.orientationでデバイスの向きは取れるのですが、アプリを起動してすぐは.unKnownを返すようになっています。
起動してすぐの向きはUIInterfaceOrientationで確認できます。
iOS12まで
UIApplication.shared.statusBarOrientationiOS13以降
self.view.window?.windowScene!.interfaceOrientation*ただし、画面内のインターフェイスの向きなので、
viewが現れていない状態でUIInterfaceOrientationを確認するとnilになります。
viewDidAppear ~ viewWillDisAppear の間で確認するようにします。
また、デバイスを寝かせて置いた状態(.faceUp, .faceDown)は認識できません。
Core MLを使ったアプリを作っています。
機械学習関連の情報を発信しています。contact:
rockyshikoku@gmail.com