I'm trying to understand how to make a duplicate of a uiview that has a set of uibuttons inside it.
Been trying to follow this question/answer but I'm really confused atm:
Make a deep copy of a UIView and all its subviews
Basically trying to make a vc that displays two sets of uiviews with buttons. This is what the regular view will look like:
Points of team 1:
+ + + +
1 2 3 P
- - -
Points of team 2:
+ + + +
1 2 3 P
- - -
And I need to make a copy of it. I can probably just drag objects onto the viewcontroller but it'll have way too many IBactions for it if I create another copy.
Thoughts on how to deal with this?
EDIT:
This is how I solved adding multiple buttons
Add a multiple buttons to a view programmatically, call the same method, determine which button it was
First I would create a UIView subclass called PointsView or something.
This will look like this...
Points of [name label]:
+ + + +
1 2 3 P
- - -
It will have properties like NSString *teamName and set those properties up against the relevant labels.
It may also have properties for NSUInteger score so you can set the score value of the PointView object.
This is all completely separate from your UIViewController.
Now, in your UIViewController subclass you can do something like...
PointsView *view1 = [[PointsView alloc] initWithFrame:view1Frame];
view1.teamName = #"Team 1";
view1.score1 = 1;
view1.score2 = 2;
view1.score3 = 3;
[self.view addSubView:view1];
PointsView *view2 = [[PointsView alloc] initWithFrame:view2Frame];
view2.teamName = #"Team 2";
view2.score1 = 1;
view2.score2 = 2;
view2.score3 = 3;
[self.view addSubView:view2];
Now there is not copying involved. You just create two instances of an object.
EDIT
Creating your view subclass...
The easiest way to create your view subclass is to do the following...
Create the files... PointsView.m and PointsView.h
The .h file will look something like this...
#import <UIKit/UIKit.h>
#interface PointsView : UIView
#property (nonatomic, strong) UILabel *teamNameLabel;
// other properties go here...
#end
The .m will look like this...
#import "PointsView.h"
#implementation PointsView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.teamNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 100, 21)];
self.teamNameLabel.backgroundColor = [UIColor clearColor];
[self addSubView:self.teamNameLabel];
// set up other UI elements here...
}
return self;
}
#end
Then in your view controller you add the PointsView to it IN CODE (i.e. not with Interface builder) like this...
- (void)viewDidLoad
{
[super viewDidLoad];
PointsView *pointsView1 = [[PointsView alloc] initWithFrame:CGRectMake(0, 0, 320, 200)];
pointsView1.teamNameLabel.text = #"Team 1";
[self.view addSubView:pointsView1];
// add the second one here...
}
You can also create and add these views in Interface Builder but it's a lot harder to explain on here.
If you set it up this way then you can use IB for setting up the rest of your UIViewController. Just don't use IB to set up the PointsViews. It won't work with the way I've shown here.
Related
I'm try to return a UITextField and a UIView (A rectangle box) at the same time so I can have a text field inside a rectangle UIView coloured box, but I can only return one value at a time. Is it possible to return 2 values? Or can I edit a text field to have a rectangle coloured background? Also, the UITextField is being called programatically, not from the story board.
Any kind of help is greatly appreciated. Thank you.
you can return two values with help of block.
Please find code below it may be helpful to you.
- (void)getUIControlles:(void (^)(UITextField * objTextFiled, UIView * objView))completionBlock {
UITextField * textFiled = nil;
/*
do code here for textfiled
*/
UIView * viewDemo = nil;
/*
do code here for Uiview.
*/
completionBlock (textFiled, viewDemo);
}
- (void) testMethod {
// Call function with following way.
[self getUIControlles:^(UITextField *objTextFiled, UIView *objView) {
// objTextFiled = This is your textfiled object
// objView = This is your view object
}];
}
The common way to return multiple independent values in C, C++, and Objective-C is through pointers:
#interface MyController (UIViewController)
- (void)getView:(UIView **)viewOut textField:(UITextField **)textFieldOut;
#end
#implementation MyController
- (void)getView:(UIView **)view textField:(UITextField **)textField {
UIView *view = [[UIView alloc] init];
// ... initialize view
UITextField *textField = [[UITextField alloc] init];
[view addSubview:textField];
// ... initialize textField
*viewOut = view;
*textFieldOut = textField;
}
#end
Apple uses this pattern, for example in +[NSStream getStreamsToHostWithName:port:inputStream:outputStream:] (which is not in the documentation but is in the header files).
Example use:
UIView *view;
UITextField *textField;
[myController getView:&view textField:&textField];
[myController.view addSubview:view];
Another approach is to return one object directly and the other through a pointer:
- (UITextField *)newTextFieldWithWrapperView:(UIView **)viewOut {
UIView *view = [[UIView alloc] init];
// ... initialize view
UITextField *textField = [[UITextField alloc] init];
[view addSubview:textField];
// ... initialize textField
*viewOut = view;
return textField;
}
Apple uses this pattern, for example in -[NSAttributedString initWithFileURL:options:documentAttributes:error:], which returns the string directly, and optionally returns a document attributes dictionary and an error object through pointers.
Well I've never programming in Objective C but if you insist on returning 2 values, create a Pair class an return that.
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 am wanting to create a custom UIView class that will show a dynamic number of UISegmentedControl objects depending on some input. For example, if a client has 5 products in their cart, the UIView should generate 5 UISegmentedControl objects that I will then link with each item.
The problem I am having is getting this to work in a UIView. Here is what I have done so far. I am successfully able to create a UISegmentedControl object and display it programmatically within my main UIViewController. I don't get any display when adding it to my UIView class. Here is the implementation code for the UIView class:
#import "ajdSegmentView.h"
#implementation ajdSegmentView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
NSArray *itemArray = [NSArray arrayWithObjects:#"Yes", #"No", nil];
UISegmentedControl *button = [[UISegmentedControl alloc] initWithItems:itemArray];
button.frame = CGRectMake(35,44, 120,44);
button.segmentedControlStyle = UISegmentedControlStylePlain;
button.selectedSegmentIndex = 1;
[self addSubview:button];
}
return self;
}
#end
I created a new UIView object via Storyboard and placed it inside the UIViewController scene. I made sure to set the class from the generic UIView class to my new custom class. I added and outlet for the UIView in my UIViewController class. Here is the code inside the implementation of UIViewController:
#import "ajdViewController.h"
#interface ajdViewController ()
#end
#implementation ajdViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.segmentView = [[ajdSegmentView alloc] init];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
That's all I have tried. I have been searching through a lot of pages and trying to implement this without asking here, but I seem to be looking in the wrong places.
First you need to check ajdSegmentView is UIVIew or UIViewController. It is fine if it is UIView. If it is type of UIViewController then you need to add this line while adding Segment.
[self.view addSubview:button];
In place of:
[self addSubview:button];
And One more thing You forget to add this View to your main after allocating so You can declare like this:
objajdSegmentView = [[ajdSegmentView alloc] init];
[self.view addSubview:objajdSegmentView.view];
I have just added this thing. i got result like this way.
Hope this will work for you.
You're initializing your custom view using the init method, but your initialization for ajdSegmentView is in your initWithFrame: method (which in your case is not getting called).
So replace:
self.segmentView = [[ajdSegmentView alloc] init];
with:
// Change the frame to what you want
self.segmentView = [[ajdSegmentView alloc] initWithFrame:CGRectMake(0,0,100,40)];
Also don't forget to add your view to the view controller's view also.
[self.view addSubview:self.segmentView];
Unless this view is being created with interface builder, in which case you will need to override initWithCoder: in your ajdSegmentView class.
I'm not familiar with Storyboard though, so maybe I'm missing something, but in a standard scenario what I said above will solve your problem.
HI have a custom view class that is loaded and placed within my main view using the following code. The reason it that i want to populate it with different content so rather than build a view in code each time if I create a custom class i can reuse this in a loop etc, I got this to work just fine in code, that is laying out the buttons label etc.
But rather than hand code it all I thought if i create a new User Interface View, then construct visually my text fields, labels and buttons on this view.
Then connect it to my custom class.
Bu this is where I am having an issue, how do I connect this view xib file so that it becomes visible when placed on my my code. I have assigned the custom class attribute within the xib file to my custom file, but what else am i missing?
.h File:
#import <UIKit/UIKit.h>
#interface blogView : UIView
#end
.m File:
#import "blogView.h"
#implementation blogView
- (id)init
{
self = [super initWithFrame:CGRectMake(0, 0, 478, 220)];
if (self != nil)
{
NSLog(#"Blog View loaded");
self.backgroundColor = [UIColor yellowColor];
UILabel *titleLbl = [[UILabel alloc] initWithFrame:CGRectMake(20, 20, 400, 40)];
[titleLbl setText:#"This is the Title"];
[self addSubview:titleLbl];
}
return self;
}
#end
my xib file has the same name blogView.xib which is a View User Interface.
Within my main view controller and in the ViewDidLoad i have
blogView *blogItem = [[blogView alloc]init];
[self.view addSubview:blogItem];
When I run this is all works fine, but I would like to link to a .xib file to save time etc.
Thanks
Well having look around and trying bits of clues and suggestion I managed to do this with the following:
Within my .m file I placed/Changed the following:
self = [super init];
if (self != nil)
{
NSArray *theView = [[NSBundle mainBundle] loadNibNamed:#"blogView" owner:self options:nil];
UIView *nv = [theView objectAtIndex:0];
.. rest of code.
[self addSubview:nv];
.. rest of code.
Many Thanks
I struggled with this for an hour when I RENAMED my viewcontroller class. This is what worked for me in Xcode 5
Go to your XIB file
Click on Files owner transparent box on the left
Open up your inspections tab(Third button on right in the View Section - in between Editor and Organizer)
Go to your identity Inspector(3rd from the left) underneath the editor organizer view tab.
Fix the custom class - Class option to whatever class you want it to respond to.
Lets just say I was extremely annoyed after wasting time with that
You might want to create a controller for your view and load that view using initWithNibName:bundle:
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).