How do you use TTURLCache to save images to disk?

134 views
Skip to first unread message

Erotsppa

unread,
Oct 18, 2009, 12:15:54 AM10/18/09
to Three20
It seems that storeImage method only stores the image to memory. By
calling imageForURL I get the image back but on next launch, it's
gone. Any tips?

codergeek

unread,
Oct 18, 2009, 4:13:51 PM10/18/09
to Three20
Glad I was able to get to your post before the thinly-veiled insult
squad that's sadly been patrolling this group for awhile.
These days, anyone asks a question that's even slightly been
discussed, the flaming begins.
Shame, The contributors of this project have done such an excellent
job...

<steps down from soapbox>

I found the solution to this here:
http://groups.google.com/group/three20/browse_frm/thread/1832afc42e5af9fd/c1c844465a6f1892?lnk=gst&q=tturlcache#c1c844465a6f1892

Specifically, in my case anyway, I added an action sheet to
TTPhotoViewController
loadview looks like so:

- (void)loadView {
CGRect screenFrame = [UIScreen mainScreen].bounds;
self.view = [[[UIView alloc] initWithFrame:screenFrame]
autorelease];

CGRect innerFrame = CGRectMake(0, 0,
screenFrame.size.width,
screenFrame.size.height);
_innerView = [[UIView alloc] initWithFrame:innerFrame];
_innerView.autoresizingMask = UIViewAutoresizingFlexibleWidth|
UIViewAutoresizingFlexibleHeight;
[self.view addSubview:_innerView];

_scrollView = [[TTScrollView alloc] initWithFrame:screenFrame];
_scrollView.delegate = self;
_scrollView.dataSource = self;
_scrollView.backgroundColor = [UIColor blackColor];
_scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth|
UIViewAutoresizingFlexibleHeight;
[_innerView addSubview:_scrollView];


_actionButton = [[UIBarButtonItem alloc] initWithImage:
TTIMAGE(@"bundle://Three20.bundle/images/imageAction.png")
style:UIBarButtonItemStylePlain target:self action:@selector
(popupActionSheet)];
_nextButton = [[UIBarButtonItem alloc] initWithImage:
TTIMAGE(@"bundle://Three20.bundle/images/nextIcon.png")
style:UIBarButtonItemStylePlain target:self action:@selector
(nextAction)];
_previousButton = [[UIBarButtonItem alloc] initWithImage:
TTIMAGE(@"bundle://Three20.bundle/images/previousIcon.png")
style:UIBarButtonItemStylePlain target:self action:@selector
(previousAction)];

UIBarButtonItem* playButton = [[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:
UIBarButtonSystemItemPlay target:self action:@selector
(playAction)] autorelease];
playButton.tag = 1;

UIBarItem* space = [[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:
UIBarButtonSystemItemFlexibleSpace target:nil action:nil]
autorelease];

_toolbar = [[UIToolbar alloc] initWithFrame:
CGRectMake(0, screenFrame.size.height - TT_ROW_HEIGHT,
screenFrame.size.width, TT_ROW_HEIGHT)];
_toolbar.barStyle = self.navigationBarStyle;
_toolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth|
UIViewAutoresizingFlexibleTopMargin;
_toolbar.items = [NSArray arrayWithObjects:
_actionButton, space, _previousButton, space,
_nextButton, space, nil];
[_innerView addSubview:_toolbar];
}

(Just to add an action sheet)

At the bottom of that codefile -- I added:
-(void)popupActionSheet {
UIActionSheet *popupQuery = [[UIActionSheet alloc]
initWithTitle:nil
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:@"Save Image",nil];

popupQuery.actionSheetStyle = UIActionSheetStyleBlackOpaque;
[popupQuery showInView:self.view];
[popupQuery release];
}

-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:
(int)buttonIndex {
//UIImage* image = [[TTURLCache sharedCache] imageForURL:imageURL];

if (buttonIndex==0){
UIImage* thisImage = [[TTURLCache sharedCache] imageForURL:
[_centerPhoto URLForVersion:TTPhotoVersionLarge]];
UIImageWriteToSavedPhotosAlbum(thisImage, nil, nil, nil);
//{UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"URL"
message:[_centerPhoto URLForVersion:TTPhotoVersionLarge]
// delegate:self cancelButtonTitle:@"No"
otherButtonTitles:@"Yes", nil];
//[alert show];}
}
}

