2009-07-29: Awesome missing stuff is back.
Apologies all, I was on hiatus for a while as I biked across the country. During this period the computer that redirected cache.ubergeek.tv to my ip stopped working. This caused animations and things to stop working. I had added the dns entry but for some reason it still didn't work. Now that I'm back home it was obvious: I didn't add the alias to Apache! :O uber indeed.
Things should be working better now!
2009-03-13: A little tweak to help make compilation more straightforward.
One of the more interesting Flex compiler features is conditional compilation. This has been a real boon for us in development on a larger project because we're using it to conditionally include fake testing data in an offline mode, or to use online services. The docs talk about using 'debugging' and 'release' variables to conditionally include things. The code they give looks like this:
<compiler>
<define>
<name>CONFIG::debugging</name>
<value>true</value>
</define>
<define>
<name>CONFIG::release</name>
<value>false</value>
</define>
</compiler>
But why would I want to update two variables for something that is mutually exclusive? In my use case I want to include one thing, and if that's false, include the other thing. More importantly I don't want to introduce errors into the system. Conditional compilation only includes when something is true. BUT I've found a good workaround:
<compiler>
<define>
<name>CONFIG::debugging</name>
<value>true</value>
</define>
<define>
<name>CONFIG::release</name>
<value>!CONFIG::debugging</value>
</define>
</compiler>
And then you can do something similar to what they have in the docs:
package default{
CONFIG::debugging
public class MyClass{
...
}
CONFIG::release
public class MyClass{
...
}
}
2008-09-30: What is a realistic approach for bringing Flash to the iPhone?
So Adobe has decided to publicly talk about (again?) the news that they are working on a flash port to the iphone. While this is a great idea, I definitely would agree with Steve that it won't run very well on the ARM processor. But lets pretend we're Adobe, and look at what's involved with making flash work on the iPhone.
The Pros:
Its only one phone.
Compared to writing Flash for a pc, Adobe only needs to concern themselves with one system. This means they can perform optimizations that would probably cause them compatibility issues otherwise.
Video Support is ARM-Friendly
When Macromedia/Adobe chose On2's video tech, one of their considerations was portability.This means that we can assume quality video playback support, which should give most people warm fuzzies.
The processor isn't slow
Looking at the specs of the iphone shows that its no slowpoke. Let's assume 400mhz. What can we do with 400Mhz on a very specific platform? A lot!
Flash only draws what it needs
Let's also look at this in the context of 'screen real estate'. The iPhone has a 480x320 pixel screen. Say you're viewing a flash movie on a web page. How much of the web page is going to be this movie? Let's assume that the flash is 25% of the screen. That's 120x80. Flash is optimized for drawing only what is necessary, and in most flash movies, this is the most cpu-intensive activity.
So as a user resizes the screen, the flash will only need to draw whatever is necessary to fill the screen. If Safari for iPhone can report to the Flash Player the actual screen size, then the Flash Player can only draw what is needed, and sip the CPU as daintily as it can.
What happens when the flash player fills the screen? Good question. I'll assume that the Flash Player will be an openGL accelerated surface within Safari. And this surface will be layered within Safari. Generally speaking, this is a sub-optimal way to use opengl. What you really want is to have Flash be unfettered with other views being composited on it. This is similar to having a wmode of 'window' on a pc. The flash does its thing, and the browser is almost completely out of the picture. This would be a tough trick to do, but if you could somehow transition out of Safari, and into 'just' flash, then you could get the power where you need it most.
And the Cons
But daintily sipping CPUs is not what the Flash Player is known for. In fact it can easily bring a computer to its knees if used wrongly. What are the major hurdles to overcome?
The Great Unknown
With a web page, browsers have their hands full. There is limitless possibilities in a web page. With flash, there are limitless possibilities, but these also need to be displayed 30 times a second. Regardless of how awesome the developers at Adobe are, they are not going to be able to make everything work at an acceptable speed. What about maybe only displaying every 30th frame? Nope, it can't work that way.
Perhaps Safari will by default display a flash image, requiring activation before display. This is a pretty safe bet, as I'm sure Apple doesn't want Safari to be perceived as slow.
RAM
The Flash Player uses a lot of ram. A LOT. The iPhone has 128MB of ram. And most of that will not be made available to Flash. If there is one place that Adobe developers will lose sleep and hair, it will be attempting to get Flash to work with practically no RAM. This seems to me the biggest hurdle, but I'm very interested to hear if anyone else has other ideas.
It seems almost impossible for Flash to be able to operate on such a small footprint. The flash player will be writing memory to the drive faster than it can allocate it. Features such as BitmapData are so memory intensive that they could possibly be cut.
So which version?
With such rigid requirements, how possible is it that we can see a fully compatible release that mirrors a version on the pc? One that would get Big Steve's signoff?
- Flash 7 - If the Wii can do it, so can the iPhone :)
- Flash 8 - A possibility, but the BitmapData object makes things a bit hairy.
- Flash 9 - BitmapData, and supporting TWO virtual machines (for backwards compatibility). Not likely.
- Flash 10 - I've got an app idea for the iphone called Snowballs in Hell.
No, I'd bet money that if flash came to the iPhone, it would not be any existing release. This sounds crazy, I know, because the point of flash is that it works so consistently across platforms. As the mobile market becomes increasingly relevant Adobe needs to be more of a player.
And Flash Lite hasn't really taken off as it should. Flash Lite is also specific to mobile, with the expectation that mobile would be 'its own thing'. But what's happened is that the 'normal' web is being put on phones, and so Flash Lite isn't an ideal candidate. Unless you're *screaming* for MIDI support.
Instead it would be a new version that would have a sub-set of features. The important bits for video playback, but perhaps not everything you would expect. There would be no need for re-compilation of swfs. It would throw an exception if users used any part of the API that did not exist. Most swfs would load and it would be immediately obvious if they used any 'hardcore' features that didn't make the cut. Let's call it 'Flash 9.5'.
Regardless, we're at the mercy of two pretty secretive companies. We'll just have to wait and see.
2008-09-27: Newest OS X Java update breaks jEdit and many other java apps.
The other day I was upgrading my java, and suddenly the program I probably use the most every day, jEdit suddenly stopped working. After a couple painful days I've gotten it back to 100%. After following this support thread and checking out this blog entry I've found that the following will probably work for you:
rm /Applications/jEdit.app/Contents/MacOS/jedit
cp /System/Library/Frameworks/JavaVM.framework/Resources/MacOS/JavaApplicationStub /Applications/jEdit.app/Contents/MacOS/jedit
If you have jEdit installed in the default location this should work as expected. A similar method should work with other java programs that have stopped functioning with the new update.
The basic problem has to do with symlinks to the latest java application stub. It used to be that you could symlink to the stub, so that you could use whatever was installed on the user's system. Now it seems that there are some problems with the launcher looking for files in the wrong directory, and using the actual stub instead of a symlink is required.
Hope this gets you moving quickly. Thanks to Doug Letterman and 'sbd76' for finding this solution.
2008-09-23: Want to display long pieces of text in a table on the iphone? Here's how.
Overview
I've been trying out iPhone development, getting to know the model. Development is quite different, and I will not pretend to be an expert. But I've compiled a couple nuggets out there on the forums into something coherently useful.
One of the challenges I found was that I wanted to dynamically layout text that could be very long. I wanted this text to be available in a table view. The help files explain how to use the UITableViewCell class. There are also tutorials on the web that contain information on subclassing and creating subviews. So this entry will assume that you are familiar with the basics of UITableViewCell and can create a subview.
Setting up our UIViewController
First we must set up our UIViewController to delegate the correct methods for UITableViewDataSource and UITableViewDelegate. Most importantly is the heightForRowAtIndexPath() and cellForRowAtIndexPath()
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *text;
CGSize s;
UIFont *f;
text = [self getTextForIndexPath:indexPath];
f = [UIFont systemFontOfSize:14];
s = [self GetSizeOfText:text withFont: f];
return s.height +11;
}
This function calls two custom functions: getTextForIndexPath() and getSizeOfText(). getTextForIndexPath() gets an NSString of the text used for this row and section. I then pass this to a GetSizeOfText() which is explained below. Finally I fudge it with 11px of space. Why 11 pixels? There is 5px on top and bottom of padding, and then 1 extra pixel for the outline on the bottom. Hey, I'm not proud of this code....baby steps.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = @"MyCell";
DetailViewCell *cell = (DetailViewCell *)[tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[DetailViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:identifier] autorelease];
}
NSString *cellText = [self getTextForIndexPath:indexPath];
[cell setData:cellText];
return cell;
}
This code is very similar to the normal code in cellForRowAtIndexPath(). I include it here for completeness. What we're doing is basically initialising the UITableViewCell for use in drawing the cell. If we have an existing cell, let's reuse it. We then pass the data to the cell using the getTextForIndexPath() function used earlier to also measure the text.
Its important to understand that you cannot assume a correlation between your UITableViewCell and the data within. In other words, the iPhone attempts to efficiently re-use views, and so as it redraws, the data you put in there could be stale. cellForRowAtIndexPath() is your place to initialize your cell with data before it gets drawn.
- (CGSize)GetSizeOfText:(NSString *)text withFont:(UIFont *)font
{
return [text sizeWithFont: font constrainedToSize:CGSizeMake(280, 500)];
}
I've also added a utility function GetSizeOfText() which returns the size of the text with the font you've specified. One of the curiosities of Cocoa: the string primitive object tells you the bounds of the string if you give it a font and a bounding rectangle (CGRect). I would've never looked there. This is one of those 'delegation idiosynchracies' that occur, since delegates can be placed on whatever you like.
- (void)viewWillAppear:(BOOL)animated
{
self.title = @"Details";
[tblView reloadData];
}
Another important function to override is the viewWillAppear() delegate. If your view is controlled by a NavigationController (which is pretty likely), it could be that your view will draw correctly the first time, but not reload the data and redraw on a subsequent view.
For example you create a list of news items. You click on an item and go to a subscreen displaying the item's details. You click back and go to another option, yet the view does not redraw. NavigationController's already drawn the view, so it just displays it without updating it. What you want to do is trigger a redraw by overriding the viewWillAppear() delegate and reloading the table view's data via [tblView reloadData];.
Subclassing our UITableViewCell
Ok, so now we have our ViewController set up correctly to report the heights of our variable length cells based upon the text and font we are using. Now we need to create our subclass of UITableViewCell. We'll use the same method used in similar tutorials:
- Subclass UITableViewCell
- Add subviews to the UITableViewCell's contentView
- Override layoutSubviews
- Initialize data into our view
Subclass UITableViewCell and add subviews
- (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) {
// Initialization code
// we need a view to place our labels on.
UIView *myContentView = self.contentView;
//init font.
UIFont *font = [UIFont systemFontOfSize:14];
// init the label
self.label = [[UILabel alloc] initWithFrame:CGRectZero];
self.label.backgroundColor = [UIColor whiteColor];
self.label.font = font;
self.label.numberOfLines = 0;
[myContentView addSubview:self.label];
[self.label release];
}
return self;
}
Here we grab out contentView from the superclass, and then add a label as a subview.
Override layoutSubviews
-(void)layoutSubviews {
[super layoutSubviews];
//get the cell size.
CGRect contentRect = self.contentView.bounds;
if(!self.editing){
CGFloat boundsX = contentRect.origin.x;
CGRect frame;
NSString *text = self.label.text;
UIFont *font = self.label.font;
CGSize constraint = CGSizeMake(280,500);
CGSize size = [text sizeWithFont:font constrainedToSize:constraint];
frame = CGRectMake(boundsX + 10, 0, 280, size.height + 10);
self.label.frame = frame;
}
}
What we do here is resize the UILabel to fit within the contentView accurately. You may have noticed that we are doing something similar to GetSizeOfText() within the view controller. In fact its practically duplicate code. We get the size of the text, and then add 10px to the x origin. The 10px amount is because of the rounded rectangle styling of the table. 280px is a good width, there is a 20px margin on each side of the table cell. The height is based upon the CGSize returned from sizeWithFont(). Another 10px is added for styling.
Initialize data into our view
-(void)setData:(NSString *)text {
self.label.text = text;
}
For these purposes there was no need for any model or data objects within our UITableViewCell. Instead I just set the label's text directly.
Areas for improvement
I'm sure there are some savvy Obj-C programmers out there who can easily pick out some coding flaws here. There is code duplication between the view controller and the view cell. There could be memory leaks. Styling info is hardcoded. But hey, if you want to create a variable length cell, then I hope this code can be useful to you!
DetailViewCell
#import
@interface DetailViewCell : UITableViewCell {
UILabel *label;
}
// gets the data from another class
-(void)setData:(NSString *)text;
@property (nonatomic, retain) UILabel *label;
@end
#import "DetailViewCell.h"
@implementation DetailViewCell
@synthesize label;
- (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) {
// Initialization code
// we need a view to place our labels on.
UIView *myContentView = self.contentView;
//init font.
UIFont *font = [UIFont systemFontOfSize:14];
// init the label
self.label = [[UILabel alloc] initWithFrame:CGRectZero];
self.label.backgroundColor = [UIColor whiteColor];
self.label.font = font;
self.label.numberOfLines = 0;
[myContentView addSubview:self.label];
[self.label release];
}
return self;
}
-(void)setData:(NSString *)text {
self.label.text = text;
}
-(void)layoutSubviews {
[super layoutSubviews];
//get the cell size.
CGRect contentRect = self.contentView.bounds;
if(!self.editing){
CGFloat boundsX = contentRect.origin.x;
CGRect frame;
NSString *text = self.label.text;
UIFont *font = self.label.font;
CGSize constraint = CGSizeMake(280,500);
CGSize size = [text sizeWithFont:font constrainedToSize:constraint];
frame = CGRectMake(boundsX + 10, 0, 280, size.height + 10);
self.label.frame = frame;
}
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (void)dealloc {
[label dealloc];
[super dealloc];
}
@end
FeedItemController
#import
@interface FeedItemController : UIViewController
{
IBOutlet UITableView *tblView;
NSDictionary *currentStory;
UILabel *textView;
}
-(NSString *) testFunction;
- (CGSize)GetSizeOfText:(NSString *)text withFont:(UIFont *)font;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
- (NSString *)getTextForIndexPath:(NSIndexPath *)indexPath;
@property (nonatomic, retain) UITableView *tblView;
@property (nonatomic, retain) NSDictionary *currentStory;
@property (nonatomic, retain) UILabel *textView;
@end
#import "FeedItemController.h"
#import "DetailViewCell.h"
#import "UIKit/UITableView.h"
#import
@implementation FeedItemController
@synthesize tblView, currentStory, textView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Initialization code
NSLog(@"currentStory initWithNibName %@", [currentStory objectForKey:@"summary"]);
}
return self;
}
- (void)awakeFromNib
{
self.title = @"Details";
NSLog(@"currentStory awakeFromNib %@", [currentStory objectForKey:@"summary"]);
}
// Implement loadView if you want to create a view hierarchy programmatically
- (void)loadView {
NSLog(@"currentStory loadView %@", [currentStory objectForKey:@"summary"]);
//newsSummary.text = @"Interesting";
}
//If you need to do additional setup after loading the view, override viewDidLoad.
- (void)viewDidLoad {
NSLog(@"currentStory viewDidLoad %@", [currentStory objectForKey:@"summary"]);
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = @"MyCell";
DetailViewCell *cell = (DetailViewCell *)[tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[DetailViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:identifier] autorelease];
}
NSString *cellText = [self getTextForIndexPath:indexPath];
//NSLog(@"Setting text to %@", cellText);
[cell setData:cellText];
return cell;
}
- (NSString *)getTextForIndexPath:(NSIndexPath *)indexPath
{
switch(indexPath.section){
case 0:
switch (indexPath.row){
case 0:
return [currentStory objectForKey:@"title"];
break;
case 1:
return [currentStory objectForKey:@"date"];
break;
}
break;
case 1:
return [currentStory objectForKey:@"summary"];
break;
case 2:
return @"View in Safari";
break;
}
return @"";
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *text;
CGSize s;
UIFont *f;
text = [self getTextForIndexPath:indexPath];
f = [UIFont systemFontOfSize:14];
s = [self GetSizeOfText:text withFont: f];
//NSLog(@"Width: %f Height: %f", s.width, s.height);
return s.height +11;
}
- (NSString *)testFunction;
{
return @"hi";
}
- (CGSize)GetSizeOfText:(NSString *)text withFont:(UIFont *)font
{
return [text sizeWithFont: font constrainedToSize:CGSizeMake(280, 500)];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
//boy i need to learn how to use arrays :P
switch (section) {
case 0:
return @"Name and Date";
break;
case 1:
return @"Details";
default:
return @"";
break;
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 3;
}
-(NSInteger)tableView:(UITableView *)tblView numberOfRowsInSection:(NSInteger)section {
//Every cell is going to have two rows.
switch (section) {
case 0:
return 2;
break;
case 1:
return 1;
default:
return 1;
break;
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic
if(indexPath.row == 0 && indexPath.section == 2){
NSString * storyLink = [currentStory objectForKey: @"link"];
// clean up the link - get rid of spaces, returns, and tabs...
storyLink = [storyLink stringByReplacingOccurrencesOfString:@" " withString:@""];
storyLink = [storyLink stringByReplacingOccurrencesOfString:@"\n" withString:@""];
storyLink = [storyLink stringByReplacingOccurrencesOfString:@" " withString:@""];
// NSLog(@"link: %@", storyLink);
// open in Safari
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:storyLink]];
}
}
#pragma mark UIViewController delegates
- (void)viewWillAppear:(BOOL)animated
{
//NSLog(@"currentStory viewWillAppear %@", [currentStory objectForKey:@"summary"]);
//newsSummary.text = [currentStory objectForKey:@"summary"];
self.title = @"Details";
[tblView reloadData];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
- (void)dealloc {
[currentStory release];
[tblView release];
[super dealloc];
}
@end
|