UITextView Scrolling Doesn’t Work

I had an issue where I was unable to get scrolling on a UITextView to work when editing was turned off. I searched and searched through the vast sea of information that is the internet and came up empty.

Basically the problem was that I had nested my UITextView inside of a UIImageView. This seems innocuous enough but UIImageView does not have the userInteractionEnabled flag set to YES by default. Therefore the event was dispatched up to that view from the main thread and was blocked because the view would not handle and propagate the event.

So the solution was to set the UIImageView’s userInteractionEnabled flag to YES.

    self.userInteractionEnabled = YES;

I think there could be other views that are set up like this as well, although I am not sure which ones they would be. So watch out for this, especially if you are defining your views in code.

I hope that this shows up in someone’s Google search and helps them out.

Tags: , , , , ,

Custom Fonts in iOS 4

Up till now there hasn’t been an easy way to add custom fonts to your iPhone applications.  As of iOS 4 it has become very easy to do.  Here is what you need to do in order to add custom fonts:

  1. Add your custom font files into your project using XCode as a resource
  2. Add a key to your info.plist file called UIAppFonts.
  3. Make this key an array
  4. For each font you have, enter the full name of your font file (including the extension) as items to the UIAppFonts array
  5. Save info.plist
  6. Now in your application you can simply call [UIFont fontWithName:@"CustomFontName" size:12] to get the custom font to use with your UILabels and UITextViews, etc…

It’s that simple!

Tags: , , , ,

Deploying a Read/Write Resources with your iPhone Application

iPhone development has been a great experience but that does not mean it has not been without its challenges. For instance, if you are planning on saving some sort of dynamic content in your application, you will quickly find that it is not simply a matter of discovering where your application is being stored and writing some files in there.  It is a little bit more involved than that.  In order to help those who come after, we thought it would be worthwhile to share some of our findings.

I think the first thing to realize is that when your application is deployed onto your iPhone/iPod/Simulator you are deploying a complete package. This means that the contents, i.e your code, resources, database and anything else that might be part of your deployment bundle are uniquely signed using your developer’s key which verifies that everything you intended to be in there is there and nothing else. This is a very important concept from a security standpoint and it allows the target device to verify the validity of the bundle to ensure that your application has not been compromised. Now that we have stated that, it’s pretty obvious that it will be impossible to change the deployed project. This doesn’t mean that Apple didn’t think of this inevitability.

The first step to looking into this deployment will be looking at the target deployed file system. The easy way to find this is to log the applications bundle path in the applicationDidFinishLoading method on the appDelegate. You can do this with the following code:

NSLog(@”Application Directory: %@”, NSHomeDirectory());

The output (in the console) for this will show you where you can find the file system. I usually just copy and paste this into a terminal window and jump right to it (just make sure to wrap the directory name in double quotation marks).

If you navigate tot this directory you will see your application (yourapp.app) which is a directory containing all of your code and resources and a Documents and a Library directory. That Documents directory is where our dynamic content will reside.

This seems straightforward enough, just put what we need in the directory and away we go, right? Well not so quick cowboy, there is no idea of an installer that executes when your application downloads. This means we are going to have to figure out how to copy the files into the Documents directory the first time the application loads.

Over here at BeefyApps, we developed this little snippet of code to handle this initial load of data.

-(void)initializeDocumentDirectory
{

NSFileManager *manager = [NSFileManager defaultManager];
NSArray *templateDir = [manager directoryContentsAtPath:[NSString stringWithFormat:@"%@/Documents", [[NSBundle mainBundle] bundlePath]]];

for (id file in templateDir) {

if ([manager fileExistsAtPath:[NSString stringWithFormat:@"%@/Documents/%@", NSHomeDirectory(), file]])

continue;

NSString *srcPath = [NSString stringWithFormat:@"%@/Documents/%@", [[NSBundle mainBundle] bundlePath], file];
NSString *dstPath = [NSString stringWithFormat:@"%@/Documents/%@", NSHomeDirectory(), file];

NSError *error;
[manager copyItemAtPath:srcPath toPath:dstPath error:&error];

}

}

So the code above simply takes a look inside of the bundle, navigated to via the [[NSBundle mainBundle] bundlePath] command. The result of this call will provide you with the string location of the applications executing bundle directory. For the purposes of this code we have a Documents directory which resides inside of the bundle path. We then take the contents of that directory and conditionally copy it file by file into the writable Documents directory.

Pretty simple solution really. I think where this gets interesting is when we start to consider the ramifications for updating your application. I’ll leave that exercise up to the reader at this time though.

Tags: , , , , ,

Upcoming: New iPhone Game

