Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
NoDamage
Dec 2, 2000

plasticbugs posted:

When my app launches, I'm trying to handle the selecting of the row in my TableViewController's viewDidAppear method. Unfortunately, at that point, the fetched results controller has not completed its fetch, so the fetchedObjects array is empty inside viewDidAppear. performFetch is being handled by a parent class in viewWillAppear (I'm using a boilerplate "Core Data Table View Controller" from the Stanford iTunes U class -- which is adapted from an Apple docs sample). I can tinker with that code, but I'd rather not because it scares me.
The "correct" answer is probably to not be afraid of that code and to go in and fix it there.

But let me back up a second here. [NSFetchedResultsController performFetch:] is a blocking operation. Once it returns, assuming no error occurred, fetchedObjects should be populated and your data should be ready. You shouldn't need a completion handler to know when it's done. Hence, if performFetch is being called in viewWillAppear, your fetchedObjects should be ready by the time you've gotten to viewDidAppear.

(The only reason I can think of where that wouldn't be the case is if your parent class is doing something to spin off the fetch into a background thread...but setting that up is quite complicated and presumably outside the scope of an intro to iOS course, so I'll assume that's not being done.)

Adbot
ADBOT LOVES YOU

NoDamage
Dec 2, 2000

Doctor w-rw-rw- posted:

2) FRCs blow pretty hard, especially with concurrent contexts.

pokeyman posted:

As for "don't use FRCs": if they work for you then use them, but they're especially persnickety. Make your own instead of trying to work around its limitations.

Just curious. If you don't use FRCs, what alternative do you use to keep your table views up to date as your data changes?

I had to write my own FRC because I wanted to be able to run my fetches in a background thread. However, this was a massive pain in the rear end to accomplish and I can't imagine that most developers would want to go down that road.

Doh004
Apr 22, 2007

Mmmmm Donuts...

Doctor w-rw-rw- posted:

Comedy answer (half serious, I kind of did this recently sort of): Replace UINavigationBar with something that doesn't suck balls for custom styling? :getin:

Constructive answer: Maybe play with [CATransaction setDisableActions:YES] and see if there's some implicit animations getting run? Dunno.

Random note: we use Objective-C++ for some nifty things like an Objective-C++ class to save the value of disableActions on construction then set it to yes, then restore it to its original value in its destructor. makes it pretty easy to ensure particular blocks of code never execute with implicit animations, without affecting state outside of its scope.

I'm seriously considering the comedy answer. :(

I'll try around with the disbaled actions but this is foreign territory to me :ohdear:

*edit* holy poo poo [CATransaction setDisableActions:YES] just worked? Is setting this when I add the UISearchBar to the navBar going to mess with anything else? I'm playing around with everything else and I don't see anything else funky.

Doh004 fucked around with this message at 15:26 on Aug 21, 2013

lord funk
Feb 16, 2004

Doh004 posted:

I'm seriously considering the comedy answer. :(

Yeah I'm hitting this answer more often these days. Just now I decided to ditch using a UIPopover and just dump view controllers into the view myself.

Doctor w-rw-rw-
Jun 24, 2008
Guess how I've been debugging some refcount errors in ARC lately? Using MRR from ARC is an error, and the flags are set up so it's an error to do things that can cause leaks. So...
Objective-C code:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  [(NSObject *)someObject.delegate performSelector:NSSelectorFromString(@"retain")];
#pragma clang diagnostic pop
:shepface:

Anyways...

Doh004 posted:

I'm seriously considering the comedy answer. :(

I'll try around with the disbaled actions but this is foreign territory to me :ohdear:

*edit* holy poo poo [CATransaction setDisableActions:YES] just worked? Is setting this when I add the UISearchBar to the navBar going to mess with anything else? I'm playing around with everything else and I don't see anything else funky.

A suggestion, and some background on what this means.

CALayers are animated by default. Set a property, and it'll animate over. Each tick of the runloop is punctuated by an implicit start and end of a CATransaction. UIViews are a thin abstraction over CALayers, but they don't just act as a shim for event handling, they also do things like standardize the positioning of views by masking CALayer's slightly more nuanced API.

(Specifically, handling of anchorPoint, position, bounds, and transform. Frame is just a bounds + anchorpoint + position combination whose behavior is undefined for a non-identity transformation. It's effectively a transformation of bounds to parent coordinates)

So anyways, UIViews aren't just handling events, they also disable animations by default. The issue here is, most likely, that *that part* of the nav bar, for whatever reason, did not disable actions, and possibly set a layer value while actions were not disabled.

Suggestion:
Objective-C code:
BOOL actionsDisabled = [CATransaction disableActions];
[CATransaction setDisableActions:YES];
// Do something
[CATransaction setDisableActions:actionsDisabled];
or
C++ code:
class action_disabler {
	BOOL previousValue;

	action_disabler(){
		previousValue = [CATransaction disableActions];
		[CATransaction setDisableActions:YES];
	}

	~action_disabler() {
		[CATransaction setDisableActions:previousValue];
	}
 };
used like so:
Objective-C code:
{
	action_disabler disabler;
	// actions will be disabled within this code block, so go hog wild setting properties
}
// actions returned to original state
(you might have to edit this to make it work, I'm just typing it from memory and it's Objective-C++)

lord funk posted:

Yeah I'm hitting this answer more often these days. Just now I decided to ditch using a UIPopover and just dump view controllers into the view myself.
As you should be. Also consider using view controller containment and making your own popover class.

pokeyman
Nov 26, 2006

That elephant ate my entire platoon.

plasticbugs posted:

Thanks for your help. This almost works. -controllerDidChangeContent: is only called when the table populates for the first time (and when there are changes to any of the objects). So, this is working reliably only once - when the app is launched for the very first time and the database has yet to be populated.

Unfortunately, I'm back to where I started. Is there a way to do something like trigger a method only once when frc.fetchedObjects goes from being NULL to not being NULL? I'm looking into KVO, but when I try to observe the frc's fetchedObjects property, I'm not being notified when the value goes from NULL to however many objects are in my database.

I can't think of a scenario where fetchedObjects goes from nil to non-nil without the delegate being informed of the content change. What, exactly, are you trying to do? Can you show some code? It sounds like something is busted.

Also, you should assume that the only KVO-observable properties on Apple classes are those explicitly documented as such. It might work anyway (I'm looking at you UIScrollView.contentOffset) but it might not work always or even at all.

NoDamage posted:

Just curious. If you don't use FRCs, what alternative do you use to keep your table views up to date as your data changes?

Either a thin wrapper around NSManagedContextObjectsDidChangeNotification or use other callbacks/notifications (e.g. a callback after a network fetch).

NoDamage
Dec 2, 2000

pokeyman posted:

Either a thin wrapper around NSManagedContextObjectsDidChangeNotification or use other callbacks/notifications (e.g. a callback after a network fetch).
Presumably callling [UITableView reloadData] after getting the notification/callback?

The benefit of the FRC of course is that it parses out the NSManagedContextObjectsDidChangeNotification for you, and tells you specifically what sections & rows have changed, and in the right order so you can animate the insertions/deletions of those sections & rows into the table view.

pokeyman
Nov 26, 2006

That elephant ate my entire platoon.

NoDamage posted:

Presumably callling [UITableView reloadData] after getting the notification/callback?

The benefit of the FRC of course is that it parses out the NSManagedContextObjectsDidChangeNotification for you, and tells you specifically what sections & rows have changed, and in the right order so you can animate the insertions/deletions of those sections & rows into the table view.

-reloadData is for chumps. Use relevant info from the notification or callback. The network callback can tell me which objects were involved in the fetch/update/delete, or yeah I weed the userInfo from NSManagedContextObjectsDidChangeNotification.

Basically it's an issue of NSFetchedResultsController being opaque, so if I need to do something just slightly beyond its ability I'm stuck.

Doh004
Apr 22, 2007

Mmmmm Donuts...

Thanks for this - very helpful and informative. The amount of knowledge I've gotten from this thread is outstanding.

Doctor w-rw-rw-
Jun 24, 2008

Doh004 posted:

Thanks for this - very helpful and informative. The amount of knowledge I've gotten from this thread is outstanding.

While I'm randomly infodumping, I might as well add a trick I like for extending delegate protocols in subclasses.

The situation in which I use this is when a class has a delegate with a protocol, you have a subclass and a sub-protocol with methods that serve a similar purpose, so you don't want two delegate properties.

header:
Objective-C code:

@class SomeSubClass;

@protocol SomeExtendedProtocol <SomeProtocol>

- (void)someSubClass:(SomeSubClass *)blah didDoFooWithBar:(NSString *)bar;

@end


@interface SomeSubClass : SomeClass

// etc

// Redeclaration of parent's delegate property which implements SomeProtocol
@property (nonatomic, weak) id<SomeExtendedProtocol> delegate;

// etc

@end
implementation:
Objective-C code:
@implementation SomeSubClass

// etc

@dynamic delegate;

// etc

@end
Voila, your subclass uses your better, extended protocol.

It's also nice because it's hands-off of the superclass and original protocol, so any code that uses those is unaffected. If you do actually have the ability to change the parents, you *could* add your own special methods as @optional since they're only relevant for your own special subclass's delegate, but then you lose the compiler's whining when it's not implemented.

NoDamage
Dec 2, 2000

pokeyman posted:

-reloadData is for chumps. Use relevant info from the notification or callback. The network callback can tell me which objects were involved in the fetch/update/delete, or yeah I weed the userInfo from NSManagedContextObjectsDidChangeNotification.

Basically it's an issue of NSFetchedResultsController being opaque, so if I need to do something just slightly beyond its ability I'm stuck.
In that case you deal with these scenarios by hand?

- Coordinating insertion/deletion of corresponding sections as objects are added/removed.
- When a property change occurs that affects the search predicate, instructing the table view to add/remove rows depending on whether the predicate still applies.
- When a property change occurs that affects the sort descriptors, instructing the table view to move the object to its new sort location.
- When a property change occurs that affects the section name key path, instructing the table view to move the object to its new section (and possibly having to add/remove the corresponding sections).
- If multiple objects change at once, and you end up with some sections being added, other sections being removed, some objects being added, other objects being removed, all in the same update. Coordinating the order of operations so the table view behaves correctly.

Juul-Whip
Mar 10, 2008

We've finally started patching up the various cosmetic problems our app has when we compile from XC5. One thing we found is when you're editing text in a text field, the cursor indicating the insertion point is white, so basically invisible in the vast majority of text fields. This might confuse some users who expect to see a cursor when editing text. It would also make it difficult for users to insert characters mid-string.

Apparently we can change the cursor colour, but it's undocumented so it'll get us rejected.

I noticed in Apple's iOS 7 apps the cursor is its usual blue. Maybe this is a bug? I mean are they really going to start showing a white cursor in text fields?

pokeyman
Nov 26, 2006

That elephant ate my entire platoon.

NoDamage posted:

In that case you deal with these scenarios by hand?

- Coordinating insertion/deletion of corresponding sections as objects are added/removed.
- When a property change occurs that affects the search predicate, instructing the table view to add/remove rows depending on whether the predicate still applies.
- When a property change occurs that affects the sort descriptors, instructing the table view to move the object to its new sort location.
- When a property change occurs that affects the section name key path, instructing the table view to move the object to its new section (and possibly having to add/remove the corresponding sections).
- If multiple objects change at once, and you end up with some sections being added, other sections being removed, some objects being added, other objects being removed, all in the same update. Coordinating the order of operations so the table view behaves correctly.

Yes.

- Often I only encounter a subset of those scenarios.
- I'm tired of dealing with weirdness (I won't say outright bugs) involving section name key paths.
- And with weirdness when objects get moved and updated in the same batch.

I'm not knocking FRCs if they work for you. When they work, they cover a lot of scenarios and corner cases that I'd rather not think about. But you get burned enough times you learn to back off, you know?

I always try a FRC first and I stick with it as long as I can. I'd recommend the same to anyone else. It's just not as general solution as it first seems, and you should be willing to go without.

Doctor w-rw-rw-
Jun 24, 2008

NoDamage posted:

In that case you deal with these scenarios by hand?

- Coordinating insertion/deletion of corresponding sections as objects are added/removed.
- When a property change occurs that affects the search predicate, instructing the table view to add/remove rows depending on whether the predicate still applies.
- When a property change occurs that affects the sort descriptors, instructing the table view to move the object to its new sort location.
- When a property change occurs that affects the section name key path, instructing the table view to move the object to its new section (and possibly having to add/remove the corresponding sections).
- If multiple objects change at once, and you end up with some sections being added, other sections being removed, some objects being added, other objects being removed, all in the same update. Coordinating the order of operations so the table view behaves correctly.

Animating row additions or subtractions or relocations shouldn't be too hard. Some sort of elaboration on the following, maybe?
Objective-C code:
[_tableView beginUpdates];
_someMutableArray = someDataController.data;
[_tableView reload...]; //one of the reload methods here
[_tableView endUpdates];
reloadData is the easy/lazy way out, but it's not bad. Just not great, either.

pokeyman
Nov 26, 2006

That elephant ate my entire platoon.

THC posted:

We've finally started patching up the various cosmetic problems our app has when we compile from XC5. One thing we found is when you're editing text in a text field, the cursor indicating the insertion point is white, so basically invisible in the vast majority of text fields. This might confuse some users who expect to see a cursor when editing text. It would also make it difficult for users to insert characters mid-string.

Apparently we can change the cursor colour, but it's undocumented so it'll get us rejected.

I noticed in Apple's iOS 7 apps the cursor is its usual blue. Maybe this is a bug? I mean are they really going to start showing a white cursor in text fields?

Going out on a limb: is the insertion point related to the text view's tintColor?

If not, file a radar. An invisible insertion point ain't right.

Juul-Whip
Mar 10, 2008

It is the tint color. Kinda odd but kay, whatever.

ultramiraculous
Nov 12, 2003

"No..."
Grimey Drawer

Doctor w-rw-rw- posted:

While I'm randomly infodumping, I might as well add a trick I like for extending delegate protocols in subclasses.

The situation in which I use this is when a class has a delegate with a protocol, you have a subclass and a sub-protocol with methods that serve a similar purpose, so you don't want two delegate properties.

implementation:
Objective-C code:
@implementation SomeSubClass

// etc

@dynamic delegate;

// etc

@end
Voila, your subclass uses your better, extended protocol.

It's also nice because it's hands-off of the superclass and original protocol, so any code that uses those is unaffected. If you do actually have the ability to change the parents, you *could* add your own special methods as @optional since they're only relevant for your own special subclass's delegate, but then you lose the compiler's whining when it's not implemented.

You don't need the @dynamic, technically, because the getter/setter for delegate property is already there. The magic @synthesize stuff will wire the property to the super. For example we have a UIScrollView subclass that looks like:

Objective-C code:

@protocol ScrollViewPlusDelegate <UIScrollViewDelegate>

-(void)somethingHappened;

@end

@interface

@property (nonatomic, weak) id<ScrollViewPlusDelegate> delegate;

@end


The scroll view delegates get called normally, and calling self.delegate returns an id<ScrollViewPlusDelegate>. You can also do the same thing if your UIViewController exclusively talks to a certain subclass of UIView, by putting something like this in your header:

@property SpecialUIView *view;

Doh004
Apr 22, 2007

Mmmmm Donuts...
Good info up in here the past couple of posts.

Question: anyone ever run into the issue where they add a subview to a UINavigationBar, hide the back button - some time passes - remove the subview from UINavigationBar (removeFromSuperview), show the back button but the back button doesn't work? It won't respond to any touches. I don't think there are any views or gestureRecognizers over it but I'm not sure. Maybe removing the subview (a UISearchBar) isn't sufficient and any gesture recognizers it may have with it aren't removed as well?

*edit* Figured it out, I was removing the searchbar and not its container view (which you need to wrap around a UISearchBar in order to make the cancel button show up in a UINavigationBar? Stupid).

Doh004 fucked around with this message at 21:58 on Aug 22, 2013

Sinestro
Oct 31, 2010

The perfect day needs the perfect set of wheels.
Is Core Data still the "best"/least worst method to have a robustish persistent cache? I'm not exactly on the "cutting edge" of Mac development, and using Core Data as a simple persistent cache seems like using the Death Star to kill a fly.

lord funk
Feb 16, 2004

I'm just about ready to give up on translucent tab bars. The blurring only happens on some devices, it's jarring when it flashes bright or dark, and I can't get it to land on any color but [UIColor colorWithSuck:1.0] :(

Doctor w-rw-rw-
Jun 24, 2008

Sinestro posted:

Is Core Data still the "best"/least worst method to have a robustish persistent cache? I'm not exactly on the "cutting edge" of Mac development, and using Core Data as a simple persistent cache seems like using the Death Star to kill a fly.
I wouldn't think so. Use NSCoding/NSKeyedArchiver if all you want is a simple persistent cache, IMO. If you're worried about versions, maybe write a cache manager to clear the cache if the version of the cache is less than a certain number.

pokeyman
Nov 26, 2006

That elephant ate my entire platoon.

Sinestro posted:

Is Core Data still the "best"/least worst method to have a robustish persistent cache? I'm not exactly on the "cutting edge" of Mac development, and using Core Data as a simple persistent cache seems like using the Death Star to kill a fly.

What are you caching? How robust? How persistent? Why are you caching it? You have many options. Off the top of my head:

- Lazily-initialized property.
- NSCache.
- Property lists.
- NSKeyedArchiver.
- SQLite. (Either in memory or on disk. If on disk, either in the Caches folder or not, either backed up or not.)
- Core Data. (Ditto the choices for SQLite.)
- Web service.

And you can insert "some custom solution" anywhere in that list.

Doh004
Apr 22, 2007

Mmmmm Donuts...

lord funk posted:

I'm just about ready to give up on translucent tab bars. The blurring only happens on some devices, it's jarring when it flashes bright or dark, and I can't get it to land on any color but [UIColor colorWithSuck:1.0] :(

We said gently caress it and we're going with a solid black background instead. Old habits die hard I guess.

lord funk
Feb 16, 2004

One stupid question, though: where do I set the unselected bar item image tint color? It's always gray, even when I set the title text attribute's color to a lighter color.

edit: why exactly when I post do I find the answer:

quote:

If you wish to also customize the unselected image appearance, you must
use -setFinishedSelectedImage:finishedUnselectedImage: on individual tab bar items.


edit2: setImage, setSelectedImage, using -imageWithRenderingMode where appropriate

lord funk fucked around with this message at 15:09 on Aug 23, 2013

Meat Street
Oct 17, 2004

knowin' nothin' in life but to be legit
Anyone here using a UIImagePickerController in [redacted] yet? The camera view seems to override any settings regarding the status bar, which has to be a bug since the content offsets aren't set up right. By default the status bar clips over the flash settings, etc, and I haven't found a way to fix this. Any tips?

LP0 ON FIRE
Jan 25, 2006

beep boop
Let me know if there's a better place for this question. I'm not posting it in the OpenGL thread because that looks to pertain more to 3D.

In Cocos2D I've been trying all different kinds of blend function combinations to make white circle with feathering on it blank out the whole scene except for the circle with transparency.

The mask sprite is:



and my code is:

code:

[heroLightMaskSprite setBlendFunc:(ccBlendFunc){GL_ONE_MINUS_SRC_ALPHA, GL_ONE}];
		
[self.theMap addChild:heroLightMaskSprite z:14];
		
heroLightMaskSprite.position = ccp(500,656);

Just to show what happens, it turns out like this:



Which is wrong. I need the space that was once transparent to be black instead of white, and everything outside of the mask sprite to be black. Is that possible just by changing some blending options around and not adding any extra sprites? I know the mask needs some work since the edges aren't completely transparent. By the way, Cocos2D's background is transparent, and the gradient you see there is outside of Cocos2D.

LP0 ON FIRE fucked around with this message at 18:25 on Aug 23, 2013

HiriseSoftware
Dec 3, 2004

Two tips for the wise:
1. Buy an AK-97 assault rifle.
2. If there's someone hanging around your neighborhood you don't know, shoot him.
It sounds like you want GL_DST_COLOR, GL_ZERO to multiply the destination pixels by the mask pixels to darken the areas on the mask that are non-white. For that you don't need any alpha transparency in your mask (if I understand what you're trying to do anyway)

Edit: I think this would have been appropriate for either the 3D Graphics thread or the Game Development thread, since it's OpenGL which of course doesn't have to be 3D.

HiriseSoftware fucked around with this message at 18:43 on Aug 23, 2013

carry on then
Jul 10, 2010

by VideoGames

(and can't post for 10 years!)

Ok, should be a simple question. I'm new to Cocoa development of any kind and making a Mac application that I want to have a source list and table view like Finder. What's the best way to lay out this view? I've got a mockup with a NSSplitView, but when I put the SourceList and TableView into their respective sides of the split view (and choose fill container horizontally and vertically, I'm not using AutoLayout, should I?) there's extra lines around them that I don't see in any actual Mac application. Is there a better way to do this layout?

pokeyman
Nov 26, 2006

That elephant ate my entire platoon.

carry on then posted:

Ok, should be a simple question. I'm new to Cocoa development of any kind and making a Mac application that I want to have a source list and table view like Finder. What's the best way to lay out this view? I've got a mockup with a NSSplitView, but when I put the SourceList and TableView into their respective sides of the split view (and choose fill container horizontally and vertically, I'm not using AutoLayout, should I?) there's extra lines around them that I don't see in any actual Mac application. Is there a better way to do this layout?



