Disable copy in UIWebView

Great answer from Zubaba at Stackoverflow. I’m using a webView to display colored and bolded text and I had the same problem. I put his solution into a method and call it just after I initialize the webView. I don’t seem to need the delegate.


// This is the end of the method where I initialize the web view
    self.textView = [[UIWebView alloc] initWithFrame:textFrame];
    [self longPress:self.textView];

// This is the method that I added
- (void)longPress:(UIView *)webView {    
    UILongPressGestureRecognizer* longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress)];
    
// Making sure the allowable movement isn't too narrow
    longPress.allowableMovement=100; 
// This is important - the duration must be long enough to allow taps but not longer than the period in which the scroll view opens the magnifying glass
    longPress.minimumPressDuration=0.3;

    longPress.delaysTouchesBegan=YES;
    longPress.delaysTouchesEnded=YES;
    
    longPress.cancelsTouchesInView=YES; // That's when we tell the gesture recognizer to block the gestures we want
    
    [webView addGestureRecognizer:longPress]; // Add the gesture recognizer to the view and scroll view then release
    [webView addGestureRecognizer:longPress];
}

// I just need this for the selector in the gesture recognizer.
- (void)handleLongPress {
        
}

Turning the complier up to 11

I was tracking down a crash caused by memory not being released and I thought it would be helpful to turn on all the compiler options, thinking that maybe it would point to something I did wrong. It didn’t help, but it did generate around a thousand warnings. Only two were actual bugs in the code. Most of the rest were conversion warnings. The most common were defining variables as NSInteger when the method that used them was expecting a CGFloat. Second most common were things like this:


return [[[self fetchedResultsControllerForTableView:tableView] sections] count];

ObjectAtIndex is an unsigned integer so and count is an NSInteger so to get rid of the warning I just cast count to NSUInteger.

return [[[[self fetchedResultsControllerForTableView:tableView] sections] objectAtIndex:(NSUInteger)section] name];

And this


return [[[[self fetchedResultsControllerForTableView:tableView] sections] objectAtIndex:section] numberOfObjects];

which should return an NSInteger rather than an unsigned NSUInteger so here I cast the return value.


 return (NSInteger)[[[[self fetchedResultsControllerForTableView:tableView] sections] objectAtIndex:(NSUInteger)section] numberOfObjects];