So, you mash the button on the photo CenterView-- an action sheet
shows. You pick save. It saves to the camera roll. No frills.
As for the saving implemented here. It's covered briefly at:
http://stackoverflow.com/questions/178915/how-to-save-picture-to-iphone-photo-library

My example is rudimentary and does nothing with regard to visual
feedback of save in progress and of course involves editing 320, which
is hackish, but hey - it works....

Best,

Bill

uprise78

unread,
Oct 18, 2009, 4:40:37 PM10/18/09
to Three20
To not disappoint codergeek and his huge code example (which could
have easily been handled without hacking the framework) I shall be
'that guy' that he refers to while on his soap box. I personnaly don't
feel people even have a right to use an someone elses framework at all
without at the very least paying some respect and going through the
source code. A LOT of time went into three20. It is very well written.
Dive into the source and you will learn a ton. That being said, a
quick look at the TTURLCache interface will find a few methods for
saving NSData to disk.

codergeek

unread,
Oct 18, 2009, 7:07:56 PM10/18/09
to Three20
Yeah, I'll dare I try to help the guy.

Yeah, I guess in my case, I felt the save the disk belonged in the 320
framework for my project and I see it as a "hey, we just haven't
gotten to adding that button yet because yes we all have full time
jobs and oh by the way this is free in all of it's awesomeness"

So, yeah, my hack is exactly where I want it.

I really appreciate everyone's effort on 320, it was the first real
example of an enterprise-capable codebase I ever found for iPhone dev,
back in the beginning of the project. I have looked extensively
through its' codebase and I've learned much that I've applied in even
my non-320 based projects. I'll be the first to admit I have plenty
to learn, still.

That being said, I really will never agree with answering peoples'
posts for help with snarky "You fail at objective C" oriented posts.
That really defeats the point, IMHO. More and more of those started
cropping up on this group, recently and I really hate to see that.
The newbs will be experienced developers one day and since all of us
were newbs at one point, I'm thinking being a smart ass to each poster
probably isn't a good practice. Today's newbs are tomorrow's
potential contributors, unless 3 or 4 people run them all off.

But, hey, I didn't mean to interrupt the steady stream of Make Money
Here $$$ Bot posts with a working solution for the guy.

Yay for the spirit of open source. May it's elitist snarkiness bless
us each and every one.

Gareth Townsend

unread,
Oct 18, 2009, 7:48:49 PM10/18/09
to thr...@googlegroups.com
+1

I don't think snarky "read the codebase, you n00b" comments help anyone.

They certainly don't make people feel welcome.
Cheers,
Gareth Townsend
http://www.garethtownsend.info
http://www.melbournecocoaheads.com




Mike D

unread,
Oct 18, 2009, 8:18:49 PM10/18/09
to thr...@googlegroups.com
-1

If you don't at least try just a little bit it is wasting everyones
time and it is a disservice to the authors of the code. At the very
least read the header files for the class in question. That will
answer most basic questions.



On Oct 18, 2009, at 4:49 PM, Gareth Townsend <gareth....@me.com>
wrote:

codergeek

unread,
Oct 18, 2009, 8:58:55 PM10/18/09
to Three20
Yeah, I mean, I've been trolling this forum for quite a bit but never
really seen an opportunity to be of use. Then, recently a few times
when I've seen some of those basic questions that I could probably
pose some answer for, I hesitate to answer because I knew the elitest
- we know everything and your questions are beneath us - flame war was
around the corner.

I've learned a ton from the posts on contributers, here. Uprise, for
instance posted something awhile back in the topic range of remote
photo loading that saved my arse, and I appreciate that effort.

I just think we're kinda missing the spirit of this project when you
jump down peoples' throats for posting questions. Especially since
there really isn't a strong document base, only sample code which
leaves alot of questions unanswered for the uninitiated.

If all of the geniuses spending time griping at posters lack of
knowledge spent that time writing an entry to a document of some
fashion or useful blog posts like some have done, there would be fewer
newb questions here to annoy them.

That, or hey -- just go ahead and make someone pass a proficiency test
in Objective C before downloading the library, that would solve it,
right? :)

Even with this one, as I typed that, I knew around the corner someone
griping about how I did it so poorly and yada yada, blah blah blah,
software architecture nazis of the world unite.