You're doing it right. That's probably the border type, try toggling that setting.

Only registered members can see post attachments!

lord funk
Feb 16, 2004

Thinking out loud again, but I think Core Data is too much for what I need. With a well designed UIDocument and my own undo management, I should be able to get what I want. I mistakenly thought Core Data was a requirement to use iCloud backup, which I guess it's not.

Man it's a lot to get Core Data up and running. Totally makes sense if you have a complex model, and I'm sure it gets better once you know how it works, but it is a steep hill to climb.

carry on then
Jul 10, 2010

by VideoGames

(and can't post for 10 years!)

pokeyman posted:

You're doing it right. That's probably the border type, try toggling that setting.



Thanks, I knew it would be something simple. It didn't do it entirely, but shifting the table view one pixel up and right made it look decent. I wonder why it isn't lining up right.

LP0 ON FIRE
Jan 25, 2006

beep boop

HiriseSoftware posted:

It sounds like you want GL_DST_COLOR, GL_ZERO to multiply the destination pixels by the mask pixels to darken the areas on the mask that are non-white. For that you don't need any alpha transparency in your mask (if I understand what you're trying to do anyway)

Edit: I think this would have been appropriate for either the 3D Graphics thread or the Game Development thread, since it's OpenGL which of course doesn't have to be 3D.

