- 投稿日: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-12T22:19:29+09:00
ファイルをNSViewにドラッグしてパスを取得する (Swift)
概要
- 以前にファイルをNSViewにドラッグしてパスを取得する (Objective-C) - QiitaというObjective-Cの記事を書きました。
- こちらを
Swift
で書き直してみます。GitHub
参考
- ファイルをNSViewにドラッグしてパスを取得する (Objective-C) - Qiita
- クリップボードから画像を取得する - Qiita
- Pasteboardのときと同様の実装を行っています
ドラッグ・アンド・ドロップを受け付けるNSViewのカスタムクラスの作成
- はじめに、呼び出し元にドラッグされた画像の情報を返すため、プロトコルのメソッドとして宣言しておきます。
protocol DragAndDropViewDelegate: class { func dragAndDropView(_ view: DragAndDropView, didDragImageFileURLs draggedImages: [DraggedImage]) }
- 今回、ローカルに保存されている画像ファイルだけでなく画像自体をドラッグした場合のため、ドラッグされるアイテムを下記のstructとして扱うようにします。
struct DraggedImage { let image: NSImage? let url: URL? var uti: String? // 拡張子情報 }
NSView
のカスタムクラスDragAndDropView
を作成し、ファイルがドラッグされたときの処理を書いていきます。- 後ほど使用するプロパティを宣言します。
var delegate: DragAndDropViewDelegate? // Viewがドラッグを許可するファイルのタイプ let acceptableTypes: [NSPasteboard.PasteboardType] = [.fileURL, .tiff, .png, .URL, .string] // URL先の画像のUTIが、NSImage.imageTypesのUTIに合致するかという条件 let filteringOptions: [NSPasteboard.ReadingOptionKey : Any] = [.urlReadingContentsConformToTypes : NSImage.imageTypes] // ViewのUI制御用 var isDragging = false { didSet { needsDisplay = true } }
View
上に画像がドラッグされている間、Viewの周りをハイライトさせるための処理。override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) // ドラッグされている場合にハイライトさせる if isDragging { NSColor.selectedControlColor.set() } else { NSColor.windowFrameColor.set() } let path = NSBezierPath(rect: bounds) path.lineWidth = 5 path.stroke() }
- ViewにDragできるファイルのタイプを設定します。
required init?(coder: NSCoder) { super.init(coder: coder) self.registerForDraggedTypes(acceptableTypes) }
- ファイルがドラッグされた場合に処理を続けるかを判断するためのメソッドと、UTI判別用のファイルを宣言しておきます。
// MARK: - Helper Methods func shouldAllowDrag(_ draggingInfo: NSDraggingInfo) -> Bool { var canAccept = false let pasteBoard = draggingInfo.draggingPasteboard if pasteBoard.canReadObject(forClasses: [NSURL.self], options: nil) || pasteBoard.canReadObject(forClasses: [NSImage.self], options: nil) { canAccept = true } return canAccept } func uti(url: URL) -> String? { guard let r = try? url.resourceValues(forKeys: [.typeIdentifierKey]) else { return nil } return r.typeIdentifier }
- 最後にドラッグに関する処理を実装します。
class DragAndDropView: NSView { // (省略) // MARK:- NSDraggingDestination Protocol Methods /// Viewの境界にファイルがドラッグされるときに呼ばれる /// 宛先がどのドラッグ操作を実行するのかを示す値を返す必要があります。 override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation { let allow = shouldAllowDrag(sender) isDragging = allow // NSDragOperation.copy := The data represented by the image can be copied. return allow ? NSDragOperation.copy : NSDragOperation() } /// View上にファイルがドラッグで保持されている間、短い間隔毎に呼ばれるメソッド /// 宛先がどのドラッグ操作を実行するのかを示す値を返す必要があります。 // override func draggingUpdated(_ sender: NSDraggingInfo) -> NSDragOperation { // } /// View上にファイルがドラッグされなくなった際に呼ばれる override func draggingExited(_ sender: NSDraggingInfo?) { isDragging = false } /// View上でファイルがドロップされた際に呼ばれる /// メッセージが返された場合はYES、performDragOperation:メッセージが送信されます。 // override func prepareForDragOperation(_ sender: NSDraggingInfo) -> Bool { // } /// View上でファイルがドロップされた後の処理 override func performDragOperation(_ draggingInfo: NSDraggingInfo) -> Bool { isDragging = false let pasteBoard = draggingInfo.draggingPasteboard // fileURLがドラッグされた場合 if let urls = pasteBoard.readObjects(forClasses: [NSURL.self], options: filteringOptions) as? [URL], urls.count > 0, urls[0].isFileURL { var draggedImages = [DraggedImage]() for url in urls { let uti = self.uti(url: url) draggedImages.append(DraggedImage(image: nil, url: url, uti: uti)) } delegate?.dragAndDropView(self, didDragImageFileURLs: draggedImages) return true } // 画像イメージがドラッグされた場合 if let pasteboardItems = pasteBoard.pasteboardItems { var draggedImages = [DraggedImage]() for pasteboardItem in pasteboardItems { for type in pasteboardItem.types { if let data = pasteboardItem.data(forType: type) { if let image = NSImage(data: data) { draggedImages.append(DraggedImage(image: image, url: nil, uti: type.rawValue)) } } } } if draggedImages.count > 0 { delegate?.dragAndDropView(self, didDragImageFileURLs: draggedImages) return true } } // 画像のURL(http://~)がドラッグされた場合 // 必要無いかも? if let urls = pasteBoard.readObjects(forClasses: [NSURL.self], options: nil) as? [URL], urls.count > 0 { var draggedImages = [DraggedImage]() for url in urls { draggedImages.append(DraggedImage(image: nil, url: url, uti: nil)) } delegate?.dragAndDropView(self, didDragImageFileURLs: draggedImages) return true } return false } /// 一連のドラッグ操作が完了したときに呼ばれる // override func concludeDragOperation(_ sender: NSDraggingInfo?) { // } }
- 投稿日:2020-08-12T22:19:29+09:00
画像ををNSViewにドラッグして情報を取得する (Swift)
概要
- 以前にファイルをNSViewにドラッグしてパスを取得する (Objective-C) - QiitaというObjective-Cの記事を書きました。
- こちらを
Swift
で書き直してみます。GitHub
参考
- ファイルをNSViewにドラッグしてパスを取得する (Objective-C) - Qiita
- クリップボードから画像を取得する - Qiita
- Pasteboardのときと同様の実装を行っています
ドラッグ・アンド・ドロップを受け付けるNSViewのカスタムクラスの作成
- はじめに、呼び出し元にドラッグされた画像の情報を返すため、プロトコルのメソッドとして宣言しておきます。
protocol DragAndDropViewDelegate: class { func dragAndDropView(_ view: DragAndDropView, didDragImageFileURLs draggedImages: [DraggedImage]) }
- 今回、ローカルに保存されている画像ファイルだけでなく画像自体をドラッグした場合のため、ドラッグされるアイテムを下記のstructとして扱うようにします。
struct DraggedImage { let image: NSImage? let url: URL? var uti: String? // 拡張子情報 }
NSView
のカスタムクラスDragAndDropView
を作成し、ファイルがドラッグされたときの処理を書いていきます。- 後ほど使用するプロパティを宣言します。
var delegate: DragAndDropViewDelegate? // Viewがドラッグを許可するファイルのタイプ let acceptableTypes: [NSPasteboard.PasteboardType] = [.fileURL, .tiff, .png, .URL, .string] // URL先の画像のUTIが、NSImage.imageTypesのUTIに合致するかという条件 let filteringOptions: [NSPasteboard.ReadingOptionKey : Any] = [.urlReadingContentsConformToTypes : NSImage.imageTypes] // ViewのUI制御用 var isDragging = false { didSet { needsDisplay = true } }
View
上に画像がドラッグされている間、Viewの周りをハイライトさせるための処理。override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) // ドラッグされている場合にハイライトさせる if isDragging { NSColor.selectedControlColor.set() } else { NSColor.windowFrameColor.set() } let path = NSBezierPath(rect: bounds) path.lineWidth = 5 path.stroke() }
- ViewにDragできるファイルのタイプを設定します。
required init?(coder: NSCoder) { super.init(coder: coder) self.registerForDraggedTypes(acceptableTypes) }
- ファイルがドラッグされた場合に処理を続けるかを判断するためのメソッドと、UTI判別用のファイルを宣言しておきます。
// MARK: - Helper Methods func shouldAllowDrag(_ draggingInfo: NSDraggingInfo) -> Bool { var canAccept = false let pasteBoard = draggingInfo.draggingPasteboard if pasteBoard.canReadObject(forClasses: [NSURL.self], options: nil) || pasteBoard.canReadObject(forClasses: [NSImage.self], options: nil) { canAccept = true } return canAccept } func uti(url: URL) -> String? { guard let r = try? url.resourceValues(forKeys: [.typeIdentifierKey]) else { return nil } return r.typeIdentifier }
- 最後にドラッグに関する処理を実装します。
class DragAndDropView: NSView { // (省略) // MARK:- NSDraggingDestination Protocol Methods /// Viewの境界にファイルがドラッグされるときに呼ばれる /// 宛先がどのドラッグ操作を実行するのかを示す値を返す必要があります。 override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation { let allow = shouldAllowDrag(sender) isDragging = allow // NSDragOperation.copy := The data represented by the image can be copied. return allow ? NSDragOperation.copy : NSDragOperation() } /// View上にファイルがドラッグで保持されている間、短い間隔毎に呼ばれるメソッド /// 宛先がどのドラッグ操作を実行するのかを示す値を返す必要があります。 // override func draggingUpdated(_ sender: NSDraggingInfo) -> NSDragOperation { // } /// View上にファイルがドラッグされなくなった際に呼ばれる override func draggingExited(_ sender: NSDraggingInfo?) { isDragging = false } /// View上でファイルがドロップされた際に呼ばれる /// メッセージが返された場合はYES、performDragOperation:メッセージが送信されます。 // override func prepareForDragOperation(_ sender: NSDraggingInfo) -> Bool { // } /// View上でファイルがドロップされた後の処理 override func performDragOperation(_ draggingInfo: NSDraggingInfo) -> Bool { isDragging = false let pasteBoard = draggingInfo.draggingPasteboard // fileURLがドラッグされた場合 if let urls = pasteBoard.readObjects(forClasses: [NSURL.self], options: filteringOptions) as? [URL], urls.count > 0, urls[0].isFileURL { var draggedImages = [DraggedImage]() for url in urls { let uti = self.uti(url: url) draggedImages.append(DraggedImage(image: nil, url: url, uti: uti)) } delegate?.dragAndDropView(self, didDragImageFileURLs: draggedImages) return true } // 画像イメージがドラッグされた場合 if let pasteboardItems = pasteBoard.pasteboardItems { var draggedImages = [DraggedImage]() for pasteboardItem in pasteboardItems { for type in pasteboardItem.types { if let data = pasteboardItem.data(forType: type) { if let image = NSImage(data: data) { draggedImages.append(DraggedImage(image: image, url: nil, uti: type.rawValue)) } } } } if draggedImages.count > 0 { delegate?.dragAndDropView(self, didDragImageFileURLs: draggedImages) return true } } // 画像のURL(http://~)がドラッグされた場合 // 必要無いかも? if let urls = pasteBoard.readObjects(forClasses: [NSURL.self], options: nil) as? [URL], urls.count > 0 { var draggedImages = [DraggedImage]() for url in urls { draggedImages.append(DraggedImage(image: nil, url: url, uti: nil)) } delegate?.dragAndDropView(self, didDragImageFileURLs: draggedImages) return true } return false } /// 一連のドラッグ操作が完了したときに呼ばれる // override func concludeDragOperation(_ sender: NSDraggingInfo?) { // } }
- 投稿日:2020-08-12T22:19:29+09:00
画像をNSViewにドラッグして情報を取得する (Swift)
概要
- 以前にファイルをNSViewにドラッグしてパスを取得する (Objective-C) - QiitaというObjective-Cの記事を書きました。
- こちらを
Swift
で書き直してみます。GitHub
参考
- Drag and Drop Tutorial for macOS
- ファイルをNSViewにドラッグしてパスを取得する (Objective-C) - Qiita
- クリップボードから画像を取得する - Qiita
- Pasteboardのときと同様の実装を行っています
ドラッグ・アンド・ドロップを受け付けるNSViewのカスタムクラスの作成
- はじめに、呼び出し元にドラッグされた画像の情報を返すため、プロトコルのメソッドとして宣言しておきます。
protocol DragAndDropViewDelegate: class { func dragAndDropView(_ view: DragAndDropView, didDragImageFileURLs draggedImages: [DraggedImage]) }
- 今回、ローカルに保存されている画像ファイルだけでなく画像自体をドラッグした場合にも対応したいため、ドラッグされるアイテムを下記のstructとして扱うようにします。
struct DraggedImage { let image: NSImage? let url: URL? var uti: String? // 拡張子情報 }
NSView
のカスタムクラスDragAndDropView
を作成し、ファイルがドラッグされたときの処理を書いていきます。- 後ほど使用するプロパティを宣言します。
var delegate: DragAndDropViewDelegate? // Viewがドラッグを許可するファイルのタイプ let acceptableTypes: [NSPasteboard.PasteboardType] = [.fileURL, .tiff, .png, .URL, .string] // URL先の画像のUTIが、NSImage.imageTypesのUTIに合致するかという条件 let filteringOptions: [NSPasteboard.ReadingOptionKey : Any] = [.urlReadingContentsConformToTypes : NSImage.imageTypes] // ViewのUI制御用 var isDragging = false { didSet { needsDisplay = true } }
View
上に画像がドラッグされている間、Viewの周りをハイライトさせるための処理。override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) // ドラッグされている場合にハイライトさせる if isDragging { NSColor.selectedControlColor.set() } else { NSColor.windowFrameColor.set() } let path = NSBezierPath(rect: bounds) path.lineWidth = 5 path.stroke() }
- ViewにDragできるファイルのタイプを設定します。
required init?(coder: NSCoder) { super.init(coder: coder) self.registerForDraggedTypes(acceptableTypes) }
- ファイルがドラッグされた場合に処理を続けるかを判断するためのメソッドと、UTI判別用のファイルを宣言しておきます。
// MARK: - Helper Methods func shouldAllowDrag(_ draggingInfo: NSDraggingInfo) -> Bool { var canAccept = false let pasteBoard = draggingInfo.draggingPasteboard if pasteBoard.canReadObject(forClasses: [NSURL.self], options: nil) || pasteBoard.canReadObject(forClasses: [NSImage.self], options: nil) { canAccept = true } return canAccept } func uti(url: URL) -> String? { guard let r = try? url.resourceValues(forKeys: [.typeIdentifierKey]) else { return nil } return r.typeIdentifier }
- 最後にドラッグに関する処理を実装します。
class DragAndDropView: NSView { // (省略) // MARK:- NSDraggingDestination Protocol Methods /// Viewの境界にファイルがドラッグされるときに呼ばれる /// 宛先がどのドラッグ操作を実行するのかを示す値を返す必要があります。 override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation { let allow = shouldAllowDrag(sender) isDragging = allow // NSDragOperation.copy := The data represented by the image can be copied. return allow ? NSDragOperation.copy : NSDragOperation() } /// View上にファイルがドラッグで保持されている間、短い間隔毎に呼ばれるメソッド /// 宛先がどのドラッグ操作を実行するのかを示す値を返す必要があります。 // override func draggingUpdated(_ sender: NSDraggingInfo) -> NSDragOperation { // } /// View上にファイルがドラッグされなくなった際に呼ばれる override func draggingExited(_ sender: NSDraggingInfo?) { isDragging = false } /// View上でファイルがドロップされた際に呼ばれる /// メッセージが返された場合はYES、performDragOperation:メッセージが送信されます。 // override func prepareForDragOperation(_ sender: NSDraggingInfo) -> Bool { // } /// View上でファイルがドロップされた後の処理 override func performDragOperation(_ draggingInfo: NSDraggingInfo) -> Bool { isDragging = false let pasteBoard = draggingInfo.draggingPasteboard // fileURLがドラッグされた場合 if let urls = pasteBoard.readObjects(forClasses: [NSURL.self], options: filteringOptions) as? [URL], urls.count > 0, urls[0].isFileURL { var draggedImages = [DraggedImage]() for url in urls { let uti = self.uti(url: url) draggedImages.append(DraggedImage(image: nil, url: url, uti: uti)) } delegate?.dragAndDropView(self, didDragImageFileURLs: draggedImages) return true } // 画像イメージがドラッグされた場合 if let pasteboardItems = pasteBoard.pasteboardItems { var draggedImages = [DraggedImage]() for pasteboardItem in pasteboardItems { for type in pasteboardItem.types { if let data = pasteboardItem.data(forType: type) { if let image = NSImage(data: data) { draggedImages.append(DraggedImage(image: image, url: nil, uti: type.rawValue)) } } } } if draggedImages.count > 0 { delegate?.dragAndDropView(self, didDragImageFileURLs: draggedImages) return true } } // 画像のURL(http://~)がドラッグされた場合 if let urls = pasteBoard.readObjects(forClasses: [NSURL.self], options: nil) as? [URL], urls.count > 0 { var draggedImages = [DraggedImage]() for url in urls { draggedImages.append(DraggedImage(image: nil, url: url, uti: nil)) } delegate?.dragAndDropView(self, didDragImageFileURLs: draggedImages) return true } return false } /// 一連のドラッグ操作が完了したときに呼ばれる // override func concludeDragOperation(_ sender: NSDraggingInfo?) { // } }
- 投稿日:2020-08-12T18:58:40+09:00
[SwiftUI]TextField�のリターンイベントはonCommit
TextFieldを使う時にリターンが押された際の処理は必須だと思います。
SwiftUIのTextFieldでは、リターンのイベント処理をonCommitで定義できます。TextField("ぷれいすほるだー", text: $name, onCommit: { //任意の処理 })
- 投稿日:2020-08-12T18:46:27+09:00
Swift値型、参照型 初心者が違いを説明してみた
はじめに
皆さんは以下のようにほぼ同じコードを書いているのにも関わらず、
構造体ではエラーが起きて、クラスではエラーが起きない理由を説明することができますか。構造体(値型)
struct Sample { var num: Int } var sample1 = Sample(num: 1) let sample2 = Sample(num: 1) sample1.num = 2 sample2.num = 2 //=> エラークラス(参照型)
class Sample { var num: Int init(num: Int) { self.num = num } } var sample1 = Sample(num: 1) let sample2 = Sample(num: 1) sample1.num = 2 sample2.num = 2自分はできませんでした。ところがクラスは参照型、構造体は値型ということがきちんと理解できればこの理由は説明することができ、クラスと構造体に対する理解も深まると思います。
値型の場合
値型の場合、今回だとnumという変数を格納するための入れ物がまず作られます。そしてその入れ物に直接値を格納していきます。
sample2はletで定義されているので、入れ物の中身を後から変えることはできません。
入れ物を作って値を格納する。初心者としてはイメージ通りの挙動だと感じると思います。しかし参照型はこれとはちょっと違った方法で値をセットします。
参照型の場合
参照型でも値型と同様に入れ物が作られます。しかし入れ物に入れる物が値そのものではなく、別の場所に格納された値の住所を入れます。参照型は、その住所を使って値にアクセスをします。ここが値型と参照型の大きな違いで、
let sample2 = Sample(num: 1) //参照型 sample2.num = 2がエラーを起こさない理由は、入れ物に入った住所を書き換えるのではなく、住所の先にある値を変えているからです。
住所の先の値を変える分には、sample2がletで定義されていようが関係なく変更することができます。
参照型のいい点
let sample1 = Sample(a: 1, b: 2, c: 5, d: 1, .....) let sample2 = sample1上記のようにプロパティの数が膨大な場合に、sample1からsample2を複製しようとしたとき、値型では入れ物から中身に値まですべてコピーします。これではコンピュータの容量もたくさん食うことになります。
その点参照型ではコピーするのは住所だけとなっているので、容量も食わずにコンピュータにも優しいです。
let sample1 = Sample(a: 1, b: 2, c: 5, d: 1, .....) let sample2 = sample1 sample1.b = 100 print(sample2.b) //=> 100 //住所の先の値を変えてしまっているので、sample1と同じ住所を持つsample2の値も変化するまた参照型で複製をしているときに、値を変更してしまうと大本の値を変更していることになり、他の要素にも影響を与えます。ここは嬉しい点でもあり、知らずに使ってしまうと混乱の原因にもなるので気をつけましょう。
まとめ
値型と参照型の違いを解説してみました。間違い等ありましたら知らせていただけると嬉しいです。
- 投稿日:2020-08-12T18:46:27+09:00
Swift値型、参照型 初心者がイラストを使って説明してみた
はじめに
皆さんは以下のようにほぼ同じコードを書いているのにも関わらず、
構造体ではエラーが起きて、クラスではエラーが起きない理由を説明することができますか。構造体(値型)
struct Sample { var num: Int } var sample1 = Sample(num: 1) let sample2 = Sample(num: 1) sample1.num = 2 sample2.num = 2 //=> エラークラス(参照型)
class Sample { var num: Int init(num: Int) { self.num = num } } var sample1 = Sample(num: 1) let sample2 = Sample(num: 1) sample1.num = 2 sample2.num = 2自分はできませんでした。ところがクラスは参照型、構造体は値型ということがきちんと理解できればこの理由は説明することができ、クラスと構造体に対する理解も深まると思います。
値型の場合
値型の場合、今回だとnumという変数を格納するための入れ物がまず作られます。そしてその入れ物に直接値を格納していきます。
sample2はletで定義されているので、入れ物の中身を後から変えることはできません。
入れ物を作って値を格納する。初心者としてはイメージ通りの挙動だと感じると思います。しかし参照型はこれとはちょっと違った方法で値をセットします。
参照型の場合
参照型でも値型と同様に入れ物が作られます。しかし入れ物に入れる物が値そのものではなく、別の場所に格納された値の住所を入れます。参照型は、その住所を使って値にアクセスをします。ここが値型と参照型の大きな違いで、
let sample2 = Sample(num: 1) //参照型 sample2.num = 2がエラーを起こさない理由は、入れ物に入った住所を書き換えるのではなく、住所の先にある値を変えているからです。
住所の先の値を変える分には、sample2がletで定義されていようが関係なく変更することができます。
参照型のいい点
let sample1 = Sample(a: 1, b: 2, c: 5, d: 1, .....) let sample2 = sample1上記のようにプロパティの数が膨大な場合に、sample1からsample2を複製しようとしたとき、値型では入れ物から中身に値まですべてコピーします。これではコンピュータの容量もたくさん食うことになります。
その点参照型ではコピーするのは住所だけとなっているので、容量も食わずにコンピュータにも優しいです。
let sample1 = Sample(a: 1, b: 2, c: 5, d: 1, .....) let sample2 = sample1 sample1.b = 100 print(sample2.b) //=> 100 //住所の先の値を変えてしまっているので、sample1と同じ住所を持つsample2の値も変化するまた参照型で複製をしているときに、値を変更してしまうと大本の値を変更していることになり、他の要素にも影響を与えます。ここは嬉しい点でもあり、知らずに使ってしまうと混乱の原因にもなるので気をつけましょう。
まとめ
値型と参照型の違いを解説してみました。間違い等ありましたら知らせていただけると嬉しいです。
- 投稿日: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-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-12T00:12:15+09:00
メニューバーに常駐するmacOSアプリを作成し、画像をドラッグ・アンド・ドロップで認識できるようにする
概要
- Macの右上のメニューバーにアイコンを表示するアプリを作成します。
- また最終的に、そのアイコン上に画像ファイルをドラッグ・アンド・ドロップできるよう実装を行います。
- 上のバーの名称は以下メニューバーと呼ぶこととします。
GitHub
基本的な実装
- 以下の記事の通り実装を行います。
class AppDelegate: NSObject, NSApplicationDelegate { let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength) func applicationDidFinishLaunching(_ aNotification: Notification) { if let statusBarButton = statusItem.button { statusBarButton = NSImage(named:NSImage.Name("StatusBarButtonImage")) } constructMenu() } @objc func printQuote(_ sender: Any?) { let quoteText = "Never put off until tomorrow what you can do the day after tomorrow." let quoteAuthor = "Mark Twain" print("\(quoteText) — \(quoteAuthor)") } func constructMenu() { let menu = NSMenu() menu.addItem(NSMenuItem(title: "Print Quote", action: #selector(AppDelegate.printQuote(_:)), keyEquivalent: "P")) // 大文字なので、Commant + "Shitf + p"となる menu.addItem(NSMenuItem.separator()) menu.addItem(NSMenuItem(title: "Quit Quotes", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")) statusItem.menu = menu } }
- これより以下は、記事の内容外の実装を紹介します。
メニュー項目にサブメニューを持たせる
- 以下のようなメニューを入れ子にすることを考えます。
- クラスとしては、下記のように入れ子になっています
submenu
として追加するのがポイントです。func constructMenu() { let menu = NSMenu() menu.addItem(NSMenuItem(title: "Parent_01", action: #selector(AppDelegate.printQuote(_:)), keyEquivalent: "")) menu.addItem(NSMenuItem(title: "Parent_02", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "")) menu.addItem(NSMenuItem.separator()) // サブメニューを持つNSMenuItemを作成する let paren_03 = NSMenuItem(title: "Parent_03", action: #selector(AppDelegate.printQuote(_:)), keyEquivalent: "") let menuForParent = NSMenu() menuForParent.addItem(NSMenuItem(title: "Children_01", action: #selector(AppDelegate.printQuote(_:)), keyEquivalent: "")) menuForParent.addItem(NSMenuItem.separator()) menuForParent.addItem(NSMenuItem(title: "Children_02", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "")) paren_03.submenu = menuForParent menu.addItem(paren_03) statusItem.menu = menu }メニューアイコンにファイルをドラッグ・アンド・ドロップする
概要
- 以下のようにメニューアイコンに画像ファイルをドラッグし、アプリ側で認識するよう実装を行います。
- 以下の方針を参考に実装します。
- NSDraggingDestinationに適合しているのは下記のクラスです。
Conforming Types
NSCollectionView, NSView
- またNSButton、もっというと今回はNSStatusBarButtonは
NSView
を継承しています。- よって結論としては、NSStatusBarButtonのNSDraggingDestinationのメソッドをoverrideしてやれば良いですね。
- 当初はカスタムクラスを作ろうとしましたが、
NSStatusItem.NSStatusBarButtonがget only
なので、extensionで動作をoverrideを行いました。実装
- AppDelegate外でアイコンを変更するため、publicメソッド
changeMenubarIconImage
を用意しておく。
- Notificationを使うほうが疎でいいかも。
class AppDelegate: NSObject, NSApplicationDelegate { public let statusItem = NSStatusBar.system.draggableStatusItem(withLength: NSStatusItem.squareLength) public func changeMenubarIconImage(isDragging isDragIcon: Bool) { if let statusBarButton = statusItem.button { if isDragIcon { statusBarButton.image = NSImage(named:NSImage.Name("StatusBarButtonImageDragged")) } else { statusBarButton.image = NSImage(named:NSImage.Name("StatusBarButtonImage")) } } } public func receiveImageDraggedOnMenubarIcon(for urls: [URL]) { print("?URL Detected!") for url in urls { print(url) } } ...
- 特定のファイル形式を受け取るための
NSStatusItem
を返すメソッドを用意しておきます。
- この辺は「ファイルをNSViewにドラッグしてパスを取得する (Swift) - Qiita」と同じですね。
registerForDraggedTypes
でドラッグできるアイテムのタイプを指定します。extension NSStatusBar { func draggableStatusItem(withLength length: CGFloat) -> NSStatusItem { let statusItem = self.statusItem(withLength: length) if let button = statusItem.button { button.image = NSImage(named:NSImage.Name("StatusBarButtonImage")) button.registerForDraggedTypes([.fileURL, .tiff, .png, .URL, .string]) } return statusItem } }
- こちらも「ファイルをNSViewにドラッグしてパスを取得する (Swift) - Qiita」と同様です。
- 要するに、ファイルのドラッグ状態によって、メニューバーのアイコンを切り替えることをしています。
extension NSStatusBarButton { // MARK: - Helper Methods private func filteringOptions() -> [NSPasteboard.ReadingOptionKey : Any] { return [.urlReadingContentsConformToTypes : NSImage.imageTypes] } private func shouldAllowDrag(_ draggingInfo: NSDraggingInfo) -> Bool { var canAccept = false let pasteBoard = draggingInfo.draggingPasteboard if pasteBoard.canReadObject(forClasses: [NSURL.self], options: filteringOptions()) || pasteBoard.canReadObject(forClasses: [NSImage.self], options: filteringOptions()) { canAccept = true } return canAccept } private func setDraggingInfo(isDragging: Bool) { if let appDelegate = NSApplication.shared.delegate as? AppDelegate { appDelegate.changeMenubarIconImage(isDragging: isDragging) } } // MARK:- NSDraggingDestination Methods open override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation { print("draggingEntered") let allow = shouldAllowDrag(sender) setDraggingInfo(isDragging: allow) return allow ? .copy : NSDragOperation() } open override func draggingExited(_ sender: NSDraggingInfo?) { setDraggingInfo(isDragging: false) } open override func performDragOperation(_ sender: NSDraggingInfo) -> Bool { let pasteBoard = sender.draggingPasteboard // 今回はfileURLがドラッグされた場合のみ実装 if let urls = pasteBoard.readObjects(forClasses: [NSURL.self], options: filteringOptions()) as? [URL], urls.count > 0, urls[0].isFileURL { if let appDelegate = NSApplication.shared.delegate as? AppDelegate { appDelegate.receiveImageDraggedOnMenubarIcon(for: urls) } return true } return false } open override func draggingEnded(_ sender: NSDraggingInfo) { setDraggingInfo(isDragging: false) print("draggingEnded") } }余談
- registerForDraggedTypes with NSStatusBarButton?
NSStatusBarButton
にドラッグ・アンド・ドロップ可能なNSView
をaddSubview
すればと回答- 試しましたが、アイコンの外観は変更はできましたが、ドラッグのアクションを受け取るのが不可でした。
Responder Chain
あたりを考えるとうまくいくのだろうか…?メニューバーのアイコンを非表示にする
- isVisibleを設定等から設定できるようにすれば良さそうです。
statusItem.isVisible = false // で非表示になるステータスメニューを実装する際にはそれを非表示にすることができるオプションを環境設定などに用意しておくべきです。
その他参考した記事
- カスタムなウィンドウを表示した状態でNSStatusItemをハイライトする - a.out
- Mac - メニューバーのアイコンを並び替え・削除
- Commandを押しながらでアイコンの順番を入れ替えれます。
- ターゲットのアイコン4
- 投稿日: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を切り替えて再生する。
参考