I created a very very basic iPhone app with File/New Projet/View-Based application.
No NIB file there.
Here is my appDelegate
.h
#interface MyAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
MyViewController *viewController;
}
.m
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after app launch
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
And here is my loadView method in my controller
- (void)loadView {
CGRect mainFrame = [[UIScreen mainScreen] applicationFrame];
UIView *contentView = [[UIView alloc] initWithFrame:mainFrame];
contentView.backgroundColor = [UIColor redColor];
self.view = contentView;
[contentView release];
}
Now, in order to catch the touchesBegan event, I created a new subclass of UIView:
.h
#interface TouchView : UIView {
}
.m
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Touch detected");
}
and modified the second line in my loadView into this :
TouchView *contentView = [[UIView alloc] initWithFrame:mainFrame];
Why is touchesBegan never called?
If you change the loadView into this:
TouchView *contentView = [[TouchView alloc] initWithFrame:mainFrame];
You should have no problem catching the touches in TouchView. In your code, you didn't create a TouchView instance, you created a UIView instance.
Found the solution : touchesBegan gets called on the viewController, not on the view...
Related
I encounter a problem using UITabBarItem and UIButton in my application. My button is inside a UITabBarItem. When I press it I want to be pushed to another controller to display a PDF.
Here is a piece of code that works in other cases :
- (void)viewDidLoad {
UIImage* imageButton = [UIImage imageNamed:#"pdf-button.png"];
UIButton *buttonPDF = [UIButton buttonWithType:UIButtonTypeCustom];
buttonPDF.frame = CGRectMake(SCREEN_WIDTH/2 - 100, 100, 200, 36);
[buttonPDF setImage:imageButton forState:UIControlStateNormal];
buttonPDF.contentMode = UIViewContentModeScaleAspectFit;
buttonPDF.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
[buttonPDF addTarget:self action:#selector(displayPDFParams:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:buttonPDF];
}
-(void)displayPDFParams:(UIButton *)sender {
PDFProduitController *pdfController = [[PDFProduitController alloc] init];
pdfController.pdf = documentParametres;
[self.navigationController pushViewController:pdfController animated:YES];
}
displayPDFParams is called but it not push me on my pdfController. I think it's because I'm not able to target the parent navigation controller of my application...
Anyone help would be much appreciated. Thanks in advance !
You need To initialize your root view controller with the navigation controller. Here is the code.
In your AppDelegate.h
#import <UIKit/UIKit.h>
#import "HomeViewController.h"
#class HomeViewController;
#interface IDSAppDelegate : UIResponder <UIApplicationDelegate>{
UINavigationController *nav;
}
#property (strong, nonatomic) HomeViewController *homeViewController;
#end
In your AppDelegate.m
#import "IDSAppDelegate.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.homeViewController = [[HomeViewController alloc] initWithNibName:#"HomeViewController" bundle:nil];
nav=[[UINavigationController alloc]initWithRootViewController:self.homeViewController];
self.window.rootViewController = nav;
[self.window makeKeyAndVisible];
return YES;
}
Problem solved by define a property in my UIViewController (as UITabBarItem) like this :
#property (nonatomic, retain) UINavigationController *superNavController;
And set it in my UITabBarController :
self.myViewController.superNavController = self.navigationController;
Finally I modified my displayPDFParams method :
-(void)displayPDFParams:(UIButton *)sender {
PDFProduitController *pdfController = [[PDFProduitController alloc] init];
pdfController.pdf = self.documentParametres;
[self.superNavController pushViewController:pdfController animated:YES];
}
Works perfectly !
I would like to write a custom full-screen container view controller with the intention of putting a UINavigationController in it as a child view controller. The view of the UINavigationController will fill up the view of the container view controller so that it looks like the UINavigationController is the root view controller. (One would want to do something like this to, say, create the sliding sidebar menu UI popularized by Facebook.)
What I've done works EXCEPT there is a glitch when presenting another view controller that hides the status bar when the iPhone is in landscape orientation. Typically, the navigation bar slides up when the status bar disappears and slides down when it reappears. Instead, the navigation bar stays where it is when it is suppose to slide up, and when it is suppose to slide down, it is first positioned with the status bar overlapping it and then jumps to its correct position below the status bar. Basically, I'm trying to have the UINavigationController behave as it would if it were not inside a custom container view controller.
Below is some code that you can run to see the problem, but if you don't want to do that, just take a look at the ContainerViewController class, which implements a minimal custom container view controller. What is it that I'm missing in my custom container view controller that is causing this problem? It works when I use a UITabBarController as the container view controller, so it seems like I'm just missing something in my implementation.
MORE STUFF BELOW TO READ IF YOU WANT
If you would like to run the sample code to see the problem, here is an overview. There is a preprocessor definition called MODE defined in AppDelegate to conditionally compile the app in three ways.
When MODE == 1, ViewController is inside a UINavigationController. You can then press a "Present" button to present ViewControllerWithStatusBarHidden, and then you can press a "Dismiss" button to dismiss this view controller. This mode of the app shows the behavior that I am seeking.
When MODE == 2, we have the same thing as in MODE == 1 except that the UINavigationController is inside of a ContainerViewController. This mode of the app shows the undesirable behavior I have currently.
When MODE == 3, we have the same thing as in MODE == 1 except that the UINavigationController is inside of a UITabBarController. This mode of the app shows that it is possible to get the behavior I am seeking.
Again, to see the problem, just press the "Present" button and then the "Dismiss" button when the iPhone is in landscape orientation.
CODE
Four classes:
ContainerViewController
AppDelegate
ViewController
ViewControllerWithStatusBarHidden
ContainerViewController.h
#import <UIKit/UIKit.h>
#interface ContainerViewController : UIViewController
#property (nonatomic) UIViewController * viewController;
#end
ContainerViewController.m
#import "ContainerViewController.h"
// This custom container view controller only has one child view controller,
// whose view fills up the view of the container view controller.
#implementation ContainerViewController
- (UIViewController *)viewController {
if (self.childViewControllers.count > 0) {
return [self.childViewControllers firstObject];
}
else {
return nil;
}
}
- (void)setViewController:(UIViewController *)viewController {
UIViewController *previousViewController = [self.childViewControllers firstObject];
if ((previousViewController == nil && viewController != nil)
|| (previousViewController != nil && viewController == nil)
|| (previousViewController != nil && viewController != nil
&& previousViewController != viewController))
{
if (previousViewController != nil) {
// Remove the old child view controller.
[previousViewController willMoveToParentViewController:nil];
[previousViewController.view removeFromSuperview];
[previousViewController removeFromParentViewController];
}
if (viewController != nil) {
// Add the new child view controller.
[self addChildViewController:viewController];
self.viewController.view.frame = self.view.bounds;
self.viewController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:self.viewController.view];
[self.viewController didMoveToParentViewController:self];
}
}
}
- (UIViewController *)childViewControllerForStatusBarHidden {
return self.viewController;
}
- (UIViewController *)childViewControllerForStatusBarStyle {
return self.viewController;
}
#end
AppDelegate.h
#import <UIKit/UIKit.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#end
AppDelegate.m
#import "AppDelegate.h"
#import "ViewController.h"
#import "ContainerViewController.h"
#define MODE 2 // Mode can be 1, 2, or 3.
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
ViewController *vc = [[ViewController alloc] initWithNibName:nil bundle:nil];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
#if MODE == 1 // No container view controller.
self.window.rootViewController = nav;
#elif MODE == 2 // Use custom container view controller.
ContainerViewController *container = [[ContainerViewController alloc] initWithNibName:nil bundle:nil];
container.viewController = nav;
self.window.rootViewController = container;
#elif MODE == 3 // Use tab bar controller as container view controller.
UITabBarController *tab = [[UITabBarController alloc] initWithNibName:nil bundle:nil];
tab.viewControllers = #[nav];
self.window.rootViewController = tab;
#endif
[self.window makeKeyAndVisible];
return YES;
}
#end
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#end
ViewController.m
#import "ViewController.h"
#import "ViewControllerWithStatusBarHidden.h"
// This view controller will serve as the content of a navigation controller.
// It also provides a button in the navigation bar to present another view controller.
#implementation ViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = #"Title";
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Present"
style:UIBarButtonItemStylePlain
target:self
action:#selector(pressedPresentButton:)];
}
return self;
}
- (void)loadView {
self.view = [[UIView alloc] init];
self.view.backgroundColor = [UIColor whiteColor];
}
- (void)pressedPresentButton:(id)sender {
ViewControllerWithStatusBarHidden *vc = [[ViewControllerWithStatusBarHidden alloc] initWithNibName:nil bundle:nil];
[self presentViewController:vc animated:YES completion:nil];
}
#end
ViewControllerWithStatusBarHidden.h
#import <UIKit/UIKit.h>
#interface ViewControllerWithStatusBarHidden : UIViewController
#end
ViewControllerWithStatusBarHidden.m
#import "ViewControllerWithStatusBarHidden.h"
// This view controller is meant to be presented and does two things:
// (1) shows a button to dismiss itself and (2) hides the status bar.
#implementation ViewControllerWithStatusBarHidden
- (void)loadView {
self.view = [[UIView alloc] init];
self.view.backgroundColor = [UIColor yellowColor];
}
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *dismissButton = [UIButton buttonWithType:UIButtonTypeSystem];
[dismissButton setTitle:#"Dismiss" forState:UIControlStateNormal];
[dismissButton addTarget:self
action:#selector(pressedDismissButton:)
forControlEvents:UIControlEventTouchUpInside];
dismissButton.frame = CGRectMake(100, 100, 100, 100);
[self.view addSubview:dismissButton];
}
- (void)pressedDismissButton:(id)sender {
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
- (BOOL)prefersStatusBarHidden {
return YES;
}
//- (NSUInteger)supportedInterfaceOrientations {
// return UIInterfaceOrientationMaskPortrait;
//}
#end
I have declared a void touchesbegan method in my controller but it doesn't work! i don't know why. i have an image view which i plan to click it to move to next controller. so i set the touches began method. i have linked the image view in the xib file but when i click the image. nothing happens. Please help, Thanks.
ViewController.h
#import <UIKit/UIKit.h>
#interface imageViewViewController : UIViewController
{
IBOutlet UIImageView *testing;
}
#property(nonatomic, retain) IBOutlet UIImageView *testing;
#end
ViewController.m
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if(touch.view == testing)
{
TestViewController *testviewcontroller = [[TestViewController alloc]initWithNibName:nil bundle:nil];
[self.navigationController pushViewController:testviewcontroller animated:YES];
}
}
#end
P.S.
here i tried another method using tap gestures.
testing is the name of my image view. as u can see i comment out the ns log in the imagedidtapped method. it works til that point. however when i tried to navigate it out to another page it fails.
- (void)viewDidLoad
{
UITapGestureRecognizer *tapRecognizer;
[testing setTag:0];
[testing setUserInteractionEnabled:TRUE];
tapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imageViewDidTapped:)] autorelease];
tapRecognizer.numberOfTapsRequired = 1;
[testing addGestureRecognizer:tapRecognizer];
[self.view addSubview:testing];
[testing release];
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)imageViewDidTapped:(UIGestureRecognizer *)aGesture {
UITapGestureRecognizer *tapGesture = (UITapGestureRecognizer *)aGesture;
UIImageView *tappedImageView = (UIImageView *)[tapGesture view];
switch (tappedImageView.tag) {
case 0:
//NSLog(#"UIImageView 1 was tapped");
[self navigate];
break;
case 1:
NSLog(#"UIImageView 2 was tapped");
break;
default:
break;
}
}
-(void)navigate
{
TestViewController *testviewcontroller = [[TestViewController alloc]initWithNibName:nil bundle:nil];
[self.navigationController pushViewController:testviewcontroller animated:YES];
}
The problem is that by default userInteractionEnabled is NO for a UIImageView so you won't get any touches. Set it to YES.
Also the message handling for touchesBegan is really complicated. You'll be much happier if you attach a UITapGestureRecognizer to the image view.
EDIT: Now you say your touches handling is working, but the navigation is not taking place. So let's concentrate on this part of your code:
-(void)navigate
{
TestViewController *testviewcontroller = [[TestViewController alloc]initWithNibName:nil bundle:nil];
[self.navigationController pushViewController:testviewcontroller animated:YES];
}
Put logging in there to make sure navigate is being called! If it isn't being called, you need to figure out why your other code is not running and calling it. If it is being called, then the problem is probably that self.navigationController is nil, i.e. you are not inside a navigation interface to start with.
I want to write a custom UIView that does the following:
It takes an image and adds it to a view.
It has a method to flip that image.
I want to pass this Custom UIView to the iCarousel class:
https://github.com/nicklockwood/iCarousel
How can I create a custom UIView using Objective C and Cocoa?
I started by doing the following:
CItem.h
#import <UIKit/UIKit.h>
#interface CItem : UIView
{
UIImageView *box;
}
#property (nonatomic, retain) UIImageView * box;
#end
CItem.m
#import "CItem.h"
#implementation CItem
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect {
// Drawing code
box = [[UIImageView alloc] initWithFrame:CGRectMake(0,0, 240, 240)];
[box setImage:[UIImage imageNamed:# "cleaning.png"]];
[self addSubview:box];
}
#end
You should not add your addSubview: in drawRect:. See this method's disscussion:
Discussion
The default implementation of this method does nothing.
Subclasses that use native drawing technologies (such as Core Graphics
and UIKit) to draw their view’s content should override this method
and implement their drawing code there. You do not need to override
this method if your view sets its content in other ways. For example,
you do not need to override this method if your view just displays a
background color or if your view sets its content directly using the
underlying layer object. Similarly, you should not override this
method if your view uses OpenGL ES to do its drawing.
If you don't use a xib file for your CItem you can add your code to initWithFrame:.
//CItem.h
#import <UIKit/UIKit.h>
#interface CItem : UIView
- (void)flip;
#end
// CItem.m
#import "CItem.h"
#interface CItem()
#property (assign, nonatomic) BOOL displayingPrimary;
#property (strong, nonatomic) UIImageView *primaryView;
#property (strong, nonatomic) UIImageView *secondaryView;
#end
#implementation CItem
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
_primaryView = [[UIImageView alloc] initWithFrame:frame];
[_primaryView setImage:[UIImage imageNamed:# "cleaning.jpg"]];
[self addSubview:_primaryView];
_secondaryView = [[UIImageView alloc] initWithFrame:frame];
[_secondaryView setImage:[UIImage imageNamed:# "adding.jpg"]];
[self addSubview:_secondaryView];
}
return self;
}
- (void)flip
{
[UIView transitionFromView:(self.displayingPrimary ? self.primaryView : self.secondaryView)
toView:(self.displayingPrimary ? self.secondaryView : self.primaryView)
duration:1.0
options:(self.displayingPrimary ? UIViewAnimationOptionTransitionFlipFromRight :
UIViewAnimationOptionTransitionFlipFromLeft) | UIViewAnimationOptionShowHideTransitionViews
completion:^(BOOL finished) {
if (finished) {
self.displayingPrimary = !self.displayingPrimary;
}
}];
}
#end
Then you can use CItem like this:
CItem *subView = [[CItem alloc] initWithFrame:CGRectMake(0, 0, 320, 400)];
[self.view addSubview:subView];
You should only implement drawRect if you intend to do custom drawing. If you want to use subviews you can just move your code into the initWithFrame:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
box = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 240.0f, 240.0f)];
[box setImage:[UIImage imageNamed:#"cleaning"]];
[self addSubview:box];
}
return self;
}
You can safely remove the drawRect method. Not sure what you mean by "flipping" the image, but you should be able to manipulate the imageView object using the "box" property of your custom class.
I'm new to Cocoa development and suspect that this is a noob issue.
I'm creating a simple background canvas with a bespoke UIView. I was expecting that I would be able to create this canvas to be slightly indented down from the main view so that I could add a toolbar at a later date. However, when I use initWithFrame (and any other init for that matter), the canvas is always created to be the size of the full screen rather than slightly smaller than the full screen. Even if I completely override the values for CGRect it doesn't make a difference. I've set a touchesBegan event and set the background colour to green to be able to determine success but all I get are a completely green screen and a touch event that works everywhere.
Please help - this is driving me mad and I can't find the answer anywhere on the web.
The relevant code is as follows (there actually isn't much more code than this in the whole app so far):
AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.objMainViewController = [[MainViewController alloc] init];
self.window.rootViewController = self.objMainViewController;
[self.window makeKeyAndVisible];
return YES;
}
MainViewController.h
#import <UIKit/UIKit.h>
#import "viewCanvas.h"
#interface MainViewController : UIViewController
#property (strong, nonatomic) viewCanvas *objViewCanvas;
-(id)init;
#end
MainViewController.m
#import "MainViewController.h"
#import "viewCanvas.h"
#implementation MainViewController
#synthesize objViewCanvas;
-(id)init
{
if (self = [super init]) {
CGRect frame = CGRectMake(100, 100, 100, 100);
objViewCanvas = [[viewCanvas alloc] initWithFrame:frame];
}
return self;
}
- (void)loadView {
self.view = objViewCanvas;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
viewCanvas.h
#import <UIKit/UIKit.h>
#import "viewCanvas.h"
#interface viewCanvas : UIView {
}
//Init
- (id) initWithFrame:(CGRect)frame;
//Events
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
#end
viewCanvas.m
#import "viewCanvas.h"
#implementation viewCanvas
//Standard inits
- (id) initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
[self setBackgroundColor:[UIColor greenColor]];
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Touches Began Canvas" message:#"Canvas Touches Began" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles: nil];
[alert show];
}
#end
I think by default the view of a view controller fills the whole screen, so you need to add a subview to that view that's your smaller viewCanvas object (BTW, you should conform to the naming rules and name your classes with capital letters, ViewCanvas). Try this in your MainViewController .m file:
-(id)init{
if (self = [super init]) {
CGRect frame = CGRectMake(100, 100, 100, 100);
objViewCanvas = [[ViewCanvas alloc] initWithFrame:frame];
self.contentView = [[UIView alloc] initWithFrame:CGRectMake(1, 1, 1, 1)]; //this frame doesn't matter
[self.contentView setBackgroundColor:[UIColor whiteColor]];
}
return self;
}
- (void)loadView {
self.view = self.contentView;
[self.view addSubview:objViewCanvas];
}