Instantly share code, notes, and snippets.
Last active
August 4, 2021 19:39
-
Save MohamedKari/ec35e9e1c46af9205059f044527c7d0d to your computer and use it in GitHub Desktop.
VolumeButtonEvents.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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