Second most common warnings were for unused variables. Since I use the same code for 18 different games I have if statements that determine what the locations of buttons on the screen. Many of the buttons are not used in all of the games so the compiler gives an unused variable warning. To get rid of the warning, I use this line after the last redefinition of the variable. Just so I remember that I’m using it I put it just before I use the “unused” variable. The compiler still compiles correctly when the button is needed, it just doesn’t throw the warning.


  #pragma unused(showHideButtonX)
  #pragma unused(showHideButtonY)
  if ( DISPLAY_SHOWHIDE_BUTTON ) {
      CGRect showHideFrame = CGRectMake(showHideButtonX, showHideButtonY, buttonsSize, buttonsSize);

Using an NSDictionary as an argument to a method.

I have a series of animated scenes that I am adding as rewards to my apps. The animation takes place on three layers at the moment, but I keep adding layers and every time I do that I have to change my method definition and all the method calls. I decided to put all my arguments into an NSDictionary so that I don’t have to keep changing the method.

This is the original code.


NSInteger numRewards = 10;
NSInteger randomReward = arc4random() % numRewards;

if (randomReward == 0 ) {
    UIColor *backgroundColor = [UIColor colorWithRed:102/255.0 green:204/255.0 blue:255/255.0 alpha:1];
    [self animatedReward:@"Boat"
     withForegroundCount:4
    withMovingPartsCount:6
     withBackgroundColor:backgroundColor];
}

Every time I add a layer I need to change the method. And all the method calls. By making it an NSDictionary, I can play around with layers, and anything else I want to add, without having to change the method each time.


NSInteger numRewards = 10;
NSInteger randomReward = arc4random() % numRewards;

if (randomReward == 0 ) {
    UIColor *backgroundColor = [UIColor colorWithRed:102/255.0 green:204/255.0 blue:255/255.0 alpha:1];
    NSDictionary *partsCounts = [NSDictionary dictionaryWithObjectsAndKeys:
                                 [NSNumber numberWithInt:4],@"foreground",
                                 [NSNumber numberWithInt:3],@"moving", nil];
    NSLog(@"Parts counts%i",partsCount);
    [self animatedReward:@"Boat"
             partsCounts:partsCounts
     withBackgroundColor:backgroundColor];
}

The method is declared as:


- (void)animatedReward:(NSString *)scene
           partsCounts:(NSDictionary)partsCounts
   withBackgroundColor:(UIColor *)backgroundColor;

Notice that the number of images in each layer are stored as NSNumbers in the dictionary. That’s because NSIntegers aren’t objects. To get the integers back out of the dictionary, you use this line.


    NSInteger foregroundCount = [[partsCounts objectForKey:@"foreground"]  integerValue];

Using an animated gif in iOS

Kite gif

You can’t use gif’s in iOS but you can do the same thing with a series of png’s. Here’s the code that I use to put a kite on the screen and then animate the tail.

- (void)animatedReward {
    UIImageView *staticView = [[UIImageView alloc] initWithFrame:self.view.frame];
    staticView.image = [UIImage imageNamed:@"kite.png"];
    [self.view addSubview:staticView];
    
    NSArray *imagesArray = [NSArray arrayWithObjects:[UIImage imageNamed:@"kite-tail1.png"], [UIImage imageNamed:@"kite-tail2.png"], [UIImage imageNamed:@"kite-tail3.png"], [UIImage imageNamed:@"kite-tail2.png"], [UIImage imageNamed:@"kite-tail1.png"], nil];
   
    UIImageView *animatedView = [[UIImageView alloc] initWithFrame:staticView.frame];
    
    animatedView.animationImages = imagesArray;
    animatedView.animationDuration = 1;
    [animatedView startAnimating];
    [self.view addSubview:animatedView];
}

Updating apps in iOS – Icons for Retina Display

The Apple documents on icon sizes is a bit out of date. It does not include the icon for the new retina iPad. And it is not updated for the new 1024×1024 iTunesArtwork requirement.

You need to include a new file that is 144×144 pixels and call it ‘Icon-72@2x.png’.

Then add it to your icons list in the Info.plist file.

Since I have lots of apps, I edited the Info.plist files in BBEdit and cleaned out all of the old icon files. You can also edit them in XCode by right-clicking on the Info.plist and choosing ‘Open As-Source Code’. The original files had the icon information between the ${EXECUTABLE_NAME} and CFBundleIdentifier keys so the new file looks like this.


  <string>${EXECUTABLE_NAME}</string>
  <key>CFBundleIconFiles</key>
  <array>
  <string>Icon.png</string>
  <string>Icon@2x.png</string>
  <string>Icon-72.png</string>
  <string>Icon-72@2x.png</string>
  <string>Icon-Small-50.png</string>
  <string>Icon-Small.png</string>
  <string>Icon-Small@2x.png</string>
  </array>
  <key>CFBundleIdentifier</key>

XCode will use the files to populate the icon display in the summary view, so you can check to see if you did everything correctly. If no icon shows up, make sure they are associated with the app and then drag the icon to the appropriate empty place in the summary. You should get an error message telling you why the icon is not appropriate. Usually it’s a size issue. If it fills in the spot, you probably have a naming issue.

Icon is 57×57 and Icon-Small is 29×29. The rest are obvious from the naming convention. The rest of the sizes are listed in the document, referenced below, along with their intended use.

According to the Apple document Core Foundation Keys you shouldn’t append the .png extension so that the system will automatically use the @2x version when appropriate. However, this doesn’t work for me.

XCode 4.5 doesn’t support any version of iOS before 4.3 so do not use CFBundleIconFile.

You also need to include a file called iTunesArtwork and iTunesArtwork@2x (no .png extension) in your application bundle that are 512×512 and 1024×1024 pixels respectively. Do not list them in the Info.plist file.

Two posts with more info Jared Sinclair and Peter Levine