Thanks for your input. It's not working out right, partly because of how my Cocos2D is set up with transparency, and also I was thinking about this masking thing totally wrong. I thought you could just have one small square sprite placed on the screen and with some blending options make everything outside of it black and the white part show any sprites behind it.

What I think I need is that white sprite I posted earlier to mask out an area on a black rectangle sprite that covers the entire screen.

HiriseSoftware
Dec 3, 2004

Two tips for the wise:
1. Buy an AK-97 assault rifle.
2. If there's someone hanging around your neighborhood you don't know, shoot him.

LP0 ON FIRE posted:

Thanks for your input. It's not working out right, partly because of how my Cocos2D is set up with transparency, and also I was thinking about this masking thing totally wrong. I thought you could just have one small square sprite placed on the screen and with some blending options make everything outside of it black and the white part show any sprites behind it.

What I think I need is that white sprite I posted earlier to mask out an area on a black rectangle sprite that covers the entire screen.

If you're trying to make the white sprite act as a "light source", if I understand correctly, I sent you a PM detailing a way to do it. Plus the way I typed things out originally might have been wrong anyway.

kitten smoothie
Dec 29, 2001

It's my first day of my second week as a full-time iOS developer, and today I had my first memory leak I had to chase down. Trouble is, I couldn't get the leak profiler to work properly when running on a device. The auto-snapshots didn't auto-snapshot, and hitting the button to do it manually just causes it to get stuck on "Analyzing Process."

