I'm new to programming with iOS and Swift. I have a label that should automatically be updated when the status of the StreamingKit Framework changes.
Currently the label only changes when I press a different button (it calls the self.updateView() function again), but I want it to happen automatically when the status has changed, not by pressing a different button.
Here is the code:
func updateView() {
dispatch_async(dispatch_get_main_queue()) {
if let label = self.bufferingLabel, let button = self.playerButton{
if let audioPlayer = self.player{
if(audioPlayer.state == STKAudioPlayerStateBuffering)
{
self.playerState = "Loading"
button.setImage(self.image1, forState: UIControlState.Normal)
}
else(audioPlayer.state == STKAudioPlayerStatePlaying)
{
self.playerState = self.defaultPlayerState
self.playerState = "Playing"
button.setImage(self.image1, forState: UIControlState.Normal)
}
label.text = self.playerState!
}
}
So when I press this button, it gives the first state (which is loading), but after that is done (status changed), the label should change to play, but it doesn't.
#IBAction func pressStart(sender: AnyObject) {
//declare path to streaming
let filePath = "http://mp3.streampower.be/mnm-high.mp3"
if player == nil {
self.player = STKAudioPlayer()
self.player?.delegate = self
}
if let audioPlayer = player{
if (audioPlayer.state == STKAudioPlayerStateReady) ||
(audioPlayer.state == STKAudioPlayerStateStopped){
audioPlayer.play(filePath)
}else{
audioPlayer.stop()
}
}
self.updateView()
}
Some of the delegates I tried as well
func audioPlayer(audioPlayer: STKAudioPlayer!, didStartPlayingQueueItemId queueItemId: NSObject!) {
self.updateView()
}
func audioPlayer(audioPlayer: STKAudioPlayer!, didFinishBufferingSourceWithQueueItemId queueItemId: NSObject!) {
self.updateView()
}
func audioPlayer(audioPlayer: STKAudioPlayer!, stateChanged state: STKAudioPlayerState, previousState: STKAudioPlayerState) {
self.updateView()
}
You should look and use the delegate property of the STKAudioPlayer.
I'm not familiar with it, but generally in the iOS world, that's how you receive events from an object (the object delegates some stuff to the object of your choice).
You can use NSNotifications to send a signal whenever the streaming kit changes. Then you can add an observer to your UI view controller that will trigger a function whenever it receives a signal.
If you want to try out functional reactive programming you can also use The Reactive Cocoa framework to do a similar signal observer pattern. And finally, there is plain old key-value observation. One of these options should be able to help you.
Related
I trying implement Rewarded Ad - Rewarded Ads New APIs (Beta). Video is load and isReady property is changing to true in a couple of seconds.
I have a button on which user press and Rewarded Video appear
This is function which is fire when user press on button
func presentRewardAd(from viewController: UIViewController) {
if rewardedAd.isReady {
rewardedAd.present(fromRootViewController: viewController, delegate: self)
}
}
The problem is
I want to hide button until video isReady == true, and when it's ready show button. So i want to get notify when rewardedAd.isReady is changing.
What i try so far:
class CustomRewardAd: GADRewardedAd {
private var observation: NSKeyValueObservation?
override init(adUnitID: String) {
super.init(adUnitID: adUnitID)
observation = observe(\.isReady, options: [.old, .new]) { object, change in
print("isReady changed from: \(change.oldValue!), updated to: \(change.newValue!)")
}
}
}
Also i tried this Using Key-Value Observing in Swift but same result.
But changeHandler never gets called. Am i doing something wrong?
Thanks!
I found solution, not ideal but it's works! Maybe this is can help someone in future.
When new rewarded request finishes, isReady property is set to true or false depends what response is.
private func createAndLoadRewardedAd() -> GADRewardedAd {
let rewardedAd = GADRewardedAd(adUnitID: "ca-app-pub-3940256099942544/1712485313")
rewardedAd.load(GADRequest()) { [weak self] error in
guard let self = self else { return }
self.videoIsReady?(rewardedAd.isReady) // already set
}
return rewardedAd
}
You are welcome!
I am using xcode 8.2 and swift to make a simple application.
I have added a UIButton to my View using the Interface Builder.
I have added the appropriate outlets for the button:
#IBOutlet weak var myBtn: UIButton!
I want this button to be hidden on start so in viewDidLoad I am setting is to Hidden. Like this:
override func viewDidLoad() {
super.viewDidLoad()
...
myBtn.isHidden = true
...
mqttConfig = MQTTConfig(clientId: "iphone7", host: "192.xx.xx.150", port: 18xx, keepAlive: 60)
mqttConfig.onMessageCallback = { mqttMessage in
if ( mqttMessage.topic == "status" ) {
if ( mqttMessage.payloadString?.localizedStandardContains("show") )! {
self.showButton = true
} else if ( mqttMessage.payloadString?.localizedStandardContains("hide") )! {
self.showButton = false
}
self.showHideSeatButtons()
} else {
// something to do in case of other topics
}
}
Later in the code I have a function to show/hide this button.
func showHideButton(){
if ( self.showButton ) {
print("button enabled!")
myBtn.isHidden = false
} else {
print("button disabled!")
myBtn.isHidden = true
}
}
When I call this function (by receiving a certain message using MQTT) I get the print outs but I don't see the button.
If I press where I know the button is, then the button gets shown.
Any idea what could be going on here? I have spent and hour googling this now! Please don't suggest object-c way of solving this issue, as I don't know object-c.
In onMessageCallback block
Replace following line
self.showHideSeatButtons()
with
DispatchQueue.main.async {
self.showHideSeatButtons()
}
Note: UI related changes/updates must be handled by main queue (thread).
Since you're calling a service it's possible you're not working in the same thread. Try this:
func showHideButton(){
DispatchQueue.main.async {
if (self.showButton ) {
print("button enabled!")
self.myBtn.isHidden = false
} else {
print("button disabled!")
self.myBtn.isHidden = true
}
}
}
try this
myBtn.isHidden = true
myBtn.alpha = 0
Upon a button press, I need to pull down paginated data (pulling down pins from Pinterest IOS SDK, the pins are limited to 25 per call), i.e. I need my code to iterate until responseObject no longer .hasnext. What's the best way to approach this? I can press the button over and over to pull it all down, but I want to automate it so there's only 1 button press required.
#IBAction func didPressGetPinsButton(sender: UIButton) {
if self.fetchingMore == false && self.currentResponseObject.hasNext() {
self.fetchingMore = true
self.currentResponseObject.loadNextWithSuccess({ (nextResponseObject :PDKResponseObject!) -> Void in
self.fetchingMore = false
self.currentResponseObject = nextResponseObject
guard let unwrappedArrayOfPins = nextResponseObject.pins() as? [PDKPin] else {return}
for pin in unwrappedArrayOfPins {
pins.append(pin)
}
put the action code into a separate function, so that you call it for the first time from the button press, and then you keep re-calling it while .hasNext
#IBAction func didPressGetPinsButton(sender: UIButton) {
actionGetPins()
}
func actionGetPins() {
// all your code
if nextResponseObject.hasNext() {
actionGetPins()
}
}
Depending on how long it takes to get all of the data, you might also want to run the whole function in a background task, but you should at least add a busy indicator to show that there's still work being done.
I have UITextView on which I want to add highlight as custom menu item. I have registered to following notification UIMenuControllerWillShowMenuNotification.
The method for the notification is something like this:
if textIsHighlighted {
let highlightMenuItem = UIMenuItem(title: "Highlight", action: Selector("highlightText"))
UIMenuController.sharedMenuController().menuItems = [highlightMenuItem]
}
else {
let highlightMenuItem = UIMenuItem(title: "Dehighlight", action: Selector("highlightText"))
UIMenuController.sharedMenuController().menuItems = [highlightMenuItem]
}
Although the first time the menucontroller fails to update even though it executes the part of code. It shows the last value. Where should I write this part of code as I feel that during willShow menuController it's already created and thus fails to update.
Hopefully you've solved this by now, but I've just figured this one out myself:
Other answers have said you can update the menu items by adding it when the UIMenuControllerWillShowMenuNotification is called, but this wasn't working for me (iOS 9, Swift 2).
Instead I implemented the UITextView delegate method: textViewDidChangeSelection and set the relevant menu items there:
func textViewDidChangeSelection(textView: UITextView) {
if self.currentSelectionIsInHighlightedRange() {
self.setUpUnhighlightMenuItem()
} else {
self.setUpHighlightMenuItem()
}
}
private func currentSelectionIsInHighlightedRange() -> Bool {
let allHighlightedRanges = self.document.highlightedRanges()
let selectedTextRange = self.documentView.textView.selectedRange
for range in allHighlightedRanges {
let intersectionRange = NSIntersectionRange(range, selectedTextRange)
if intersectionRange.length > 0 {
return true
}
}
return false
}
In my program 2 functions (IBAction player.Move(UIButton) and autoMove()) are supposed to be called by turns till all of the fields (UIButtons) has been clicked. For this I've created a function play(). However, I don't know how can I put the IBAction playerMove inside of play() function, because I need no parameter here.
I've found some answers and tried self.playerMove(nil) and self.playerMove(self) but it doesn't work.
import UIKit
class ViewController: UIViewController {
#IBOutlet var cardsArray: Array<UIButton> = []
var randomCard = 0
override func viewDidLoad() {
super.viewDidLoad()
self.play()
// Do any additional setup after loading the view, typically from a nib.
}
func play () {
self.autoMove()
self.playerMove(self) // <----- here is my problem
}
#IBAction func playerMove(sender: UIButton) {
switch (sender) {
case self.cardsArray[0]:
self.cardPressedAll(0)
case self.cardsArray[1]:
self.cardPressedAll(1)
case self.cardsArray[2]:
self.cardPressedAll(2)
case self.cardsArray[3]:
self.cardPressedAll(3)
default: break
}
}
func cardPressedAll (cardNumber: Int) {
self.cardsArray[cardNumber].enabled = false
self.cardsArray[cardNumber].setBackgroundImage(UIImage(named: "cross"), forState: UIControlState.Normal)
self.cardsArray.removeAtIndex(cardNumber)
}
func autoMove (){
self.randomCard = Int(arc4random_uniform(UInt32(self.cardsArray.count)))
self.cardsArray[self.randomCard].enabled = false
self.cardsArray[self.randomCard].setBackgroundImage(UIImage(named: "nought"), forState: UIControlState.Normal)
self.cardsArray.removeAtIndex(self.randomCard)
}
}
Either you have to call playerMove: without a button, in which case you have to declare the sender parameter as an optional. Like:
#IBAction func playerMove(sender: UIButton?) {
UIButton means that you have to pass in a button. nil is not a button, but with UIButton?, that is to say Optional<UIButton>, nil is a valid value meaning the absence of a button.
Or you have to work out which button you want to pass to playerMove: to make it do what you want. Sit down and work out what you want to have happen, and what the code needs to do in order to make that happen.
Try
self.playerMove(UIButton())
Your func playerMove has parameters expecting sender to be of type UIButton, self or nil would be an unexpected object.
Edit:
You could us optional parameters by placing ?. This would allow you to call self.playerMove(nil) if needed.
#IBAction func playerMove(sender: UIButton?) {
if sender != nil {
//handle when button is passed
} else {
//handle when nil is passed
}
}
doSomeTask(UIButton()) in swift 5.0 and onward worked for me