Archive for October, 2009

Simon says, MonoTouch downloading images like AppStore app in less than 50 lines with CoreAnimation Transitions!

Written by Simon Guindon. Posted in Coding, Diggify, Mobile, MonoTouch, Project-D

Project-D-02_thumb

In Project-D I needed to dynamically load images in the same manner the Apple AppStore loads the application icons over the web and have them nicely render into place when the download is completed. Obviously this can’t occur in the UITableView methods because scrolling performance would be degraded significantly waiting for even 1 image to download.

I was amazingly surprised how simple this is in MonoTouch using ThreadPool.QueueUserWorkItem() and some Objective-C classes MonoTouch has wrapped bindings for. I did this all in 50 lines of code. I could have optimized it more to shrink it, but it’s 50 lines with broken out methods and Core Animation transitions. Very small and easy to do!

This is based on my blog about how to build custom UITableViewCell’s in Interface Builder which you can find here.

Inside the GetCell() method of my UITableViewDelegate I call GetImage() passing the UITableViewCell controller and the story (which contains the image uri). GetImage() queues a task with the ThreadPool to go fetch us the image so that the UITableView scrolling is unaffected.

The great thing about GetCell() on the iPhone is, it only gets called for cells which are being rendered. So we do not have to handle all the logic about calculating what is visible, what do we fetch etc. We already know what we need to download! I fade the images alpha so that they appear to fade in nicely once the download is complete.

Play Video

[code]
public override UITableViewCell GetCell(UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
// ... Do your usual stuff...
GetImage(controller, story);
}

private void GetImage(StoryCellController controller, Story story)
{
controller.ImageThumbnail.Alpha = 0.0f;
if (story.Thumbnail != null && story.Thumbnail.Src != string.Empty)
{
if (images.ContainsKey(story.Thumbnail.Src) == true)
{
UIImage imageThumbnail = images[story.Thumbnail.Src];
controller.ImageThumbnail.Image = imageThumbnail;
controller.ImageThumbnail.Alpha = 1.0f;
}
else
{
controller.ImageUri = story.Thumbnail.Src;
ThreadPool.QueueUserWorkItem(RequestImage, controller);
}
}
}

private void RequestImage(object state)
{
StoryCellController controller = state as StoryCellController;
if (controller != null)
{
NSUrl imageUrl = NSUrl.FromString(controller.ImageUri);
NSData imageData = NSData.FromUrl(imageUrl);
controller.ImageThumbnail.Image = UIImage.LoadFromData(imageData);

images.Add(controller.ImageUri, controller.ImageThumbnail.Image);

InvokeOnMainThread(delegate { RefreshImage(controller); });
}
}

private void RefreshImage(StoryCellController controller)
{
UIView.BeginAnimations("imageThumbnailTransitionIn");
UIView.SetAnimationDuration(0.5f);
controller.ImageThumbnail.Alpha = 1.0f;
UIView.CommitAnimations();
}
[/code]

Simon say’s Project-D UITableViewCells autosize based on text height in MonoTouch. Here’s how!

Written by Simon Guindon. Posted in Coding, Diggify, Mobile, MonoTouch, Project-D

Project-DWhile building the main story view in Project-D which uses a UITableView I needed to have each cell dynamically snap in their height based on the height of the content, which in this case is text.

To do this you must implement UITableViewDelegate along with UITableViewDataSource.

When implementing UITableViewDelegate the key override is GetHeightForRow(). This method lets us return what the height is for a particular row.

I’m doing 2 key things for performance reasons in GetHeightForRow() which are:

  1. Caching a font instance which represents the font I am using on the UITableViewCell
  2. Caching the cell height in my model, so that I only ever do this calculation once for a particular UITableViewCell

Basically what I am doing here is giving the measuring API a constraint width wise, and it will give me back a size of how high the text with this particular font consumes:

[code]
public class StoryViewTableController : UITableViewDelegate
{
private static UIFont font = null;
private StoryViewController viewController = null;

public StoryViewTableController(StoryViewController viewController)
{
this.viewController = viewController;
if (font == null)
font = UIFont.FromName("Helvetica", 14.0f);
}

public override float GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
Story story = viewController.DataSource.Stories.Stories[indexPath.Row];

if (story.CellSize.Height == 0)
{
story.Title = story.Title.Trim();
story.Description = story.Description.Trim();

story.TitleSize = tableView.StringSize(story.Title, font, new SizeF(237.0f, 1000.0f), UILineBreakMode.WordWrap);
story.DescriptionSize = tableView.StringSize(story.Description, font, new SizeF(237.0f, 1000.0f), UILineBreakMode.WordWrap);

SizeF sizeTotal = new SizeF(237.0f, story.TitleSize.Height + story.DescriptionSize.Height + 20);
story.CellSize = sizeTotal;

if (story.CellSize.Height < 108)
story.CellSize = new SizeF(story.CellSize.Width, 108);
}

return story.CellSize.Height;
}
}
[/code]

In my UITableViewDataSource I am doing the same technique I showed in the tutorial
Let’s Build A Custom UITableViewCell and applying the new sizes that I store in the model to the cell itself.

