I have a UIView in which I display a movie if a particular chapter demands it. This view is resized via constraints in Main.storyboard to adapt to iPhone. All works fine on iPad. It also works fine on iPhone unless the app is asked to reload when a movie containing chapter was last active meaning that a movie will be first-up on loading. In this scenario, the movie is loaded into the iPad dimensions instead of the smaller iPhone specs.
It appears that the constraints on the movie's view are not engaged in a timely manner. The issue centers on a query of the the view's bounds. If I insert a delay before using the bounds the issue goes away. In fact, a delay of 0.0 seconds does the job!
Using a kludge that relies on a delay seems pretty funky to me. I can also move the call that uses the bounds to viewDidLoad to resolve the issue but then I see some underlying “garbage” when reloading the app, the launch image seemingly not in effect. Any suggestions?
It sounds like you're using bounds before your views have been lain out. Without actually seeing your code, I would suggest executing your described code inside of viewDidLayoutSubViews().
Related
I'm looking for help with a performance issue in an Objective-C based iOS app.
I have an iOS application that captures the screen's contents using CALayer's renderInContext method. It attempts to capture enough screen frames to create a video using AVFoundation. The screen recording is then combined with other elements for research purposes on usability. While the screen is being captured, the app may also be displaying the contents of a UIWebView, going out over the network to fetch data, etc... The content of the Web view is not under my control - it is arbitrary content from the Web.
This setup is working but as you might imagine, it's not buttery smooth. Since the layer must be rendered on the main thread, there's more UI contention than I'd like. What I'd like to do is to have a setup where the responsiveness of the UI is prioritized over the screen capture. For instance, if the user is scrolling the Web view, I'd rather drop frames on the recording than have a terrible scrolling experience.
I've experimented with several techniques, from dispatch_source coalescing to submitting the frame capture requests as blocks to the main queue to CADisplayLink. So far they all seem to perform about the same. The frame capture is currently being triggered in the drawRect of the screen's main view.
What I'm asking here is: given the above, what techniques would you suggest I try to achieve my goals? I realize the answer may be that there is no great answer... but I'd like to try anything, however wacky it might sound.
NOTE: Whatever techniques need to be App Store friendly. Can't use something like the CoreSurface hack that Display Recorder used/uses.
Thanks for your help!
"Since the layer must be rendered on the main thread" this is not true, as long as you don't touch UIKit.
Please see https://stackoverflow.com/a/12844171/136305
Maybe you can record at half resolution to speed up things, if that fits the requirements?
I'm displaying a series of images on an iPad that are being sent over a network connection. It seems to work fine, but the images have a lot of ghosting for some reason (see image below). Is there some kind of technique for drawing that would eliminate this? I'd say it's an issue with the refresh rate of the screen, but that wouldn't explain why using the iPad's screenshot functionality captures the phenomenon.
You are probably switching between images in a way that triggers an implicit animation that cross-fades between the old image and the new one.
The documentation for layer actions explains how CoreAnimation is deciding to run that implicit animation, and how to override it.
The two easiest ways IMHO are:
When you change the image, use a CATransaction to disable actions
In your layer's delegate, implement -actionForLayer:forKey: and return [NSNull null].
(If you are using a UIView, it's already the layer's delegate.)
This question gives a few more options -- it might even be a duplicate of your situation.
Please see this simple CATiledLayer example https://github.com/seanhess/CATiledLayer-Example
It consists of one viewController with a hierarchy like this:
view: (frame = window size)
scrollView: (frame = window size, content size = 200 x 4000)
contentView: (frame = content size = 200 x 4000, tile size = 100 x 100)
The content view's layer has been overriden to be a CATiledLayer.
If you run the linked code, you'll see that tiles with the same rect are requested multiple times. It happens both when you first run the code, and when you scroll.
Switch to branch "one-column" - it only happens on init, never when you scroll down.
Switch to branch "default-tile-size" - it only happens on init, but very rarely (you have to run it multiple times before it happens)
I'm trying to write some code in drawLayer:inContext: that locates the correct data and draws it. It could be expensive, and I don't want to do it more than once.
Any idea what is going on? What could I do differently?
This is a bug in IOS. It happens when the CPU is dual-core, in that case there are two threads each requesting each tile. That means that the bug is present in the simulator, and on the iPhone 4S, but not on the other iPhone models. I assume it will also be present on dual-core iPads.
I have reported the bug to Apple long ago already (for the simulator) and recently again (for the iPhone 4S). Apple recently gave the impression that they have solved it in IOS 6.
Very well solved here:
https://stackoverflow.com/a/8783396/341994
The problem is that (at least in my testing) drawRect: is called simultaneously on two threads. By logging, I can see individual lines of my code being executed twice before we go on to the next line, which is also executed twice! The logging shows that this is because one thread is executing a line and then another thread is executing the same line - and then they both go on to the next line.
This is as nasty as it gets. The solution is to wrap the the whole interior of drawRect: in a dispatch_sync on a serial queue.
Have you tried your code on a device? I consistently see this behavior in the simulator, but not on my iPad 1. I just tried this again in an app that is displaying a large tiled image. My tile size is 256 and on the device my drawRect: method is being called 12 times, which is how many tiles are on the screen. In the simulator, the method gets called between 20 and 23 times, with all but the last few tiles being drawn twice.
Isn't this due to the setLevelsOfDetailBias ? When I leave everything standard the tiles only get rendered once (testing on the device). But when I set the levelofdetailbias to 2, the tiles get loaded multiple times. I think that's due to the fact that CATiledLayer just starts caching extra detail levels when the device is idle.
In the end, the answers didn't work. It is fairly simple, though, to make your delegate method be aware of whether or not it has drawn that cycle.
I'm using core-plot in my iPhone app to display datas in plots from NSMutableArrays.
I display 10 values on the screen and I allowed horizontal scroll only like that I can see all the values when I'm scrolling.
I've got something like 100-200 values in total to load.
I use differents tabBars to handle my differents needs.
Everything works fine, but when I launch my app on the iPhone I have some monstrous laggs when scrolling. Something like 2 seconds after the graph move (on the simulator there is no laggs).
I reload graph when I shake the iPhone.
I work on a 3GS, and there is no other app running in background.
Did someone had and resolved something like that?
Do you need more info?
UITable (and UIScrollView) has a bug in which it causes it to call layoutSubviews on its parent. Check to make sure this isn't causing your trouble. If it is, you may be able to solve it by wrapping the table in another UIView. I do this routinely to avoid this problem.
Another thing that can cause really slow scrolling is a lot of masks, transparencies, etc in your cells. For this, you might try setting the views to rasterize, which means they'll draw themselves once into a buffer, rather than draw themselves every time.
I'm trying to workaround a long-running bug in Apple's SDK here, but I can't see how to achieve it without huge amounts of code.
Here's the bug:
Create a view.
Put another view inside it, with an origin ANYTHING except (0,0).
Configure the subview to resize to fill
...then, at runtime:
4. Set the superview size to zero
5. Set the superview back to ANY non-zero size
BANG! Apple's SDK goes haywire, resets the origin of the subviews, loses all ability to tell up from down, etc. (seems like the programmer "Forgot" to include a variable to record what the spring/strut-size was before it went to 0).
I've heard this bug has existed in OS X for 5 years or so, and Apple still hasn't fixed it. What I would like is some way (any way!) of working around it, without re-writing Apple's entire springs/struts system and implementing it properly, without the bug. Unfortunately, I can't even find the original references to the OS X bug that someone showed me previously.
EDIT:
...I have a resizing "drawer" at the bottom of the screen that has to shrink to small height. The problem is subviews "collapsing" all down to origin 0 (amongst other things) at the drop of a hat (technically: they're reducing their origin.height, but not re-increasing it).
The best solution I have found is to limit your minimum window size so that none of your views can ever hit zero size.
EDIT: an hour later, the collapsing behaviour has returned, without me touching the source files. I'll use source-control to double-check, but I believe all that happened is I added other files to a different part of the view-hierarchy and re-built. Argh!
(what follows SEEMED to work, but is now failing again, in the same way)
Hmm. So, I had two UIVC's on one screen. Refactoring so that I only had one made the resizing bug vanish. It seems to be a bug in the UIVC layout code.
NB: I had the idea this might be a problem because I know Apple currently advises you to only have one UIVC on screen at once with iPhone, even though this means you have to ignore their MVC architecture for complex apps. So, technically, they warn you away from this. But, at the same time, I'm now unable to use the MVC stuff for this code.
The following worked (for my current example - may not be universal!).
HOW:
If you're adding any UITableViewController instances ... give up. You'll have to re-implement that class yourself
Find my subviews that were in UIVC instances, and change the extended class in the UIVC header from "UIViewController" to "NSObject"
Move all code from viewDidLoad to init (remembering that "init" has special rules about first few lines etc)
Replace the instantiation of that UIVC from "[[vc alloc] initWithNibName:nil bundle:nil]" with a plain "[[vc alloc] init]"
Another potential workaround (that works in my current situation):
The only View I have right now that MUST have a non-zero origin and MUST resize-to-fit is a UITableView.
Apple has a special feature of letting you put any view as a "table-header".
So ... I've taken all my other components, stuck them in a large view, and made that the table-header view.
The downside is that they now scroll off the top when you scroll the table, but it allows me to give the table a zero-origin, working around the SDK bug.