Skip to content

Instantly share code, notes, and snippets.

@MohamedKari
Last active August 4, 2021 19:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MohamedKari/ec35e9e1c46af9205059f044527c7d0d to your computer and use it in GitHub Desktop.
Save MohamedKari/ec35e9e1c46af9205059f044527c7d0d to your computer and use it in GitHub Desktop.
VolumeButtonEvents.swift
//
// VolumeButtons.swift
// multitouch
//
// Created by Mo Kari on 04.08.21.
//
import Foundation
import SwiftUI
import UIKit
import MediaPlayer
import AVFoundation
@objcMembers
class VolumeButtonsUIViewController: UIViewController {
var delegate: VolumeButtonsDelegate?
var volumeSlider: UISlider = (MPVolumeView(frame: CGRect(x: 0, y: 0, width: 0, height: 0)).subviews.filter{NSStringFromClass($0.classForCoder) == "MPVolumeSlider"}.first) as! UISlider
var audioSession = AVAudioSession()
init(delegate: VolumeButtonsDelegate) {
super.init(nibName: nil, bundle: nil)
self.delegate = delegate
volumeSlider.frame = .zero
volumeSlider.clipsToBounds = true
view.addSubview(self.volumeSlider)
self.setup()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func becomeFirstResponder() -> Bool {
return true
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let change = change else {
return
}
guard let valAny = change[.newKey] else {
return
}
let val = Double("\(valAny)")!
guard let delegate = self.delegate else {
return
}
// print(val)
if val == 0.5 {
return
}
if val < 0.5 {
self.reset()
delegate.volumeDownPressed()
} else if val > 0.5 {
self.reset()
delegate.volumeUpPressed()
}
}
func setup() {
try! audioSession.setActive(true)
audioSession.addObserver(self, forKeyPath: "outputVolume", options: NSKeyValueObservingOptions.new, context: nil)
self.reset()
}
func reset() {
// There seems to be a bug in the API. Took hours to get it. We need to use both the setter and the assignment.
// However, pressing first up, then down turn by turn randomly still doesn't work. Probably, we would need to track the exact values and infer which value sequences would have caused which updates, but let's keep it simple and wrong for now.
// TODO: Resetting costs times (100 to 200 ms I'd guess), thus reducing event frequency.
// To increase frequency, we could not reset after every event but only e. g. < 0.1 or > 0.9.
// Then however, we don't know direction from < or > 0.5 but rather must memorize the last value and see if it got smaller.
volumeSlider.setValue(0.5, animated: false)
volumeSlider.value = 0.5
}
}
protocol VolumeButtonsDelegate {
func volumeUpPressed()
func volumeDownPressed()
}
struct VolumeButtonsView: UIViewControllerRepresentable {
typealias UIViewControllerType = VolumeButtonsUIViewController
var delegate: VolumeButtonsDelegate
func makeUIViewController(context: Context) -> VolumeButtonsUIViewController {
let volumeButtonsUIViewController = VolumeButtonsUIViewController(delegate: delegate)
volumeButtonsUIViewController.delegate = delegate
return volumeButtonsUIViewController
}
func updateUIViewController(_ uiViewController: VolumeButtonsUIViewController, context: Context) {
}
}
class VolumeButtonsHandler: VolumeButtonsDelegate {
func volumeUpPressed() {
print("volume up pressed")
}
func volumeDownPressed() {
print("volume down pressed")
}
}
struct ContentView: View {
var body: some View {
// Make sure the VolumeButtonsView is not deallocated (e.g. by putting it in a switch here)
// If so, the UISlider would be deallocated resulting ina EXC_BAD_ACCESS exception when accesses again
// If you wanna do this, rewrite the code to create the UISlider as a singleton in the ContentView directly and passing it into the VolumeButtonsView as a reference
VolumeButtonsView(delegate: VolumeButtonsHandler()).frame(width: 0, height: 0, alignment: .center)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment