How to change the frame of UIKeyBoard programmatically in iOS - iphone

Well, i have gone through some decent goggling before posting this question but was unsuccessful in finding the right answers.
I cant really explain my entire app scenario here as it is a bit complex to explain. So, let me make this question very very simple. How can i change the frame of the UIKeyBoard.i.e. I want the UIKeyBoard to rotate or translate 90 degrees upwards to support my view position.
Is there a way out for me?

You can't change the default keyboard. You can, however, create a custom UIView to be used as keyboard replacement by setting it as inputView on, for example, a UITextField.
While creating a custom keyboard takes a bit of time, it works well with older iOS versions (inputView on the UITextField is available in iOS 3.2 and later) and supports physical keyboards (the keyboard is hidden automatically if one is connected).
Here's some sample code to create a vertical keyboard:
Interface:
#import <UIKit/UIKit.h>
#interface CustomKeyboardView : UIView
#property (nonatomic, strong) UIView *innerInputView;
#property (nonatomic, strong) UIView *underlayingView;
- (id)initForUnderlayingView:(UIView*)underlayingView;
#end
Implementation:
#import "CustomKeyboardView.h"
#implementation CustomKeyboardView
#synthesize innerInputView=_innerInputView;
#synthesize underlayingView=_underlayingView;
- (id)initForUnderlayingView:(UIView*)underlayingView
{
// Init a CustomKeyboardView with the size of the underlying view
// You might want to set an autoresizingMask on the innerInputView.
self = [super initWithFrame:underlayingView.bounds];
if (self)
{
self.underlayingView = underlayingView;
// Create the UIView that will contain the actual keyboard
self.innerInputView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, underlayingView.bounds.size.height)];
// You would need to add your custom buttons to this view; for this example, it's just red
self.innerInputView.backgroundColor = [UIColor redColor];
[self addSubview:self.innerInputView];
}
return self;
}
-(id)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
// A hitTest is executed whenever the user touches this UIView or any of its subviews.
id hitTest = [super hitTest:point withEvent:event];
// Since we want to ignore any clicks on the "transparent" part (this view), we execute another hitTest on the underlying view.
if (hitTest == self)
{
return [self.underlayingView hitTest:point withEvent:nil];
}
return hitTest;
}
#end
Using the custom keyboard in some UIViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
CustomKeyboardView *customKeyboard = [[CustomKeyboardView alloc] initForUnderlayingView:self.view];
textField.inputView = customKeyboard;
}

Related

How to add a UIImage with UIGesture to most views in an application.

I want to make an Advertising banner in my app. A bit like iAd's.
I was going to make it by having a UIImage on the view then assigning the banner image. I would then add a touch gesture so the user could click it and go to another view in my app. I Know That I can do this on one view quite easily but I want this to be on most views in the app. Whats the best way for adding the banner to more than one view with out writing the same code more that once?
The below design shows the sort of banner im after.
Thanks
#import <UIKit/UIKit.h>
#class custom;
#protocol adDelegate
- (void)viewAd:(NSString *)adRate;
#end
#interface custom : UIView
#property (strong, nonatomic) UIImage *viewImage;
#property (assign) id <adDelegate> delegate;
#end
// Main class
#import "custom.h"
#implementation custom
#synthesize viewImage;
#synthesize delegate;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.image = viewImage;
[self addSubview:imageView];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if ((self = [super initWithCoder:aDecoder]))
{
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.delegate viewAd:#"view"];
}
You can Create a UIView Class and call it BannerView for instance.
// in the bannerView.h
#import <UIKit/UIKit.h>
#interface BannerView : UIView{
UIImageView* bannerImage;
}
#property(nonatomic,retain) UIImageView* bannerImage;
#end
//in the bannerView.m
#import "BannerView.h"
#implementation BannerView
#synthesize bannerImage;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
bannerImage=[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"banner-image.png"]];
bannerImage.frame=CGRectMake(0, 0, 320, 100);
[self addSubview:bannerImage];
// add a uibutton on top of the uiimageview and assign an action for it
// better than creating an action recogniser
UIButton* actionButton=[UIButton buttonWithType:UIButtonTypeCustom];
actionButton.frame=CGRectMake(0, 0, 320, 100);
[actionButton addTarget:self action:#selector(yourAction) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:actionButton];
}
-(void) yourAction{
// do what ever here like going to an other uiviewController as you mentionned
}
#end
Now you can call this view from any View Controller this way
BannerView* banner=[[BannerView alloc] initWithFrame:CGRectMake(0, 300, 320, 100)];
[self.view addSubview:banner];
Try creating a parent class from UIView where you do all the display and handling of the banner using your UIImageView and gesture recognizers. Then whichever views need this functionality, derive them from this parent class, and override default handling in method so that you can customize the behavior in your child class.
A few suggestions:
First, why not just use a UIButton instead of a UIImage with a Gesture? All you're really doing is replicating button functionality after all...
Second, I'd probably tackle the overall problem by creating a class that includes the UIButton, like so:
#interface YourSuperViewController : UIViewController
#property (nonatomic, strong) IBOutlet UIButton *adButton;
- (IBAction)adTouched:(id)sender;
#end
In the viewDidLoad for this class, create the button, and add it to the view, and add your ad-specific logic to the adTouched action.
Then create the rest of the views in your app as an instance of YourSuperViewController. Like so:
#interface SomeOtherViewController : YourSuperViewController
Now the SomeOtherViewController will auto-magically have the ad button and respond to a touch on it properly. Done!
What everyone else has said is the best way. If you need custom functionality, subclassing is probably the way to go.
I just wanted to add one pedantic thing. Its important to remember that a UIImage is not a view. There has never been a UIImage on the screen, ever. A UIImage is a model object. It is just a collection of data. A UIImageView is a view object and as such, a UIImageView can display itself on the screen.
This might seem overly pedantic and nitpicky, but its important to have these things sorted out in our heads in order to effectively use MVC (model, view, controller)