[code]
public override UITableViewCell GetCell(UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
UITableViewCell cell = null;
StoryCellController controller = null;

cell = tableView.DequeueReusableCell("StoryCell");
if (cell == null)
{
controller = new StoryCellController();
NSBundle.MainBundle.LoadNib("StoryCellController", controller, null);
cell = controller.Cell;

cell.Tag = Environment.TickCount;
cellControllers.Add(cell.Tag, controller);
}
else
{
controller = cellControllers[cell.Tag];
}

Story story = stories.Stories[indexPath.Row];
controller.Title = story.Title.Trim();
controller.Description = story.Description.Trim();

// Sizing the cell.
RectangleF rectCell = cell.Frame;
rectCell.Height = story.CellSize.Height;
cell.Frame = rectCell;

// Sizing the background image.
RectangleF rectImageBackground = controller.ImageBackground.Frame;
rectImageBackground.Height = story.CellSize.Height;
controller.ImageBackground.Frame = rectImageBackground;

// Sizing the LabelTitle.
controller.LabelTitle.Frame = new RectangleF(
new PointF(
controller.LabelTitle.Frame.X,
controller.LabelTitle.Frame.Y),
story.TitleSize);

// Sizing the LabelDescription.
controller.LabelDescription.Frame = new RectangleF(
new PointF(
controller.LabelDescription.Frame.X,
story.TitleSize.Height + 10),
story.DescriptionSize);

return cell;
}
[/code]

Hopefully this helps people out. As always you can find me on Twitter via @simongui and I appreciate any retweets!

Simon say’s Project-D ticker CoreAnimation howto with MonoTouch

Written by Simon Guindon. Posted in Coding, Diggify, Mobile, MonoTouch, Project-D

project-d-01_thumb

I’ve been tweeting about Project-D for awhile now, but this will be my first blog post speaking about it. I’m sure you will catch on about what Project-D actually is but the real name is going to remain secret for a little longer.

I want Project-D to be a community driven application. I want to hear people’s feedback and put the features people really want. I also want to take the opportunity of building the application and advocating the use of MonoTouch through howtos. So in a series of blog posts as I build Project-D, I hope to show the source code of items I built and how I built them in MonoTouch.

This week I got an interesting idea from someone on Twitter who said I should have a ticker similar to CNN. I started thinking about it throughout the week and then it came to me. Some very subtle CoreAnimation effects could really make the ticker bling out at the same time not being too over the top. I also couldn’t remember if anyone did any CoreAnimation based howto for MonoTouch yet. So I decided this was a good opportunity to accomplish a couple things at once.

Here’s what I ended up with so far. Keep your eye on the bottom of the screen, so you can see the transition of the ticker. Also every 10 seconds it refreshes, so keep watching the ticker.

[youtube]7AhcwKwcglc[/youtube]

I have 2 view controllers. One for the story listing and one seperate for the ticker itself. I add it to the story view controller via:

[code]
StoryTickerController tickerController = new StoryTickerController();
this.View.AddSubview(tickerController.View);
[/code]

As you probably noticed I had the ticker slide up on load with a nice animation. I used MonoTouch’s CoreAnimation API bindings to achieve this:

[code]
void AnimateShowTicker()
{
UIView.BeginAnimations("tickerShowIn");
UIView.SetAnimationDuration(1.0f);
RectangleF rect = this.View.Frame;
rect.Location = new PointF(rect.Left, 0);
this.View.Frame = rect;
UIView.CommitAnimations();
}
[/code]

The nice thing about CoreAnimation is any properties you change after calling BeginAnimations() start to be recorded for the animation. Once you call CommitAnimations() the changes will occur. It’s a really nice way to do animations by just coding your property changes as normal. I really like how this is done.

After I got that completed for the nice on load transition, I wanted to also transition when the upcoming story changes. I decided a nice fade out to fade in would be nice. I did it as follows:

[code]
void UpdateTicker()
{
UIView.BeginAnimations("tickerTransitionOut");
UIView.SetAnimationDelegate(this);
UIView.SetAnimationDidStopSelector(new Selector("fadeOutDidFinish"));
UIView.SetAnimationDuration(0.5f);
labelTicker.Alpha = 0.0f;
UIView.CommitAnimations();
}

[Export("fadeOutDidFinish")]
public void FadeOutDidFinish()
{
labelTicker.Text = story.Title;

UIView.BeginAnimations("tickerTransitionIn");
UIView.SetAnimationDuration(0.5f);
labelTicker.Alpha = 1.0f;
UIView.CommitAnimations();
}
[/code]

Once the fade-out animation completes, I want to swap the text, and fade the label back into view.

Line 5 we tell which selector we wish to be called when the animation is finished. We declare using the [Export] attribute which method is going to be associated with a selector name so that when the animation is completed, that method gets executed. From here I do another animation but this time set the alpha to 1.0 so that the label is again visible with the new text.

And that’s all that was needed to make both effects. All very simple, subtle but slick using MonoTouch.

You can catch me on Twitter for more information and much more via @simongui. Please retweet!

Startups