Regarding the Silver Challenge


#1

Hi everyone,

I’m a bit of newbie at iOS programming. I got the Silver Challenge to work. Can you help me out and critique if you see potential bad habits?

Here are the relevant parts of the BNRAppDelegate.m:

[code]@interface BNRAppDelegate ()
{
CGFloat lastScale;
}
@property (strong, nonatomic) BNRHypnosisView *theBNRHypnosisView;

@end

@implementation BNRAppDelegate

  • (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    //Create CGRects for frames
    CGRect screenRect = self.window.bounds;
    CGRect bigRect = screenRect;
    bigRect.size.width *= 2.0;
    //bigRect.size.height *= 2.0;

    //Create a screen-sized scroll view and add it to the window
    UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:screenRect];

    UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(scale:)];

    //Create a screen-sized hypnosis view and add it to the scroll view
    //BNRHypnosisView *theBNRHypnosisView = [[BNRHypnosisView alloc] initWithFrame:screenRect];
    self.theBNRHypnosisView = [[BNRHypnosisView alloc] initWithFrame:screenRect];

    [scrollView addSubview:self.theBNRHypnosisView];
    [self.theBNRHypnosisView addGestureRecognizer:pinchRecognizer];

    //Tell the scroll view how big its content area is
    scrollView.contentSize = bigRect.size;

    //Forces scroll view to snap its viewing port to one of hte views
    scrollView.pagingEnabled = NO;
    [self.window addSubview:scrollView];

    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
    }
    [/code]

This is the selector method:

[code]- (void) scale: (id) sender {

if([(UIPinchGestureRecognizer*)sender state] == UIGestureRecognizerStateEnded)
{
    lastScale = 1.0;
    return;
}

CGFloat scale = 1.0 - (lastScale - [(UIPinchGestureRecognizer*)sender scale]);

CGAffineTransform currentTransform = [(UIPinchGestureRecognizer*)sender view].transform;
CGAffineTransform newTransform = CGAffineTransformScale(currentTransform, scale, scale);

[[(UIPinchGestureRecognizer*)sender view] setTransform:newTransform];

lastScale = [(UIPinchGestureRecognizer*)sender scale];

}[/code]


#2

I disregarded the whole UIPinchGesture-thing. Works as expected. Created a class extension with the hypnosisView property. then referring to this property where appropriate (self.hypnosisView). Setting the delegate (self.delegate = self) and then implementing the only method from the UIScrollViewDelegate protocol (viewForZoomingIn…)

[code]#import “BNRAppDelegate.h”
#import “BNRHypnosisView.h”
@interface BNRAppDelegate ()
@property (nonatomic, strong) BNRHypnosisView *hypnosisView;
@end

@implementation BNRAppDelegate

  • (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.

    // Create CGRects for frames
    CGRect screenRect = self.window.bounds;
    CGRect bigRect = screenRect;
    bigRect.size.width *= 2.0;
    // Create a screen-sized scroll view and add it to the window
    UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:screenRect];
    //Setting self as delegate of scrollview
    scrollView.delegate = self;
    scrollView.pagingEnabled = NO;
    [self.window addSubview:scrollView];
    // Create a screen-sized hypnosis view and add it to the scroll view, using the property
    self.hypnosisView = [[BNRHypnosisView alloc] initWithFrame:screenRect];
    [scrollView addSubview:self.hypnosisView];
    // Tell the scroll view how big its content area is
    scrollView.contentSize = screenRect.size;
    //Setting minimum and maximum zoomscale
    scrollView.maximumZoomScale = 3.0;
    scrollView.minimumZoomScale = 0.5;
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
    }
    [/code]

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { return self.hypnosisView; }


#3

I can’t get the scrolling to work if the tab bar controller is visible. When I remove the code for the tab bar, the scroll view work works great.

Are we supposed to get the scroll view working WITH the tab bar?


#4

The first solution is actually not the way the book intended, as it is not using delegation.
The beauty of delegation is, it is so simple.
Works much like listeners or decorators in JAVA.
I just removed the "anotherView altogether and:
Extended the delegate with an interface with the UIScrollViewDelegate protocol
@interface BNRAppDelegate() <UIScrollViewDelegate> @property BNRHypnosisView *hypnosisView; @end
Pointed the UIScrollView delegate to BNRAppDelegate

scrollView.contentSize = screenRect.size;
    
    scrollView.minimumZoomScale=1;
    scrollView.maximumZoomScale=6.0;
    scrollView.delegate = self;

And finally implemented the method that returns the BNRHypnosisView

  • (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
    {
    return _hypnosisView;
    }

#5

A tip on dealing with the typedef “id”: If you know what kind of typedef you’ll be dealing with when you create your method, you can actually change that “(id)” to the typedef you want. For example: [code]- (void) scale: (id) sender {

if([(UIPinchGestureRecognizer*)sender state] == UIGestureRecognizerStateEnded)
{
    lastScale = 1.0;
    return;
}
[/code]

You can actually change the “id” in the method definition to (UIPinchGestureRecognizer *). That way you don’t have to declare that typedef in your if/else statement. Like this: [code]- (void) scale: (UIPinchGestureRecognizer *)sender {

if(sender.state == UIGestureRecognizerStateEnded)
{
    lastScale = 1.0;
    return;
}
[/code]