iphone paint on top / overlay with event pass through

I would like to be able to paint on top of my subviews, or in other words: have an overlay that does not block the events. So far I discovered:
- any instructions in drawRect are painted below subviews,
- putting a transparent UIView on top blocks events.
Is there another trick I can try?
Use a transparent UIView on top, and in IB uncheck "User Interaction Enabled" for that view, then input events will go down to your controls beneath it.
Or, in code do:
UIView *overlayView = [[UIView alloc] init...];
overlayView.userInteractionEnabled = NO;
To solve this you want to forward the hitTest events. Add the code below to your project, add a UIImageView to your interface, set its Class to ClickThroughImageView and connect the "onTopOf" outlet to whatever UIView is below the image.
The ClickThroughImageView.h file:
#import <Foundation/Foundation.h>
#interface ClickThroughImageView : UIImageView
{
IBOutlet UIView *onTopOf;
}
#property (nonatomic, retain) UIView *onTopOf;
#end
The ClickThroughImageView.m file
#import "ClickThroughImageView.h"
#implementation ClickThroughImageView : UIImageView
#synthesize onTopOf;
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
return [onTopOf hitTest:point withEvent:event];
}
#end

Adding an overlay to a grid tableView

I have a tableview which each row has 4 images. I have implemented a share option which will allow the user to select multiple images. How can I add an overlay or some kind of visual effect to show that the image is selected?
I would like to add some overlay to display that an image is selected, but How would this be done without adding a new set of subview for each thumbnail? And once that is done, how would the selection of the new views be linked back to the images behind them so that they can be added to an array?
Or is there an easier way to do this?
Thanks
Depending on how you're implementing this grid view, it might make sense to track all of the selecting and deselecting at that level.
As for the overlay, the quick and dirty way is to subclass UIImageView, add a BOOL property called selected. Then you can override the setter for selected and handle showing or hiding your overlay view.
Here's how I would setup my subclass. First the interface:
#interface SelectableImageView : UIImageView
#property (nonatomic, assign, getter = isSelected) BOOL selected;
#end
and the implementation...
#interface SelectableImageView ()
#property (nonatomic, retain) UIView *overlayView;
#end
#implementation SelectableImageView
#synthesize selected;
#synthesize overlayView;
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
overlayView = [[UIView alloc] initWithFrame:frame];
overlayView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.75];
overlayView.hidden = YES;
[self addSubview:overlayView];
}
return self;
}
- (void)setSelected:(BOOL)flag
{
selected = flag;
self.overlayView.hidden = !flag;
}
- (void)dealloc
{
[overlayView release], self.overlayView = nil;
[super dealloc];
}
#end