I'll dare someone download this excellent piece of work that isn't
fully documented yet and have the nerve to come here with questions.
Because, I'm sure we've all read thoroughly through the foundation
classes. I'm sure those budding Microsoft-turned Apple Developers
reading this fully inspected the .Net Framework.

Everybody grab a pitchfork and stake, we've got potential new posters
and users to scare off!

Mike D

unread,
Oct 18, 2009, 11:56:13 PM10/18/09
to thr...@googlegroups.com
Let me clearly state that I am NOT in any way shape or form saying to
not ask questions. That is ludicrous. What I am saying is that if you
go to a message list of developers and you don't even take one single
minute of your precious time to look at a header file or try to solve
things on your own you are doing a disservice to everyone on the list
and yourself.

I love a good question and if I know the answer I'll be the first to
help out. If you as a developer ask a question that has an answer in a
header file that you didn't have the courtesy to read than you aren't
asking a good question and you didn't even put an ounce of effort into
finding a solution.

Alec Flett

unread,
Oct 19, 2009, 3:05:55 AM10/19/09
to thr...@googlegroups.com
On Sun, Oct 18, 2009 at 8:56 PM, Mike D <des...@gmail.com> wrote:

Let me clearly state that I am NOT in any way shape or form saying to
not ask questions. That is ludicrous. What I am saying is that if you
go to a message list of developers and you don't even take one single
minute of your precious time to look at a header file or try to solve
things on your own you are doing a disservice to everyone on the list
and yourself.

Wow. I'm flabbergasted by this attitude.

If you post on a list to ask a question, and are met with a rude answer, you walk away, never to be seen from again. Whose time is wasted there? The success of an open source community depends not so much on the quality of the code or the nice formatting of the headers, but in fact the quality of the people involved with the project. Its a social problem, not a technical one.

Nobody does a disservice to the list by asking a question. In fact the more questions you get, the greater an indication that the project is having an impact out there in the world. If you don't want to answer someone's question, then don't answer it. If you spend the time to answer a question rudely, *you* are the one wasting time. If you answer rudely, *that* is doing a disservice to both the original person, and to the general perception of the people using the codebase. And open source projects without good communities fail to thrive. Its the economics of open source.

The irony is that the documentation which the average Joe (sorry...) would use to answer a question is incredibly hard to find, virtually non-existant - scattered among google group wiki pages and people's blogs. You have to be quite the sleuth to track down the answer to your question, and usually you end up reading a lot of source, *if* you know what you're doing. Most folks developing for apple products, or coming from other closed-source worlds, are used to dealing with closed-source solutions, which means going and reading headers is not going to be their first inclination. Cut them some slack.

I offered a constructive solution: to improve documentation. I even started to set up a framework for documenting stuff on the github wiki, and started documenting the TTNavigation component. I communicated to others "here's where to participate" and nobody from the community has actually done anything. So it seems like people are talking out of both sides of their mouths here - telling people to RTFM (or RTF headers) without backing that up with good docs.

Here's an idea: the next time anyone reading this starts to reply to a user saying "Go read the docs/headers before asking that dumb quesiton again," instead go update the docs for that person's question, and direct them there. "I just documented that here..." with a URL is a whole lot more constructive and welcoming.

Alec

Eelco Lempsink

unread,
Oct 19, 2009, 4:08:11 AM10/19/09
to thr...@googlegroups.com
On 19 okt 2009, at 09:05, Alec Flett wrote:
> Here's an idea: the next time anyone reading this starts to reply to
> a user saying "Go read the docs/headers before asking that dumb
> quesiton again," instead go update the docs for that person's
> question, and direct them there. "I just documented that here..."
> with a URL is a whole lot more constructive and welcoming.

Great idea!

To add my 2 cents to the discussion: open source projects don't
magically manage themselves. There's a fine balance between new users
coming to a project, the people that have a bit of experience and
(still) love to answers questions, and the experts that keep one eye
open for interesting questions only. It's mostly the 'middle layer'
of people that is missing in this group, partly because the new users
are (most of the time) not only new to Three20, but new to iPhone
development (or even software programming in general, it sometimes
seems), requiring a huge 'staff' to answer not so interesting
questions to keep the group balanced.

Anyways, this is nobody's fault. People are not emailing simple
questions just to annoy you. Perhaps Three20 could be advertised as a
more 'advanced' framework to scare away some people (mostly for their
own good, but also for the health of the group). Perhaps if the group
was moderated and sent a nice welcome message explaining what to do
when solving a problem, before asking questions. There's probably a
lot more that can be done, but it makes no sense at all telling people
not to ask dumb questions or to be snarky about people being snarky ;)

