现在的位置: 首页 > 综合 > 正文

Custom Input View Tutorial

2013年09月16日 ⁄ 综合 ⁄ 共 9476字 ⁄ 字号 评论关闭

This is the third part of a three part series to help get iPhone
Developers up-to-speed with iPad development by focusing on three of the
most interesting new classes (at least to me): UISplitView,
UIPopoverController, and Custom Input Views.

In the first part of the series

,
we made an app with a split view that displays a list of monsters on
the left side, details on the selected monster on the right side.

In the second part of the series

, we added a popover view to allow changing of colors.

In this part, we’re going to add a custom input view (or “keyboard”)
to allow selecting your favorite way to kill these monsters.

We’ll start out with where we left off the project last time, so grab a copy

if you don’t have it already.

Custom Input View Overview

To display a custom input view, what you need to depends if you’re using a UITextView or UITextField, or something else.

If you’re using a UITextView or UITextField, you’re in luck. All you
need to do is assign a custom view to the “inputView” property.

However, if you’re using something else (like we are in this case
with our UIImageView), you’ll need to make a custom subclass of the view
you’re using so that you can override the inputView getter and return
your own custom view.

So we have two classes to write: a custom UIImageView, and our view
controller for our custom input view. Let’s start with our custom
input view.

Creating a Custom Input View

Go to “File/New File…”, choose “UIViewController subclass”, make sure
“Targeted for iPad” and “With XIB for user interface” are checked but
“UITableViewController subclass” is NOT checked, and click Next. Name
the class “WeaponInputViewController”, and click Finish.

Open up WeaponInputViewController.xib. By default it makes the view
the size of the iPad screen, but we want something much smaller in
height. Interface Builder won’t let us resize the default view, so
delete the default view and add a new one, with width 768 and height
110. Also don’t forget to control-drag from “File’s Owner” to View to
reconnect the default view for the owner.

Update:

: Jerry Beers

from the comments section below pointed out that instead of
deleting/re-adding, you can just turn off the simulated UI elements, and
then you can resize at will. Thanks Jerry!

Create 5 70×70 buttons along the left side of the view as follows:

Custom Input View Layout

Then set tye type of each Button from “Rounded Rect” to “Custom” and set the image for each button to a weapon, as shown:

Custom Input View Layout With Button Images

Now let’s fill in the class definition. Replace WeaponInputViewController.h with the following:

#import <UIKit/UIKit.h>



#import "Monster.h"



 
@protocol


 WeaponInputControllerDelegate
-


 (


void


)


weaponTapped:


(


Weapon)


weapon;
-


 (


void


)


doneTapped;
@end



 
@interface


 WeaponInputViewController :


 UIViewController {



    id<WeaponInputControllerDelegate> _delegate;
}



 
@property


 (


nonatomic, assign)


 id<WeaponInputControllerDelegate> delegate;
 
-


 (


IBAction)


blowgunTapped:


(


id


)


sender;
-


 (


IBAction)


fireTapped:


(


id


)


sender;
-


 (


IBAction)


ninjastarTapped:


(


id


)


sender;
-


 (


IBAction)


smokeTapped:


(


id


)


sender;
-


 (


IBAction)


swordTapped:


(


id


)


sender;
-


 (


IBAction)


doneTapped:


(


id


)


sender;
 
@end


This should all look familiar – we just set up a protocol so we can
notify a listener when a weapon is selected (or the done button), and
set up a bunch of action methods.

So let’s hook up those action methods now. Go back to
WeaponInputViewController.xib and control-drag from each button to
“File’s Owner and hook the button up to the appropriate action method.

Then wrap it up by replacing WeaponInputViewController.m with the following:

#import "WeaponInputViewController.h"



 
@implementation


 WeaponInputViewController
@synthesize


 delegate =


 _delegate;
 
-


 (


BOOL


)


shouldAutorotateToInterfaceOrientation:



    (


UIInterfaceOrientation)


interfaceOrientation {



    return


 YES


;
}



 
-


 (


void


)


didReceiveMemoryWarning {



    [


super didReceiveMemoryWarning]


;
}



 
-


 (


void


)


viewDidUnload {



    [


super viewDidUnload]


;
}



 
-


 (


void


)


dealloc {



    self.delegate =


 nil


;
    [


super dealloc]


;
}



 
-


 (


IBAction)