We at BeefyApps have not been idle since the 1.1 release of our iPhone Soundboard.  A new project has been underway, this time with a little more focus on skill and competitiveness.  Finishing touches are being done on a  recreational pursuit of high scores that can be shown off to your friends.  A little sneak peak of an early alpha build:

Electrophobe for the iPhone
Electrophobe for the iPhone

We are most electrified about the features that you can’t see in the screenshot.

Tags: , , , , ,

BAUIStarSlider Source Package

We got some requests from people asking if they can use our BAUIStarSlider code in their applications. We are happy to provide you with the source code and to let you use it in your applications. We only ask that you don’t redistribute it as your own code and if anyone inquires to point them our way. If you do use the star slider code, we would appreciate it if you could give us some credit and link to our site. Other than that feel free to use it however you want and let us know what applications you’ve used it in.

Here is the zip file: BAUIStarSlider Package

Tags: , ,

Custom Star Ratings Slider Using Quartz2D Part 2

In Part 1 we talked about creating a custom UIView class that draws fillable stars. We’ve edited the code in Part 1 to fix some errors that were discovered when implementing the slider, mainly that the scaling of the coordinates should only happen once, (upon initialization) and not in the drawRect method.

In this part we will talk about using this fillable star to create a custom UISlider. Here is the interface definition for the BAUIStarSlider.

typedef enum
{
ApproximationModeNone,
ApproximationModeWhole,
ApproximationModeHalf,
ApproximationModeQuarter

} SliderApproximationMode;

@interface BAUIStarSlider : UIControl {

NSMutableArray * starArray;
int numStars;
SliderApproximationMode approxMode;
float value;

}
@property(nonatomic) SliderApproximationMode approxMode;
@property(nonatomic) float value;

-(id)initWithFrame:(CGRect)frame andStars:(int)inNumStars;

@end

As you can see the BAUIStarSlider is a subclass of UIControl which will allow us to make use of the event dispatching methods found in UIControl. The implementation of the custom slider is pretty straight forward. There are only a few methods that need to be implemented, the first being the initialization method:

-(id)initWithFrame:(CGRect)frame andStars:(int)inNumStars
{
if (self = [super initWithFrame:frame])
{
numStars = inNumStars;
starArray = [[NSMutableArray alloc] initWithCapacity:numStars];
approxMode = ApproximationModeWhole; //default approximation mode

float width = frame.size.width/numStars;
float height = frame.size.height;

for(int i=0; i < numStars; i++)
{

BAFillableStar * star = [[BAFillableStar alloc] initWithFrame:CGRectMake(0+(i*width), 0, width, height)];
[starArray addObject:star];
[self addSubview:star];
[star release];

}

}

return self;

}

The next two methods deal with setting the value on the slider using touch. When a user touches a point on the star, we want to fill the stars of the slider up to that point. We also want to allow the user to slide their finger along the slider and when they do this we want to fill or clear the stars along the slider appropriately.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];

//make sure the points are within our bounds
if(touchPoint.x < 0 )
{

touchPoint.x = 0;

}
if(touchPoint.x > self.frame.size.width)
{

touchPoint.x = self.frame.size.width;

}

value = touchPoint.x / (self.frame.size.width/numStars);
[self fillStars];

}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{

//just call the touchesBegan method
[self touchesBegan:touches withEvent:event];

}

The final two methods to implement do the actual filling of the stars based on the value that was set using the touch methods. The implementation of setFillPercent in the BAFillableStar class calls [self setNeedsDisplay] so that when the fill percent is set, the star will automatically redraw itself. We want to implement our own setValue method so that when the value of the slider is initially set the correct number of stars get filled properly. The getApproximatedPart method uses the approximation mode to round the part value, so if the approximation mode is none, no rounding happens, if the mode is half, then it will round it to the nearest half value, etc…

-(void)fillStars{
float whole = floor(value);
float part = value – whole;
part = [self getApproximatedPart:part];

//dispatch a value changed event
value = whole + part; //since we approximated
[self sendActionsForControlEvents:UIControlEventValueChanged];

int i = 0;
for(BAFillableStar * star in starArray)
{

if( i < whole)
{
star.fillPercent = 1;

}
else if ( part > 0 )
{

star.fillPercent = part;
part = 0;

}
else
{

star.fillPercent = 0;

}
i++;

}

}
-(void)setValue:(float)inValue
{

value = inValue;
[self fillStars];

}

That’s it for this tutorial. Hope you found it informative. If you have any questions ask in the comments section and we will try to answer them as best we can.

Tags: , ,

BeefyBoard Soundboard and Skin Listings

We at BeefyApps are proud to announce that you can now browse the list of skins and soundboards that users have submitted, online! This will allow you to see the comments and ratings that will be available in our upcoming release. You can view the skins here: http://sounds.beefyapps.com/skin/list and the soundboard list is here: http://sounds.beefyapps.com/board/list.

