I'm building an iPhone app (in iOS 5) with several different views, one of which contains a map (also has separate view controller). The first time I go over to this view, it takes a while for it to load, and things such as animating pin drops also don't work well as a result of it. Not to mention that it takes a bit for the location to stabilize.
What I want to know is if there's a way to load MKMapView before a user goes to that view, so by the time they get there, these issues aren't present anymore.
I've already seen this and this question but they haven't been very helpful. Keep in mind that I don't really need to interact with that view before I get there, I just want it to be fully loaded by the time I go there.
Related
This is so weird. I have been working on a series of iterations of the same application for almost 2 months, have not touched the code related to the view management in weeks, and this bug just appeared.
Basically I am at the root level view, i go to the last view in the stack by way of a "skip" type button. This page is used for settings. I then change some things (not related to the view hierarchy) and save, which takes me back to root. Then i try going to the first view in the stack.
Initially all of the textfields for the view are visible, but their uilabels are not. The view then slowly drifts to the right, revealing the same view with labels intact directly under it. I have never seen a bug like this before in over 2 years of iOS dev, so here I am.
The only code I have changed in the last week is related to web services and threading, nothing having to do with the view hierarchy.
If anyone has seen this bug before please help! Probably important as well, I have not seen this happen on the simulator, only on the actual device (iPad running 4.3.3)
Thanks!
Threading is the most likely culprit: making UI calls anywhere other than the main thread can cause any number of strange problems. Make sure anything you do to the UI, even a -popViewControllerAnimated:, is wrapped in a -performSelectorOnMainThread:withObject:waitUntilDone: or an appropriate dispatch_async(dispatch_get_main_queue(), ^{...}) call.
I'm trying to investigate a very long start up time for my app and could use some help. It takes about 6-7 seconds to start up and that's just FAR too long. I'm not loading any data sets or anything, but the app is a calculator so my xib does have lots of buttons and button images.
I'd like to use Instruments (or something else) to analyze where all of the time is being spent so that I can optimize, but the documentation for Instruments isn't really giving me much on specifics of how to use it for this specific problem and/or how to interpret the results.
Do you have any suggestions for how to test this? A pointer to a tutorial on how to use Instruments for this? An iTunes U video or something else?
Any suggestions are welcome!
Kenny
Palimondo asked how I eventually solved my problem... it was a bunch of small changes and I'm not truly sure where the savings came in to play and I'll explain why below.
My app is a calculator, so it has a ton of buttons. I thought at first it must be the button loading causing the problem, but as I explained in the comments above to Paul, even if I loaded 0 buttons, that only shaved 1 second off of the start up time. After changing many small things, I was able to save 3 seconds off of start up time which was good enough.
I had a background image that was a nice textured-gradient-ish image. I reduced the file size on it (changed the texture, exported it with different options).
I went from loading 100 buttons up front (it's a calculator) to loading about 15, but attempted to do it in a secret fashion so the user wouldn't notice. About 50 of the buttons aren't visible to start anyway because you have to tap a Shift button to see them, so they were easy to lazy-load. The others are visible, but I figured that the user isn't going to tap most of those right at start-up because they need to hit some numbers first. So I left all of the number pad buttons and the Clear button in the xib so they are loaded on start up but removed everything else from the xib.
That left me with a big blank area on the screen and you could actually see the buttons filling in the blank area as I lazy loaded them (filling very quickly, but you could see the blank area and then all the buttons appear). So I updated the background gradient graphic to include images of the buttons. The buttons appear to be there, but they aren't, so unless the user taps a fake button within 2 seconds they never notice a problem... and usually they just try to tap that button again anyway and by then it usually has been lazy-loaded and therefore works.
Before doing any of the above suggestions:
Is this 6-7 seconds while running from XCode? Or from starting directly by tapping it on the iPhone?
Debuggers and instruments won't help you much here, since they just add to the overhead, and won't help as much in profiling because it poisons the data you'll see.
Edit:
In terms of profiling tools, you may want to look into using Shark:
http://www.switchonthecode.com/tutorials/using-shark-to-performance-tune-your-iphone-app
It's pretty simple to use in general. It's sorta self-explanatory.
Here are couple of suggestions:
do you need all those buttons loading up at once?
do you (over)use transparencies in your images?
are the images the exact size required? Stretching (resizing) takes time to compute
do you perform any operation that is blocking the main thread?
I would measure the startup time of an empty dummy app (maybe the XCode Window-based app template) on your actual device (debugger disconnected). Then start adding your initialization code, views and objects from your app into this dummy app until you find what's slowing the startup time the most.
I have a horizontally scrolling UIScrollview with paging enabled. Each page represents a data feed. Each data feed consumes a fairly large amount of memory including text and images displayed in a UITableView. Since the user can have an unlimited amount of data feeds, I need to lazy load them to prevent maxing out my memory usage. My thought is to keep up to 5 data feeds in memory at any given point, and release anything outside of that range. My initial take is to keep the page in the viewport in memory, and the 2 pages to either side of it. This way when the user scrolls, the next sequential page will always be in memory and will display quickly.
Here is my problem:
We also need to support a scenario where a user can skip to a specific data feed, possibly 10 or more pages to the right or left, which throws my entire lazy loading scheme out the window.
Might there be a better strategy to support this scenario?
Yes, what you can do is create an outer scroll view, which houses individual cells akin to a tableview. In this respect, the cells have a content view which you can place your data, let's just assume you know how to do that since it seems you do.
Once you have this architecture in your mind, it becomes fairly clear: You can, knowing the width of your cells, and the size of the screen, some simple math can tell you how many you have on screen, and you can add one to the left or right so you're preloading some data for when the user scrolls.
This will say, give you the ability to have in memory, at most 5 feeds, if 3 are visible, at the start or end of the content view in the scrollview, 4 feeds, regardless of whether or not you have a billion feeds.
One critical part of this is cell reuse. You maintain a couple NSSets, one for recycled cells, and one for visible cells. Add items that have gone off screen to the recycled cells, dequeue items from recycled cells when setting up the cell, as to save additional memory allocations, which can be expensive. Just remember, using this strategy, you are still subjective to the same caveats as a UITableView with respect to cell reuse.
I'm plugging some software I wrote here, so forgive me for that, but I'm doing so as an example of what I'm talking about if you get stuck implementing what I discussed here, it's available here for your perusal.
One final note. To support skipping of cells, it's just simple math again to adjust the point in the scrollview you are at. The action for this can be done with a gesture, though now we're talking a pan gesture recognizer, with some specific properties that will be specific to your application, as an example. Or you can use buttons if you really must. Just ensure you know how to calculate your offsets to your cells, and scroll to that point. You'll be fine.
It's hard to give accurate advice with the information provided in the question. So I will just present a few thoughts I got when reading the question.
Does it take long to download a feed? Would it be possible to just download a few item (a screenful) to get the first few items showing right away? Perhaps lazy load the images if possible.
Have you considered caching the feed data on disk? That way you can present some data right away and then update the feed as new data gets downloaded.
Since each page is a feed view, would the user just scroll past a feed without looking at it? Do you really need to load 2 views on each side or can you get away with just 1 view on each side.
I don't think your lazy loading scheme stops working just because the user can skip directly to any page. You would have to start loading the data for that page when it is selected but by using a disk cache or downloading a small sample could make the app feel faster. I also think that using some kind of animation to animate to the selected page could buy you a little time.
Here is my problem: We also need to support a scenario where a user can skip to a specific data feed, possibly 10 or more pages to the right or left, which throws my entire lazy loading scheme out the window.
Then you need to fix your data structure. I wrote pretty much exactly this (a UITableView-like paging scroll view) and it works fine (there are some reloading quirks, but hey...).
Keep track of which pages you've loaded. I used a ring-buffer-alike modulo 8 and spent a while getting the code right; it's probably a lot easier to just use an NSDictionary with NSNumber keys and UIView values. This means when you change from page 1 to 10, you can still tell that the loaded views are for pages 1, 2, and 3, and drop them.
Load the required pages in -layoutSubviews. I would load 1 or 3; 5 is probably too many (but you need to load at least 2 if you're between pages).
Load 3 (one to either side) in -scrollViewDidScroll:. This is so that when you scroll from page 1 to 2, it doesn't try to load page 4 (which hasn't been loaded yet).
Load 5 (two to either side) in –scrollViewDidEndDragging:willDecelerate: if willDecelerate is NO, –scrollViewDidEndDecelerating:, –scrollViewDidScrollToTop:, and –scrollViewDidEndScrollingAnimation:. The idea is that the animation's over, so you can load the extra views without the user perceiving lag.
Possibly load 5 in –scrollViewWillBeginDragging:, to make sure that page 3 is loaded if you're currently on page 1.
I decided to do "two pages either side" because when you flick from page 1 to page 2, it often shows a pixel-wide sliver of page 3 when it bounces. This can be avoided with scroll view insets, but we didn't think of that at the time (oops). There's otherwise not that much of a reason to choose 5 over 3.
It's a bit of work to get right, but otherwise seems to work flawlessly.
I want my view controller to display different views based on a condition that depends on some runtime checks (lets say a uiwebview at even hours and a form on odd hours).
What is the best pattern to achieve it in iphone project? Maybe I can hide controls based on aforementioned condition or maybe it is better to load different views?
or maybe i should load different view controllers and make the check in my parent view controller?
The iPhone UI is so small and crowded that you are usually better off creating a different view/view-controller pair for different UIs. Hiding UI elements leaves gaps that are very noticeable and manually rearranging the UI elements on the fly is a pain.
More importantly, different views communicate to the user that the app is in a different state. You don't want the app to switch to a different state without informing the user. It's hard enough for them to understand what is going on without confusing them with UI slight of hand.
It is very bad UI design to have the same user action present a different UI for no reason discernible to the user. For example, presenting a "a uiwebview at even hours and a form on odd hours" would be a fantastically bad idea because the user would have no idea why clicking on a specific button sometimes gets them a web view and a form view other times. Do you expect the user to check the clock before clicking so that they know what to expect?
If some UI element is unavailable for some reason e.g. no network access, then it's better UI design to present the element as disabled instead of disappearing it. The interface grammar teaches users that grayed out controls are temporarily unavailable. That is much less confusing that a constantly changing UI.
I have used to loading a default image in my appication. So i have set to,
Sleep(3); in my delegate.m class.
But sometimes it will take more than 6 to 7 minutes. So i want to display the image 3 seconds only and then it goes to my appilcation based on my requirements.
Which one is best way to do that?
Sleep(3) or [NSThread sleepForTimeInterval:3.0] or something else;
And i must display the image 3 seconds only. Please explain me.
(Note: And I declared setter and getter methods only in my deleagte class.)
Please explain me.
As Rob noted, Apple strongly recommends against a splash screen unless it hides some necessary behind the scenes process (like loading game graphics.) It is so strongly discouraged that some people have claimed that their apps have been rejected for using an unnecessary splash screen.
The default.png doesn't exist to create a splash screen. Instead it exist to allow you to create the illusion that your initial view loads faster than it does. You supposed to use it to provide an image of your initial view so that the enduser can begin to cognitively orient themselves to the interface. By the time they have oriented themselves to the interface and moved their finger to touch the interface, it is live.
Why? Because iPhone apps are supposed be quick in, quick out. People don't sit down to use them at a desk like a desktop. People use then on the go. Sometimes they use them in the middle of a conversation.
I tell my clients to test out the usability of their apps (except for games) while walking, riding an exercise bike etc as well as in the middle of a face-to-face and phone conversation. In those circumstances, a three second pause is a big deal and very noticeable especially if the app is a practical app. Imagine if every time you opened the Contact app you had to pause three seconds to see an Apple splash screen. You would get peeved in a hurry.
The key thing here is that an unnecessary splash screen doesn't add any value for the user. It is a selfish act on the part of the software publisher to eat the end users time so that the publisher can build brand recognition for the sole benefit of the publisher. Wasting three seconds of the users time every time they use the app adds up in a hurry. (In my experience, it also makes the user perceive that the overall app is slow and clunky.)
However, if you do want to shoot yourself in the foot or if you have a client hell bent on a splash screen, you do it like this:
The splash screen appears until the first view loads so you delay the loading of the first view. In the app delegates applicationDidFinishLaunching: method, remove all the code that loads the first view into the window. Replace it with a NSTimer. Put the code to load the first view in the timer's fire method.
With that setup the app will display the default.png as it launches, when it gets to applicationDidFinishLaunching:it will appear to pause from the end users perspective because no view will appear to replace the default.png.
You should note that the standard launch time for an app is 3-5 seconds. So you may not have to do anything to show the splash screen for 3 seconds. It might happen automatically.
Apple strictly recommends against this (using sleep in this way), especially in the scenario of showing a splash screen.
The best thing to do is create a view that looks like your Default.png file, then have that be the first NIB.. you could then set an NSTimer to transition (with animation if you want) to your main view/window/controller.