- 投稿日:2020-08-12T22:45:25+09:00
[Swift5]AVAudioEngineを使ってリアルタイム処理を行う最小実装
環境
- macOS: Catalina
- Xcode 11.6
- Swift 5.2
リアルタイムに音声に処理を行う
- 入力をそのまま出力
- 入力にエフェクトをかけて出力
ソースコード
class ViewController: UIViewController { var engine = AVAudioEngine() override func viewDidLoad() { super.viewDidLoad() setupAudioSession() let input = engine.inputNode let output = engine.outputNode let format = engine.inputNode.inputFormat(forBus: 0) engine.connect(input, to: output, format: format) try! engine.start() } func setupAudioSession() { do { let session = AVAudioSession.sharedInstance() try session.setCategory(.playAndRecord, options: [.defaultToSpeaker, .allowBluetooth]) try session.setActive(true) } catch { fatalError("Failed to configure and activate session.") } }補足1 AVAudioEngine
AVAudioEngineは暗黙的にinputとoutputを持っているためノードを
attach
する必要性がない。
つなげる時はconnect
を用いる。補足2 AudioSession
func setupAudioSession() { do { let session = AVAudioSession.sharedInstance() try session.setCategory(.playAndRecord, options: [.defaultToSpeaker, .allowBluetooth]) try session.setActive(true) } catch { fatalError("Failed to configure and activate session.") } }これは
AVAudioSession
の設定でステレオオーディオを録音するには、recordまたはplayAndRecordカテゴリのいずれかを使用するアプリのオーディオセッションが必要。
また、
option
でiPhoneのデフォルトのスピーカーを使用することとBluetoothイヤホンで再生と録音が可能に設定。詳しくはappleサンプルを参考にしてください!
エフェクトをかけたい場合
エフェクトをかけたい時にはノードを追加してあげると簡単にエフェクトをかけることができる。
アプリ起動時はイヤホン付けてデバックしないととハウリングが起きるので注意!var engine = AVAudioEngine() var delay = AVAudioUnitDelay() var reverb = AVAudioUnitReverb() override func viewDidLoad() { super.viewDidLoad() setupAudioSession() let input = engine.inputNode let output = engine.outputNode let format = engine.inputNode.inputFormat(forBus: 0) delay.delayTime = 2.0 reverb.loadFactoryPreset(.largeHall) reverb.wetDryMix = 40 engine.attach(delay) engine.attach(reverb) engine.connect(input, to: delay, format: format) engine.connect(delay, to: reverb, format: format) engine.connect(reverb, to: output, format: format) try! engine.start() }補足3 エフェクトをつなげる時は
attach
してから
inputNode
とoutputNode
はattach
する必要性がなかったが、エフェクトNodeを使用する際にはattach
しないといけない。
遅延を入れてリバーブをかけるとコンサートホールにいるみたいなエフェクトになった!Bluetoothでもわかるくらいリバーブがいい感じにかかってて感動!
エフェクトクラス一覧
他にもエフェクトがあるので紹介(今回のも含めて)
- AVAudioUnitReverb リバーブ処理
- AVAudioUnitTimeEffect 非リアルタイムエフェクト処理
- AVAudioUnitTimePitch 高品質の再生速度と音程シフトを互いに独立して提供
- AVAudioUnitVarispeed 再生速度の制御
- AVAudioUnitDelay 遅延処理
- AVAudioUnitEQ マルチバンドイコライザの実行 EQ→イコライザの略
- AVAudioUnitDistortion 歪みエフェクト
まだ使っていないエフェクトを使って音遊びしていきたい。
まとめ
AVAudioEngineは暗黙的にinputとoutputを持っているためノードをつなげる必要性がない。
WWDC2019に新しくAVAudioEngineが新しくなったらしいので、
このセッションを見て復習する。
- 投稿日:2020-08-12T18:58:40+09:00
[SwiftUI]TextField�のリターンイベントはonCommit
TextFieldを使う時にリターンが押された際の処理は必須だと思います。
SwiftUIのTextFieldでは、リターンのイベント処理をonCommitで定義できます。TextField("ぷれいすほるだー", text: $name, onCommit: { //任意の処理 })
- 投稿日:2020-08-12T17:32:04+09:00
【iOS】Pythonista3でGIFアニメ。でハマったこと。
はじめに
GIFアニメを作るアプリはたくさんありますが、せっかくPythonista3があるので、多くの先輩方の記事を参考にしながら、自分でも作ってみようと思いました。
すぐにハマる
以前にWin10上のPythonでGIFアニメを作ったことがあったので、その記憶を頼りにPILで書いたのですが、1枚目の画像しか保存されませんでした。
testGIF.pyw,h = 100,100 images = [] for c in range(0,256,8): img = Image.new('RGB',(w,h),(c,c,c)) images.append(img) images += reversed(images) SaveName = 'test.gif' images[0].save(SaveName, save_all=True, append_images=images[1:], optimize=False, duration=20, loop=0)いろいろ調べたのですが、結局win10上では動作確認ができたのでiOSのPILではうまくいかないという結論にしました。
その名も「images2gif」
調べてるうちに同様の質問があり、「images2gif」の存在を知りました。
僕のやりたかったことがそのまま名前になったようなモジュール名です。それは公式のドキュメントにも紹介されていて初めからインストールされてるものでした。
「images2gif」の使い方
基本的には次の記述でいいみたいです。
writeGif( SaveName, ImageList, duration=0.1,repeat=True)
testGIF2.pyfrom PIL import Image from images2gif import writeGif w,h = 100,100 images = [] for c in range(0,256,8): img = Image.new('RGB',(w,h),(c,c,c)) images.append(img) images += reversed(images) SaveName = 'test.gif' writeGif( SaveName, images, duration=0.02,repeat=True)PythonのGIFアート
日本語ヘルプ
最後にビックリしたのは、探してたどり着いた日本語ヘルプの記事が、「以前、僕が自分で投稿したもの」で、いよいよヤバいと思いました。
- 投稿日:2020-08-12T14:23:11+09:00
initの初歩 ?【Swift】
initとは??
- インスタンスを生成するときに、自動で呼び出されるメソッド。
- イニシャライザとも呼びます。
- クラスのプロパティの初期値を設定するときに使用。
Swiftのメソッドは、3種類あります。
その一つが、「initメソッド」 Swiftのメソッドの種類class Car { var color = "" var wheel = 0 init() { print("initは、インスタンスを生成するときに自動で呼び出される、特殊なメソッドです。") } } let car = Car() // インスタンス生成。実行結果.initは、インスタンスを生成するときに自動で呼び出される、特殊なメソッドです。
- 英 initialize: 「初期化する」
- 「初期化」=初めて変数にデータを入れること。
インスタンスとは??
クラスは設計図。
クラスは、インスタンス化しないと使うことができません。
instance=「実態」let car = Car() // クラス名()を、変数に代入。 「インスタンス化」インスタンス化とは、
クラスという「設計図」から、インスタンスという実際に使える「モノ」を作ること。
補足
クラス名()
とすることでインスタンスが生成できます。Car() // インスタンス生成。
クラス名().変数名
とすることで、そのクラスの変数にアクセスできます。print(Car().wheel) // インスタンス生成。「クラス名().変数名」の実行結果.initは、インスタンスを生成するときに自動で呼び出される、特殊なメソッドです。 0しかし、インスタンスを「変数や定数に代入」してから
使用する方法が一般的です。
init内で、プロパティの初期値を設定してみる。
class Car { var color : String // 型の指定。 var wheel : Int init() { color = "Red" // init内で、プロパティの初期値を設定。 wheel = 4 // 同じく。 } } let car = Car() // インスタンス生成。 print(car.color) print(car.wheel)実行結果.Red 4
var color = ""
でも良いですが、不要なので型指定だけ。- (
var
を削除すると「colorって何?」となるので、エラー。)- 型指定では、
:
を使います。- インスタンスは、
(car.color)
のように「インスタンス名.○○」で値を参照。また、インスタンス生成時にinitが優先的に呼び出されるので、
var color = "Green"
でも、var color = "Blue"
でも、
実行結果には反映されない。なぜ、initを使うのか。?
理由は色々あるらしいです。
クラスの記述内容がより分かりやすくなり、
明確に初期化を行いソースコードの安全性を高める為。プロパティが正しく初期化できない場合...
インスタンス生成時に「引数で、インスタンスプロパティの初期値を与える」
ということが出来ない。メモリ安全でない。
(メモリ確保、初期化がされる前にインスタンスにアクセスしてしまう)initメソッドに、『引数』を指定してみる。
self.
= color
,= wheel
(color: String, wheel: Int)
『self』
selfとは、「インスタンス自身」を指す言葉。
クラスのインスタンスメソッド内でのselfは、
自分自身(クラスのインスタンス)を示します。自分のクラス内の何かにアクセスしたい時に使います。
(color: String, wheel: Int)について。
クラスの引数に、値だけ指定するとエラー?
// インスタンス生成のとき。 let car = Car("Red", 4) // <--- 引数に、値だけ指定するとエラー。error.Missing argument labels 'color:wheel:' in callパラメーター(ラベル)、必須です。
パラメータとラベルは、同名でもOKだけど、違うモノらしい。
[Swift] 関数ラベルの使い方を学ぶ順番が違ってもエラー?
// インスタンス生成のとき。 let car = Car( wheel: 4, color: "Red") // <--- 順番が違ってもエラーerror.Argument 'color' must precede argument 'wheel'おしまい。
- 投稿日:2020-08-12T14:23:11+09:00
initの初歩?【Swift】
initとは??
- インスタンスを生成するときに、自動で呼び出されるメソッド。
- イニシャライザとも呼びます。
- クラスのプロパティの初期値を設定するときに使用。
Swiftのメソッドは、3種類あります。
その一つが、「initメソッド」 Swiftのメソッドの種類class Car { var color = "" var wheel = 0 init() { print("initは、インスタンスを生成するときに自動で呼び出される、特殊なメソッドです。") } } let car = Car() // インスタンス生成。実行結果.initは、インスタンスを生成するときに自動で呼び出される、特殊なメソッドです。
- 英 initialize: 「初期化する」
- 「初期化」=初めて変数にデータを入れること。
インスタンスとは??
クラスは設計図。
クラスは、インスタンス化しないと使うことができません。
instance=「実態」let car = Car() // クラスの呼び出しを、変数に代入。 「インスタンス化」インスタンス化とは、
クラスという「設計図」から、インスタンスという実際に使える「モノ」を作ること。
init内で、プロパティの初期値を設定してみる。
class Car { var color : String // 型の指定。 var wheel : Int init() { color = "Red" // init内で、プロパティの初期値を設定。 wheel = 4 // 同じく。 } } let car = Car() // インスタンス生成。 print(car.color) print(car.wheel)実行結果.Red 4
var color = ""
でも良いですが、不要なので型指定だけ。- (
var
を削除すると「colorって何?」となるので、エラー。)- 型指定では、
:
を使います。- インスタンスは、
(car.color)
のように「インスタンス名.○○」で値を参照。また、インスタンス生成時にinitが優先的に呼び出されるので、
var color = "Green"
でも、var color = "Blue"
でも、
実行結果には反映されない。なぜ、initを使うのか。?
クラスの記述内容がより分かりやすくなり、
明確に初期化を行いソースコードの安全性を高める為。initメソッドに、『引数』を指定してみる。
self.
= color
,= wheel
(color: String, wheel: Int)
self
selfとは、「インスタンス自身」を指す言葉。
クラスのインスタンスメソッド内でのselfは、
自分自身(クラスのインスタンス)を示します。自分のクラス内の何かにアクセスしたい時に使います。
- 「インスタンス変数」 それぞれのインスタンスに属する変数。
- 「インスタンス メソッド」 基本的に、"メソッド"と言うと、これを指します。
(color: String, wheel: Int)について。
クラスの引数に、値だけ指定するとエラー?
// インスタンス生成のとき。 let car = Car("Red", 4) // <--- 引数に、値だけ指定するとエラー。error.Missing argument labels 'color:wheel:' in callパラメーター(ラベル)、必須です。
パラメータとラベルは、同名でもOKだけど、違うモノらしい。
[Swift] 関数ラベルの使い方を学ぶ順番が違ってもエラー?
// インスタンス生成のとき。 let car = Car( wheel: 4, color: "Red") // <--- 順番が違ってもエラーerror.Argument 'color' must precede argument 'wheel'おしまい。
おまけ
タイヤ8個の緑車を追加。
class Car { var color : String var wheel : Int init(color: String, wheel: Int) { self.color = color self.wheel = wheel } } let car = Car(color: "Red", wheel: 4) // インスタンス生成。 let secondCar = Car(color: "Green", wheel: 8) // インスタンス生成。 print(car.color, car.wheel) // 「,」で区切れます。 print(secondCar.color, secondCar.wheel)
- 投稿日:2020-08-12T13:48:05+09:00
How to build MediaPipe hand tracking (iOS) as an Xcode project
Agenda
- Preparation (MediaPipe)
- Build from terminal
- Preparation (Tulsi)
- Build as an Xcode project
Demo
Hand Tracking on iOS?#MediaPipe pic.twitter.com/mgHPfUIHwK
— MIWA Tetsushi (@WWWPONTE) August 11, 2020DEV
- MacBook Pro: Catalina 10.15.4
- Xcode: 11.6
- iPhone SE (2nd generation): iOS 13.5.1
Preparation (MediaPipe)
1. Install Homebrew
- Copy the command at https://brew.sh
- Paste to your terminal and execute
- The version of Homebrew I used this time was 2.4.7.
$ brew -v # Homebrew 2.4.72. Install Command Line Tools
- Go to https://developer.apple.com/download/more and download Command Line Tools for Xcode XX ※You need to sign in Apple Developer Program.
- Double-click
.dmg
- Execute the following command
$ sudo xcodebuild -license3. Check the Python version
Python is pre-installed on mac by default. Depending on the Python version, the build may not pass, so it is necessary to check the Python version. I confirmed that the build passed with
Python 3.7.5
, so I recommend building with this version of Python. At that time, there is a version manager calledpyenv
that can switch the Python version, so I will explain how to use it.
- Clone the pyenv repository
$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv
- Add to path
.zsh_profile
zsh
$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zsh_profile $ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zsh_profile $ echo 'eval "$(pyenv init -)"' >> ~/.zsh_profile
- Install
Python 3.7.5
$ pyenv install 3.7.5
- Rehash
shim
$ pyenv rehash
- Specifying the Python version
The global command sets the global Python version. This can be overridden with other commands, but is useful for ensuring you use a particular Python version by default. If you wanted to use
3.7.5
by default, then you could run this:$ pyenv global 3.7.5The local command is often used to set an application-specific Python version. You could use it to set the version to
3.7.5
:$ pyenv local 3.7.54. Install six library
Install
six
library for absorbing the difference between Python 2 and Python 3.$ pip install –user future six5. Clone the MediaPipe repository
$ git clone https://github.com/google/mediapipe.git6. Install Bazel
- Insatll
Bazel
$ brew install bazel
- The version of bazel I used this time was 3.3.0.
$ bazel --version # bazel 3.3.07. Install OpenCV and FFmpeg
$ brew install opencv@38. Install numpy
$ pip install numpy9. Check installation with Hello World
- Execute
Hello World desktop example
$ export GLOG_logtostderr=1 $ bazel run --define MEDIAPIPE_DISABLE_GPU=1 \ mediapipe/examples/desktop/hello_world:hello_world # After building bazel (it takes a few minutes), it is OK if "Hello World!" is displayed 10 times as shown below. # Hello World! # Hello World! # Hello World! # Hello World! # Hello World! # Hello World! # Hello World! # Hello World! # Hello World! # Hello World!Build from terminal
1. Prepare Provisioning Profile
In order to execute the iOS app on your device, a file for identifying the iOS device or app called the
Provisioning Profile
is required. Apple Developer Program subscribers can create and download from https://developer.apple.com/jp. Rename the downloaded file toprovisioning_profile.mobileprovision
and place it inmediapipe/mediapipe/
.2. Change Bundle Identifier
Next, fix
BUILD
atmediapipe/mediapipe/examples/ios/handtrackinggpu/
. Changebundle_id
to the same one which you set atProvisioning Profile
.mediapipe/examples/ios/handtrackinggpu/BUILD:36bundle_id = BUNDLE_ID_PREFIX + ".HandTrackingGpu", ↓ bundle_id = "(Bundle Identifier)",3. Build
Move
mediapipe/
and execute the following command.$ bazel build -c opt –config=ios_arm64 mediapipe/example/ios/handtrackinggpu:HandTrackingGpuAppAn IPA file is generated at the following directory.
bazel-bin/mediapipe/examples/ios/handtrackinggpu/Preparation (Tulsi)
1. Clone the Tulsi repository
$ git clone https://github.com/bazelbuild/tulsi.git2. Apply the patch
$ cd tulsi $ git fetch origin pull/99/head:xcodefix $ git checkout xcodefix3. Execute the build script
sh build_and_run.shOccurred an error!!
ERROR: /Users/miwa/tulsi/BUILD:62:18: Linking of rule '//:tulsi.__internal__.apple_binary' failed (Exit 1) wrapped_clang failed: error executing command external/local_config_cc/wrapped_clang -Xlinker -objc_abi_version -Xlinker 2 -fobjc-link-runtime -ObjC -arch x86_64 -filelist ... (remaining 26 argument(s) skipped)After checking this, I could execute the build script!
tulsi/WORKSPACE:6tag = "0.17.2", ↓ tag = "0.18.0",4. Open MediaPipe.tulsiproj and generate the Xcode project
- Launch Tulsi.app and open
Mediapipe.tulsiproj
atmediapipe/mediapipe/
.
- Push
Generate
button inConfigs
tabBuild as an Xcode project
Now connect your iPhone to your mac. Then open the generated Xcode project and start the build. After a while, the build will be completed and it will be installed on your iPhone.
Set to use the rear camera.
mediapipe/examples/ios/handtrackinggpu/ViewController.mm:107_cameraSource.cameraPosition = AVCaptureDevicePositionFront; ↓ _cameraSource.cameraPosition = AVCaptureDevicePositionBack;Displaying reversed image, so I changed the value of
Mirrored
.mediapipe/examples/ios/handtrackinggpu/ViewController.mm:111_cameraSource.videoMirrored = YES; ↓ _cameraSource.videoMirrored = NO;Summary
I succeed in implementing high-precision real-time hand tracking on iOS.
In the future, I will develop learning iOS applications for the visually impaired with this technology.
- 投稿日:2020-08-12T12:20:58+09:00
[iOS] FirebaseDistributionへのテスター登録手順(developer証明書の場合)
iOSのbetaアプリ配布にFirebaseDistributionを利用している場合、且つ証明書がEnterprise版ではなくDeveloper版の場合、追加手順を共有するのが厄介だと思いましたので、ざっとまとめました。
追加したいメンバーなどにこのドキュメントを共有することで楽ができればとw
ちなみに、Firebase側管理画面の更新により手順が変わることがあると思います。
本ドキュメントは 2020/06月時点 のものですので、あしからず。
※下記ブログの転載です
https://rc-code.info/others/post-339/管理側手順 (2020/06現在)
1.FirebaseDistribution のアプリ管理画面から招待リンクを取得します。
2.テスターにテスター側手順の 22 まで進めていただきます。
3.管理者には下記のようなメールが届きますので、Appleの証明書にDevice登録を行います。
DeveloperサイトでUDIDを登録してください
https://developer.apple.com/account/resources/devices/list
4.更新された証明書でアプリをビルドし、ipaを FirebaseDistribution にアップしてください。
テスター側手順 (2020/06現在)
1.招待リンクを頂いてください
2.テストしたい端末からリンクをタップしてください
下記画面が開きます
6.下記左の画面が開くので、ボタンを押して右側の画面に進み、
チェックボックスを埋めた後、Accept Invitation をタップしてください。
7.下記画面になりますので、再度チェックを埋め Start testing on this device をタップしてください。
8.下記画面になりますので、Install をタップしてください。
9.すると下記画面になりますので、Download profile ボタンをタップしてください
10.下記のようなアラートが表示されますので、許可 ボタンを押してください。
(これはテストアプリを利用するために端末に悪意のないファイルを入れますよ、というアラートですので、ご安心ください?)
11.ダウンロードが終わると下記アラートが出ますので、画面を閉じて 設定アプリ を開いてください。
14.Firebase App Distribution をタップ
16.パスコードを聞かれるので、端末を開く際のパスワードを入力してください。
18.インストールが完了するので、設定アプリを閉じてください。
19.App Distribution というアプリがインストールされているので、タップしてください。
20.アプリを開くと再度 SignUp を求められるので、SignUpしてください。
21.インストールできるテストアプリが表示されるので、タップしてみてください。
(サンプルは2つですが、おそらく1つ表示されていると思います)
22.アプリを洗濯すると Waiting for developer と表示されていると思いますので、この状態で次のアプリ配信をお待ちください!
開発者があなたの端末をテスト端末として許可しますので次回配布のアプリからインストールが可能になります。
23.開発者が端末の許可作業を終えたのち、再度アプリ配布が行われると、下記画面のように App Distribution のアプリでダウンロードが可能になります!
ダウンロードボタンをタップして、アプリのインストールを待ちましょう!
24.以上でテストアプリの導入作業はおしまいです!
以後、テストアプリがアップデートされると同様に App Distribution のアプリからダウンロードできます。
ご協力ありがとうございます?♂️
お疲れ様でしたお疲れ様でした!
- 投稿日:2020-08-12T11:42:34+09:00
UIBarButtonItemのSystemItemで使えるUIImageを取得する
経緯
アプリからTwitterやメモアプリ等の外部アプリに画像など、情報を共有したい時のボタンにアクションボタンを使用したい場合がある
↓こういうやつ
こういう場合に限らず、画像の横にある本の画像、またはゴミ箱画像とかも使いたい。
UIBarButtonItemだと使えるけど、それからUIImageを引き出す事は基本的にできない…
それに画像をネットから同じような画像を探すにしても、商用利用などを確認するのも面倒すぎる…?♂️という事で、UIBarButtonItemのSystemItemからimageだけを使用する方法を共有します?♂️
実装
検証環境は下記の通りです
OS:10.15.6
Xcode:11.6
Swift:5.0早速ですが、実装したコードの紹介をします
UIBarButtonItem.SystemItem+extention.swiftextension UIBarButtonItem.SystemItem { func image() -> UIImage? { let tempItem = UIBarButtonItem(barButtonSystemItem: self, target: nil, action: nil) let bar = UIToolbar() bar.setItems([tempItem], animated: false) bar.snapshotView(afterScreenUpdates: true) // imageを取得する let itemView = tempItem.value(forKey: "view") as! UIView for view in itemView.subviews { if let button = view as? UIButton, let image = button.imageView?.image { return image.withRenderingMode(.alwaysTemplate) } } return nil } }使う時はこのように↓
UIBarButtonItem.SystemItem.action.image()候補: SF Symbol・systemNameを検討する
そもそも論ですが、iOS13からはSF SymbolやUIImageViewでsystemNameが使用できるようになっています。
使用する方法は以下の通りです。// UIKit UIImageView(systemName: "xxx") // SwiftUI Image(systemname: "xxx")この"xxx"に入る文字列は、下記URLよりDLできるSF Symbolアプリから確認できます。
https://developer.apple.com/design/resources/
FigamaやSketch等のデザインツールを利用すると細かくサイズ等を調整したりできるみたいなので
対応できる方はこっちの方がいいと思います。
※記事投稿時点ではBeta版です最後に
Twitterのアカウントがありますのでフォローしてくれると嬉しいです!!
@swift_nita
なお今回のサンプルはGithubに上げていますのでご参考までに!
https://github.com/ni-ta/ButtonItemSystemItem参考
Use UIBarButtonItem icon in UIButton
SF Symbolsの使い方とカスタマイズの仕方
- 投稿日:2020-08-12T10:18:52+09:00
StoryBoardでAn internal error occurred. Editing functionality may be limited.が発生した時の対処
事象
StoryBoardでAutoLayoutを作成中、
「An internal error occurred. Editing functionality may be limited.」という警告が発生し、青い線のみになった
そのままビルドするとレイアウトが崩れることも発生(必ずではない)いろいろなサイトの情報から解消を試みるも尽く失敗。。。
ようやく解消できたので祝砲代わりの投稿動作環境
Xcode 11.5
Xcode 11.3.1やったこと
自分の環境では解消しなかったが、解消できることもあるらしい
プロジェクトのクリーン → 解消せず
- Product > [option] + Clean Build Folder
- Xcodeを再起動 → 解消せず
制約のエラーを解消 → 解消せず(Xcode 11.3.1で発生した時はこれで解消)
- Product > [option] + Clean Build Folder
- Xcodeを閉じる
- エラーが発生する直前のリビジョンに戻す
- Xcodeを起動 → 解消せず
DerivedDataを削除 → 解消せず
- Product > [option] + Clean Build Folder
- Xcodeを閉じる
- DerivedDataを削除
rm -rf ~/Library/Developer/Xcode/DerivedData/*
- Macの再起動
- Xcodeを起動 → 解消せず
Xcodeのキャッシュを削除 → 解消せず
- Product > [option] + Clean Build Folder
- Xcodeを閉じる
- DerivedDataを削除
rm -rf ~/Library/Developer/Xcode/DerivedData/*
- Xcodeのキャッシュを削除
rm -rf ~/Library/Caches/com.apple.dt.Xcode/
- Macの再起動
- Xcodeを起動 → 解消せず
Xcodeを再インストール → 解消せず
- Product > [option] + Clean Build Folder
- Xcodeを閉じる
- DerivedDataを削除
rm -rf ~/Library/Developer/Xcode/DerivedData/*
- Xcodeのキャッシュを削除
rm -rf ~/Library/Caches/com.apple.dt.Xcode/
- アプリケーションフォルダのXcodeを全てゴミ箱へ!
- Xcodeを完全アンインストール
- Xcodeを新しくインストール
- Xcodeを新規プロジェクトで起動 → 解消せず
これで解決!!
/private/tmp に権限を付与 → 解消した!
※Xcodeを再インストールの後で実施したが、おそらく再インストールは必要なかった
1. /private/tmpディレクトリが存在するか確認
ls -l /private/tmp
2. /private/tmpディレクトリが存在しなかった場合
$ sudo mkdir /private/tmp
3. 所有者を変更
sudo chown -R $(whoami) /private/tmp
4. Xcodeを起動 → 解消!/private/tmp ディレクトリは存在したが、所有者を変更したら無事エラーが解消された
長い道のりだった。。。参考
https://www.seishin.me/xcode-the-folder-disabled-plist-doesnt-exist/
https://qiita.com/UJIPOID/items/015805c89bddca540129
https://qiita.com/shtnkgm/items/c96a58579ec406194fa8
https://qiita.com/stoneBK7/items/146ee235a46abc9178da
https://qiita.com/y-aimi/items/209e7acce54ee1d38144
https://stackoverflow.com/questions/33456411/ios-project-showing-error-an-internal-error-occurred-editing-functionality-may
- 投稿日:2020-08-12T04:00:02+09:00
モバイルブラウザのキーボードの種類は制御できない
スマートフォンなどのモバイル端末のブラウザでは、input要素をフォーカスするとキーボードが出現する。
このキーボードは、「英語」「日本語 - かな」などの種類があり、日本語利用者なら切り替えながら使っているだろう。
で、input要素をフォーカスした時に出てくるこのキーボードの種類を制御したい、という要望はままあるだろう。
たとえば、英数字のみからなる何らかのシリアルコードのようなものの入力欄は、日本語ではなく英語キーボードを表示させたいだろう。
結論からいうと、キーボードの種類は制御できない。
もう少し正確に言うと、日本語ではなく英語キーボードを常に表示させるようにすることはできない。type属性で制御できるのではないか
巷の技術系ブログなどではよく書かれている。
<!-- email入力用だから、英語キーボードであるべき? --> <input type="email"> <!-- URL入力用だから、英語キーボードであるべき? --> <input type="url">たしかに、多くのモバイルブラウザは、type属性を
url
に指定することによって、それに適したキーボードを表示する機能をサポートしてはいる。
iOS Safariの場合、type="email"
のときは、スペーキーの隣に@
や.
など、メールアドレスによく使う文字キーが配置されている。(前掲のスクリーンショット画像がまさにそれだ)一見すると、求めていた挙動が実現されているように思うが、ちょっと待って欲しい。
日本語キーボードに切り替えしてみる
type="email"
をフォーカスしてキーボードを表示したら、わざと日本語に切り替えた上でinput要素のフォーカスをはずしてみよう。そして、再び同要素をフォーカスしてキーボードを表示させてみる。
このとき日本語キーボードが表示されるだろう。これはあなたの求めている挙動だろうか。
次にページをリロードしてみて、再び同要素をフォーカスする。やはり日本語キーボードが出るだろう。
同じページ内に、別要素のtype="email"
があるならば、そちらにフォーカスを当ててみよう。やはり日本語キーボードが出るはずだ。これはあなたの求めている挙動だろうか。
なんなら、他のページのtype="email"
要素でキーボードを日本語にした後、当該サイトのtype="email"
にフォーカスしてみよう。iOS Safariでは日本語キーボードが出るだろう。(Androidでは英語キーボードが出る)
何度も言うが、これはあなたが求めている挙動だろうか。1
あなたが求めているのは、当該要素をフォーカスしたときには常に英語キーボードが表示されることではないのか。2その要素に入力されるべきは英数字だけなのだから。メールアドレスはマルチバイト文字が含まれ得る
なぜこんなことになるのか。
メールアドレス入力用のtype属性値ならば、英語キーボードだけ表示されればいいのではないのか。
現に、input[type="tel"]
は数字キーボードのみ、input[type="password"]
は英語キーボードのみであり、キーボードの種類を変更することはできない。
理由は、メールアドレスの入力には英語キーボードだけでは不十分だからだ。
RFCの定義によれば、メールアドレスに使われる文字には制限があり、マルチバイト文字は含まれないらしいのだが、Gmailではマルチバイト文字を含むメールアドレスへの受送信が可能だ。
本来の規格としてNGだったとしても、Gmailがサポートしている以上、マルチバイト文字を含むメールアドレスは存在する。存在する以上は、入力できなければならない。入力するためには、日本語キーボードに切り替え可能でなければならないのだ。
そして、ひとたびユーザがtype="email"
要素で日本語キーボードに切り替えたならば、ブラウザは「ユーザーは日本語でemailを入力しようとしている」と解釈し、それ以後type="email"
フォーカス時には日本語キーボードを表示するようになると考えられる。ユーザビリティとしては筋が通っている。これは、
input[type="url"]
についても同様だ。
日本語ドメインは存在している。そうでなくても、ディレクトリ名やパラメータにマルチバイト文字が含まれることは普通にある。3
であるならば、日本語キーボードが表示されうる。inputmodeという属性もあるが
inputmode="email"
inputmode="url"
とすることで、入力キーボードを指定できるという属性値だが、これとてもtype属性と同じ挙動だ。入力値が英数字でなければならないtype属性は存在しない
ただし、パスワード
type="password"
を除く。
type="password"
は当然ながら伏字になるので、通常はパスワード以外には使えない。
というわけで、常に英語キーボードを表示させるようにする手段は、我々には与えられていない。我々はどうすべきか
諦めよ。そして、ユーザを信頼せよ。
たとえ日本語キーボードが開いたとしても、英数字で書くべきことを理解すれば、ユーザーは自らの手で英語キーボードに切り替えて入力するであろう。バリデーションと入力補助
通常は誤った値が入力された場合には、それと分かるエラー文言を表示するものだろう。ユーザに気づかせて軌道修正させればよい。
全角文字が入力された際には、JavaScriptで半角に自動変換する機能くらいは実装しておくと親切かもしれない。なぜこの記事を書いたか
この入力欄に入力する値は英数字なので、英語キーボードが出るようにします。
その方がユーザに負担がかからないので。あなたはこのように仕様を決めた手前、フォーカス時に日本語キーボードが出てしまう挙動を何とかしたいと思うかもしれない。
だが、立ち止まってよく考えてみて欲しい。これはそれほど問題なことなのか。
ユーザは自分でキーボードを切り替えることができ、適切に入力するのに何の支障もないではないか。
ユーザは自分がどうすべきか(英数字で入力)をきちんと理解できるようになっていれば、「英語キーボードを出す」などという些末な事象にとらわれる必要はどこにもない。スマホで「英語キーボードを出す」ということについて、できる、できる、と書いてある記事ばかりなので、当然実装しなければならなくなってしまう。確かに概ね期待通りにできるのだが、重箱の角をつつくようなデバッグ4には耐えられず、不具合として報告されてしまう。報告された以上は、何とか対応しようとするものの、完璧に制御することはできないのだと理解した。
このことについて触れている記事がなく、とても困ったので書いた。5
実装を担当しているあなたが、クライアントやディレクターに、この記事を見せて、ここに書いてある通り、表示するキーボードを完璧に制御することはできません。
type属性をと適当な妥結点を見出してくれることを期待する。
- 投稿日:2020-08-12T00:07:43+09:00
【入門】iOS アプリ開発 #3【Sound を再生する】
Sound
今回はゲーム中のサウンドを流す処理を作成したい。SpriteKit は簡単なサウンド制御も提供している。「パックマン」程度であれば、この範囲で実現することができそうだ。
サウンドは効果音(Sound Effect) と BGM(BackGround Music) がある。この2種類を実現するクラスを作成する。
Sound Manager class の作成
効果音を再生する API は、
playSE(.EatDot)とする。
BGMを再生する API は、
playBGM(.BgmPower)とする。
このような API を持つ、次の CgSoundManager クラスを作成する。
CgSoundManager class
/// Sound management class plays sound with SpriteKit. class CgSoundManager { // Kind of sound items to play back. enum EnKindOfSound: Int { case EatDot = 0 case EatFruit // ・・・略・・・ case Intermission } // List of sound files to load. private let table_urls: [[(resourceName: String, typeName: String, interval: Int)]] = [ [ ("16_pacman_eatdot_256ms", "wav", 256) ], [ ("16_pacman_eatfruit_438ms", "wav", 438) ], // ・・・略・・・ [ ("16_pacman_intermission_5200ms", "wav", 5200) ] ] private var view: SKScene? private var actions: [SKAction] = [] private var table_playingTime: [Int] = [] // Adjustment time for processing to play sound. private let triggerThresholdTime: Int = 16 //ms /// Create and initialize a sound manager object. /// - Parameters: /// - view: SKScene object that organizes all of the active SpriteKit content. init(view: SKScene) { self.view = view table_playingTime = Array<Int>(repeating: 0, count: table_urls.count) for t in table_urls { appendSoundResource(resourceName: t[0].resourceName, typeName: t[0].typeName) } // ・・・略・・・ } /// Append sound resources to SpriteKit. /// - Parameters: /// - resourceName: File name for sound resource. /// - typeName: Type name for sound resource. private func appendSoundResource(resourceName: String, typeName: String) { let fileName = resourceName+"."+typeName let sound: SKAction = SKAction.playSoundFileNamed(fileName, waitForCompletion: false) actions.append(sound) } /// Play back a specified sound. /// If the specified item is playing back, it will not be played back. /// - Parameter number: Kind of sound items to play back. func playSE(_ number: EnKindOfSound) { guard soundEnabled && number.rawValue < actions.count else { return } let _number = number.rawValue if table_playingTime[_number] <= triggerThresholdTime { let table = table_urls[_number] table_playingTime[_number] = table[0].interval view?.run(actions[_number]) } }enum で定義された値と、再生するサウンド・ファイルの table_urls テーブルを対応させておく。
クラス初期化時に、これらのファイルを SKActionのオブジェクトとして生成しておく。
またサウンド・ファイルの再生時間を値として管理しておく。これは同じ効果音が複数重ならないようにするためで、ある効果音が再生中に同じものを再生する場合は再生しないようにする。
また BGM を再生するときには、指定したサウンド・ファイルの再生が終了したことを知り、同じものを繰り返し再生するために使用する。自動で開始するためのトリガーは、updateメソッド内で行う。
サウンド・ファイル
今回のパックマンに使うサウンド・ファイルは 15個で、フォーマットは WAV形式、16bit、モノラル、サンプリング・レート 22050Hz で作成した。
8bit だと、少し音がこもる感じになるので、16bit にして処理の重さを気にしてサンプリング・レートを 22050Hzへ落とした。
再生でプチプチ・ノイズが出ないように、先頭は FadeIn、終端は FadeOut処理をしておく。
サウンド・ファイルの再生時間は編集ツールで確認しておく。
[Sound Files]
* 16_pacman_eatdot_256ms.wav 11,372bytes
* 16_pacman_eatfruit_438ms.wav 19,360bytes
* 16_pacman_eatghost_544ms.wav 24,040bytes
* 16_pacman_miss_1536ms.wav 67,788bytes
* 16_pacman_extrapac_1952ms.wav 86,166bytes
* 16_credit_224ms.wav 9,634bytes
* 16_BGM_normal_400ms.wav 17,852bytes
* 16_BGM_power_400ms.wav 17,750bytes
* 16_BGM_return_528ms.wav 23,366bytes
* 16_BGM_spurt1_352ms.wav 15,592bytes
* 16_BGM_spurt2_320ms.wav 14,212bytes
* 16_BGM_spurt3_592ms.wav 26,272bytes
* 16_BGM_spurt4_512ms.wav 23,018bytes
* 16_pacman_beginning_4224ms.wav 187,054bytes
* 16_pacman_intermission_5200ms.wav 229,388bytesテスト・プログラム
GitHub に公開しているテスト・プログラムを実行すると、以下のような画面が表示され、BGMが5秒毎に切り替わる。スクリーンをタッチすると効果音が再生される。
Sound Manager クラスは、SoundManager.swift ファイルにコーディングしている。コメント入れて 200行未満となった。
class GameScene: SKScene { private var sound: CgSoundManager! override func didMove(to view: SKView) { // Create and reset sound object. sound = CgSoundManager(view: self) } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { sound.playSE(.EatDot) } private let bgm: [CgSoundManager.EnKindOfSound] = [.BgmNormal, .BgmSpurt1, .BgmSpurt2, .BgmPower, .BgmReturn] private var bgmIndex: Int = 0 private var bgmTime: Int = 0 override func update(_ currentTime: TimeInterval) { // Called before each frame is rendered // Play back BGM. if bgmTime == 0 { bgmTime = 16*60*5 // 5s sound.playBGM(bgm[bgmIndex]) bgmIndex += 1 if bgmIndex >= bgm.count { bgmIndex = 0 } } else { bgmTime -= 16 } // Update sound manager. sound.update(interval: 16 /* ms */) } }CgSoundManagerクラスは、SKViewオブジェクトをパラメータとして、オブジェクトを生成する。
SKScene からオーバーライドした touchesEndedイベントのメソッドで、SEを再生する。
また、同様に updateイベントのメソッドで、5秒毎にBGMを切り替えて再生する。
参考