Check it out and let us know what you think.

Tags: , , , , ,

iPhone Upload Progress Bar

If you are developing for the iPhone and you have been utilizing the web components, you have probably noticed that while you can observe the downloading progress, via the NSConnection class, you are unable to hook into the outbound stream from the Cocoa Touch framework classes.   In order to solve this problem you need to go beneath the covers and use the CFNetwork class.

The CFNetwork class will allow you to monitor the inbound and outbound streams.  The NSConnection and NSRequest methods are actually built on top of their Core Foundation counterparts.

Here is a link to the apple documents relating to the CFNetwork Classes: http://developer.apple.com/iphone/library/documentation/Networking/Conceptual/CFNetwork/Introduction/Introduction.html

This whole system might look fairly complex and it is.  You need to understand about streams and headers and binary data and all that fun stuff, but luckily the guys over at All Seeing Interactive (http://www.allseeing-i.com) have devised a couple of classes that you can implement in your own code.

Now I have a minor critique of this code in that I don’t like the way that it’s ASIRequest object consumes a UIProgressView (or it’s Mac Equivalent) only.  Personally I like to have a little more control over how my progress bars update.  This may sound a little obtuse but consider the following scenario: Imagine you have a multi-stage upload and save that must first assemble some binary data, compress it and then upload it.  The progress bar then should have 1/3 of it used for each step.  With the current implementation the progress is assumed to go from 0% to 100%.

This really isn’t a big problem though because the code is available for download from their public repository at github.  A couple of minor changes and it is easy enough to simply provide an additional selector for the progress updating and voila, you have full control.

So to the guys over at All Seeing Interactive, thanks for the framework.  It gets us all going in the right direction.  Good Work!

Tags: , , , , ,

Easier iPhone Soundboard Creation

Recently, we at BeefyApps, have been working hard at the next release of our iPhone Soundboard.  Some of our early adopters mentioned we could raise the coolness level by allowing people to create soundboards using the internet.

Now that the bulk of application code has been written for our upcoming v1.1 release, we turned our attention to the website to facilitate uploading of sound files.  Our efforts paid off, as we now support soundboard upload.  Upload some audio files, and in about five minutes your board will be available on all iPhones with the BeefyBoard installed.

Tags: , , , ,

Star Rating Slider

I remember looking at Apple’s iTunes rating slider and thinking to myself, that will be perfect to integrate into BeefyBoard: The iPhone Soundboard for rating soundboards and skins alike.  Imagine my dismay upon combing through the SDK and coming up empty handed.  Not to be deterred though, I managed to recreate something similar to apple’s slider using a couple images and the base UISlider implementation that Apple provides.

sliderblog1

Apple’s UISlider component is made up of two images one for the left, which I like to call the filled side, and another image for the right, or the empty side.  Apple deals with these images by using some stretchable images with caps and this lets them resize the slider to their heart’s content.  We however do not really have that luxury.

Gotcha #1: The left and right images need to be the same size and you need to account for the frame padding that Apple adds in as part of the component.  On the slider that I worked with, my two images were 250px wide but my slider control had to be 254px wide in order to display properly.  You will notice if you start playing with the size that it will mess up your image if you get this wrong.

Gotcha #2: The thumb image that you choose will affect the size and the position of the two images and where they meet.  Even though you might have figured out how to get the images to line up, if you are using a thumb control image the UISlider will adjust the size of the image to take this into account.  For instance, if you make your thumb image 50px then 50 px will be added to the width of the control to take into account the width of the thumb on either side of the slider.

Now that we have talked about all the sizing problems we should be great right?  Not yet, we still need to worry about the way that you control the slider.  Between the 2.1 and 2.2 releases of the iPhone SDK the behaviour has changed for the touch sensitivity of the UISlider.  In 2.1 you used to be able to touch anywhere on the slider and the slider would move to that position.  In 2.2 some people complained that they were accidentally changing settings and thus it was removed. So…

Gotcha #3: If you do not add a thumb image in your slider you will not have any hit area to click on in order to adjust the value of the slider.  You could add a transparent thumb icon if you wanted to provide a hit area but not have the graphic, but remember gotcha #2 above, the UISlider will adjust the size of the slider and you will need to account for that.

To solve this problem, I went in and enabled touch support for a UISlider subclass that I created.  I added touch support for the slider being pressed and again for a finger being moved on the slider.  One important thing that I almost missed was to fire the value changed action on the UISlider so that it would tell any registered selectors that the rating value had changed.

I hope this helps some people out there to avoid some problems and if you have any questions please feel free to leave a comment.  I will try to answer the best I can.

Tags: , , , ,