View is not being updated when calling UIButton.isHidden = true/false - ios

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

Related

How can i handle the multiple click action to single button for api calling and get the true response from api

i just want to know the method to handle multiple click event on single button.(e.g FACEBOOK like button if i tapped multiple time then it may work perfect)
below code give you some idea and if there is any appropriate solution then give me as soon as possible.
class LikeViewController: UIViewController {
//MARK:- Outlets
#IBOutlet weak var btnLike: UIButton!
#IBOutlet weak var lblDescription: UILabel!
//MARK:- Variables
var objModelWatchList:WatchListModel?
var objUser = WatchListModel()
//MARK:- Lifecycle methods
override func viewDidLoad() {
super.viewDidLoad()
getWatchList()
}
//MARK:- Functions
//Function for prepare UI
func prepareUI() {
btnLike.isSelected = isLike()
}
//Function for prepare data from api
func getWatchList() {
objUser.videoId = 216
objUser.type = "VIDEO"
APIService.sharedInstance.getWatchList(parameters: objUser.toDictionary() as [String : AnyObject], success: { (dataSuccess) -> (Void) in
self.objModelWatchList = dataSuccess
DispatchQueue.main.async {
self.prepareUI()
self.lblDescription.text = self.objModelWatchList?.message
}
}) { (resultFailure) -> (Void) in
print(resultFailure)
}
}
//Function to varify the status of like
func isLike() -> Bool {
return objModelWatchList!.status == 1 ? true : false
}
//MARK:- Actions
#IBAction func btnLikeClicked(_ sender: UIButton) {
sender.isSelected = !sender.isSelected
self.getWatchList()
}
}
Thank You.
You have to disable your button when API call and when u will get right response after the processing on that response you have to enable it.
Its works for me. I have a same issue.
if you refer to manage several events taps...I recommend that use tapGestureRecognizer or variants, these can manage events that you comment for example...:
Single tap,double tap or more taps to trigger function event
Hold tap [UILongPressGestureRecognizer]
if you refer to same button called severals function in diferents situations i recommend the use un tag button for example:
function examplefunctionA(){
//Another Proccess
//Another Proccess
self.button.tag = 2
}
function examplefunctionB(){
//Another Proccess
//Another Proccess
self.button.tag = 1
}
func buttonclicked(sender: UIButton) {
if sender.tag == 1 {
examplefunctionA()
}else if sender.tag == 2 {
examplefunctionB()
}
}

UISwitch switched off by user but is still programatically on

