I'm trying to create a basic project that uses labels and gesture recognition but am having a little difficulty. I created an interface of 4 UILabels positioned vertically along the center of the app screen. Looks something like:
Question
Next Question
Answer
Show Answer
...just 4 labels, positioned one after the next. I make connections from Interface Builder to my ViewController.h file to create IBOutlet properties for each of the labels. Simple enough. I then go ahead and write all the implementation code in the .m file (seen below). The only issue is when the app runs, all that is displayed is four positioned labels with the default "Label" text from Interface Builder. The gesture recognizers I added to two of the labels do not seem to be connected/triggering either.
Hoping someone could shed a little light on the issue for me. What started as a simple exercise is driving me a little mad.
Here's my Viewcontroller.h file:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (nonatomic, strong) IBOutlet UILabel *labelQuestion;
#property (nonatomic, strong) IBOutlet UILabel *labelNextQuestion;
#property (nonatomic, strong) IBOutlet UILabel *labelAnswer;
#property (nonatomic, strong) IBOutlet UILabel *labelShowAnser;
#end
And here's my Viewcontroller.m file:
#import "ViewController.h"
#interface ViewController ()
#property (nonatomic, copy) NSArray *arrayQuestions;
#property (nonatomic, copy) NSArray *arrayAnswers;
#property (assign) int incrementer;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// First create the datasource arrays with five elements each
self.arrayQuestions = [[NSArray alloc] init];
self.arrayQuestions = [NSArray arrayWithObjects:#"Who am I?", #"Who are you?", #"Where are we?", #"What's going on?", #"Why is this happening?", nil];
self.arrayAnswers = [[NSArray alloc] init];
self.arrayAnswers = [NSArray arrayWithObjects:#"Damian", #"Dmitri", #"I don't know.", #"You tell me.", #"I have no clue", nil];
// Reset the incrementer's value
self.incrementer = 0;
// Next configure the labels
self.labelQuestion = [[UILabel alloc] init];
self.labelQuestion.text = [self.arrayQuestions objectAtIndex:self.incrementer];
self.labelNextQuestion = [[UILabel alloc] init];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(nextQuestionLabelPressed)];
[tap setNumberOfTapsRequired:1];
[self.labelNextQuestion addGestureRecognizer:tap];
self.labelNextQuestion.text = #"Show next question";
self.labelAnswer = [[UILabel alloc] init];
self.labelAnswer.text = #"?????";
self.labelShowAnser = [[UILabel alloc] init];
self.labelShowAnser.userInteractionEnabled = YES;
UITapGestureRecognizer *tapp = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(showAnswerLabelPressed)];
[tapp setNumberOfTapsRequired:1];
[self.labelShowAnser addGestureRecognizer:tapp];
self.labelShowAnser.text = #"Show answer";
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)nextQuestionLabelPressed {
self.incrementer++;
self.labelQuestion.text = [self.arrayQuestions objectAtIndex:self.incrementer];
self.labelAnswer.text = #"?????";
}
- (void)showAnswerLabelPressed {
self.labelAnswer.text = [self.arrayAnswers objectAtIndex:self.incrementer];
}
#end
Let's say you've connected a label from the storyboard to self.labelQuestion. So far so good. When you launch, self.labelQuestion points to the label from the storyboard, which is the one that you see in the visible interface of the running app.
But then you say:
self.labelQuestion = [[UILabel alloc] init];
That disconnects the outlet and replaces the value of that variable (self.labelQuestion) with a new blank label! So anything you now do to labelQuestion cannot possibly affect the one from the storyboard, as you have broken the connection.
You do that for all four labels, so you have smashed all four outlets into dust. Thus, your other code has no effect on the visible labels, which come from the storyboard.
Just delete all four [[UILabel alloc] init] lines, and all will be well.
Related
I am trying to implement an UIPickerView programmatically. I have implemented the delegate and datasource.
When I first navigate to the UIPickerView everything works fine. If I leave the View and come back to it later, the UIPickerView looks fine.
But when I try to select an other item it crashes.
When I debug it, I saw that my data array is empty. But I don’t know why.
In on class I init the UIPickerView:
DropDownController *objPickerView = [[DropDownController alloc] init];
objPickerView.userInfo = userInfo;
[objPickerView setDataSourceForPickerView:[dropDownItem valueForKey:#"dropDownEntries"] withPreselectedItem:preSelectedItem];
[dropDownContainer addSubview:objPickerView.picker];
The Picker is in this Controller:
#interface DropDownController : UIViewController <FormElement, UIPickerViewDelegate, UIPickerViewDataSource>
{
NSArray *dropDownData;
UIPickerView *picker;
UIElement *userInfo;
}
#property (strong, nonatomic) NSArray *dropDownData;
#property (strong, nonatomic) IBOutlet UIPickerView *picker;
#property (nonatomic, retain) UIElement *userInfo;
-(void)setDataSourceForPickerView:(NSArray *)dataDictionary withPreselectedItem:(NSString*) preSelectedItem;
#end
Here I set the Delegate and Datasource:
-(void)setDataSourceForPickerView:(NSMutableArray *)dataDictionary withPreselectedItem:(NSString*) preSelectedItem{
picker = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 0, 300, 162)];
picker.delegate = self;
picker.dataSource = self;
picker.showsSelectionIndicator = YES;
dropDownData = dataDictionary;
}
After returning to the View dropDownData is empty.
I've found the problem. I need to copy the dictionary, not only set a reference.
// dropDownData = dataDictionary; -> wrong
dropDownData = [dataDictionary copy];
Writing my first iphone app through this tutorial--I have a custom UIView with the method showDie and I have two UIView elements on my storyboard that I've hooked up to my ViewController. However, both the UIView elements have the error message "No visible #interface for 'UIView' declares the selector 'showDie'. I think that's because the UIViews aren't subclassing XYZDieView, but when I control-clicked from my Storyboard, only UIView appeared in the dropdown--no XYZDieView. I tried just changing the text manually in the ViewController.h, but that didn't work (I didn't think it would). Am I on the right track? How do I use my subclass? (xcode 5)
XYZDieView.m
-(void)showDie:(int)num
{
if (self.dieImage == nil){
self.dieImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 90, 90)];
[self addSubview:self.dieImage];
}
NSString *fileName = [NSString stringWithFormat:#"dice%d.png", num];
self.dieImage.image = [UIImage imageNamed:fileName];
}
XYZViewController.h
#import <UIKit/UIKit.h>
#import "XYZDieView.h"
#interface XYZViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *sumLabel;
#property (strong, nonatomic) IBOutlet UIButton *rollButton;
#property (strong, nonatomic) IBOutlet UIView *leftDieView;
#property (strong, nonatomic) IBOutlet UIView *rightDieView;
#end
XYZViewController.m
#import "XYZViewController.h"
#import "XYZDiceDataController.h"
#interface XYZViewController ()
#end
#implementation XYZViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)rollButtonClicked:(UIButton *)sender {
XYZDiceDataController *diceDataController = [[XYZDiceDataController alloc] init];
int roll = [diceDataController getDiceRoll];
int roll2 = [diceDataController getDiceRoll];
[self.leftDieView showDie:roll];
[self.rightDieView showDie:roll2];
int sum = roll + roll2;
NSString *sumText = [NSString stringWithFormat:#"Sum is %i", sum];
self.sumLabel.text = sumText;
}
#end
Yes you're right, the problem is subclassing. In order to have a view conform to a subclass in the interface builder you need to
Click on the UIView in your storyboard
Open the right inspector panel
Click on the third tab for identity inspector
Add your class
Then try to connect it to your ViewController again.
Edit: Afterwards you may need to cast XYZDieView myView = (XYZDieView*)leftDieView;
I'm having a main view controller containing a UIScrollView called containerScrollView. This scrollview has on each page another scrollview with the size of the screen containing two view controllers: MessagesViewController and InfoViewController. Here's a schema.
The personScrollView in the containerScrollView works fine but the problem is in the adding of the two view controllers' view to the personScrollView.
#property (nonatomic, retain) MessagesViewController *matchesVC;
#property (nonatomic, retain) InfoViewController *standingsVC;
for (int i = 0; i < 3; i++) {
UIScrollView *personScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(i*320, 0, 320, self.containerScrollView.frame.size.height)];
NSArray *colors = #[[UIColor blueColor], [UIColor orangeColor], [UIColor greenColor]];
[personScrollView setBackgroundColor:[y objectAtIndex:i]];
[personScrollView setPagingEnabled:YES];
[personScrollView setContentSize:CGSizeMake(self.view.frame.size.width * 2, personScrollView)];
[self.containerScrollView addSubview:personScrollView];
/* Populate the scrollview */
// Messages
if (self.messagesVC == nil)
{
self.messagesVC = [[MessagesViewController alloc] init];
[self.messagesVC setFrame:CGRectMake(0, 0, 320, self.containerScrollView.frame.size.height)];
}
[personScrollView addSubview:self.messagesVC.view];
// Info
if (self.infoVC == nil)
{
self.infoVC = [[InfoViewController alloc] init];
[self.infoVC setFrame:CGRectMake(320, 0, 320, self.containerScrollView.frame.size.height)];
}
[personScrollView addSubview:self.infoVC.view];
}
[self.containerScrollView setContentSize:CGSizeMake(320*3, self.containerScrollView.frame.size.height)];
The problem is that the two view controllers (messages and info) only get added once, and to the last personScrollView of containerScrollView.
How to get the view controllers added to all of my personScrollViews? Something wrong with the property declaration?
I have read something about this abusing view controllers, but this is the only solution. There is really a lot of code in the two view controllers and I can't add it to my rootviewcontroller.
The problem is with your understanding of the difference between view controllers and views. You need to read up on Creating Custom Container View Controllers.
Apple doc says:
Views can have only one superview. If view already has a superview and that view is not the receiver, this method removes the previous superview before making the receiver its new superview.
You create your controllers once, but you want to add theirs views three times, to three different parent views. You can't do that.
I ended up creating multiple instances of my view controllers and storing them in an array. Not a great solution, but the best I could find.
#property (strong, nonatomic) MessagesViewController *messagesVC1;
#property (strong, nonatomic) MessagesViewController *messagesVC2;
#property (strong, nonatomic) MessagesViewController *messagesVC3;
#property (strong, nonatomic) MessagesViewController *messagesVC4;
#property (strong, nonatomic) MessagesViewController *messagesVC5;
#property (strong, nonatomic) MessagesViewController *messagesVC6;
self.messagesVC1 = [[MessagesViewController alloc] initWithData:data];
self.messagesVC2 = [[MessagesViewController alloc] initWithData:data];
self.messagesVC3 = [[MessagesViewController alloc] initWithData:data];
self.messagesVC4 = [[MessagesViewController alloc] initWithData:data];
self.messagesVC5 = [[MessagesViewController alloc] initWithData:data];
self.messagesVC6 = [[MessagesViewController alloc] initWithData:data];
self.messagesVCArray = #[self.messagesVC1, self.messagesVC2, self.messagesVC3, self.messagesVC4, self.messagesVC5, self.messagesVC6];
MessagesViewController *messagesVC = [self.messagesVCArray objectAtIndex:i];
[messagesVC setFrame:CGRectMake(0, 0, 320, leagueScrollView.frame.size.height)];
[leagueScrollView addSubview:messagesVC.view];
I am quite new to iOS development and thus new to the concept of storyboard as well.
As this seems to be the 'new thing', everyone should use, I thought I might give it a try as well.
I got a project here, created with a Foo.xib file.
The xib file has several view objects included.
Then I have a class Foo.h and Foo.m class with following content:
Foo.h
#import <UIKit/UIKit.h>
#interface Foo : UIView
#property (strong, nonatomic) IBOutlet UIView *view01;
#property (strong, nonatomic) IBOutlet UIView *view02;
#property (strong, nonatomic) IBOutlet UIView *view03;
#property (strong, nonatomic) IBOutlet UIView *view04;
- (NSUInteger)viewCount;
#end
Foo.m
#import "Foo.h"
#interface Foo()
#property (nonatomic, strong) NSArray *views;
#end
#implementation Foo
- (id)init {
self = [super init];
if (self) {
self.views = [[NSBundle mainBundle] loadNibNamed:#"Foo" owner:self options:nil];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (NSUInteger)viewCount {
return [self.views count];
}
#end
In my ViewController I would then load all the views and make it scrollable, like this:
#import "ViewController.h"
#import "Foo.h"
#interface ViewController ()
#property (nonatomic, strong) UIScrollView *scrollView;
#property (nonatomic, strong) Foo *views;
#end
#implementation ViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.views = [[Foo alloc] init];
CGSize fooSize = self.views.view01.bounds.size;
NSUInteger viewCount = [self.views viewCount];
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, fooSize.width, fooSize.height)];
[self.scrollView setContentSize:CGSizeMake(viewCount*fooSize.width, fooSize.height)];
[self.scrollView setBounces:YES];
[self.scrollView setPagingEnabled:YES];
self.scrollView.delegate = self;
NSArray *views = #[ self.views.view01,
self.views.view02,
self.views.view03,
self.views.view04
];
for (int i=0; i<viewCount; i++) {
UIView *curView = views[i];
CGRect frame = curView.frame;
frame.origin.x = i*fooSize.width;
frame.origin.y = 0;
curView.frame = frame;
[self.scrollView addSubview:curView];
}
[self.view addSubview:self.scrollView];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
However, I have no clue, how to realize this with a storyboard. It seems to me that I have to have a NavigationController which is then linked to the Master View Controller. And now I would have to add a new ViewController for each view? Or is there a way to include all views within one ViewController like I did 'the old way'?
There is a massive mis conception that when using a storyboard it limits you to what you can do. A storyboard is simply like an array of .xib files, it holds many screens in the one file so you can see the entire flow of you app in one place. Inside a storyboard you can create a single viewController and assign a custom viewController class to it and then load / modify what ever you like inside the code of this viewController, as you have done above.
However the benefit of using the storyboard is to have multiple viewController objects so you can design all the screens and navigation there were you can see it, aiding you in debugging and design work.
If you already have the app working without a storyboard and you simply want to use it because its new but keep the old style of coding, you are not going to see much of the benefits. I would suggest following this example from the developer library on how to use a storyboard properly. After you complete this you will see the benefits of the new style and you can decide whether to do it in code or using the interface builder:
http://developer.apple.com/library/ios/#documentation/iPhone/Conceptual/SecondiOSAppTutorial/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011318
I have seen many of the posts on this site as well as many more on Google, and I am stuck on what I am sure is something easy. I am new to iOS, and I have most likely been reading the other posts incorrectly. I am trying to learn and write my own code with my own logic and attempts, so mine looks a good deal different than the others I have found... which is why it doesn't work I am sure!
I am trying to get a picture show using swipe from right to left, but I want the next picture to be pulled over and slide into place, instead of the instant switch I have done here. Based on many recommendations, I am trying to use a UIScrollView. I can't get anything to load and the app crashes on the last line of loadImages. I have debugged but can't seem to figure out why it is not showing the subView.
Here is my viewDidLoad:
[super viewDidLoad];
// Do any additional setup after loading the view.
i = 0;
//add UIPanGestureRecognizer
UIPanGestureRecognizer *aPan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(loadImages)];
[aPan setMaximumNumberOfTouches:1];
[aPan setMinimumNumberOfTouches:1];
[self.view addGestureRecognizer:aPan];
_PhotoBundle = [[NSBundle mainBundle] pathsForResourcesOfType:#".jpg"inDirectory:#"Otter_Images"];
_PhotoArray = [[NSMutableArray alloc] initWithCapacity:_PhotoBundle.count];
for (NSString* path in _PhotoBundle)
{
[_PhotoArray addObject:[UIImage imageWithContentsOfFile:path]];
}
[self loadImages];
Here is my loadImages:
-(void)loadImages
{
NSLog(#"in loadImages");
_currentImage = [_PhotoArray objectAtIndex:i];
_nextImage = [_PhotoArray objectAtIndex:i + 1];
_prevImage = [_PhotoArray objectAtIndex:i -1];
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
[scrollView setScrollEnabled:YES];
[scrollView setClipsToBounds:YES];
if (i <= _PhotoArray.count)
{
[scrollView addSubview:_currentImage]; ////crash!!!!
}
else
{
_currentImage = [_PhotoArray objectAtIndex:0];
[scrollView addSubview:_currentImage]; /// Crash!!!!
}
NSLog(#"end of loadImages");
}
I have left off functionality until I can get one image loaded. I will then add the functionality for the slide.... I hope I am approaching this right.
Thank you very much for any help!!
edit:
Here is my interface in my .m that sets up the ImageViews:
#interface ScrollViewViewController ()
#property (nonatomic, retain) NSArray *PhotoBundle;
#property (nonatomic, retain) NSMutableArray *PhotoArray;
#property (nonatomic, retain) UIImageView *currentImage;
#property (nonatomic, retain) UIImageView *nextImage;
#property (nonatomic, retain) UIImageView *prevImage;
#end
You are adding UIImage objects as subviews. You can't do this. You have to make a UIImageView first - only UIView subclasses can be added as subviews.
Making an image view is simple - you can alloc/initWithImage: and pass in your image object. You'll need to adjust the frames of each image view otherwise they'll all be on top of each other.
Additionally, you don't need a pan gesture recogniser if you're using a scroll view. The scroll view handles this for you.