I'm a noob to iOS development (so please bear with me on this), and I'm trying to create a simple touch application with Xcode from scratch. I have spent a week on this, but I couldn't seem to find out what I have been missing so here I am, asking for some guidance :)
First, I created an empty application, then created a xib file (MainWindow.xib) and added a window object (Main_Window) to it. Then, I created a View object (Main_View) within this Main_Window, and added a label object (lblTitle) to this view. The Main_View object pretty much covered the entire Main_Window screen.
So, in short, the hierarchy of my MainWindow.xib is like this: Main_Window --> Main_View --> lblTitle.
Finally, I created a ViewController object (Main_View_Controller) with its "view" set to Main_View and its "rootViewController" set to "Main_Window".
In the project,
I subclassed UIView with "TouchEvent_View", hooked up to "Main_View" in the xib file.
I subclassed UIViewController with "TouchEvent_ViewController", hooked up to "Main_View_Controller" in the xib file.
In my AppDelegate.h,
I created an "IBOutlet UIWindow *window", hooked up to "Main_Window" in the xib file.
I created an object for my viewController and view classes.
#property (strong, nonatomic) IBOutlet UIWindow *window;
#property (strong, nonatomic) TouchEvent_ViewController * myViewController;
#property (strong, nonatomic) TouchEvent_View *myView;
In AppDelegate.m, I hooked up "MainWindow.xib" with myViewController:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window.backgroundColor = [UIColor whiteColor];
self.myViewController = [[[TouchEvent_ViewController alloc]
initWithNibName:#"MainWindow" bundle:nil] autorelease];
[self.window makeKeyAndVisible];
return YES;
}
In "Touch_Event_ViewController.m", I coded the viewDidLoad message as followed:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Hi! ViewController's viewDidLoad msg sent!");
TouchEvent_View * mView = [[TouchEvent_View alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];
mView.backgroundColor = [UIColor blueColor];
[self.view addSubview:mView];
[mView release];
}
In "TouchEvent_View.m", I instantiated a UITapGestureRecognizer object and hooked it up to a handler method as followed:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
NSLog(#"[TouchEvent_View initWithFrame] sent!");
// Initialization code
//-----------------------
//Touch event declaration
//Single tap
UITapGestureRecognizer * singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(SingleTap_Handler)];
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
//singleTap.delegate = self;
[self addGestureRecognizer:singleTap];
}
return self;
}
-(void) SingleTap_Handler :(UITapGestureRecognizer *)GR
{
NSLog(#"Hi! You just touched the screen!");
}
When I compiled and deployed the project into my iPad3, every thing worked just as planned until the Touch event, which didn't work.
I got the following messages printed out to the console window:
2012-08-26 14:03:59.589 Ex_TouchEvents_01[806:707] Hi! ViewController's viewDidLoad msg sent!
2012-08-26 14:03:59.593 Ex_TouchEvents_01[806:707] [TouchEvent_View initWithFram] sent!
But I did not see the "Hi, you just touched the screen!" message after I touched the screen. In addition, I didn't see the background of the View area set to blue either. So, I must have been missing something that was very simple. I have been googling all over the web, but I couldn't figure out what I missed. Would some body kindly point out what I have done wrong?
I don't have access to my Mac to test this, but I believe you're missing a colon in this line.
UITapGestureRecognizer * singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(SingleTap_Handler)];
It needs to be:
UITapGestureRecognizer * singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(SingleTap_Handler:)];
If a method takes any parameters, the colon(s) are part of the selector.
Erm, can you try putting:
self.userinteractionEnabled = YES;
in your init method?
UIViews by default is set to not respond to touch events so you have to enabled them first before your tap gestures work.
Related
hy,
I'm new in objective-c. I'm trying to add a button into my UIView. But I can't see the button in my application. I tried a lot but nothing worked.
I know there are many posts about that but no solution worked at my code.
PinViewController.m:
#import "PinViewController.h"
#implementation PinViewController
#synthesize view,one;
-(instancetype)init{
self = [super init];
if(self){
view = [[UIView alloc] init];
}
return self;
}
-(void)viewDidLoad{
UIButton *btnname = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btnname setTitle:#"Click Me" forState:UIControlStateNormal];
btnname.frame = CGRectMake(28, 31, 42, 21);
[self.view addSubview:btnname];
}
PinViewController.h:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface PinViewController : UIViewController
#property(nonatomic, retain) UIView *view;
#property UIButton *one;
#end
In appdeligate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.rootViewController = [[PinViewController alloc]init];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
Thank you for your help :)
I take from your question that you specifically want to not have storyboards or xibs. That's a perfectly legitimate but somewhat infrequent choice, and it has it's pros and cons.
You are writing a wrong constructor for PinViewController, which should be a subclass of UIViewController, but it class UIView's constructor instead (which is not its parent).
The other thing is that you shouldn't create (and synthesise) a new property 'view', it is already there for UIViewController, and this way the default setters/getters will be overridden. And you create your new view by setting the property. You'll also have to specify frame otherwise it will have no size. So it is:
self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
Third problem is that viewDidLoad was never called. According to documentation at Apple
This method is called regardless of whether the view hierarchy was loaded from a nib file or created programmatically in the loadView method.
This does not happen since you don't create the view in the loadView method. The loadView method, in turn, is not called at all, because you create the view in the constructor. So the solution is to not override the constructor at all, but do instead:
- (void)loadView
{
self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
}
(Alternatively, you can just call viewDidLoad manually from the constructor after creating the view, which is less nice but does the job)
I have a subclass of UIViewController -> MyPopUpViewController
#protocol MyPopUpViewController Delegate;
#interface MyPopUpViewController : UIViewController
{
}
#property (nonatomic, strong) id <MyPopUpViewControllerDelegate> delegate;
-(IBAction) buttonPressed:(id)sender;
#end
#protocol MyPopUpViewControllerDelegate
-(void) popupButtonPressed: (MyPopUpViewController*)controller;
#end
I cannot have this MyPopUpViewController as an instance variable because this comes externally, and there could be many and multiple of these popups can be up. So far I tried this, and it crashes on the delegate call due to not being retained:
MyMainViewController:
-(void)externalNotificationReceived: (NSString*) sentMessage
{
MyPopUpViewController *popupView = [[MyPopUpViewController alloc] init];
popupView.delegate = self;
[self.view addSubview:popupView.view];
[popupView setInfo :sentMessage :#"View" :#"Okay"];
popupView.view.frame = CGRectMake(0, -568, 320, 568);
popupView.view.center = self.view.center;
}
-(void)popupButtonPressed:(MyPopUpViewController *)controller :(int)sentButtonNumber
{
NSLog(#"Popup Delegate Called");
[controller.view removeFromSuperview];
controller.delegate = nil;
controller = nil;
}
Once the popup comes up, and when the ok button is tapped, it crashes and never gets to that NSLog. How can I change
MyPopUpViewController *popupView = [[MyPopUpViewController alloc] init];
..so it would retain without making it an instance variable?
Thanks in advance.
You should be doing proper view controller containment by calling addChildViewController:.
- (void)externalNotificationReceived: (NSString*) sentMessage {
MyPopUpViewController *popupView = [[MyPopUpViewController alloc] init];
popupView.delegate = self;
[popupView setInfo :sentMessage :#"View" :#"Okay"];
popupView.view.frame = CGRectMake(0, -568, 320, 568);
popupView.view.center = self.view.center;
[self addChildViewController:popupView];
[self.view addSubview:popupView.view];
[popupView didMoveToParentViewController:self];
}
This will keep a proper reference to the view controller as well as properly pass various view controller events. Read about this in the docs for UIViewController and the "View Controller Programming Guide for iOS".
BTW - you should name your methods better. Example:
popupButtonPressed::
should be named:
popupButtonPressed:buttonNumber:
Usually delegates are weak-referenced instead of strong. I, myself, would name it something else as to not confuse other people.
Also, the following bit of code will have no effect:
-(void)popupButtonPressed:(MyPopUpViewController *)controller :(int)sentButtonNumber
{
...
controller = nil;
}
the controller would be released (set to nil) automatically at the end of the scope.
I have a pretty simple set up in mind, having a mainViewController that has a GLKViewController on top of it. The idea is having my GLKViewController in a box, that take sup 1/3 of the screen, on the mainViewController. This can be seen below:
That white box is my own custom GLKViewController with the follow code:
boxViewController.h
//boxViewController.h
#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
#interface boxViewController : GLKViewController
#end
boxViewController.m
//boxViewController.m
#import "boxViewController.m"
#interface boxViewController () { }
#property (strong, nonatomic) EAGLContext *context;
#end
#implementation boxViewController
-(void)viewDidLoad {
[super viewDidLoad];
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!self.context) {
NSLog(#"Failed to create ES context");
}
GLKView *view = (GLKView *)self.view;
// view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
}
#end
On my mainViewController in the viewDidLoad I simply call boxViewController like this:
boxViewController* box = [[boxChartViewController alloc] init];
box.view.layer.frame = CGRectMake(10, 50, self.view.frame.size.width-20, self.view.frame.size.height/3);
[self.view addSubview:box.view];
which works perfect.
Notice that in my boxViewController.m I had view.context = self.context commented out. If you uncomment it, my application crashes without any error messaging (it breaks with a EXC_BAD_ACCESS in the objc_msgSend assembly code [line 8 to be specific]).
What am I doing incorrectly that when I set the context the application crashes? From all the tutorials I noticed that they have the same set up, except not setting the controller on another controller. Though I don't understand why GLKViewController couldn't be framed on another controller, so I don't think that's the issue.
After a few hours of messing around I found that adding the viewController as a child works:
#import "mainViewController.h"
#implementation mainViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.layer.backgroundColor = [UIColor colorWithRed:242.0f/255.0f green:242.0f/255.0f blue:242.0f/255.0f alpha:1.0].CGColor;
boxViewController* chart = [[boxViewController alloc] init];
chart.view.layer.frame = CGRectMake(10, 50, self.view.frame.size.width-20, self.view.frame.size.height/3);
chart.view.layer.borderColor = [UIColor blackColor].CGColor;
chart.view.layer.borderWidth = 2.0f;
[self addChildViewController:chart];
[self.view addSubview:chart.view];
}
Hey guys I'm trying to implement a scroll with page control, like the iPhone home screen, but in a uitableview cell.
What I tried to do to attempt this is create a custom table view cell with a xib file, and placed the uipagecontrol and uiscrollview on it, and connected it with iboutlets to the uitableviewcell.
This is the code in the .h file for the custom cell.
#interface ScrollableCell : UITableViewCell <UIScrollViewDelegate>
#property (nonatomic, strong) IBOutlet UIScrollView *scrollView;
#property (nonatomic, strong) IBOutlet UIPageControl *pageControl;
#property (nonatomic, strong) NSMutableArray *viewControllers;
This is the code from the .m file for the custom cell after synthesizing the properties.
- (CGSize)contentSizeForPagingScrollView {
CGRect bounds = self.scrollView.bounds;
return CGSizeMake(bounds.size.width * [self.viewControllers count] , bounds.size.height );
}
#- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
UIView *blueView = [[UIView alloc] initWithFrame:CGRectZero];
[blueView setBackgroundColor:[UIColor blueColor]];
UIView *redView = [[UIView alloc] initWithFrame:CGRectZero];
[redView setBackgroundColor:[UIColor redColor]];
UIView *yellowView = [[UIView alloc] initWithFrame:CGRectZero];
[yellowView setBackgroundColor:[UIColor yellowColor]];
self.viewControllers = [NSArray arrayWithObjects:blueView, redView, yellowView,nil];
for (UIView *view in self.viewControllers) {
[view setFrame:CGRectMake([self.viewControllers indexOfObject:view]*self.scrollView.frame.size.width+5,
5,
self.scrollView.frame.size.width-10,
self.scrollView.frame.size.height-10.0)];
[self.scrollView addSubview:view];
}
self.pageControl.numberOfPages = [self.viewControllers count];
self.pageControl.currentPage = 0;
self.scrollView.pagingEnabled = YES;
self.scrollView.backgroundColor = [UIColor blackColor];
self.scrollView.showsVerticalScrollIndicator = NO;
self.scrollView.showsHorizontalScrollIndicator = NO;
self.scrollView.contentSize = [self contentSizeForPagingScrollView];
self.scrollView.delegate = self;
[self.contentView addSubview:self.scrollView];
[self.contentView addSubview:self.pageControl];
}
return self;
}
-(void) scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat pageWidth = scrollView.frame.size.width;
int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
self.pageControl.currentPage = page;
}
Hope you guys can help me get this working. If there is a different approach/ better approach that what I'm attempting, let me know.
For visual reference of what I'm trying to achieve, i think the pulse news app fits the bill
http://itunes.apple.com/us/app/pulse-news-for-iphone/id377594176?mt=8
its a table view, with horizontal scrolling on each cell.
Thanks.
If there is a different approach/ better approach that what I'm attempting, let me know.
For visual reference of what I'm trying to achieve, i think the pulse news app fits the bill.
If your main reference for designing this is the Pulse app, you should know that the developers actually did NOT resort to scrollviews. The Pulse app is all tableviews. One main vertical tableview and several horizontal "hacked" tableviews inside each cell that composes the vertical one.
Its quite a smart implementation for a tableview. But who better to explain this to you than the developers of Pulse themselves; they were invited to a lecture at Stanford for their iOS programming course.
Its on iTunes U and it's at 6 min in the lecture called 10 Hacks to Go From Idea To #1 App. You should watch it and maybe rethink your app design if it suits you better.
I'm sure I'm overlooking the obvious as I've got countless working buttons...but...for whatever reason this one is not cooperating...
I've added a UIButton (Rounded Rect) to a UIView subclass (DialogView) which is a subview of my view controller's view. This subview is created almost entirely in IB. I've wired up the button to (IBAction)okButtonPressed:(id)sender in IB to Touch Up Inside and created a corresponding method in DialogView. However when I "touch" this button it doesn't trigger the method. userInteractionEnabled is true for the VC's view, DialogView and the UIButton.
Thinking maybe initWithCoder had to do some frame manipulation or something I added the following which successfully logs to console.
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super initWithCoder:decoder]) {
NSLog(#"DialogView initWithCoder called");
}
return self;
}
In further exploration I wired up an IBOutlet to the button and then if I try to change the titleLabel from the view controller I notice that it get's severely truncated. Default text of say "Press Me!" set in IB displays fine when view is first drawn. But if I change the text...
self.DialogView.okButton.titleLabel.text = #"Not Working";
...it gets truncated to "N..."
Dunno if this is related. Probably...
Anyone see what I've screwed up here?
Edit (adding code related to showing UIButton):
From the View Controller:
self.DialogView = [[[NSBundle mainBundle] loadNibNamed:#"DialogView" owner:self options:nil] objectAtIndex:0];;
self.DialogView.myVC = self;
self.DialogView.backgroundColor = [UIColor clearColor];
self.DialogView.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2);
self.DialogView.nameLabel.text = loan.fullName;
self.DialogView.noteLabel.text = loan.summaryOfLoan;
self.DialogView.amountLabel.text = [currencyFormatter stringFromNumber:loan.originalAmount];
self.DialogView.alpha = 0.0;
[self.view addSubview:DialogView];
The UILabels all displaying as expected. As is the problem UIButton. I can see it I just can't interact with it!?!
DialogView's interface:
#class MyViewController;
#interface DialogView : UIView {
IBOutlet UILabel *nameLabel, *noteLabel, *amountLabel;
IBOutlet UIImageView *arrowView;
IBOutlet UIButton *okButton;
MyViewController *myVC;
}
#property (nonatomic, retain) UILabel *nameLabel, *noteLabel, *amountLabel;
#property (nonatomic, retain) UIImageView *arrowView;
#property (nonatomic, assign) MyViewController *myVC;
#property (nonatomic, retain) UIButton *okButton;
- (IBAction)okButtonPressed:(id)sender;
#end
And DialogView's implementation:
#import "DialogView.h"
#import "MyViewController.h"
#implementation DialogView
#synthesize nameLabel, noteLabel, amountLabel, arrowView, okButton;
#synthesize myVC;
- (void)dealloc {
[nameLabel release];
[noteLabel release];
[amountLabel release];
[arrowView release];
[okButton release];
[super dealloc];
}
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
}
return self;
}
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super initWithCoder:decoder]) {
NSLog(#"DialogView initWithCoder called");
}
return self;
}
- (IBAction)okButtonPressed:(id)sender {
NSLog(#"pressed DialogView OK button");
[self.myVC.navigationController popViewControllerAnimated:YES];
}
- (void)drawRect:(CGRect)rect {
// Drawing code
}
#end
I thought that we should use -setTitle:forState: in order to set button's title ?
An other thought, did you check that the button's frame is not CGRectZero ? And by the way, all the frames for the view in the hierarchy ? And check that one superview in the hierarchy is not user interaction disabled ?
And, I think imageView does not respond to touches, do you have one in your code ?
I was just having more or less the same problem and I found that my containing view did not have "User Interaction Enabled".
Hope this helps.
Do you maybe have two buttons on top of one another? Change the IB project window to the detail view and see if your view has more buttons than you are expecting. Maybe you've wired up a button that's not actually getting the press you're expecting.