I started writing a project without really using the storyboard (as i was following a tutorial about creating an options menu for a game).
So i am trying to retroactively implement a way of moving between 2 UIViewControllers (MainGame and MatchScreen) that each has a UIView (MainMenu and VSComputer respectively).
Now in the MainMenu UIView I have created 4 buttons, with all their positions and settings, two of which are subviews - options and stats - that so appear and disappear when clicked on the current ViewController.
Now, the problem is, I need to somehow move from the main menu to the match screen using one of the other buttons named 'matchScreen'. I feel there must be something that i'm missing as no answer found in my research has worked for me yet.
I have embedded my main UIViewController with a navController (as suggested in other questions that i've seen) and then added a push segue to the 2nd ViewController. But i don't know how to make this apparent to my code. My code recognises nothing that i do in the storyboard it seems.
#interface JDTHMainMenu(){
JDTHOptionsScreen *theOptionScreen;
JDTHPlayerStats *thePlayerStatsScreen;
UIButton *optionScreenButton;
UIButton *matchScreenButton;
UIButton *deckScreenButton;
UIButton *statsScreenButton;
bool isPhone;
bool isOptionScreenOpen;
bool isPlayerStatsScreenOpen;
bool matchScreenButtonPressed;
UIView *tint;
int screenWidth;
int screenHeight;
AppData *appData;
}
So to be clear, JDTHMainMenu is a UIView in the JDTHViewController - the original, JDTHMatchScreen is the second ViewController and there is a UIView called JDTHVersusScreen. Im trying to go from one to the other. Also the JDTHAppDelegate only has return YES in the didFinishLaunching method. I feel i'm getting closer....but perhaps not...
-(id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
appData = [AppData sharedData];
NSLog(#"Main Menu has loaded");
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
isPhone = YES;
} else {
isPhone = NO;
}
screenWidth = self.frame.size.width;
screenHeight = self.frame.size.height;
isOptionScreenOpen = NO;
isPlayerStatsScreenOpen = NO;
matchScreenButtonPressed = NO;
[self showOptionScreenButton];
[self showPlayerStatsScreenButton];
[self matchViewControllerButton:matchScreenButton];
[self deckViewerControllerButton:deckScreenButton];
}
return self;
}
-(IBAction)matchViewControllerButton:(UIButton *)sender{
CGRect rect;
UIFont *theFont;
if (isPhone == YES) {
rect = CGRectMake(38, 150, 134, 116);
theFont = [UIFont fontWithName:#"Zapfino" size:22];
} else {
rect = CGRectMake(275, 600, 200, 150);
theFont = [UIFont fontWithName:#"Zapfino" size:28];
}
matchScreenButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[matchScreenButton addTarget:self action:#selector(goToMatchView:sender:) forControlEvents:UIControlEventTouchDown];
[matchScreenButton.titleLabel setFont:theFont];
[matchScreenButton setTitle:#"VS Comp" forState:UIControlStateNormal];
matchScreenButton.frame = rect;
[matchScreenButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[matchScreenButton setBackgroundImage:[UIImage imageNamed:#"MainMenuOptionButton"] forState:UIControlStateNormal];
[self addSubview:matchScreenButton];
}
-(void)goToMatchView:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"pushToMatchView"]) {
JDTHMatchScreen *vc2 = (JDTHMatchScreen *)segue.destinationViewController;
vc2.name = self.textField.text;
}
}
A storyboard app has an entry in the info.plist file that tells the app that it's a storyboard app, and what the name of the storyboard is ("Main storyboard file base name" is the name of the key, and the value is the file name, without extension, of your storyboard). You would need to add this to your project to get it to recognize the storyboard. Also, non-storyboard apps usually have code in the app delegate to set up the initial controllers. This should be deleted for a storyboard app (the only code in the default template in didFinishLaunchingWithOptions: is "return YES"). It might be easier to just start a new storyboard based single view project and copy your files to it.
Related
I have a custom UIImagePickerController that works nicely, only I am facing one issue that I feel should be fairly simple - I just have yet to figure out the solution.
Upon touching my custom added "photo" button, I have it targeted to the build in takePicture method of the UIIPC. Here is my code
#interface CustomCameraController ()
#end
#implementation CustomCameraController {
CGFloat width, height;
}
- (instancetype)init {
if (self = [super init]) {
width = self.view.frame.size.width, height = self.view.frame.size.height;
self.allowsEditing = YES;
self.sourceType = UIImagePickerControllerSourceTypeCamera;
self.showsCameraControls = NO;
self.toolbarHidden = YES;
[self buildCameraOverlay];
}
return self;
}
- (void)buildCameraOverlay {
UIView *customOverlay = [UIView alloc] ...
// ... Custom overlay setup done here
_takePhoto = [[CustomButton alloc] initWithFrame:CGRectMake(0, 0, heightBottomBar*.5, heightBottomBar*.5)];
_takePhoto.center = CGPointMake(bottomBar.frame.size.width/2, bottomBar.frame.size.height/2);
[_takePhoto setImage:[UIImage imageNamed:#"camera button icon"] forState:UIControlStateNormal];
[_takePhoto addTarget:self action:#selector(takePicture) forControlEvents:UIControlEventTouchUpInside];
[bottomBar addSubview:_takePhoto];
// ...
self.cameraOverlayView = customOverlay;
}
This is done in my custom controller CustomCameraController init call.
The problem is, upon taking the picture via takePicture, the camera shutter goes off, everything works just fine, but the controller dismisses itself. I'm trying to figure out how to stop it from closing immediately after taking the picture, so I can A)present the taken picture, and B) give the user the option to choose the image or cancel and retake another one (returning to the camera)
If anyone knows why this happens or something that I am missing / doing incorrectly please let me know. I'm sure it's a simple answer - just can't seem to figure it out. Thanks!
The most common reason for such a weird behaviour is usually lack of delegate methods (for UIImagePickerController in this case) or their wrong implementation.
I have programmatically generated UIButtons that all share the same selector method. When the method runs I would like the method to know which button was pressed and then be able to load a corresponding UIViewController.
-(void)buildButtons
{
for( int i = 0; i < 5; i++ ) {
UIButton* aButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[aButton setTag:i];
[aButton addTarget:self action:#selector(buttonClicked:)forControlEvents:UIControlEventTouchUpInside];
[aView addSubview:aButton];
}
Then:
- (void)buttonClicked:(UIButton*)button
{
NSLog(#"Button %ld clicked.", (long int)[button tag]);
// code here that picks the correct viewController to push to...
// for example tag 1 would create an instance of vcTwo.m and would then be pushed to the navigationController and be displayed on screen
}
say I have three UIViewController classes (vcOne.m, vcTwo.m, vcThree.m) and I want it so that when the button is pressed 'buttonClicked' is run and the code picks the corresponding viewController to push to. I don't want to use a series of if statements as there may be dozens/hundreds of viewControllers in the end. Would I have to instantiate all of the viewControllers and put them in an array? Is there a better way?
Are you using storyboards? so you can chose a segue according to the button tag:
int i = (int)[button tag];
[self performSegueWithIdentifier:[NSString stringWithFormat:#"Segue%d", i] sender:self];
or:
UIViewController *viewController= [controller.storyboard instantiateViewControllerWithIdentifier:NSString stringWithFormat:#"ViewControllerNumber%d", i];
In the end I went with this:
- (void) buttonClicked:(id)sender
{
NSLog(#"Button tag = %li", (long)[sender tag]);
FormularyVC *formularyVCInstance = [FormularyVC alloc];
ProceduresVC *proceduresVCInstance = [ProceduresVC alloc];
VetMedVC *vetMedVCInstance = [VetMedVC alloc];
NSArray *vcArray = [NSArray arrayWithObjects:formularyVCInstance, proceduresVCInstance, vetMedVCInstance, nil];
UIViewController *vcToLoad = [vcArray objectAtIndex:(int)[sender tag]];
vcToLoad.view.backgroundColor = [UIColor whiteColor];
[self.navigationController pushViewController:vcToLoad animated:NO];
}
I created an array of the ViewControllers I wanted to be able to load based on which button was pressed. When the button is pressed the method is run and the tag is taken as a parameter. This tag is used to find the ViewController needed by checking its location on the array's index.
I have 2 Custom View classes(CustomView_A, CustomView_B) derived from UIView. I have UIViewController that should be able to switch between views at run-time..
What so far, I have done is.. in the Storyboard, I am using CustomView_A class as the View class.
#interface MyViewController: UIViewController
#property (nonatomic, weak) CustomView_A *customView_A;
Now I have the second CustomView_B class and I want to change view of MyViewController's view to CustomView_B at run-time.
How can I do that? Thanks in advance..
okay, here is the code as you want -
in your MyViewController.h put -
#property (nonatomic, retain) CustomView_A *customView_A;
#property (nonatomic, retain) CustomView_B *customView_B;
-(void)switchView; // to switch the views.
in your MyViewController.m put -
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.customView_A = [[CustomView_A alloc]initWithFrame:self.view.frame];
self.customView_A.backgroundColor = [UIColor redColor];
UIButton *trigger = [UIButton buttonWithType:UIButtonTypeRoundedRect]; // Just take this button so that your switchView methods will get called on click of this method.
[trigger setFrame:CGRectMake(10, 10, 50, 30)];
[trigger setTitle:#"Click" forState:UIControlStateNormal];
[trigger addTarget:self action:#selector(switchView) forControlEvents:UIControlEventTouchUpInside];
[self.customView_A addSubview:trigger];
[self.view addSubview:self.customView_A];
self.customView_B = [[CustomView_B alloc]initWithFrame:self.view.frame];
self.customView_B.backgroundColor = [UIColor yellowColor];
self.customView_B.hidden = YES;
[self.view addSubview:self.customView_B];
}
- (void)switchView
{
[UIView animateWithDuration:10 delay:10 options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.customView_A.hidden = YES;
self.customView_B.hidden = NO;
} completion:nil];
}
Do opposite when you again want to switch the views.
Don't. What you're describing is an essential misunderstanding of UIViewController. Once a UIViewController instance has a view, that is its view forever.
If you want two different views then either:
Use two view controllers (for example, you can present view controller B and its view on top of view controller A and its view, using a modal segue), or
Make at least one of those views not be owned by a view controller: just place that view in front of the other view and later remove it again, at will.
Try this:
- (IBAction)changeView {
if (self.customView_A.hidden == YES) {
self.customView_A.hidden = NO;
self.customView_B.hidden = YES;
//You should use a UIView animation here to do this.
}
else {
self.customView_A.hidden = YES;
self.customView_B.hidden = NO;
//Same here
)
}
In your viewDidLoad add the view to CGRectZero
- (void)viewDidLoad {
self.customView_A = [[CustomView_A alloc]initWithFrame:CGRectZero];
[self.view addSubview:self.customView_A];
//do the same with the other custom view
}
Sorry if the code is a little faulty, I didn't use Xcode to type this up.
I'm looking for popover in iPhone and i want to make it like iOS 5 Reader feature:
After little research i found WEPopover and FPPopover but i'm looking if there anything like this API built-in iphone SDK.
You could make a UIView with some custom artwork and display it with an animation on top of your view as a "popover" with some buttons like so:
UIView *customView = [[UIView alloc] initWithFrame:CGRectMake(25, 25, 100, 50)]; //<- change to where you want it to show.
//Set the customView properties
customView.alpha = 0.0;
customView.layer.cornerRadius = 5;
customView.layer.borderWidth = 1.5f;
customView.layer.masksToBounds = YES;
//Add the customView to the current view
[self.view addSubview:customView];
//Display the customView with animation
[UIView animateWithDuration:0.4 animations:^{
[customView setAlpha:1.0];
} completion:^(BOOL finished) {}];
Don't forget to #import <QuartzCore/QuartzCore.h>, if you want to use the customView.layer.
Since iOS8 we are now able to create popovers, that will be the same on iPhone, as on iPad, which would be especially awesome for those who make universal apps, thus no need to make separate views or code.
You can get the class as well as demo project here: https://github.com/soberman/ARSPopover
All you need to do is subclass UIViewController, conform to the UIPopoverPresentationControllerDelegate protocol and set desired modalPresentationStyle along with the delegate value:
// This is your CustomPopoverController.m
#interface CustomPopoverController () <UIPopoverPresentationControllerDelegate>
#end
#implementation CustomPopoverController.m
- (instancetype)init {
if (self = [super init]) {
self.modalPresentationStyle = UIModalPresentationPopover;
self.popoverPresentationController.delegate = self;
}
return self;
}
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller {
return UIModalPresentationNone; //You have to specify this particular value in order to make it work on iPhone.
}
Afterwards, instantiate your newly created subclass in the method from which you want to show it and assign two more values to sourceView and sourceRect. It looks like this:
CustomPopoverController *popoverController = [[CustomPopoverController alloc] init];
popoverController.popoverPresentationController.sourceView = sourceView; //The view containing the anchor rectangle for the popover.
popoverController.popoverPresentationController.sourceRect = CGRectMake(384, 40, 0, 0); //The rectangle in the specified view in which to anchor the popover.
[self presentViewController:popoverController animated:YES completion:nil];
And there you have it, nice, neat blurred popover.
I know this is really basic stuff but i need to understand whether my understanding of this is correct.
So what i want to do is this. I want an view with a label on which when double tapped flips and loads another view. On the second view i want a UIPickerView and above i have a button saying back. Both views will be of same size as an UIPickerView which is 320px x 216px.
What i am thinking of to do is create two UIViewclasses named labelView and pickerView. I would then create a viewController which on loadView loads labelView then when user double taps the labelView i get an event in labelView class which is sent to my viewController that then can unload loadView and load the pickerView.
Does this sound as the best way to do this ? Is there a simpler way ? I am also unsure how i route the event from the labelView class to the viewControllerclass.
I dont exactly know the most efficient way to do it(as i am also now to this language),but it is for sure that i have solved ur problem. I made a simple program for that.Three classes involved here in my eg are BaseViewController (which will show two views),LabelView and PickerView (according to ur requirement).
In LabelView.h
#protocol LabelViewDelegate
-(void)didTapTwiceLabelView;
#end
#interface LabelView : UIView {
id <LabelViewDelegate> delegate;
}
#property(nonatomic,retain)id <LabelViewDelegate> delegate;
-(void)didTouch;
#end
In LabelView.m
#synthesize delegate;
-(id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self)
{
UILabel* labl = [[UILabel alloc] initWithFrame:CGRectMake(10, 5, frame.size.width-20,20)];
labl.text = #"Some Text";
[self addSubview:labl];
[labl release]; labl = nil;
self.backgroundColor = [UIColor grayColor];
UITapGestureRecognizer* ges = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(didTouch)] autorelease];
ges.numberOfTapsRequired = 2;
[self addGestureRecognizer:ges];
}
return self;
}
-(void)didTouch
{
[delegate didTapTwiceLabelView];
}
//=============================================================
In Pickerview.h
#protocol PickerViewDelegate
-(void)didTapBackButton;
#end
#interface PickerView : UIView <UIPickerViewDelegate,UIPickerViewDataSource>{
id <PickerViewDelegate> delegate;
}
#property(nonatomic,retain)id <PickerViewDelegate> delegate;
#end
In Pickerview.m
#implementation PickerView
#synthesize delegate;
-(id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self)
{
UIPickerView* picker = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 30, 320, 216)];
picker.delegate = self;
picker.dataSource = self;
[self addSubview:picker];
[picker release]; picker = nil;
self.frame = CGRectMake(frame.origin.x, frame.origin.y, 320, 250);
UIButton* btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn setFrame:CGRectMake(10, 1, 50, 27)];
[btn setTitle:#"Back" forState:UIControlStateNormal];
[btn addTarget:self action:#selector(backButton) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:btn];
}
return self;
}
-(void)backButton
{
[delegate didTapBackButton];
}
//====================================================================
in BaseViewController.h
#import "LabelView.h"
#import "PickerView.h"
#interface VarticalLabel : UIViewController<UITextFieldDelegate,PickerViewDelegate,LabelViewDelegate> {
PickerView* myPickerView;
LabelView* myLabelView;
}
#end
In BaseViewController.m
-(void)viewDidLoad
{
[super viewDidLoad];
myPickerView= [[PickerView alloc] initWithFrame:CGRectMake(0, 50, 320, 250)];
[self.view addSubview:myPickerView];
myPickerView.delegate = self;
myLabelView= [[LabelView alloc] initWithFrame:CGRectMake(0, 50, 320, 250)];
[self.view addSubview:myLabelView];
myLabelView.delegate = self;
myPickerView.hidden = YES;
}
#pragma mark PickerViewDelgate
-(void)didTapBackButton
{
myPickerView.hidden = YES;
myLabelView.hidden = NO;
}
#pragma mark LabelViewDelegate
-(void)didTapTwiceLabelView
{
myPickerView.hidden = NO;
myLabelView.hidden = YES;
}
To get events from a button to the view controller, just hook up the button's event, e.g. touch up inside, to a method in the view controller, using interface builder. (Double tapping is probably more complicated though.)
When you say 'flips', do you mean it actually shows an animation of flipping over a view to show a 'reverse' side? Like in the weather app when you hit the 'i' button? I'm assuming this is what you mean.
Perhaps check TheElements sample example on the iPhone Reference Library, it has an example of flip animation.
Btw, it's not strictly necessary to unload the loadView that is being 'hidden' when you flip -- it saves you having to construct it again when you flip back -- but it may be pertinent if you have memory use concerns, and/or the system warns you about memory being low.
Also, what do you mean by "create a UIView"? Do you mean subclass UIView, or just instantiate a UIVIew and add children view objects to it? The latter is the usual strategy. Don't subclass UIView just because you want to add some things to a UIView.
If you've got one screen of information that gives way to another screen of information, you'd normally make them separate view controllers. So in your case you'd have one view controller with the label and upon receiving the input you want, you'd switch to the view controller composed of the UIPickerView and the button.
Supposing you use Interface Builder, you would probably have a top level XIB (which the normal project templates will have provided) that defines the app delegate and contains a reference to the initial view controller in a separate XIB (also supplied). In the separate XIB you'd probably want to add another view controller by reference (so, put it in, give it the class name but indicate that its description is contained in another file) and in that view controller put in the picker view and the button.
The point of loadView, as separate from the normal class init, is to facilitate naming and linking to an instance in one XIB while having the layout defined in another. View controllers are alloced and inited when something that has a reference to them is alloced and inited. But the view is only loaded when it is going to be presented, and may be unloaded and reloaded while the app is running (though not while it is showing). Generally speaking, views will be loaded when needed and unnecessary views will be unloaded upon a low memory warning. That's all automatic, even if you don't put anything in the XIBs and just create a view programmatically within loadView or as a result of viewDidLoad.
I've made that all sound more complicated than your solution, but it's actually simpler because of the amount you can do in Interface Builder, once you're past the curve of learning it. It may actually be worth jumping straight to the Xcode 4 beta, as it shakes things up quite a lot in this area and sites have reported that a gold master was seeded at one point, so is likely to become the official thing very soon.
With respect to catching the double tap, the easiest thing is a UITapGestureRecognizer (see here). You'd do something like:
// create a tap gesture recogniser, tell it to send events to this instance
// of this class, and to send them via the 'handleGesture:' message, which
// we'll implement below...
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleGesture:)];
// we want double taps
tapGestureRecognizer.numberOfTapsRequired = 2;
// attach the gesture recogniser to the view we want to catch taps on
[labelView addGestureRecognizer:tapGestureRecognizer];
// we have an owning reference to the recogniser but have now given it to
// the label. We don't intend to talk to it again without being prompted,
// so should relinquish ownership
[tapGestureRecognizer release];
/* ... elsewhere ... */
// the method we've nominated to receive gesture events
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer
{
// could check 'gestureRecognizer' against tapGestureRecognizer above if
// we set the same message for multiple recognisers
// just make sure we're getting this because the gesture occurred
if(gestureRecognizer.state == UIGestureRecognizerStateRecognized)
{
// do something to present the other view
}
}
Gesture recognisers are available as of iOS 3.2 (which was for iPad only; so iOS 4.0 on iPhone and iPod Touch).