I tried the stuff suggested in this SO post, but after double checking the signing and build schemes, I determined that those are all totally kosher.

Anyone ever encounter anything like this? My colleagues were dumbfounded. I gave up on the profiler and used git bisect to find out that someone on my team committed a retain loop and fixed it that way, but at the same time I really would like for the profiler to work right.

lord funk
Feb 16, 2004

Are you running the beta? Instruments has been hit-or-miss for me this entire time. Not that that helps your situation at all...

kitten smoothie
Dec 29, 2001

lord funk posted:

Are you running the beta? Instruments has been hit-or-miss for me this entire time. Not that that helps your situation at all...

Nope. XCode 4.6.3, and tested on two different iPads running 6.1.3 with the same result, so I guess maybe there's still some goofy configuration issue that I'm missing. I should see if someone on the team can make it work it on his machine, in which case there must just be something goofy with my Xcode configuration or my clone of the project.

kitten smoothie fucked around with this message at 19:15 on Aug 27, 2013

lord funk
Feb 16, 2004

Animation question: I've got a nice animation for a patch object that you can drag around. It's a loop of the performance screen, and it's a really cool to drag it around instead of a static image:



It's gorgeous, and runs super smooth at 50fps. Trouble is it's huge. 300+ PNG files, at 7MB. The memory it takes at run time is about 100MB :stare:. I'm looping it with a UIImageView images array animation. Loading it is slow, and it can hiccup the UI when it's first dumped onto the screen.

