Posts Tagged ‘iPhone’
I’ve recently been playing around with delegates and protocols and figured, it might be a good idea to create a simple example. The following article will show you, how you could implement a custom delegate into your own XCode project.
First, why would you want to do this? Well, there are a number of reasons. For instance, picture this: You have a class, that takes care of some calculations. Depending on how complicated these are, they can take up more or less time. Once the calculations are done, you want to send a feedback message to the class, that triggered the process. Using a custom delegate will allow you to receive that message without implementing the calling class into your calculations class. The big advantage is, that you will be able to use your calculations class in any class, send feedback message through the delegate and keep your code nice and clean.
Let’s get started, shall we?
In my sample project, I start off with a “View-based Application” template. It will give me the basic view hierarchy I need and I can get right to the fun part
I create a new “Objective-C” class (subclass of NSObject), name it “DelegateClass” and add it to my project. I now got two new files: DelegateClass.h and DelegateClass.m. Now I can start creating my delegate right away.
First, I want to add a protocol for my delegate. The protocol will contain the feedback method:
@protocol MyCustomDelegate <NSObject> - (void)delegateSentFeedback; @end
Now, I create an instance of my delegate inside @interface:
@interface DelegateClass : NSObject {
id<MyCustomDelegate> delegate; //create instance
}
I also need a method, that will do something. Therefore I name it “doSomething”:
- (void)doSomething;
Finally, I add @property for the delegate, so it can be assigned from any class:
@property (assign) id<MyCustomDelegate> delegate; //@property, so delegate can be assigned from any class #importing "MyCustomDelegate.h"
Inside DelegateClass.m I need to do two things:
Synthesize the delegate:
@synthesize delegate;
and add the doSomething method:
- (void)doSomething {
if (delegate != nil && [delegate respondsToSelector:@selector(delegateSentFeedback)]) { //only trigger method IF delegate has been assigned and the method has been implemented into the target class
//do something and then send callback message
for (int i = 0; i < 10000; i++) {
printf("I'm busy, I'm doing something");
}
[delegate performSelector:@selector(delegateSentFeedback)];
}
}
The IF-Statement will make sure, that the delegate has been assigned from the calling class and that the calling class will implement the feedback method, named “delegateSentFeedback”.
Now, I move over to my viewController. I add a UIButton to start some process upon “TouchUpInside”. I connect it to an IBAction and end up with something like this:
- (IBAction)startProcessAction:(id)sender {
DelegateClass *class = [[DelegateClass alloc] init];
class.delegate = self; //IMPORTANT: if you don't assign the delegate, the feedback method will NOT be triggered!
[class doSomething]; //start process
[class release];
}
Btw, let’s not forget to #import “DelegateClass.h” into our ViewController’s .h file!
Last, but not least I implement the delegate feedback method, defined inside DelegateClass.h
- (void)delegateSentFeedback {
//the process is done. let's show a simple UIAlertView
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Success" message:@"The process is done! Delegate has successfully sent feedback" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
The feedback method will show an UIAlertView, once “doSomething” method inside DelegateClass.m has reached the bottom.
Piece of cake, right?
Feel welcome to grab the sample project here, play with it, add new feedback methods and most importantly HAVE FUN!
I’ve recently been working on an app for the iPad and ran into some strange issue, when using a modalViewController. In my case, I wanted the modalViewController to show up right after the app finished launching. I also had a pretty basic viewController setup, similar to the one you get when using the “View-based Application” template.
Inside my rootViewController I wanted to call some other viewController as modalViewController right after start up. It looked pretty much like this:
- (void)viewDidLoad {
[super viewDidLoad];
//instantiate otherViewController and call it as modalViewController
OtherViewController *viewController = [[OtherViewController alloc] initWithNibName:@"OtherWindow" bundle:nil];
self.otherViewController = viewController;
[viewController release];
[self presentModalViewController:otherViewController animated:YES];
}
Unfortunately, when I tested my app nothing happened. The reason why no errors or warnings were shown was simple: The code was fine.
So, what was the problem then? After doing some investigation I found out, that the modalViewController didn’t like being called directly from viewDidLoad. Instead I had to call it with just a tiny bit of a delay.
So, I added a new method to my class:
- (void)callOtherViewController {
if (otherViewController == nil) { //only allocate, if it has not been allocated yet
//instantiate otherViewController and call it as modalViewController
OtherViewController *viewController = [[OtherViewController alloc] initWithNibName:@"OtherWindow" bundle:nil];
self.otherViewController = viewController;
[viewController release];
}
[self presentModalViewController:otherViewController animated:YES];
}
Finally, I only had to call this method from viewDidLoad with the delay mentioned above, like this:
- (void)viewDidLoad {
[self performSelector:@selector(callOtherViewController) withObject:nil afterDelay:0.1f];
}
That did the trick!
UITableViews are awesome. They allow you to display a variety of information, can be customized extensively, take care of memory management and much much more. Since they only remember what’s currently on screen, getting a basic ToDo list up and running can be quite the challenging task for a rookie coder. In this post I’d like to show you how this can be achieved in just a few steps.
First, let’s get all the ingredients ready…
Basically, we need 3 things:
- UITableView to display stuff
- Data source array, that contains the stuff we want to display
- Dictionary, to keep track of what is checked / unchecked
On top of that, we also need:
- UITableViewCell to put inside our UITableView
- UILabel inside our UITableViewCell to display some text
- UIButton inside our UITableViewCell to check / uncheck this cell
@interface TableViewCellDemoViewController : UIViewController {
IBOutlet UITableView *tableView;
NSArray *dataSourceArray;
NSMutableDictionary *checkedDictionary;
}
- (void)checkButtonTapped:(id)sender forCellWithLabel:(NSString *)text;
@property (nonatomic, retain) IBOutlet UITableView *tableView;
@property (nonatomic, retain) NSArray *dataSourceArray;
@property (nonatomic, retain) NSMutableDictionary *checkedDictionary;
@end
Inside the .m file I need to properly create the cells…
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
BaseCell *cell = (BaseCell *)[_tableView dequeueReusableCellWithIdentifier:@"BaseCell"];
if (!cell) {
cell = [[[NSBundle mainBundle] loadNibNamed:@"BaseCell" owner:self options:nil] lastObject];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.cellLabel.backgroundColor = [UIColor clearColor];
cell.cellLabel.font = [UIFont fontWithName:@"Helvetica" size:17];
cell.cellLabel.text = [dataSourceArray objectAtIndex:indexPath.row];
cell.parentViewController = self;
//let's use the cellLabel.text as key inside our checkedDictionary. IMPORTANT: This only works, if information inside cellLabel.text is unique for every row. If you have the same text twice in your datasourceArray, this will cause to some unexpected behavior.
NSString *key = cell.cellLabel.text;
BOOL checked = [[checkedDictionary objectForKey:key]boolValue];
[cell.checkButton setBackgroundImage:(checked) ? [UIImage imageNamed:@"checked.png"] : [UIImage imageNamed:@"unchecked.png"] forState:UIControlStateNormal];
return (UITableViewCell *)cell;
}
…and add a method, that will get called, if I tap on the check / uncheck button
- (void)checkButtonTapped:(id)sender forCellWithLabel:(NSString *)text {
UIButton *button = (UIButton *)sender;
//like above, we're using the information inside the label (text) as unique identifier = key
NSString *key = text;
BOOL checked = [[checkedDictionary objectForKey:key]boolValue];
//depending on whether the row has been checked before or not, we write YES / NO to our checkedDictionary
if (checked) {
[checkedDictionary setValue:@"NO" forKey:text];
checked = NO;
}
else {
[checkedDictionary setValue:@"YES" forKey:text];
checked = YES;
}
//finally set the new button background image
[button setBackgroundImage:(checked) ? [UIImage imageNamed:@"checked.png"] : [UIImage imageNamed:@"unchecked.png"] forState:UIControlStateNormal];
}
Finally, I create an empty user interface for Interface Builder and add a UITableViewCell object to it. I also create a new class, named “BaseCell” and set it as custom class for my UITableViewCell.
After adding a UILabel and UIButton object to my cell, I properly link everything and my BaseCell.h file will look like this:
#import <UIKit/UIKit.h>
@interface BaseCell : UITableViewCell {
IBOutlet UILabel *cellLabel;
IBOutlet UIButton *checkButton;
UIViewController *parentViewController;
}
- (IBAction)checkButtonAction:(id)sender;
@property (assign) IBOutlet UILabel *cellLabel;
@property (assign) IBOutlet UIButton *checkButton;
@property (assign) UIViewController *parentViewController;
@end
Last but not least, the .m file will make a call to “checkButtonTapped” method inside my UITableViewController’s class like this:
- (IBAction)checkButtonAction:(id)sender {
if (self.parentViewController) { //call method inside TableViewCellDemoViewController and submit the sender object (= button) and the text inside the label. We will use the text as unique identifier (=key) inside our checkedDictionary (also part of TableViewCellDemoViewController class)
[self.parentViewController performSelector:@selector(checkButtonTapped: forCellWithLabel:) withObject:sender withObject:[self.cellLabel text]];
}
}
If we break down the steps, our app will basically perform the following tasks:
-
Set up custom UITableView cells, containing a UILabel and UIButton
-
If user taps on the button, make a call from BaseCell class to TableViewCellDemoViewController class and submit both the sender button and text inside the UILabel
-
Use the UILabel text to look up a matching key inside our “checkedDictionary” object
-
Change background image of the UIButton, depending on whether (3) exists and whether it is YES or NO.
We’ve just finished designing / coding the new codingsessions website. Besides the new look, you’ll also find an updated list of available sessions. We’d really like to thank all our customers out there for turning this service into a success.
Those of you, who’ve never heard about Codingsessions: It’s a service for junior iOS developers to quickly develop some basic skills and use that knowledge to write their own applications.
If you have any questions about www.codingsessions.com, please feel welcome to contact us any time.
Thank you very much!
In case you’ve ever played around with a sectioned UITableView and tried to delete rows from it, you’ve most likely experienced a crash once in a while.
When you read the console log, most of the time you find an entry similar to this one:
*** Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘Invalid update: invalid number of sections. The number of sections contained in the table view after the update (0) must be equal to the number of sections contained in the table view before the update (1), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).’
Usually, your code for deleting stuff from your UITableView will look like this:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) { //let's delete something from our table
//IMPORTANT - modify data source BEFORE deleting rows from the table
[mySourceArray removeObjectAtIndex:indexPath.row]; //we assume, that mySourceArray is a NSMutableArray we use as our data source
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
}
Now, things can get a bit more complicated when using a sectioned UITableView. In my case, I’ve been using a NSMutableArray (filled with NSDictionaries) as data source.
It looked like this:
NSMutableArray ->
……….-> NSDictionary:
………………..-> “headerTitle”: “A”
………………..-> “rowValue”: “RowValue1″, “RowValue2″, etc…
………..-> NSDictionary:
…………………-> “headerTitle”: “B”
…………………-> “rowValue”: “RowValue1″, “RowValue2″, etc…
etc,…
As you can see, a datasource like that is just a bit more complex than values directly inside a NSMutableArray.
In order to avoid the crash mentioned above, I had to make sure, that I only deleted rows from my table, when there were still OTHER rows left inside the same section. In case the row I wanted to remove was the last one within a section, I had to remove the entire section. Btw, that’s exactly what the crash log said
Here is my method again, this time with the needed modification:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) { //let's delete something from our tableNSDictionary *dict = [mySourceArray objectAtIndex:indexPath.section];
NSArray *array = [dict objectForKey:@"rowValue"];
int arrayCount = [array count]; //get the current count of the array
if(arrayCount == 1){ //we are removing the last item from this section - remove the ENTIRE section!
[tableView beginUpdates];
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
}
else { //just delete the row, since we've got more rows left inside this section
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];}
}
Hope, you find this helpful. Feel welcome to leave a comment.
Cheers.
Hello everyone,
I’ve recently been playing with UILocalNotifications and really love them. When Apple first introduced Remote notifications (aka. push notifications), they were a real treat to both developers and users. Applications could receive information, even though they were not running at that time. However, it was (still is) a bit painful to implement them. You need to create certificates, run your own server or sign up with some company, that does the message forwarding for you.
Now, in case you only want to remind your users of something or allow them to schedule an event inside your app, UILocalNotifications are what you’ve been looking for. They are quite easy to set up and you don’t have to hassle with certificates, etc…
I’ve decided to release a little class, that easily allows you to schedule an event, delete a specific event or simply delete all events.
Ok, here we go:
My UILocalNotificaionsWrapper class consists of 3 different methods:
+ (void)addLocalNotification:(int)year:(int)month:(int)day:(int)hours:(int)minutes:(int)seconds:(NSString*)alertSoundName:(NSString*)alertBody:(NSString*)actionButtonTitle:(NSString*)notificationID; + (void)cancelLocalNotification:(NSString*)notificationID; + (void)cancelAllLocalNotifications;
The first method is the most complex one and allows you to submit several different values to customize your notification event:
- year (integer) – e.g. 2011
- month (integer) – e.g. 4 (April)
- day (integer) – e.g. 29
- hours (integer) – e.g 10 (= 10 am) – use 22 (military time) for 10 pm
- seconds (integer) – e.g. 0
- alertSoundName (NSString) – . e.g. nil to use custom sound name or @”NameOfTheSoundInsideYourAppBundle.wav”
- alertBody (NSString) – e.g. @”This is a LocalNotification”
- actionButtonTitle (NSString) – e.g. @”view” – This will appear on the button, when your local notification fires
- notificationID (NSString) – e.g. @”12345″ (has to be unique. you could keep track of your IDs inside an array, dictionary or SQLite database
Now you can simply make a call like this to schedule a new event:
[UILocalNotificationWrapper addLocalNotification:2011 :4 :4 :19 :17 :0 :nil :@"This is a LocalNotification!":@"View":@"12345"];
In order to delete the notification you just scheduled, you could make a call like this:
[UILocalNotificationWrapper cancelLocalNotification:@"12345"];
Finally, to delete all scheduled notifications:
[UILocalNotificationWrapper cancelAllLocalNotifications];
One more thing…
Don’t forget to add #import “UILocalNotificationWrapper.h” inside the class you are making the calls from and always test on your device (UILocalNotifications don’t work on the simulator)
Grab the source file here and enjoy
Feedback always welcome.
Sometimes, when you’re in the process of developing a new app, you might want to spice up your interface design with a little eye candy animation. Things like that are not that important to the overall functionality of your app, but they are a real treat to general usability. Even though your users might not directly notice them, they will definitely appreciate smooth transitions on a subconscious level.
Ok, let’s get to it. There are many (and also very advanced) ways to achieve animation within your app. This time I’d like to show you one of my favorite solutions, which is also the easiest one to implement.
Basic code:
CGContextRef context = UIGraphicsGetCurrentContext(); [UIView beginAnimations:nil context:context]; [UIView setAnimationCurve:UIViewAnimationCurveLinear]; //[UIView setAnimationDidStopSelector:@selector(callFunctionAfterAnimation)]; //this would call a method named - (void)callFunctionAfterAnimation; inside this class [UIView setAnimationDuration: .5]; [UIView setAnimationDelegate: self]; //animation goes here [UIView commitAnimations];
The beauty about these couple lines of code is, that you can pretty much put them into any method and they will just work. Before we take a look at some ways to implement this, let’s explore the above code a bit more in detail.
In line 3, you can set the animation curve. This will affect the way, your animation will perform. There are several different animation curve settings available:
UIViewAnimationCurveLinear UIViewAnimationCurveEaseIn UIViewAnimationCurveEaseInOut UIViewAnimationCurveEaseOut
I suggest, you try all available options and find out the difference for yourself. Usually “UIViewAnimationCurveLinear” is a good choice, but it really depends on the case. In order to see all available options inside XCode, you type UIViewAnimationCurve and then push ESC on your keyboard. XCode will then show you a list and you can simply pick the animation curve you want to try.
Line 4 (commented out above) allows you to call a method inside the same class, right after the animation has finished. This can be useful at times, when you want/need to trigger something after the animation has stopped.
In line 5, we set the duration of our animation (in seconds): 1.0 = 1 second, 0.5 or .5 = 1/2 a second.
Finally in line 7, we tell XCode what we want to animate. Let’s take a look at some examples:
Example 1:
In our class, we have a UIButton, named “OurButton”. If we want this button to become invisible, we simply call:
OurButton.alpha = 0.0;
Example 2:
Let’s say, we want to move our button from X:0, Y:0 to X:50, Y:50. So, we call:
OurButton.frame = CGRectMake(50, 50, OurButton.frame.size.width, OurButton.frame.size.height);
Example 3:
Move our button to X:50, Y:50 and also resize it to 100 (width), 100 (height). Let’s call:
OurButton.frame = CGRectMake(50, 50, 100, 100);
It’s really simple as that
Play around with different object properties and you will soon learn the advantages/limits.
One more thing…Wrappers ![]()
I hope you agree, that the above way to animate objects on your screen is quite simple to implement and use. However, if you plan on animating several different objects inside your class, I recommend, that you create one or more wrappers if possible.
Let me show you an example:
Let’s pretend, that we frequently want to turn buttons inside our class visible/invisible. Instead of writing our animation code again and again, whenever we want to change the alpha value of one of our buttons, we create the following wrapper:
- (void)setButtonVisibility:(UIButton*)theButton:(BOOL)visible {
CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
//[UIView setAnimationDidStopSelector:@selector(callFunctionAfterAnimation)]; //this would call a method named - (void)callFunctionAfterAnimation; inside this class
[UIView setAnimationDuration: .5];
[UIView setAnimationDelegate: self];
theButton.alpha = (visible) ? 1.0 : 0.0;
[UIView commitAnimations];
}
To make a button, named “OurButton”, invisible, we’d simply call:
[self setButtonVisibility:OurButton:NO];
Piece of cake, right?
Good luck and let me know, if you run into any problems.
We already blogged about our live 1 on 1 coding sessions here.
If you’re interested in this service, feel welcome to visit our brand new website: www.codingsessions.com.
There you’ll find lots of detailed information on available classes, pricing, etc…
If you have questions, please do not hesitate to contact us any time.
Some time ago, we announced our 1 on 1 coding sessions. Developers all over the world have taken interest in our live tutorials and given us great feedback.
If you’re new to iPhone/iPod Touch or iPad development and you have a hard time getting started, feel welcome to contact us and we will do our very best to schedule an appointment asap.
Each live session usually lasts for one hour (give or take – we are definitely not watching the clock and we definitely don’t mind teaching a bit longer, if needed) and costs 99 USD (payment via paypal).
The great thing about this service is, that you can ask questions any time and get an immediate answer. This way, you learn much faster and get a better understanding of things that are going on in your code.
As of now, we can offer the following sessions:
- Introduction to XCode and Interface Builder
- UIViewController programming
- UIKit programming (UIButton, UITableView, UIView, UIImageView, UILabel, etc…)
- SQLite database programming
- APNS – Apple push notification service
- iOS <-> server communication programming
We also offer you to do a free screen recording of every session, so you can watch it over and over again at no extra cost.
All you need to get started, is an iChat account and contact us, so we can schedule an appointment.
Sessions are available in both english and german language.
If you have any questions, feel welcome to contact our support.
In my previous blog entry I wrote about Creating a multi page PDF document on iPhone/iPod Touch/iPad with encryption.
One of my readers described a problem, that caused his newly created PDF file to contain strange/weird characters. He actually tried to open an existing PDF document and added a watermark image to every single page.
After doing some research on the internet, I found out, that this issue is quite common. In fact, it has been around for quite some time (2 + years – give or take). Unfortunately, Apple hasn’t yet introduced a fix.
So, I did some more digging and read up on 3rd party libraries. Using those libraries, you can actually create/modify a PDF document without the above issues. Bad news is, that Apple might not approve your app. It’s really up to every single developer, whether he/she wants to go for a solution like that or not. Since I can not simply ask Apple, I decided to rule out that option.
So, what other solutions are left? Well, it certainly depends on the case, but in my and my reader’s situation, the following hack worked:
- Open the PDF-document
- Render it into an offscreen UIView
- Create a new PDF document
- Paste the image and add additional information (text/pictures)
- Save the new PDF document.
There is of course one major downside. The new PDF document no longer contains the real text, but an image displaying the text. This might turn into a problem, if zooming or copy/paste actions are needed later. However, for just reading, this works great.
For those, who want to see this in action, I’ve uploaded a demo project. Feel welcome to grab it here.
