UISwitch setOn(:, animated:) does not work as document - ios10

As Apple's document write, UISwitch's function setOn(on: Bool, animated: Bool) does not send action. It works fine before iOS 10, but it will send action after I call it in iOS 10. I call it in "ValueChanged" event to force switch back, so I got this event action twice. is it a bug in iOS 10?

DispatchQueue.main.async {
sender.setOn(flag, animated: animated)
}
it works for me in Xcode 8.
but call UISwitch.setOn(_:animated:) directly on main thread doesn't work.
Update
thanks to #codiction:
UISwitch.setOn(_:animated:) can be called direclty on main thread, but can't be called directly in UISwitch ValueChanged action on iOS 10.

The following solution resolves that issue. You should dispatch_async only if trying to call [UISwitch setOn:] within the action callback of that switch itself.
dispatch_async(dispatch_get_main_queue(), ^{
[switch setOn:YES animated:YES];
});

I found these solutions to partially work. However, when I used switch.setOn(false, animated: true) the onTint color would appear white in the off position. I'm assuming this is a bug but I worked around this by using this:
switch.setOn(false, animated: true)
switch.onTintColor = .clear
Don't forget to reset the onTint color when you're ready to allow the switch to be toggled on, if applicable.
switch.onTintColor = <your custom color> // Use nil to use the default onTint for UISwitch
Hopefully this helps save someone a little time.

Related

performSegue(withSender: sender:) does not work - no errors displayed