How can I slim this down? The thing I want to avoid the most is lowering the quality. I desperately want this to look as good as it does now. Does anyone have experience with playing video files with transparency? Is there another option?

haveblue
Aug 15, 2005



Toilet Rascal
Unfortunately iOS does not support transparent compressed video.

If you want it to look exactly like that, the best way is probably to implement it as a live animation using LayerKit to move, scale, and rotate a small set of static images. I'm guessing this was was designed in Flash or something similar and rendered out to a PNG sequence?

Doctor w-rw-rw-
Jun 24, 2008

lord funk posted:

Animation question: I've got a nice animation for a patch object that you can drag around. It's a loop of the performance screen, and it's a really cool to drag it around instead of a static image:



It's gorgeous, and runs super smooth at 50fps. Trouble is it's huge. 300+ PNG files, at 7MB. The memory it takes at run time is about 100MB :stare:. I'm looping it with a UIImageView images array animation. Loading it is slow, and it can hiccup the UI when it's first dumped onto the screen.

How can I slim this down? The thing I want to avoid the most is lowering the quality. I desperately want this to look as good as it does now. Does anyone have experience with playing video files with transparency? Is there another option?

If this is taking that much space you are doing something very wrong or ineffective. Disorganized braindump time!

Write a UIView that wraps a layer. Implement + (Class)layerClass on the UIView to use your custom CALayer subclass. In my case I implemented the layer and the view in the same implementation file for the view, since the layer wasn't exposed externally, just the view hosting it.

