Flash scroll indicators don't work - ios

I got my table view on top of a map view. I want to show to user that the tableView is scrollable. Here is my previos thread: Swift - How to attach bar to the top of table view?
I tried using
self.tableView.flashScrollIndicators()
in my ViewDidLoad() method but this doesn't change anything, the table view looks exactly the same as before. I read suggestion that it might be caused by reloading tableView and filling it with data, whilst created tableView is empty. Nonetheless I tried pasting the flashScrollIndicators() method in other project where table is created with cells immediately - again no significant difference.
Am I doing something wrong or using the method in wrong place?

If anyone is still fighting with this problem here is my working solution - works on iOS 11. In my case flashScrollIndicators() did not work on first invocation of viewDidAppear(animated:). Invoking flashScrollIndicators() with a delay will do the trick.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
DispatchQueue.main.asyncAfter(deadline: (.now() + .milliseconds(500))) {
self.collectionView.flashScrollIndicators()
}
}

You're using the method in the wrong place. In viewDidLoad, the view has just finished loading. It hasn't yet been displayed. A safe alternative would be to move the call into your view controller's viewDidAppear: method to make sure that you don't attempt to flash the scroll indicator until the view is already on screen.
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.tableView.flashScrollIndicators()
}

Swift 4
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.tableView.flashScrollIndicators()
}

Related

When navigating back in navigation controller view doesn't load

I have weird situation and have no clue how to debug it. I load three viewControllers in navigation controller. When Im navigating back from there second and first ViewController doesn't display anything just white screen I added print methods everywhere in lifecycle methods and it seems that it loads views but anyway they not visible. What could be the problem?
Yep It's weird, There maybe some code which do something with your view on such events like:
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// remove some subviews or change constraints.
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// remove some subviews or change constraints.
}
Please send us a code of the view controller which has the problems and code how exactly you show the controller.

How to automatically reload the data from viewcontroller?

I am trying to find the solution for how to automatically reload all the data in view controller but i can't find any. Right now i am using push to refresh to reload the button/title/data in view controller viewDidload() but i want to have it automalliy reload everything every time i come back to this viewcontroller. For example when app lunched, it load view controller A then i clicked on the button to go View controller B but i want to controller A to refresh everything after back from B so how can i do that?
Thanks
For this, there is a method viewWillAppear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
reloadData()
}
func reloadData() {
//All you need to update
}

Force Auto layout to update UIView frame correctly at viewDidLoad

As simple as it might be, I still find my self struggling with the correct solution.
I'm trying to understand what is the correct way to find the REAL UIView(or any other subview) frame inside viewDidLoad when using Auto Layout.
The main issue is that in viewDidLoad, the views aren't applied their constraints. I know that the "known" answer for this situation is
override func viewDidLoad() {
super.viewDidLoad()
view.layoutIfNeeded()
stepContainer.layoutIfNeeded() // We need this Subview real frame!
let realFrame = stepContainer.frame
}
But I found out that it's not ALWAYS working, and from time to time it give's wrong frame (ie not the final frame that is displayed).
After some more researching I found that warping this code under DispatchQueue.main.async { } gives accurate result. But I'm not sure if it's the correct way to handle that, or am I causing some kind of under-the-hood issues using this. Final "working" code:
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.async {
self. stepContainer.layoutIfNeeded()
print(self. stepContainer.frame) // Real frame..
}
}
NOTE : I need to find what is the real frame only from viewDidLoad, please don't suggest to use viewDidAppear/layoutSubviews etc.
As #DavidRönnqvist pointed out
The reason dispatch async gives you the "correct" value here is that
it get scheduled to run in the next run loop; after viewWillAppear
and viewDidLayoutSubviews has run.
Example
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.async {
print("DispatchQueue.main.async viewDidLoad")
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("viewWillAppear")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("viewDidAppear")
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print("viewDidLayoutSubviews")
}
viewWillAppear
viewDidLayoutSubviews
viewDidAppear
DispatchQueue.main.async viewDidLoad
Code inside DispatchQueue.main.async from viewDidLoad even is called after viewDidAppear. So using DispatchQueue.main.async in viewDidLoad gives you right frame but it isn't earliest as possible.
Answer
If you want to get right frame as early as possible, viewDidLayoutSubviews is the correct place to do it.
If you have to put some code inside viewDidLoad, you are doing right way. Seem like DispatchQueue.main.async is the best way to do it.
Your question is similar to this problem, and my answer will also be the same.
The frame is not guaranteed to be the same in viewDidLoad as it will be when the view is eventually displayed. UIKit adjusts the frame of your view controller's view before presenting it, based on the context in which will appear. For a better understanding of view lifecycle, you can refer this image.
The image will help you to understand why your code is working. As the picture shows that viewWillAppear gets called once a view is loaded and when you set up dispatch async it is added to the thread asynchronously after the execution of viewWillAppear. So, once viewWillAppear is called your frames are updated as per your view, and you get the correct frame in dispatch async.
References:
Image source
For more information about view life cycle you can visit this answer
So, at last, you can go for either of two options:
If you want to use auto layout then manage frames either in viewDidLayoutSubviews or viewWillAppear. (In either of these viewDidLayoutSubview one should go for as viewWillAppear will be called every time your view comes at top of view hierarchy that’s it should not be preferred).
If you're going to skip auto layout, then you can go for creating and maintaining views programmatically.
Hope this helps!
After reading the comments, maybe you can try to embed your view controller into another one with a container and do something like this:
In the parent view controller add a variable: var viewSize : CGSize?
In the child view controller add a variable: var parentViewSize : CGSize?
In the parent view controller, get the size in the viewDidLayoutSubViews and store it: viewSize = view.size
In the parent view controller, in the prepareForSegue send the size you stored: (destinationViewController as? MyViewController)?.parentViewSize = viewSize
In the child view controller, in the viewDidLoad you will access the parentViewSize variable with the good value in it
Would it do it?

Can not make view firstResponder in viewDidAppear method

I have UIViewController and I created my custom class for its view. By the way, I redefined method "canBecomeFirstResponder" in that class so it always returns true. I want my view to become first responder when the viewDidAppear method is called. But after I go to another controller and come back my program crashes. Can not understand why does this happens. Here is some code:
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.firstResponder = self.view.isFirstResponder()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if(self.firstResponder){
self.view!.becomeFirstResponder()
}
}
This link helped me a lot. https://stackoverflow.com/a/21906038/4092466 I should not have named the property "firstResponder". When I renamed it everything worked fine.

Can you detect when a UIViewController has been dismissed or popped?

I have some cleanup that needs to be performed in a shared resource any time one of my view controllers is dismissed/popped/unloaded? This could either be when the user hits the back button on that individual screen or if a call to popToRootViewController is made (in which case, I would ideally be able to clear up every controller that was popped.)
The obvious choice would be to do this in viewDidUnload, but of course, that isn't how unload works. Is there a way to catch all cases to where the ViewController is removed from the stack?
edit:Forgot to mention that I am doing this using Xamarin so that may or may not impact the answers.
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
if (isBeingDismissed() || isMovingFromParentViewController()) {
// clean up code here
}
}
EDIT for swift 4/5
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
if (isBeingDismissed || isMovingFromParent) {
// clean up code here
}
}
-dealloc is probably your best bet. The view controller will be deallocated when it is popped from the stack, unless you are retaining it elsewhere.
viewWillDisappear: and viewDidDisappear: aren't good choices because they are called any time the view controller is no longer shown, including when it pushes something else on the stack (so it becomes second-from-the-top).
viewDidUnload is no longer used. The system frameworks stopped calling this method as of iOS 6.
Building upon #Enricoza's comment, if you do have your UIViewController embedded in a UINavigationController, try this out:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if ((navigationController?.isBeingDismissed) != nil) {
// Add clean up code here
}
}

Resources