UIView background Color

I am trying to create a grid view by using a UITableView (my older questions have pointed me in this direction) and am currently setting up the views for the individual items. Having created a custom UITableViewCell that displays 3 items per row, I have decided to pull these items into a subclass of UIView called ItemView.
This ItemView will then be added as a subview to the custom UITableViewCell to display the grid. Anyway, I have managed to create the view and can get it to display a UILabel fine, however I am having trouble in changing ItemView to be transparent apart from the UIViews (Labels, buttons, images etc) within it. Here is my code for the UIView:
#import <UIKit/UIKit.h>
#interface ItemView : UIView {
}
#end
#import "ItemView.h"
#implementation ItemView
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
}
return self;
}
- (void)drawRect:(CGRect)rect {
[self setBackgroundColor:[UIColor lightGrayColor]];
}
- (void)dealloc {
[super dealloc];
}
#end
Where should I set the background color for it to work properly?
Cheers
Try:
self.backgroundColor = [UIColor clearColor];
I don't see any reason not to have that in your init method. I don't think you want to override drawRect: in this case as you'll need to draw the entire view yourself.

Detecting taps inside a UIWebView

I have a UIWebView with a navigation bar and toolbar that I want to auto hide. I'm doing that already, but I want to show them when the user taps on the UIWebView.
My problem is that the UIWebView captures all the touches events, and I cannot intercept the one I need.
Is there any workaround for this?
Taken from a blog entry (doesn't load, but thankfully it's Google Cache'd) by Mithin Kumar.
(Mithin, I hope you don't mind me reposting it here; if you do, please let me know in the comments and I will remove it.)
Recently, I was working on a project which required detection of tap and events on the UIWebView. We wanted to find out the HTML element on which the user taps in the UIWebView and then depending on the element tapped some action was to be performed. After some Googling, I found out the most of the users lay a transparent UIView on top of the UIWebView, re-implement the touch methods of UIResponder class (Ex: -touchesBegan:withEvent:) and then pass the events to the UIWebView. This method is explained in detail here. There are multiple problems with the method.
Copy/Selection stops working on UIWebView
We need to create a sub-class of UIWebView while Apple says we should not sub-class it.
A lot other UIWebView features stop working.
We ultimately found out that the right way to implement this is by
sub-classing UIWindow and re-implementing the -sendEvent: method. Here
is how you can do it. First, create a UIWindow sub-class
#import <UIKit/UIKit.h>
#protocol TapDetectingWindowDelegate
- (void)userDidTapWebView:(id)tapPoint;
#end
#interface TapDetectingWindow : UIWindow {
UIView *viewToObserve;
id <TapDetectingWindowDelegate> controllerThatObserves;
}
#property (nonatomic, retain) UIView *viewToObserve;
#property (nonatomic, assign) id <TapDetectingWindowDelegate> controllerThatObserves;
#end
Note that we have variables which tell us the UIView on which to
detect the events and the controller that receives the event
information. Now, implement this class in the following way
#import "TapDetectingWindow.h"
#implementation TapDetectingWindow
#synthesize viewToObserve;
#synthesize controllerThatObserves;
- (id)initWithViewToObserver:(UIView *)view andDelegate:(id)delegate {
if(self == [super init]) {
self.viewToObserve = view;
self.controllerThatObserves = delegate;
}
return self;
}
- (void)dealloc {
[viewToObserve release];
[super dealloc];
}
- (void)forwardTap:(id)touch {
[controllerThatObserves userDidTapWebView:touch];
}
- (void)sendEvent:(UIEvent *)event {
[super sendEvent:event];
if (viewToObserve == nil || controllerThatObserves == nil)
return;
NSSet *touches = [event allTouches];
if (touches.count != 1)
return;
UITouch *touch = touches.anyObject;
if (touch.phase != UITouchPhaseEnded)
return;
if ([touch.view isDescendantOfView:viewToObserve] == NO)
return;
CGPoint tapPoint = [touch locationInView:viewToObserve];
NSLog(#"TapPoint = %f, %f", tapPoint.x, tapPoint.y);
NSArray *pointArray = [NSArray arrayWithObjects:[NSString stringWithFormat:#"%f", tapPoint.x],
[NSString stringWithFormat:#"%f", tapPoint.y], nil];
if (touch.tapCount == 1) {
[self performSelector:#selector(forwardTap :) withObject:pointArray afterDelay:0.5];
}
else if (touch.tapCount > 1) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(forwardTap :) object:pointArray];
}
}
#end
Implement the sendEvent method in the above way, and then you can send
back the information you want back to the controller.
There are few things that one needs to keep in mind. Make sure in your
MainWindow.xib file, the window is of type TapDetectingWindow and not
UIWindow. Only then all the events will pass through the above
re-implemented sendEvent method. Also, make sure you call [super
sendEvent:event] first and then do whatever you want.
Now, you can create your UIWebView in the controller class in the
following way
#interface WebViewController : UIViewController<TapDetectingWindowDelegate> {
IBOutlet UIWebView *mHtmlViewer;
TapDetectingWindow *mWindow;
}
- (void)viewDidLoad {
[super viewDidLoad];
mWindow = (TapDetectingWindow *)[[UIApplication sharedApplication].windows objectAtIndex:0];
mWindow.viewToObserve = mHtmlViewer;
mWindow.controllerThatObserves = self;
}
Remember you’ll need to write the method userDidTapWebView in your
controller class. This is the method that is called in order to send
the event information to the controller class. In our case above we
are sending the point in the UIWebView at which the user tapped.
You can very simply use a UITapGestureRecognizer to detect tap gestures on a UIWebView. You must implement a UIGestureRecognizerDelegate method to allow the simultaneous recognition however.
- (void)viewDidLoad{
[super viewDidLoad];
UITapGestureRecognizer *targetGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
targetGesture.numberOfTapsRequired = 2;
targetGesture.delegate = self;
[self.webView addGestureRecognizer:targetGesture];
}
// called when the recognition of one of gestureRecognizer or otherGestureRecognizer would be blocked by the other
// return YES to allow both to recognize simultaneously. the default implementation returns NO (by default no two gestures can be recognized simultaneously)
//
// note: returning YES is guaranteed to allow simultaneous recognition. returning NO is not guaranteed to prevent simultaneous recognition, as the other gesture's delegate may return YES
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
NSLog(#"%#", otherGestureRecognizer);
//if you would like to manipulate the otherGestureRecognizer here is an example of how to cancel and disable it
if([otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]){
UITapGestureRecognizer *tapRecognizer = (UITapGestureRecognizer*)otherGestureRecognizer;
if(tapRecognizer.numberOfTapsRequired == 2 && tapRecognizer.numberOfTouchesRequired == 1){
//this disalbes and cancels all other singleTouchDoubleTap recognizers
// default is YES. disabled gesture recognizers will not receive touches. when changed to NO the gesture recognizer will be cancelled if it's currently recognizing a gesture
otherGestureRecognizer.enabled = NO;
}
}
return YES;
}
-(void)handleTap:(id)sender{
}
I had several issues trying to use brian.clear's answer (copied from a extinct post by Mithin Kumar) in my iOS 5-7, universal, storyboard-based, totally ARC project so I had to make several changes. I also improved some things and made it easier to understand (at least for me). If you are having trouble trying to use that answer from 2009 maybe you should try my update. Detailed instructions:
1. Create a new TapDetectingWindow class
TapDetectingWindow.h
// Created by Cristian Perez <cpr#cpr.name>
// Based on https://stackoverflow.com/a/1859883/423171
#import <UIKit/UIKit.h>
#protocol TapDetectingWindowDelegate
- (void)userDidTapView:(CGPoint)tapPoint;
#end
#interface TapDetectingWindow : UIWindow
#property (nonatomic) UIView *tapDetectingView;
#property (nonatomic) id <TapDetectingWindowDelegate> tapDetectedDelegate;
#end
TapDetectingWindow.m
// Created by Cristian Perez <cpr#cpr.name>
// Based on https://stackoverflow.com/a/1859883/423171
#import "TapDetectingWindow.h"
#implementation TapDetectingWindow
#synthesize tapDetectingView;
#synthesize tapDetectedDelegate;
- (id)initWithFrame:(CGRect)frame
{
return [super initWithFrame:frame];
}
- (void)sendEvent:(UIEvent *)event
{
[super sendEvent:event];
if (tapDetectingView == nil || tapDetectedDelegate == nil)
{
return;
}
NSSet *touches = [event allTouches];
if (touches.count != 1)
{
return;
}
UITouch *touch = touches.anyObject;
if (touch.phase != UITouchPhaseEnded)
{
return;
}
if (touch.view != nil && ![touch.view isDescendantOfView:tapDetectingView])
{
return;
}
CGPoint tapPoint = [touch locationInView:tapDetectingView];
NSString *tapPointStr = NSStringFromCGPoint(tapPoint);
if (touch.tapCount == 1)
{
[self performSelector:#selector(notifyTap:) withObject:tapPointStr afterDelay:0.4];
// Make the afterDelay value bigger in order to have more chances of detecting a double tap and thus being able to cancel the single tap event, or make it smaller if you don't care about double taps and want to get the tap event as soon as possible
}
else if (touch.tapCount > 1)
{
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(notifyTap:) object:tapPointStr];
}
}
- (void)notifyTap:(NSString *)tapPointStr
{
CGPoint tapPoint = CGPointFromString(tapPointStr);
[tapDetectedDelegate userDidTapView:tapPoint];
}
#end
2. Check you have a window declared in your app delegate
You should have something like this in YourAppDelegate.h. Don't change the name of the property!
#interface YourAppDelegate : UIResponder <UIApplicationDelegate>
{
// ...
}
// ...
// The app delegate must implement the window property if it wants to use a main storyboard file
#property (nonatomic) UIWindow *window;
#end
3. Override the window property of your app delegate
Just like this, which should be in YourAppDelegate.m. Again, don't change the name of the method!
// Replace the default UIWindow property with a TapDetectingWindow
- (TapDetectingWindow *)window
{
static TapDetectingWindow *tapDetectingWindow = nil;
if (!tapDetectingWindow)
{
tapDetectingWindow = [[TapDetectingWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
}
return tapDetectingWindow;
}
4. Set the delegate protocol in your view controller
Be sure to adopt the tap handling protocol in your MainViewController.h
#import "TapDetectingWindow.h"
#interface MainViewController : UIViewController <TapDetectingWindowDelegate, ...>
{
// ...
}
// ...
#end
5. Set up the tap detecting window and tap event handler
Specify your webview (actually any UIView should work) and tap event handler in your view controller's (void)viewDidLoad method
- (void)viewDidLoad
{
// ...
// Allow tap detection in webview
TapDetectingWindow *tapDetectingWindow = (TapDetectingWindow*)[(YouTubeRPAppDelegate*)[[UIApplication sharedApplication] delegate] window];
tapDetectingWindow.tapDetectingView = self.webView; // Your UIWebView
tapDetectingWindow.tapDetectedDelegate = self;
}
6. Handle the tap event as you wish
Just implement the userDidTapView method in your view controller
- (void)userDidTapView:(CGPoint)tapPoint
{
NSLog(#"Tap detected in webview at %#", NSStringFromCGPoint(tapPoint));
}
Most of the approaches deal with a complicated pair of UIView and UIWebView subclasses and overrode -touchesBegan:withEvent: etc. methods.
This JavaScript-based approach intercepts touches on the web DOM itself, and it seems like a clever way to sidestep the more complex process. I haven't tried it myself, but I'm curious to know the results, if you give it a shot.
I'm not sure of the exact implementation details but you need to subclass UIWindow and override sendEvent:. Then you can capture tap events and handle them accordingly, before they get down to the web view. Hope this points you in the right direction!
Create a UIView sub class containing the whole UIWebView. Then the event hitTest will be fired on touching the webview. Note: Don't put anything to the container except the webView.
#interface myWebViewContainer : UIView
... ...
#end
Then override the hitTest event:
-(UIView*) hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
NSLog(#"Hit test %#", [event description]);
UIView * returnView = [super hitTest:point withEvent:event];
[delegate userDidTapWebView];// notify the delegate that UIWebView is being touched by some naughty user :P
return returnView;
}
A much simpler approach since iOS 3.2 is simply to use a UITapGestureRecognizer.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UITapGestureRecognizer* tapGestureRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(userDidTapWebView)] autorelease];
[self.pdfWebView addGestureRecognizer:tapGestureRecognizer];
}
- (void)userDidTapWebView {
NSLog(#"TAP");
}