Use self.layer to access the layer, or redeclare the layer method (to call [super layer]) and use [self layer] with your own CALayer subtype to change any properties on the custom layer subclass you need in your UIView subclass.

Use CABasicAnimation + CAAnimationGroup on a CALayer to do the animations. You can straight-up set the contents of a CALayer to a CGImage and bam, your layer displays an image. What's so good about this? Well, your image *is* your backing store, instead of the layer having to allocate any memory for it. And since your layer is created by the view as *the* layer class, your UIView doesn't waste any memory creating a backing store for that, either (or so I am led to believe). A big memory win. That said, with the complexity here, you might want a hierarchy of CALayers, but a hierarchy of CALayers is still goddamn cheap compared to a bunch of UIViews, which you don't need for this anyways. In fact, a UIImageView array is pretty loving insane. Don't do that. Load your UIImages into properties on your view, or something and set your CALayer sublayers' contents property to those CGImages.

For rotation, the key path you'll want to animate is transform.rotation.z, and its range is 0 to 2pi, or multiples thereof. You'll probably also end up animating scale as well?

You should generally refer to animations by the key you set. Why can't you just save the reference? Well, when you call addAnimation:forKey:, it actually *copies* the animation. Your old reference is worthless, and if you use the delegate, it'll refer to the copied reference. But if you just want to chain animations starting at different times, use CAAnimationGroup instead and change the beginTime.