--
Regards,

Eelco Lempsink

Erotsppa

unread,
Oct 19, 2009, 3:06:12 PM10/19/09
to Three20
codergeek: thanks for the help but I'm quite confused. What i'm trying
to do here is to save the image to disk. I know that I can write this
code myself by saving the UIImage as NSDate but I thought if three20
already does this, might as well reuse the code. However as I said
before, the storeImage method only saves it to memory and everything
is lost on the next launch. What must I do to have it persist? I've
tried the storeToDisk methods as well, but they didn't make much sense
to me because all of them returns a URL back to me, which I have store
in order to retrieve later. So that defeates the purpose if I have to
store some arbitrary url.

And contrary to what most believe here, yes I have read through the
source code and it is still as clear as mud.

On Oct 18, 4:13 pm, codergeek <coderg...@gmail.com> wrote:
> Glad I was able to get to your post before the thinly-veiled insult
> squad that's sadly been patrolling this group for awhile.
> These days, anyone asks a question that's even slightly been
> discussed, the flaming begins.
> Shame, The contributors of this project have done such an excellent
> job...
>
> <steps down from soapbox>
>
> I found the solution to this here:http://groups.google.com/group/three20/browse_frm/thread/1832afc42e5a...
> As for the saving implemented here.  It's covered briefly at:http://stackoverflow.com/questions/178915/how-to-save-picture-to-ipho...

uprise78

unread,
Oct 19, 2009, 3:19:01 PM10/19/09
to Three20
So, this thread is getting old. A 10 second romp through the
TTURLCache.h file reveals these two methods:

/**
* Stores a data on disk.
*/
- (void)storeData:(NSData*)data forURL:(NSString*)URL;
- (void)storeData:(NSData*)data forKey:(NSString*)key;


And another 15 seconds of typing reveals storing and getting data:

NSData *d = [[TTURLCache sharedCache] dataForURL:@"tt://someUrl"];
if( d == nil )
{
d = [NSData dataWithContentsOfFile:[[NSBundle mainBundle]
pathForResource:@"Icon.png" ofType:nil]];
[[TTURLCache sharedCache] storeData:d forURL:@"tt://someUrl"];
}

Alec Flett

unread,
Oct 19, 2009, 4:22:30 PM10/19/09
to thr...@googlegroups.com
So you're not really explaining how that helps, or what the code snippet you pasted is for.

It seems like the core of what codergeek was demonstrating was

       UIImageWriteToSavedPhotosAlbum(thisImage, nil, nil, nil);

to save thisImage, an instance of UIImage, to the saved photos album.. still maybe not what Erotsppa was looking for...

Erotsppa, are you trying to save it to a specific path, because you need the image later, or are you trying to just make sure it persists in TTURL's own persistent cache?

Alec

uprise78

unread,
Oct 19, 2009, 4:34:08 PM10/19/09
to Three20
Erotsppa is trying to save an image to disk, not to the photo library.

Erotsppa

unread,
Oct 20, 2009, 6:27:40 PM10/20/09
to Three20
Yep Alec, I just want it to make sure that TTURL actually persists
what it stores in memory. Sure uprise78's code can write NSDate to
disk, but it's just silly. You'll have to first write it to disk, then
fetch it from disk and store it in TTURL's memory cache. When you
update, you have to update the memory's cache as well as writing to
disk again.

?!?

Why can't TTURL's memory method just write to disk with a boolean
parameter?
Message has been deleted

uprise78

unread,
Oct 20, 2009, 6:40:02 PM10/20/09
to Three20
I sincerely hope you are kidding with this reply...so your original
question was how do you store data on disk with TTURLCache. Now you
know how to do that and it isn't good enough?!? Maybe you should
just
call up Joe Hewitt and have him write your whole app for you.
Make a category on TTURLCache and add this method:

- (void)storeImage:(UIImage*)image forURL:(NSString*)URL cacheToDisk:
(BOOL)shouldCacheToDisk
{
[self storeImage:image forURL:URL];

if( shouldCacheToDisk )
[[TTURLCache sharedCache] storeData:UIImagePNGRepresentation
( image ) forURL:@"tt://someUrl"];
Reply all
Reply to author
Forward
0 new messages