blowgunTapped:


(


id


)


sender {



    if


 (


_delegate !=


 nil


)


 {



        [


_delegate weaponTapped:


Blowgun]


;
    }



}



 
-


 (


IBAction)


fireTapped:


(


id


)


sender {



    if


 (


_delegate !=


 nil


)


 {



        [


_delegate weaponTapped:


Fire]


;
    }



}



 
-


 (


IBAction)


ninjastarTapped:


(


id


)


sender {



    if


 (


_delegate !=


 nil


)


 {



        [


_delegate weaponTapped:


NinjaStar]


;
    }



}



 
-


 (


IBAction)


smokeTapped:


(


id


)


sender {



    if


 (


_delegate !=


 nil


)


 {



        [


_delegate weaponTapped:


Smoke]


;
    }



}



 
-


 (


IBAction)


swordTapped:


(


id


)


sender {



    if


 (


_delegate !=


 nil


)


 {



        [


_delegate weaponTapped:


Sword]


;
    }



}



 
-


 (


IBAction)


doneTapped:


(


id


)


sender {



    if


 (


_delegate !=


 nil


)


 {



        [


_delegate doneTapped]


;
    }



}



 
@end


As you can see, nothing too exciting or fancy here. All we do is
notify our delegate when the various buttons get tapped. In fact, you
could say that this view has no knowledge that it’s even being used as a
custom input view controller!

Custom View and Custom Input Controller

We want it to work so that when you tap the image, it brings up the
input controller we just made. So like we discussed above, we’ll have
to make a subclass of UIImageView so we can return our input view to
display.

While we’re at it, we’ll have to make a few oher tweaks to the image
view. By default, UIImageViews don’t accept input and don’t become
responders to input events, so we’ll have to enable support for that.

So let’s get started. Go to “File/New File…”, choose “Objective-C
subclass”, make sure “Subclass of NSObject” is selected, and click Next.
Name the class “WeaponSelector”, and click Finish.

#import <Foundation/Foundation.h>



#import "WeaponInputViewController.h"



 
@protocol


 WeaponSelectorDelegate
-


 (


void


)


weaponChanged:


(


Weapon)


weapon;
@end



 
@interface


 WeaponSelector :


 UIImageView <WeaponInputControllerDelegate> {



    WeaponInputViewController *


_weaponInputView;
    Weapon _weapon;
    id<WeaponSelectorDelegate> _delegate;
}



 
@property


 (


nonatomic, retain)


 WeaponInputViewController *


weaponInputView;
@property


 (


nonatomic, assign)


 Weapon weapon;
@property


 (


nonatomic, assign)


 IBOutlet id<WeaponSelectorDelegate> delegate;
 
-


 (


UIView *


)


inputView;
 
@end


Ok let’s explain this a bit. First, we create a delegate so that we
can notify a listener when the weapon is changed. Note we could have
possibly re-used the WeaponInputControllerDelegate for this, but it
isn’t as clean of a design because it muddles the dependencies.

Next, we declare this class as a WeaponInputControllerDelegate since
we want to know when the user selects a button in the input view. We
store the current selected weapon, and a pointer to the delegate to
notify when it changes. Note we mark the delegate as an IBOutlet so we
can assign it from Interface Builder later.

Finally, we declare a prototype for the override for the getter for the inputView that this class will contain.

Switch over to WeaponSelector.m and add in the code below. We’re
going to split it into sections so we can explain it part by part.

#import "WeaponSelector.h"



 
@implementation


 WeaponSelector
@synthesize


 weaponInputView =


 _weaponInputView;
@synthesize


 weapon =


 _weapon;
@synthesize


 delegate =


 _delegate;

We start out by adding our synthesize statement like normal.

-


 (


void


)


setWeapon:


(


Weapon)


weapon {



    _weapon =


 _weapon;
    switch


 (


weapon)


 {



        case


 Blowgun:



            self.image =


 [


UIImage imageNamed:


@


"blowgun.jpg"


]


;
            break


;
        case


 Fire:



            self.image =


 [


UIImage imageNamed:


@


"fire.jpg"


]


;
            break


;
        case


 NinjaStar:



            self.image =


 [


UIImage imageNamed:


@


"ninjastar.jpg"


]


;
            break


;
        case


 Smoke:



            self.image =


 [


UIImage imageNamed:


@


"smoke.jpg"


]


;
            break


;
        case


 Sword:



            self.image =


 [


UIImage imageNamed:


@


"sword.jpg"


]


;
            break


;            
        default


:



            break


;
    }



}