Note that repeatCount can be set to FLT_MAX for effectively an infinite animation.

timingFunctions are nice for setting different animation curves. The default curve is not necessarily linear.

Performance note: Remember how I said that setting the contents directly to a CGImage saves memory? Well, be aware that while CABasicAnimation can tween between CGImages, this means that it has to do offscreen rendering for all the intermediate states between the from/to CGImage. This offscreen rendering is a big performance loss, which (iirc) impacts CPU performance - if you need to transition, consider using alpha instead - that way, you have two CGImages that the display server can smoothly transition on the GPU.

If you really want to be space-effective, you could also generate the CGImages with Core Graphics instead of loading PNGs. You're going to end up with CGImages either way.

Also note that scaling images is cheap because the display server can do it. Anything that involves altering actual shapes/polygons will be slow, but deforming a rectangle is easycakes.

EDIT: let me know if you are confused by any of these sentences. I could tighten up some of the thoughts into something more cohesive if anyone wants.

Doctor w-rw-rw- fucked around with this message at 22:41 on Aug 28, 2013

Adbot
ADBOT LOVES YOU

lord funk
Feb 16, 2004

You are insightful as always w-rw-rw-. I finally got a chance to play around with things.

Part 1: I was using a single UIImageView, with its animationImages array set to the array of 300 UIImages loaded with +imageNamed:. I thought I might try using +imageWithContentsOfFile: to avoid the backing cache, but that didn't help. It still takes the same huge of memory as soon as you drop the image view into the frame.