I am experimenting with UISwitches on xcode using Swift. I have it to where there is a handful of Company names and their corresponding on and off switches. From top to bottom, if the user switches for example, "Google" Switch on, then they are able to view the chosen companies facebook and twitter. If they choose another company, once they turn that switch on for example "Samsung" , the Google switch turns off then the user is able to see Samsungs facebook and twitter page.
My issue is... from top to bottom, it works fine. Im able to see both pages from Google to samsung. But if I go from samsung back to Google, the google switch is on but it still shows samsung's pages. But, if I switch the saumsung switch on,off then go to google, it works fine.
My assumption is that while the switch visually looks off once another switch is turned on from bottom to top, it is still programmatically on
I'm wondering if people have had a similar issue. If not, I hope this helps those in the future if they find themselves in a similar situation
Here is my main view Controller
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var GoogleSwitch: UISwitch!
#IBOutlet weak var SamsungSwitch: UISwitch!
#IBOutlet weak var FordSwitch: UISwitch!
#IBOutlet weak var ToyotaSwitch: UISwitch!
#IBOutlet weak var SquareEnixSwitch: UISwitch!
#IBOutlet weak var ABCBankSwitch: UISwitch!
var Google: Bool = false
var Samsung: Bool = false
var Ford: Bool = false
var Toyota:Bool = false
var SquareEnix :Bool = false
var ABCBank :Bool = false
override func viewDidLoad() {
super.viewDidLoad()
// ABCBankSwitch.addTarget(self, action: Selector("switchIsChanged:"), forControlEvents: UIControlEvents.ValueChanged)
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func ActiveGoogle(sender: UISwitch) {
if sender.on == true
{
GoogleSwitch.on = true
SamsungSwitch.on = false
ToyotaSwitch.on = false
SquareEnixSwitch.on = false
ABCBankSwitch.on = false
Google = true
// GoogleSwitch.on = true
}
else
{ Google = false
sender.on = false
}
// sender.on = false
}
#IBAction func ActiveSamsung(sender: UISwitch) {
if sender.on == true
{
GoogleSwitch.on = false
SamsungSwitch.on = true
FordSwitch.on = false
ToyotaSwitch.on = false
SquareEnixSwitch.on = false
ABCBankSwitch.on = false
Samsung = true
}
else
{sender.on = false
Samsung = false}
}
#IBAction func ActiveFord(sender: UISwitch) {
if sender.on == true
{Ford = true
GoogleSwitch.on = false
SamsungSwitch.on = false
FordSwitch.on = true
ToyotaSwitch.on = false
SquareEnixSwitch.on = false
ABCBankSwitch.on = false
}
else
{ if sender.on == false {
Ford = false}
}
}
#IBAction func ActiveToyota(sender: UISwitch) {
if sender.on == true
{ Toyota = true
GoogleSwitch.on = false
SamsungSwitch.on = false
FordSwitch.on = false
ToyotaSwitch.on = true
SquareEnixSwitch.on = false
ABCBankSwitch.on = false
}
else
{ Toyota = false}
}
#IBAction func ActiveEnix(sender: UISwitch) {
if sender.on == true
{ SquareEnix = true
GoogleSwitch.on = false
SamsungSwitch.on = false
FordSwitch.on = false
ToyotaSwitch.on = false
//SquareEnixSwitch.on = true
ABCBankSwitch.on = false
}
else
{ SquareEnix = false
// sender.on = false
}
}
#IBAction func ActiveABC(sender: UISwitch) {
if sender.on == true
{ ABCBank = true
ABCBankSwitch.setOn(true, animated: true)
GoogleSwitch.on = false
SamsungSwitch.on = false
FordSwitch.on = false
ToyotaSwitch.on = false
SquareEnixSwitch.on = false
}
}
else
{
ABCBankSwitch.setOn(false, animated: true)
ABCBank = false
// sender.on = false
}
}
}
here is my FacebookViewController. (not putting the twitter on as it is almost identical to the facebook one)
import UIKit
class FacebookViewController: UIViewController{
var webAddress: String = ""
#IBOutlet weak var CompanyFB: UIWebView!
override func viewWillAppear(animated: Bool) {
let myCompany: ViewController = self.tabBarController!.viewControllers![0] as! ViewController
let showGoogle: Bool = myCompany.Google
let showSamsung: Bool = myCompany.Samsung
let showFord: Bool = myCompany.Ford
let showToyota: Bool = myCompany.Toyota
let showEnix: Bool = myCompany.SquareEnix
let showBank:Bool = myCompany.ABCBank
if showGoogle {
webAddress = "https://www.facebook.com/Google/"
//myCompany.GoogleSwitch.on = false had this to test. either way is still doesnt work
// CompanyFB.loadRequest(NSURLRequest(URL: NSURL(string: webAddress)!))
}
if showSamsung {
webAddress = "https://www.facebook.com/SamsungUSA"
// myCompany.SamsungSwitch.on = false
// CompanyFB.loadRequest(NSURLRequest(URL: NSURL(string: webAddress)!))
}
if showFord {
webAddress = "https://www.facebook.com/ford/"
// CompanyFB.loadRequest(NSURLRequest(URL: NSURL(string: webAddress)!))
}
if showToyota {
webAddress = "https://www.facebook.com/toyota"
// CompanyFB.loadRequest(NSURLRequest(URL: NSURL(string: webAddress)!))
}
if showEnix {
webAddress = "https://www.facebook.com/SquareEnix"
}
if showBank {
webAddress = "https://www.facebook.com/ABC-Bank-132047286857188/"
}
CompanyFB.loadRequest(NSURLRequest(URL: NSURL(string: webAddress)!))
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Your problem is that you aren't keeping your separate booleans in sync with your switches. Consider this code:
#IBAction func ActiveGoogle(sender: UISwitch) {
if sender.on == true {
GoogleSwitch.on = true
SamsungSwitch.on = false
ToyotaSwitch.on = false
SquareEnixSwitch.on = false
ABCBankSwitch.on = false
Google = true
}
else {
Google = false
sender.on = false
}
}
If I change the "Google" switch then then Google variable will be set to true/false accordingly. This code also turns off all of the other switches, but it doesn't change the boolean associated variables. So, if after selecting Google I then select "Samsung", the Google switch will be turned off but the Google variable will still be true.
You could use a computed property rather than the separate booleans:
var Google: Bool {
return self.GoogleSwitch.on
}
and so on.
Also, by convention variables and functions should start with a lower case letter, so it should be var google and func activateGoogle
There are a few things going on here, some unrelated to your problem, but I'll comment on them as well so you can improve as a coder.
1) Convention for Swift dictates that you name variables and methods starting with a lower case letter. So GoogleSwitch should be googleSwitch etc. Also, try and be descriptive with the names. In your FacebookViewController you have a variable myCompany for the ViewController class. First, the class should be called something like MyCompanyViewController so you know which view controller it is. And then the myCompany variable could be called myCompanyViewController. CompanyFB should be something like companyFBWebView.
2) You can really simply your code a lot here. Use a single IBAction for all of the switches. A common problem (which may be an issue here), is when you copy and paste controls in Interface Builder, it sometimes will copy the actions assigned as well, and then you add another, so it calls two methods each time it's switched. By using a single method, you save a tonne of duplicated code and avoid this problem.
Your entire first ViewController class can be condensed to this (then connect all of your switches to the same IBOutlet switchToggled::
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var googleSwitch: UISwitch!
#IBOutlet weak var samsungSwitch: UISwitch!
#IBOutlet weak var fordSwitch: UISwitch!
#IBOutlet weak var toyotaSwitch: UISwitch!
#IBOutlet weak var squareEnixSwitch: UISwitch!
#IBOutlet weak var abcBankSwitch: UISwitch!
#IBAction func switchToggled(sender: UISwitch) {
// Remember the state of the triggered switch because we're about to turn it off
let newState: Bool = sender.on
googleSwitch.on = false
samsungSwitch.on = false
fordSwitch.on = false
toyotaSwitch.on = false
squareEnixSwitch.on = false
abcBankSwitch.on = false
// Restore the state of the switch
sender.on = newState
}
}
3) Avoid using duplicate variables for the same thing, the Bool for Google etc should be the same as the switch, so just use the switch.on value. Edit: (or as Paulw11 mentioned, use a calculated property).
4) When using an if to test a variable state, or multiple variable states as you're doing in the FacebookViewController class, when only one possible case should happen, use else if instead of multiple if's. In your case, you're triggering two or more cases at once (due to your other bugs). If you used else if clauses, you'd only ever trigger one and you likely would have narrowed down your other bug earlier.
if showGoogle {
webAddress = "https://www.facebook.com/Google/"
}
else if showSamsung {
webAddress = "https://www.facebook.com/SamsungUSA/"
}
...
5) Expanding on #4 you could change this to make use of Swift's powerful enum features.
import UIKit
class FacebookViewController: UIViewController {
enum Company: String {
case Google = "https://www.facebook.com/Google/"
case Samsung = "https://www.facebook.com/SamsungUSA/"
case Ford = "https://www.facebook.com/ford/"
case Toyota = "https://www.facebook.com/toyota/"
case SquareEnix = "https://www.facebook.com/SquareEnix/"
case ABCBank = "https://www.facebook.com/ABC-Bank-132047286857188/"
}
var company: Company = .Google // Default to Google
#IBOutlet weak var companyFBWebView: UIWebView!
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
let myCompanyViewController: ViewController = self.tabBarController!.viewControllers![0] as! ViewController
if myCompanyViewController.googleSwitch.on {
company = .Google
}
else if myCompanyViewController.samsungSwitch.on {
company = .Samsung
}
else if myCompanyViewController.fordSwitch.on {
company = .Ford
}
else if myCompanyViewController.toyotaSwitch.on {
company = .Toyota
}
else if myCompanyViewController.squareEnixSwitch.on {
company = .SquareEnix
}
else if myCompanyViewController.abcBankSwitch.on {
company = .ABCBank
}
if let url = NSURLRequest(URL: NSURL(string: company.rawValue)) {
companyFBWebView.loadRequest(url)
}
}
}
6) In Swift, avoid using ! like the plague. Ideally you'll only use it for IBOutlet's and other cases you know for certain a value exists. The line let myCompanyViewController: ViewController = self.tabBarController!.viewControllers![0] as! ViewController is likely to crash in some cases. Possibly due to timing issues, or other changes you may make to the code down the road. Safer to check the options with if let and handle the error gracefully.
7) I just noticed another issue as I was proofreading. Anytime* you override a method from a super class, make sure you also call the super's version. So your viewWillAppear(animated:) etc calls need to call the super version or you can get really weird, hard to track down bugs.
I put a star on Anytime in #7 because there are some cases where you intentionally don't want to call the super method, but those are rare and you'll know it when the time comes.
I'm assuming your IBActions are hooked up to the ValueChanged event, and you expect them to fire in two instances: when you change the switch by touching it; or when you change the value of the on property.
If so, the problem is that the event does not fire in the second case, only the first. Therefore, once Google is set to true, the only way it gets set back to false is by touching the switch to off.
Since your model only wants one switch to be on, you could instead have one variable containing the switch that should be on. When you touch a switch, just update that one variable and redisplay all of them. Using an enum for this variable would be helpful. Along the lines of:
enum Switches {
case None
case Google
case Samsung
}
var onSwitch = Switches.None
Then code in each IBAction along the lines of:
#IBAction func ActiveGoogle(sender: UISwitch) {
if sender.on == true {
onSwitch = Switches.Google
updateSwitches()
}
}
And updateSwitches looks like:
func updateSwitches() {
GoogleSwitch.on = (onSwitch == Switches.Google)
SamsungSwitch.on = (onSwitch == Switches.Samsung)
}

UIMenuController doesn't update menu for first time

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
}

automatically update label in Swift by checking the status

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.

Calling IBAction from another method without parameter

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

Resources