I am currently using the following to change the button icon, when user has press a playVideo.
However, I need something that will check if the media is currently playing and update this with the correct icon?
I am wondering what is the best way to do this.
#IBAction func playVideo(_ sender: Any) {
if(defaultsKeys.musicplayer_connected == "1"){
self.defaults.set("0", forKey: defaultsKeys.musicplayer_connected)
// (sender as! UIButton).setTitle("Pause", for: [])
(sender as! UIButton).setBackgroundImage(UIImage(systemName: "pause.circle"), for: UIControl.State.normal)
MusicPlayer.shared.startBackgroundMusic()
}else
{
self.defaults.set("1", forKey: defaultsKeys.musicplayer_connected)
// (sender as! UIButton).setTitle("Play", for: [])
(sender as! UIButton).setBackgroundImage(UIImage(systemName: "play.circle"), for: UIControl.State.normal)
MusicPlayer.shared.stopBackgroundMusic()
//self.player.pause()
}
}
create a enum to store the state of the player
enum PlayerState {
case playing,paused
}
then create a property observer that will update your button on the change of the value
var playerStateChanged : PlayerState = .playing {
didSet {
switch playerStateChanged {
case .playing:
if #available(iOS 13.0, *) {
yourBtn.setBackgroundImage(UIImage(systemName: "play.circle"), for: .normal)
} else {
// Fallback on earlier versions
}
case .paused:
if #available(iOS 13.0, *) {
yourBtn.setBackgroundImage(UIImage(systemName: "pause.circle"), for: .normal)
} else {
// Fallback on earlier versions
}
}
}
}
Now in your IBAction, update the value of playerStateChanged and it will set the button image accordingly.
#IBAction func playVideo(_ sender: Any) {
// get if player is playing or not and set the value of playerStateChanged
playerStateChanged = .paused
}
You can create a Bool variable and configure the player's image and start/stop actions based on its current state.
Create a computed property isPlaying,
var isPlaying = false {
didSet {
self.playButton.setImage(UIImage(systemName: self.isPlaying ? "play.circle" : "pause.circle"), for: <#T##UIControl.State#>)
self.isPlaying ? MusicPlayer.shared.startBackgroundMusic() : MusicPlayer.shared.stopBackgroundMusic()
}
}
Now, in your playVideo(_:) action method, you need to simply change the state of isPlaying like so,
#IBAction func playVideo(_ sender: Any) {
self.isPlaying = !self.isPlaying
}
Related
This question already has answers here:
How do i keep UISwitch state when changing ViewControllers?
(3 answers)
Closed 5 years ago.
I would like to save the State of an UISwitch after to change between View Controllers. Any help would be greatly appreciated!
I have a first View Controller with an UISwitch, to control the music in the background in different View Controllers:
#IBOutlet weak var SwitchMusic: UISwitch!
let defaults = UserDefaults.standard
var switchON : Bool = false
#IBAction func checkState(_ sender: UISwitch) {
if (sender.isOn == true)
{
switchON = true
defaults.set(switchON, forKey: "switchON")
MusicHelper.sharedHelper.playBackgroundMusic()
}
if (sender.isOn == false)
{
switchON = false
defaults.set(switchON, forKey: "switchON")
MusicHelper.sharedHelper.stopBackgroundMusic()
}
}
And a Second View Controller to load or no the music in the background if the switch is On or Off:
override func viewDidLoad() {
super.viewDidLoad()
if defaults.value(forKey: "switchON") != nil{
let switchON: Bool = defaults.value(forKey: "switchON") as! Bool
if switchON == true{
MusicHelper.sharedHelper.playBackgroundMusic()
}
else if switchON == false{
MusicHelper.sharedHelper.stopBackgroundMusic()
}
}
}
Also I have a class with the music:
class MusicHelper {
let defaults = UserDefaults.standard
static let sharedHelper = MusicHelper()
var musicBackgroundIntro:AVAudioPlayer = AVAudioPlayer()
func playBackgroundMusic() {
do {
let audioPath = Bundle.main.path(forResource: "Music", ofType: "mp3")
try musicBackgroundIntro = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: audioPath!) as URL)
musicBackgroundIntro.numberOfLoops = -1
musicBackgroundIntro.prepareToPlay()
musicBackgroundIntro.play()
} catch {
print("Cannot play the file")
}
}
func stopBackgroundMusic() {
musicBackgroundIntro.stop()
}
}
Now it is working perfectly the music in the background between View Controllers, and it is possible to turn off and on... but unfortunately do not save the current state of the UISwitch, and always when I enter in the First View Controller the state of the Switch is On.
Also any idea that how will be possible to apply in a Slider too?
Any help would be greatly appreciated!
Thanks
The easiest way for you would be to create a static var isSwitchOn: Bool = false
This state will be preserved between back and forth transitions.
You should reflect the state, if music is playing...
class MusicHelper {
public isPlaying: Bool {
get {
return musicBackgroundIntro.isPlaying
}
}
// your stuff here..
}
That way in other view controllers:
SwitchMusic.isOn = MusicHelper.sharedHelper.isPlaying
If you need other view controllers to update in response to this, you can add a delegate event (aka observer) if necessary.
Try something like that: Use the UISwitch as an #IBOutlet.
#IBOutlet weak var checkState: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
self.checkState.addTarget(self, action: #selector(action(sender:)), for: .valueChanged)
}
// Save state
func action(sender: UISwitch) {
let userDefaults = UserDefaults.standard
userDefaults.set(sender.isOn, forKey:"identifier")
}
// Retrieve state
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let userDefaults = UserDefaults.standard.bool(forKey: "identifier")
self.checkState.setOn(userDefaults, animated: false)
}
You can give the switch a default value when it's created in viewcontroller1.
Assign the (default.value(forKey:"switchOn") as! Bool ) ?? false to that switch.
I am using following code to record screen. It is working fine for ios10 and ios9
#IBAction func btnRecordTapped(_ sender: UIButton) {
if RPScreenRecorder.shared().isAvailable {
if #available(iOS 10.0, *) {
RPScreenRecorder.shared().startRecording(handler: { (error) in
guard error == nil else {
print("Record failed with error \(error!.localizedDescription)")
return
}
DispatchQueue.main.async {
sender.removeTarget(self, action: #selector(self.btnRecordTapped(_:)), for: .touchUpInside)
sender.addTarget(self, action: #selector(self.stoprecording(button:)), for: .touchUpInside)
sender.setTitle("Stop", for: .normal)
sender.setTitleColor(.red, for: .normal)
}
})
} else {
RPScreenRecorder.shared().startRecording(withMicrophoneEnabled: false, handler: { (error) in
guard error == nil else {
print("Record failed with error \(error!.localizedDescription)")
return
}
DispatchQueue.main.async {
sender.removeTarget(self, action: #selector(self.btnRecordTapped(_:)), for: .touchUpInside)
sender.addTarget(self, action: #selector(self.stoprecording(button:)), for: .touchUpInside)
sender.setTitle("Stop", for: .normal)
sender.setTitleColor(.red, for: .normal)
}
})
}
} else {
print("Screen Reocrder not availble")
}
}
I can see Prompt for permission in ios10 and ios9 but not for ios11
ios11 Completion ( closure) block never calls
I have already verified that method calls correctly if condition if RPScreenRecorder.shared().isAvailable { Also allows to let in
Please help me if anyone know about it
I had the same problem as you, so I thinked in updating to iOS 11.0.2 and it worked for me! Hope it help you too.
Just in case, here are my methods:
let recorder = RPScreenRecorder.shared()
#IBAction func recordingAction(_ sender: Any) {
if recorder.isRecording {
stopRecordAction()
} else {
startRecordAction()
}
}
func startRecordAction() {
recorder.startRecording{ (error) in
if let error = error {
print("❗️",error)
}
}
}
func stopRecordAction() {
recorder.stopRecording{ (previewVC, error) in
if let previewVC = previewVC {
previewVC.previewControllerDelegate = self
self.present(previewVC, animated: true, completion: nil)
if let error = error {
print("❗️",error)
}
}
}
}
Methods of RPPreviewViewControllerDelegate:
func previewControllerDidFinish(_ previewController: RPPreviewViewController) {
dismiss(animated: true, completion: nil)
}
func previewController(_ previewController: RPPreviewViewController, didFinishWithActivityTypes activityTypes: Set<String>) {
/// This path was obtained by printing the actiong captured in "activityTypes"
if activityTypes.contains("com.apple.UIKit.activity.SaveToCameraRoll") {
recordFinshedMessage()
}
}
view controller had two check boxes buttons
1.chekcIn
2.checkOut
am saving the checkIN [ checkbox button] status in user defaults, working fine but when am using that userdefaults key in Nextviewcontroller its always showing true and not running into false block
this is the code
inHomeview controller
#IBAction func CheckInButtonClick(_ sender: UIButton) {
for senderdata in checkINOUT {
senderdata.setImage( UIImage(named:"uncheck1"), for: .normal)
print("uncheck is called")
}
sender.setImage(UIImage(named:"check1"), for: .normal)
prefs.set(true, forKey: "check")
prefs.synchronize()
}
nextviewcontroller
override func viewDidLoad() {
super.viewDidLoad()
{
let prefs:UserDefaults = UserDefaults.standard
if prefs.bool(forKey: "check") ==true
{
print("select")
} else {
print("unselect")
}
}
check box select its execute main block if unselect execute else block
how to over come this problem where I did mistake
You´re not setting your userDefault value to false. You´re only setting it to true, that´s why it´s always true. And btw no need to use synchronize() Change your code to the following instead:
HomeViewController:
#IBAction func CheckInButtonClick(_ sender: UIButton) {
for senderdata in checkINOUT {
senderdata.setImage( UIImage(named:"uncheck1"), for: .normal)
print("uncheck is called")
}
sender.setImage(UIImage(named:"check1"), for: .normal)
UserDefaults.standard.set(true, forKey: "check")
}
NextViewController:
override func viewDidLoad() {
super.viewDidLoad()
if UserDefaults.standard.bool(forKey: "check") {
print("select")
} else {
print("unselect") {
}
}
So do check where you want to set your check value to false and use it.
Update:
Just do this check:
if UserDefaults.standard.set(true, forKey: "check") {
// Show data
} else {
// segue to another viewController
}
When you set "check" value as true once, it will always true, until you set "check" value to false.
I think you should add some code to set "check" to false, when user not select the checkbox.
I'm developing a music player by using Jukebox library. It has a delegation which warn elements when songs duration/ current time etc. changed. In Jukebox example, all of codes are in ViewController. Therefore, for example when current time changes, it also changes sliders value. I want to create a player class which is usable by more than one ViewController. When I put delegate methods inside this model class. How can I reach them in ViewController classes?
First Screenshot is first ViewController which has delegates methods and initialize player.
MainViewController
I need to reach that delegate methods in secondViewController to update the UI.
class ViewController: UIViewController, JukeboxDelegate {
var jukebox : Jukebox!
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
// begin receiving remote events
UIApplication.shared.beginReceivingRemoteControlEvents()
// configure jukebox
jukebox = Jukebox(delegate: self, items: [
JukeboxItem(URL: URL(string: "http://www.kissfm.ro/listen.pls")!),
JukeboxItem(URL: URL(string: "http://www.noiseaddicts.com/samples_1w72b820/2514.mp3")!),
JukeboxItem(URL: URL(string: "http://www.noiseaddicts.com/samples_1w72b820/2958.mp3")!)
])!
}
// MARK:- JukeboxDelegate -
func jukeboxDidLoadItem(_ jukebox: Jukebox, item: JukeboxItem) {
print("Jukebox did load: \(item.URL.lastPathComponent)")
}
func jukeboxPlaybackProgressDidChange(_ jukebox: Jukebox) {
if let currentTime = jukebox.currentItem?.currentTime, let duration = jukebox.currentItem?.meta.duration {
let value = Float(currentTime / duration)
slider.value = value
populateLabelWithTime(currentTimeLabel, time: currentTime)
populateLabelWithTime(durationLabel, time: duration)
} else {
resetUI()
}
}
func jukeboxStateDidChange(_ jukebox: Jukebox) {
UIView.animate(withDuration: 0.3, animations: { () -> Void in
self.indicator.alpha = jukebox.state == .loading ? 1 : 0
self.playPauseButton.alpha = jukebox.state == .loading ? 0 : 1
self.playPauseButton.isEnabled = jukebox.state == .loading ? false : true
})
if jukebox.state == .ready {
playPauseButton.setImage(UIImage(named: "playBtn"), for: UIControlState())
} else if jukebox.state == .loading {
playPauseButton.setImage(UIImage(named: "pauseBtn"), for: UIControlState())
} else {
volumeSlider.value = jukebox.volume
let imageName: String
switch jukebox.state {
case .playing, .loading:
imageName = "pauseBtn"
case .paused, .failed, .ready:
imageName = "playBtn"
}
playPauseButton.setImage(UIImage(named: imageName), for: UIControlState())
}
print("Jukebox state changed to \(jukebox.state)")
}
func jukeboxDidUpdateMetadata(_ jukebox: Jukebox, forItem: JukeboxItem) {
print("Item updated:\n\(forItem)")
}
If you want to use music playerview from anywhere
you should write player class all code for playing files should be in this and just need to pass array of list from anywhere and it can played.and use custom delegation for appropriate response in playerview
for UI make a viewcontroller and u can show it from any class so you can call from anywhere in the app
I have a TableView where I display all my data and each cell might have 1-2 buttons. I read many topics and understand how to add target for each button through my ViewController. Since these buttons will be forwarded to the same VC and display images, I have the following code. In my TableViewCell subclass I have 2 buttons
class CODetailsTicketCell: UITableViewCel {
var onButtonTapped: (() -> Void)? = nil
#IBAction func firstBtnTapped(_ sender: UIButton) {
if let onButtonTapped = self.onButtonTapped {
onButtonTapped()
}
print("First button was pressed")
}
#IBAction func secondBtnTapped(_ sender: UIButton) {
if let onButtonTapped = self.onButtonTapped {
onButtonTapped()
}
print("Second button was pressed")
}
}
In my ViewController in cellForRowAt indexPath I have the following code
let message = messages[indexPath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: "COTicketsCell", for: indexPath) as? CODetailsTicketCell {
cell.configureCell(openTickets: message)
cell.onButtonTapped = {
self.performSegue(withIdentifier: "toImageVC", sender: message)
}
return cell
In order to pass the data through segue I use the following code in prepareForSegue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toImageVC" {
let navigationController = segue.destination as? UINavigationController
if let targetController = navigationController?.topViewController as? ImageVC {
if let data = sender as? OpenTicketsData {
targetController.loadImageURL = URL(string: data.firstImageUrl)
}
}
}
}
Everything is working FINE but I can't check for button tag in prepareForSegue. Basically, currently both buttons send the same data
targetController.loadImageURL = URL(string: data.firstImageUrl)
How can I pass data based on the button pressed? I tried to do something like this but seems it's wrong and not working.
let button = sender as? UIButton
if let data = sender as? OpenTicketsData {
if button?.tag == 1 {
targetController.loadImageURL = URL(string: data.firstImageUrl)
} else if button?.tag == 2 {
targetController.loadImageURL = URL(string: data.secondImageUrl)
}
}
You can either separate it into 2 different events or
class CODetailsTicketCell: UITableViewCell {
var onButtonTapped: ((_ sender: UIButton) -> Void)? = nil
#IBAction func firstBtnTapped(_ sender: UIButton) {
if let onButtonTapped = self.onButtonTapped {
onButtonTapped?(sender)
}
print("First button was pressed")
}
#IBAction func secondBtnTapped(_ sender: UIButton) {
if let onButtonTapped = self.onButtonTapped {
onButtonTapped(sender)
}
print("Second button was pressed")
}
}
In your assignment of the onButtonTapped, remember to add [weak self] if you ever use self to avoid the retain cycle.
cell.onButtonTapped = { [weak self] sender in
if sender.tag == 1 {
// Do something
} else {
// Do other thing
}
}