Part 2: I switched to a generic UIView, where I run a CADisplayLink timer that sets the layer.contents to the next CGImageRef in sequence. If I store the images into an array, the memory starts out at zero but builds up to the same amount as earlier:

(Part 2):
Objective-C code:
- (void)updateAnimation {
    static int counter = 0;
    self.layer.contents = (id)self.images[counter];
    counter = (counter + 1) % self.images.count;
}
Part 3: Since keeping the memory down is my primary goal, I loaded the image data (as NSData) into an array, and I create the image ref each frame, then release it immediately. It definitely takes some CPU to do this, but I could make a buffer to load maybe 30 frames at a time as a tradeoff.

(Part 3):
Objective-C code:
- (void)updateAnimation {
    static int counter = 0;
    
    CGDataProviderRef imgDataProvider = CGDataProviderCreateWithCFData((CFDataRef)self.imageData[counter]);
    CGImageRef image = CGImageCreateWithPNGDataProvider(imgDataProvider,
                                                        NULL,
                                                        NO,
                                                        kCGRenderingIntentDefault);
    
    self.layer.contents = (__bridge_transfer id)image;
    CFRelease(imgDataProvider);

    counter = (counter + 1) % self.imageData.count;
}

quote:

You can straight-up set the contents of a CALayer to a CGImage and bam, your layer displays an image. What's so good about this? Well, your image *is* your backing store, instead of the layer having to allocate any memory for it.

If I have a question it's for clarification here. I'm seeing that setting a layer's contents to a CGImage does take memory. Am I not doing it correctly?

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply