I'm working on a app which has a button to open a view that access the camera.
So, I have a button to trigger a segue (using the storyboard) and in my other view, inside viewDidLoad(), I'm doing all I need to start the video capture.
The problem is that between touching the button and view been showed it take some small amount of time and I don't like it.
If I comment all the stuff regarding the video capture, the view show up instantly.
So, I think the lag is du to the preparation to access the camera.
How to display the view, an empty view, and then doing the stuff to show the camera?
If you want to show the new view controller and then activate the camera, move your code from viewDidLoad into viewDidAppear. You could try viewWillAppear, but depending on what your code does, you may just see the same issue you have now.
You have to be careful here to only do your activation once as viewWill/DidAppear can be called multiple times: especially as a result of being exposed when a controller you push on top is popped.
Just use viewDidAppear to load all the methods in that view controller instead of using the viewDidLoad method like so:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
}
Related
I have a small problem I sadly couldn't solve. I have a few ViewControllers. I need to call a function when going back from a ViewController to the previous ViewController (which does not reload, so viewDidLoad / Appear is not called).
Mustn't there be anything like viewDidLoad, but gets called every time the ViewController is visible?
This is how I open the ViewControllers:
let vc = storyboard?.instantiateViewController(identifier: "levels_vc") as! LevelsViewController
present(vc, animated: true)
This is the way I try to get the event, but it only gets called the first time (when the ViewController loads the first time), but not when I open a new ViewController from this one and close the new one.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("VC appeared!")
}
You can use viewWillAppear or viewDidAppear for that purpose!.
viewWillAppear() is called every time the view will appear on the screen.
This can be useful for views that need to be updated every time they’re scrolled to. For example, the Now Playing section in a music app or any view that is updated when it is scrolled to.
viewDidAppear is called when the view has appeared on the users screen.
This is a good place to “lazy-load” any other elements onto your screen. If your user interface is very complicated, you don’t want to stall or cause a delay in the previous mentioned methods.
It’s better to load something onto the screen and then call some methods to update the views for some large applications.
This is also a create place to start any animations.
That would be ViewDidAppear() ViewDidAppear() - Apple Developer
I am wondering what is the best practice to keep some of the UI elements in place when going forward/backward between UIViewController for example if I am using UINavigationController.
To be specific. I am making an app that has several similar view controllers (they can be instances of one main view controller). Then user clicks the next button and goes to the next page; or swipe back to go to the previous page. I have a progress bar on top and one or more buttons on bottom that I wish to keep static in place while the rest of the content are changing with an animation (a simple push might work).
Now my question is, if is it better put the content inside a container view? or to implement custom transition to keep those items in place while moving the rest?
Here is an image of the concept:
In the navigation controller delegate you can implement navigationController(_ navigationController:, animationControllerFor:, from:, to:) to return a custom UIViewControllerAnimatedTransitioning object. If you go this route you will have to implement the whole animation yourself though.
If you want to keep the basic animations from UINavigationController and just keep your elements steady you can go another route. In your view controller implement viewWillAppear: and/or viewWillDisappear:. In there you can get the transitionCoordinator and call animate(alongsideTransition:, completion:) on that. With that you can run your custom animations in parallel to the system provided animations.
To keep fixed elements you add another copy of the fixed elements to the container view you can get from the context object that is passed to your block. In the completion block you then can remove it again.
Sounds complicated, but it actually is rather easy if you look at the code:
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
let fixedViewCopy = UIView(...)
fixedViewCopy.frame = self.fixedView.frame
transitionCoordinator?.animate(
alongsideTransition: { context in
context.containerView.addSubview(fixedViewCopy)
},
completion: { _ in
fixedViewCopy.removeFromSuperview()
}
)
}
I have a modally presented View Controller called ChangeViewController that allows the user to change some information regarding his/her profile. When the user is done, the 'ACCEPT CHANGES' button is pressed and I use an unwind segue to exit out of the ChangeViewController and go back to the main screen.
Is it possible for me to refresh the data of the entire app before performing this unwind segue? As of now, the information on the home screen and other screens remains unchanged after user modifications. Only after an app reboot does the information update. Is there a way to programmatically 'reboot' so all View Controllers are up to date? More specifically, is there a way to call the viewDidLoad functions of all View Controllers so their data is updated?
Solutions in Swift preferred. Thank you!
I oftentimes updateUI after a user changes with a function like this. This is pretty simple, and you just call the function whenever your UI is updated.
func updateUI() {
// Redraw your labels, update your UIElements, do what you have to do
}
A way you can call this function from a modally presented ViewController without closing the app is with delegation, since modal presentation does not throw the old ViewController out of the stack and heap, Delegation works like this:
In your modal controller:
protocol ChangeViewControllerDelegat: class {
func updateUI(sender:UIButton)
}
class ChangeViewController: UIViewController {
weak var delegate: ChangeViewControllerDelegat?
func opChangingUserSettings() {
// Change settings with your code
// tell your ViewController to do it.
delegate?.updateUI()
}
in your mainVC
class MainViewController: UIViewController, ExtensionViewControllerDelegate {
func updateUI() {
// Redraw your labels, update your UIElements, do what you have to do
}
}
Hope that helps!
By the way, are you trying to change language by any chance on the fly? If so, I can show you how to do that. If not, and If I understand your question, this should work.
Well, you can use the local notification for your scenario.
As in, when the user is done with the changes and presses the button to accept the changes, there you can post a local notification to reload the data.
And you can listen to that notification in all of your viewControllers and reload the data there, like if it is a table view then you can simply call the reloadData method on tableview to achieve it.
Hi all I am doing a course in Udemy, and the code calls for placing code in the viewDidLoad function as shown below:
override func viewDidLoad() {
super.viewDidLoad()
placesArray.append(["name":"Taj Mahal", "lat":"27.175607", "lon":"78.042112"])
}
The array append should only run once, however, when I segue to another viewController and come back, it runs the code to append again. So I now have an array with 2 rows, both of which are Taj Mahal.
I thought that the viewDidLoad function only runs code once?
Is there a way around this?
Thanks.
Addendum:
I am using Swift, so I don't see any alloc and init while creating and launching the viewController. And weird as it sounds, the video tutorial has it working in the viewDidLoad and the trainer is using the storyboard to segue from the initial table view controller to a map view on a view controller and just has a back button on the map view that segue's back to the table view controller via the storyboard as well. - Could be because I have the latest version of the Swift language and the trainer was using an earlier version, (cause I noticed some slight differences in coding earlier) but you never know. Either way whenever he touches the back button it does not run the append code anymore.
I am trying to get in contact with the trainer as some of the suggestions here, though they are good don't seem to work.
I will put the solution in here once I get in contact with the trainer.
The viewDidLoad method is called when your view controller's view finishes loading. The view will load when a view controller's view property is nil and something attempts to access it.
UIViewController *myVC = [[UIViewController alloc] init];
UIView *aView = myVC.view; // this loads myVC's view; viewDidLoad is called when it completes loading.
If the view has unloaded (usually due to memory limitations), it will be called when the view property is next accessed.
Manipulation of data sets should generally not be done within view methods. Consider moving this to the init of the view controller (or to a different "datasource" class).
I suppose you are trying to do data initialisation in viewDidLoad. If there is no other operation on placesArray before viewDidLoad, then instead of append, what about setting the placesArray directly:
placesArray = ["name":"Taj Mahal", "lat":"27.175607", "lon":"78.042112"]
Then even if your view is unloaded for some reasons. Taj Mahal will still be added once only.
viewDidLoad is called whenever the view controller's view property is set. When does this happen? It depends on how the view controller is contained:
UINavigationController
- View Controller views are loaded as they are added to the navigation stack and "unloaded" (although the viewDidUnload method is deprecated) as they are removed.
UITabBarController
- View Controller views are loaded as they are added to the tab bar regardless of whether they are on screen or not. They stay loaded as you change from tab to tab.
Depending on your needs and use case, you can create your own view controller container that does what you need. Checkout the Apple docs on the proper way to do this:
https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html
I'm trying to load a UIView and then right away detect touches on that new view. Currently, overriding touchesBegan gives a delay of around a second.
So, I load up the UIView and immediately keep tapping on the screen. It takes around a second for touchesBegan to be called. From that point on, all is well. However, I can't afford the ~second wait initially.
I have stripped back all the code in the UIView to just the barebones incase anything was clogging up the main thread, but the delay still persists.
How can I go about getting immediate feedback of touches from a newly presented UIView? Thanks.
-- EDIT BELOW --
I've been playing around with this for the past few hours. Even when creating a custom UIWindow and overriding sendEvent, the UITouchPhase gets halted when the new view is displayed. To begin receiving events again I have to take my finger off the screen and place it back on the screen. (I don't want to have to do this).
The problem seems to lie with the segue to the new view controller. When it segues, the touch phase is ended. If I simply add a subview to the current view controller, I see the desired functionality (i.e. instant responding to touch).
Given my newly presented view contains a lot of logic, I wanted to wrap it all up in it's own view controller rather than add it to the presenter view controller. Is there a way for me to do this and use 'addSubview` to present it? This should hopefully achieve the desired effect.
In the end, I created a custom view controller with it's own xib. Where I would have segued, I now instantiate that custom view controller and append it's view. This has eliminated the touch lag.
Have you disabled multi-touch? There's an inherent delay while the controller waits to see if there's a follow up touch (on all single touches). The initial sluggishness might be from loading up the multi-touch code and deciding what to do about it.
myViewController.view.multipleTouchEnabled=NO;
As to your final question, look into view controller containment. Since iOS 5 Apple has provided the hooks officially and safely to present one view controller as a sub view of another.
Sadly I've no insight as to the greater issue.
I found an answer that worked for me from a similar question asked here:
iOS: Why touchesBegan has some delay in some specific area in UIView
This solution isn't check-marked on that thread, so I'll copy it here to make it easier to find.
override func viewDidAppear(animated: Bool) {
let window = view.window!
let gr0 = window.gestureRecognizers![0] as UIGestureRecognizer
let gr1 = window.gestureRecognizers![1] as UIGestureRecognizer
gr0.delaysTouchesBegan = false
gr1.delaysTouchesBegan = false
}