We then override the weapon property’s setter so that we can set the image appropriately based on the weapon choice.

-


 (


UIView *


)


inputView {



    if


 (


_weaponInputView ==


 nil


)


 {



        self.weaponInputView =


 [


[


[


WeaponInputViewController alloc]


 
            initWithNibName:


@


"WeaponInputViewController"


 
            bundle:


[


NSBundle





 mainBundle]


]


 autorelease]


;
        _weaponInputView.delegate =


 self;
    }



    return


 _weaponInputView.view;
}


Here’s the most important part – we override inputView to return our
own custom view. We just allocate the view controller if we haven’t
already, set ourselves as the delegate, and return the view.

-


 (


BOOL


)


canBecomeFirstResponder {



    return


 YES


;
}



 
-


 (


void


)


touchesBegan:


(


NSSet





 *


)


touches withEvent:


(


UIEvent *


)


event {



    [


self becomeFirstResponder]


;
}


We override the method to say that yes we CAN become the first
responder to input events, and upon a touch we set ourselves as the
first responder. This has the effect of bringing up our input view.

-


 (


void


)


weaponTapped:


(


Weapon)


weapon {



    self.weapon =


 weapon;
    [


self resignFirstResponder]


;
    [


_delegate weaponChanged:


weapon]


;
}



 
-


 (


void


)


doneTapped {



    [


self resignFirstResponder]


;
}


Here we implement the WeaponInputViewControllerDelegate methods. In
the case where a weapon is tapped, we set our weapon to the new weapon
(through our property, hence calling our setter override and setting the
image as well). We then resign as the first responder, which has the
effect of removing our custom input view. Finally we notify our
delegate that the weapon has changed so it can take appropriate action.

If done is tapped, we just call resignFirstResponder to remove the input view.

-


 (


void


)


 dealloc
{



    self.weaponInputView =


 nil


;    
    [


super dealloc]


;
}



 
@end


At the end we add our cleanup code as usual.

Phew! We’ve done a lot here. Don’t worry we’re almost done – all we
have to do now is replace the plain vanilla input view with our custom
view, and handle the weaponChanged callback!

Integrating into the Right View

Double click RightViewController.xib, and select the UIImage next to
“Preferred way to kill”. Go to the fourth tab in the inspector and
change the class from UIImageView to “WeaponSelector”. Also, in the
first tab in the inspector check “User Interaction Enabled.”

Inspector Class Settings

Inspector User Interaction Enabled Setting

Finally, control drag from WeaponSelector to “File’s Owner”, and set it as the delegate.

Now make the following changes to RightViewController.h:

// In import section



#import "WeaponSelector.h"



 
// Modify class declaration



@interface


 RightViewController :


 UIViewController <MonsterSelectionDelegate, 
    UISplitViewControllerDelegate, ColorPickerDelegate, WeaponSelectorDelegate> {



 
// Modify type of weaponView variable



WeaponSelector *


_weaponView;
 
// Modify type of weaponView property



@property


 (


nonatomic, retain)


 IBOutlet WeaponSelector *


weaponView;

And make the following changes to RightViewController.m:

// Modify refresh as the following



-


 (


void


)


refresh {



    _nameLabel.text =


 _monster.name;
    _iconView.image =


 [


UIImage imageNamed:


_monster.iconName]


;
    _descrLabel.text =


 _monster.descr;
    _weaponView.weapon =


 _monster.preferredWayToKill;
}



 
// Add the following new method



-


 (


void


)


weaponChanged:


(


Weapon)


weapon {



    if


 (


_popover !=


 nil


)


 {



        [


_popover dismissPopoverAnimated:


YES


]


;
    }



    _monster.preferredWayToKill =


 weapon;
}


Compile and run, and if all works well, you shoudl be able to tap the icon to bring up a custom image view!

Custom Input View Screenshot

Show Me the Code!

Here’s a copy of all of the code we’ve developed so far

.

That’s all for the iPad for iPhone Developers 101 series for now.
However soon I will write an article about how to port iPhone apps to
the iPad!

转载自:www.raywenderlich.com

抱歉!评论已关闭.