func checkForRecipes(noRecords: Bool) {
//segue to addNewRecipe page
if noRecords == true{
print("Can't Find any Recipes!")
self.performSegue(withIdentifier: "ToAddNewRecipeVC", sender: self)
}else{
print("error, noRecords not equal to zero")
}
I am able to segue successfully via the storyboard but want to do so programmatically based on information returned from a delegate.
Upon running the app, the information from the delegate is successfully sent to the function "checkForRecipes" -i.e "noRecords" returns TRUE, but for some reason, the below line of code within that function does not seem to execute (and no errors are thrown):
self.performSegue(withIdentifier: "ToAddNewRecipeVC", sender: self)
The app starts up but stops at the main screen, whereas it should segue to the "AddNewRecipe" view controller.
The segue itself definitely has a segue ID of "ToAddNewRecipeVC". I have also tried dispatching to the main queue (to no avail) based on the following thread.
I'm stumped - what's going wrong here?
OK, it looks as though I have solved the problem. I embedded the main view controller into a navigation controller and now everything works as intended. I tried this same tactic earlier and it kept throwing up errors. grrr!
Anyway - thank you to all for the input!

Swift, unable to reload chat window using JSQMessagesViewController

My app allows users to view another users info by tapping their avatar, at which they can block that user so that their message content is not visible.
What is the best way when returning to the chat view to clear the messages and reload them?
This would allow my blocking code to work on the fly. Currently it works when I dismiss the chat view and return but not when jump to another view and then back to the chat view.
Ive tried self.collectionView!.reloadData() but that does not do anything.
All you need is to implement
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.collectionView?.reloadData()
}
that is called when you come back from an already instantiated view.
Call the reloadData method after a delay.
self.performSelector(#selector(self.delayReload), withObject: nil, afterDelay: 0.1)
func delayReload() {
self.collectionView.reloadData()
}
Hope this will help you.
Sincerely,
Harri.

Keep window always on top?

In Objective-C for Cocoa Apps it's possible to use such way to keep window always on top?
How to achieve the same with Swift?
self.view.window?.level = NSFloatingWindowLevel
Causes build error Use of unresolved identifier 'NSFloatingWindowLevel'
To change the window level you can't do it inside viewDidload because view's window property will always be nil there but it can be done overriding viewDidAppear method or any other method that runs after view is installed in a window (do not use it inside viewDidLoad):
Swift 4 or later
override func viewDidAppear() {
super.viewDidAppear()
view.window?.level = .floating
}
For older Swift syntax check the edit history
I would prefer this way. This ignores all other active apps, and makes your app upfront.
override func viewWillAppear() {
NSApplication.sharedApplication().activateIgnoringOtherApps(true)
}
While the other answers are technically correct - when your app will or did resigns active, setting the window level to .floating will have no effect.
.floating means on top of all the windows from the app you are working on, it means not on top of all apps windows.
Yes there are other levels available you could set, like kCGDesktopWindowLevel which you can and should not set in swift to make your window float above all.
None of them will change the fact that your window will go behind the focused and active apps window. To circumvent i chose to observe if the app resigns active notification and act accordingly.
var observer : Any;
override func viewDidLoad() {
super.viewDidLoad()
observer = NotificationCenter.default.addObserver(
forName: NSApplication.didResignActiveNotification,
object: nil,
queue: OperationQueue.main ) { (note) in
self.view.window?.level = .floating;
// you can also make your users hate you, to take care, don't use them.
//NSApplication.shared.activate(ignoringOtherApps: true)
//self.view.window?.orderFrontRegardless();
}
}
another way could be subclassing NSWindow and override the property .level with an always returning .floating, but the code above is less work and keeps control in the place where you want to set the window floating.
I spent a long time trying to make this work. I then realized there was a simple answer, just worded in a different way. Here it is: Change macOS window level with SwiftUI (to make a floating window)
As explained there:
You can access your windows with NSApplication.shared.windows and set the level for each one.
for window in NSApplication.shared.windows {
window.level = .floating
}
EDIT: you can use other levels, including .screenSaver (highest, I think) and ```.normal`` if you want to return to standard behavior. Source: https://developer.apple.com/documentation/appkit/nswindow/level

Label takes a couple of seconds to unhide

So I recently started using Swift and basically I have a UILabel I use to display error messages. This label is intially hidden (through storyboard's hidden checkmark, I tried setting it to hidden in viewDidLoad also).
When a user clicks login an apicontroller class posts to a backend server to check credentials. The apicontroller has a delegate which is called in the completionHandler in (NSURLSession.dataTaskWithRequest). The protocol method in the viewcontroller checks to see the http status and if it's 200 it moves on to the next view. However, if it's not 200, the view controller unhides the error label
(self.errorLabel.hidden = false and self.erorLabel.text = "ERROR MESSAGE HERE").
Here is my issue:
When the erorrlabel is supposed to be unhidden, I set it's hidden property to false, println("should be unhidden now") and display an alert. The alert and print statement are executed instantly. However, the label takes a couple of seconds before it is displayed.
I've tried this on the 4s, 5, 5s and 6 in simulator and an actual 5s and I still get this issue. I've also tried manually refreshing the view with self.view.setNeedsDisplay() and it still doesn't work.
Any ideas?
You are probably updating the label in a thread which is not the main thread.
Enclose the code inside this block:
dispatch_async(dispatch_get_main_queue()) {
// Update the label here
}
Thanks to Antonio for reffering me to this question. It has indeed resolved my problem which I asked here: https://stackoverflow.com/questions/26800093/setting-iboutlet-in-closure-result-in-swift
This is the final block of code which worked for me:
homeModel.GetHomeData({(textResult:String!) in
dispatch_async(dispatch_get_main_queue())
{
// Update the textview in the ui here
self.homeTextView.text = textResult
}
});

Unbalanced calls to begin/end appearance transitions for <FirstViewController: 0x2a2c00>

I have this problem when I simulate my app, its not an error or a warning but it appears in my console, has anyone ever experienced this before?
In my case, this error occurs when you click two tabs in a tableview very fast.
The result causes wrong titlename, back button disappear. Someone mentioned that when you push a view, set animated:NO. The error will disappear but still causes some strange behavior. It pushes two views, then you need to back twice to get back the tableview screen.
Method I tried in order to resolve this problem:
add BOOL cellSelected;
in viewWillAppear cellSelected = YES;
in didselectcell delegate if (cellSelected){cellSelected = NO; do action ; }
This helps prevent clicking two different cells very fast.
In my case it happened when I triggered [self performSegueWithIdentifier:#"SomeIdentifier" sender:self]; within a UINavigationController item's viewDidLoad method.
Moving it into the viewDidAppear method solved the problem.
The reason very likely is that in viewDidLoad not all of the fancy animations have already been finished, whereas in viewDidAppear everything's done.
I have this problem too. I found two solutions to this problem:
You can see this solution above.
I found subclass from UINavigationController where this problem resolved. Buffered Navigation Controller
You should run your code in different loop to avoid this
double delayInSeconds = 0.1;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// Put your code here
[self presentViewController:self.yourModalVC animated:YES completion:nil];
});
I had lot of problem with the same issue. I solved this by this way
1) You're not using UIViewController's designated initializer initWithNibName:bundle:. Try using it instead of just init.
2) set animated:YES to a NO, and that solved the problem.
eg. [self.navigationController pushViewController: viewController_Obj animated:NO];
I had the same issue using navigation controller and push other controllers to it.
I tried to use Buffered Navigation Controller and several other approaches, but it didn't work for me. After spending some time for figuring it out I noticed that this issue occurs if you trying to push new view controller while previous transaction (animation) in progress (about 0.5 sec duration I guess). Anyway, I made quick solution with delegating navigation controller and waiting for previous animation finishes.
Ensure that you do not forget to in -viewWillAppear, -viewDidAppear, -viewDidLoad, -viewWillDisappear, -viewDidDisappear to call proper super method in your overload of that methods.
For example in my case I mismatched method name like this:
-(void)viewDidAppear
{
[super viewDidDisappear];
//some code staff
..
}
notice that appear and disappear methods are mismatched
'Unbalanced calls to begin/end appearance transitions for '
Says an animation is started before the last related animation isn't done. So, are you popping any view controller before pushing the new one ? Or may be popping to root ? if yes try doing so without animation i.e. [self.navigationController popToRootViewControllerAnimated:NO];
And see if this resolves the issue, In my case this did the trick.
I had a similar problem that involved rewinding modal dialogs. Posted the solution here...
https://stackoverflow.com/a/38795258/324479
[Problem]
Nav Controller -> VC1 -Push--> VC2 -PopOver or Modal Segue--> VC3.
VC3 is unwinding back to VC1.
When the Segue from VC2 to VC3 is PopOver and Modal, the unwind ends in a warning: Unbalanced calls to begin/end appearance transitions for UIViewController"
If the Segue from VC to VC is push, the warning is gone.
[Solution]
It would be great if the unwind logic would take care of this. Maybe it's a bug, maybe not. Either way, the solution is to make VC2 (The controller that has the popup) the target of the rewind, then wait for it to finish appearing before popping up the nav controller. That ensures the rewind (reverse popup) animation has enough time to finish before moving further back. Even with the animations off, it still has to wait or else you get the error.
Your code for VC2 should be as follows. (Swift)
class VC2: UIViewController {
private var unwind = false
#IBAction func unwindToVC1(segue:UIStoryboardSegue) {
unwind = true
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if unwind {
self.navigationController?.popViewControllerAnimated(false)
}
}
}
I got this issue because I was calling a UIPrintInteractionController from a viewController without UITabbar, and neither UINavigationBar. It seems that the UIPrintInteractionController didn't get the correct printInteractionControllerParentViewController.
Implementing the method in the delegate and return the current rootViewController worked for me.
- (UIViewController*)printInteractionControllerParentViewController:(UIPrintInteractionController*)printInteractionController;
Swift 4
My issue was that I was presenting another VC before my current one finished to be rendered .
Solution was to present my nextVC after a quick delay.
WHAT YOU SHOULD NOT DO
override func viewDidLoad() {
super.viewDidLoad()
self.present(MyNextVC(), animated: true, completion: nil)
}
WHAT YOU SHOULD DO
override func viewDidLoad() {
super.viewDidLoad()
//Wait until the view finished to be constructed
perform(#selector(showMyNextVC), with: nil, afterDelay: 0.01)
}
#objc func showCityList() {
self.present(MyNextVC(), animated: true, completion: nil)
}
The situation can occur if you are adding a view with a modal view controller as a sub view. Best to use:
-(void) viewDidAppear:(BOOL)animated {
[self presentViewController:self.yourModalVC animated:YES completion:nil];
}
It is basically saying the view life cycle is not streamlined for those viewControllers you are trying to display then.
I have the similar problem when trying to do:
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:2] animated:YES];
in a function like - (void) popUpToLevelTwo; , and to put a return; at the end of function solves the problem
I also got this at
[self dismissModalViewControllerAnimated:YES];
I changed the YES to a NO, and that solved the problem.
i have same problem when i used navigationcontroller's pop method
In my app i use a separate logic for navigation controller,So avoided the use of navigation bar and it is always hidden. Then i use a custom view and notification for handling the backbutton and it's events. notification observers are registered and and not removed. So the notification fires twice, and it creates the above mentioned error. Check your code throughly for getting such fault's
For what it's worth, I got this same error when not including a call to [super viewDidLoad:animated] in my viewDidLoad override.
I also had this problem when I tapped a button from a NIB. It turns out I had accidentally wired the button to send an event to two IBAction methods, each of which did a pushViewController:animated:
I had some logic implemented to wait pushing the UIViewController until all data was downloaded. There was an error in this logic which caused to push the UIViewController too early while there was still another API call in progress.
It caused the same UIViewController to be pushed twice by the UINavigationController and gave this warning.
Reason For message: This message get displayed if and only if you are pushing/presenting another View controller from viewWillAppear,loadView,init or viewDidLoad method of current View Controller
Way to Remove error Message: Move your pushing/presenting code to viewDidAppear method will solve the issue
I had this problem when I forget to set Break; after pushing the view in a switch statement!
Like here:
case 1:{
SomeViewController *someViewController = [[SomeViewController alloc]initWithNibName:#"SomeViewController" bundle:Nil];
[self.navigationController pushViewController:someViewController animated:YES];
[someViewController release];
}
break; //Forgetting to set break here:
the reason behind the error " Unbalanced calls to begin/end appearance transitions" is when you navigate | segue twice at the same time
one solution would be,
[NSTimer scheduledTimerWithTimeInterval:0.05(or as required) target:self
selector:#selector(your_selector_method_to_push_the_view) userInfo:nil repeats:NO];
You can run into this if you try to dismiss a UIViewController before it is finished loading.
I had this message in the console and was focusing entirely on the UIViewController that was presenting the new UIViewController, without success. I finally discovered the problem was in the UIViewController I was presenting was dismissing itself because the user wasn't logged into their account.
Hope this helps someone.
This was a tough one for me:
I've overridden
override func shouldAutomaticallyForwardRotationMethods() -> Bool {
return true
}
without overriding:
override func shouldAutomaticallyForwardAppearanceMethods() -> Bool {
return true
}
in my window root navigation controller.
then a child navigation controller complained when pushing another view controller with the above mentioned warning.
The warning wasn't the worst,
the big problem was, that there the delegate of the child navigation controller weren't called anymore.
weired.
In my case I was fetching NSData from NSURL inside 'viewDidLoad' method.