In my application, I am trying to remove all existing subviews and add a new one created in Interface Builder. However, I don't seem to be able to connect the view.
When a button clicks, the following function is executed (inside a subclass of UIViewController):
// Display a list of settings to choose from
- (void) settings
{
SettingsRootController *settingsController = [[SettingsRootController alloc] initWithNibName:#"SettingsRootController" bundle:nil];
_settingsController = settingsController;
for (UIView *view in self.view.subviews)
{
[view removeFromSuperview];
}
[self.view addSubview:_settingsController.view2];
int a = [self.view.subviews count];
[self.view setNeedsDisplay];
......
}
#import <UIKit/UIKit.h>
#interface SettingsRootController : UIViewController
{
IBOutlet UIView *_view2;
}
#property(nonatomic, retain) IBOutlet UIView *view2;
Inside the Interface Builder, I created a new View-based xib. Set file owner to SettingsRootController. Randomly drag a UITextView into the xib. Connect the UITextView to view and view2 in SettingsRootController.
However, if the above line is:
[self.view addSubview:_settingsController.view2];
a would always be 0, and thus the new screen is empty.
But if change to:
[self.view addSubview:_settingsController.view];
I could see the UITextView.
When you create a view controller, its view will not be created at initialisation time. The view will be created at first access of the view property. Thats why it works when you use _settingsController.view. But when you access view2 the view will not be loaded. You could write a custom getter method like:
-(UIView*) view2 {
_view2 = self.view;
return _view2;
}
The instance var declaration is different from the property declaration! With and without underscore. Maybe thats why it doesn't work.
Related
I have a UIView designed in XIB file. Basically with a navigation bar and a button to close the UIVIew. I want to show this UIView in several UIViewControllers across the app.
Below is the code I have imp[emnted so far.However the UIView doesn't show up.
My ViewControllers are designed in storyboard however I needed a simple UIView that is why I created a new custom subclass of UIView alongwith a xib file of the same name.The File's Owner is also set to custom subclass. What else is required here?
#interface BottomSlidingView : UIView //this is my UIView
{
}
#implementation BottomSlidingView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setUserInteractionEnabled:YES];
NSLog(#"Bottom View Loaded"); //I get this log entry but the view doesn't showup.
}
return self;
}
- (BOOL)loadMyNibFile {
if (![[NSBundle mainBundle] loadNibNamed:#"BottomSlidingView" owner:self options:nil]) {
return NO;
}
return YES;
}
And this is how I call the custom UIView in my UIViewController.
-(void)shareButton:(UIBarButtonItem *)button
{
NSLog(#"share button clicked");
BottomSlidingView *bsv = [[BottomSlidingView alloc] initWithFrame:CGRectMake(20, 480, 280, 420)];
[bsv loadMyNibFile];
[self.view insertSubview:bsv belowSubview:self.optionsToolBar]; //this toolbar is a subview that I am adding to this view controller.
}
I see a couple problems with your approach:
Create the view instance from a nib file, rather than instantiating it with initWithFrame:. For example (if the custom view is the only root-level object in the nib file):
+ (instancetype)bottomSlidingView
{
return [[[UINib nibWithNibName:#"BottomSlidingView" bundle:nil] instantiateWithOwner:nil options:nil] lastObject];
}
Implement awakeFromNib or initWithCoder: instead of initWithFrame: (which isn't called when loading from nibs) in your UIView subclass.
In your view controller, you would use something like the following to create the view:
- (void)shareButtonPressed:(id)sender
{
BottomSlidingView *slidingView = [BottomSlidingView bottomSlidingView];
[slidingView setFrame:CGRectMake(20, 480, 280, 420)];
[[self view] insertSubview:slidingView belowSubview:[self optionsToolBar]];
}
Hope that helps.
Where ever you want to reuse a UIView of a UIViewController in another view controller you need to use this....
YourViewController * child = [[YourViewController alloc] initWithNibName:#"YourViewController" bundle:nil];//View controller of view , you want to reuse
[self.view addSubView:child.YourView]; //YourView = Name of view, you want to reuse
[self addChildViewController:child];
[child release];
After adding childViewController All IBActions will be work in child.
And after removing this view You have to remove YourViewController from parentViewController other wise there will be memory issues.......
So i'm adding UIView with UIScrollView in it to another view by pressing segment tool:
UIView.h
#interface gettingELORolesViewController : UIViewController{
UIViewController *currentController;
NSMutableArray *viewControllers;
}
- (IBAction)SegmentToggle:(UISegmentedControl *)sender;
#property (strong, nonatomic) IBOutlet UIView *containerView;
UIView.m
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Storyboard_iPhone"
bundle:nil];
firstView *FirstView = [sb instantiateViewControllerWithIdentifier:#"firstOne"];
secondView *SecondView = [sb instantiateViewControllerWithIdentifier:#"secondOne"];
viewControllers = [NSMutableArray arrayWithObjects:FirstView,SecondView,nil];
currentController = FirstView;
[containerView addSubview:FirstView.view];//containerView = self.view
and then depends on what segment is chosen, it shows different view:
- (IBAction)SegmentToggle:(UISegmentedControl *)sender {
UIViewController *selectedView=nil;
if (sender.selectedSegmentIndex==0) {
selectedView= [viewControllers objectAtIndex:0]; // retrieve object at index 0 from viewControllers array
}
else if(sender.selectedSegmentIndex==1){
selectedView= [viewControllers objectAtIndex:1]; // retrieve object at index 1 from viewControllers array
}
[currentController.view removeFromSuperview]; // remove Current displaying view from superView
currentController=selectedView; // make selected View to be the current View
[containerView addSubview:selectedView.view]; // Add newly selected view on container View
}
secondView is tableViewController and everything is working great when i choose second segment. When i choose first one it shows up but scroll doesn't work and i don't know why.
firstView.m viewDidLoad method looks like this:
-(void)viewDidLoad{
[super viewDidLoad];
mainScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
[mainScrollView setContentSize:CGSizeMake(320, 600)];
[mainScrollView setScrollEnabled:YES];
}
Paging is enabled in storyBoard. And it hooked up with .h file of firstView.
Please tell me what am i doing wrong.
When you call -SegmentToggle:,
you create a local variable called selectedView
you assign a view controller to selectedView depending on what segment is selected
the method ends, selectedView goes out of scope
In short, you're not doing anything. You should, at least, replace a view in the current view hierarchy with the view of the selected controller (and maybe stash a reference to the selected controller in an instance variable for later lookup?)
But taking a step back, it looks like you're trying to create a view controller that manages other view controllers. There are now very specific rules around that as of iOS 5. Please watch the WWDC 2011 video called "Implementing UIViewController Containment" for more information and examples.
I have a simple question that I couldn't see it answered on the whole site,
One XIB file that has a UIView as the main,
in it has another UIView that's connected to one IBoutlet so I can assign it later to load a view from another XIB file. That assignment doesn't work.. it remains blank...
#interface Subclass : UIViewController {
UIView *view1;
}
#property (nonatomic,retain) IBOutlet UIView *view1;
#end
i linked view1 to the subview in the xib file
in implementation in initWithNibName I'm loading Other XIB file and use it's view and assigning it to the view1. Still this doesn't work...
detailControler = [[XibViewControler alloc] initWithNibName:#"XibViewControler" bundle:nil];
//one aproach
[self.view1 addSubview: detailControler.view];
//another aproach
[self setView1:detailControler.view];
If I'm adding the subview programaticaly to [self.view addSubview:detailControler.view] and set a frame to it, it will go fullscreen, and hide the original view.
I also had a different approach that didn't work
How to load multiple views on each button tap when using a UISegmentedVIew
This is how I usually set up a UIView inside another view, although I'm not entirely sure if it's best practice or anything like that:
if(newViewController == nil){
newViewController = [[NewViewController alloc] initWithCoder:nil];
}
newViewController.view.autoresizingMask = UIViewAutoresizingNone;
if([newViewController.view superview] == nil){
[view1 addSubview:newViewController.view];
}
Hope that Helps!
In my viewbased application i loaded oneview as mainview and another view as subview of mainview. Its work well the code snippet is ,
In mainviewcontroller,
IBOutlet UIView *subView;
#property(nonatomic,retain) UIView *subView;
#synthesize subView;
[subView release];
//add subview
[self.view addSubview:subView];
//removefromsubview
[subView removeFromSuperview];
This code works fine.....
I dont want to create subview in mainviewcontroller, so i created a new UIView class and its named as subView, now i deleted all declarations of subView from mainviewcontroller and just import subView class in mainviewcontroller. And using this [self.view addSubview:subView];
This things not work great. Can anyone help me ... How can i interact a separate UIView class with UIViewcontroller.One more thing is that UIView class have labels and textboxes can i set values from UIViewController to UIView labels and textboxes ......
Is it possible ?
Thanks in advance.......Sorry for my bad english
You have a sub-class called Subview which is declared as a UIView, i.e.
#interface Subview : UIView {
UILabel *foo;
}
#property (nonatomic, retain) UILabel *foo;
#end
Now you want to use this sub-class inside of your main UIView, which you had from the start. There are a few things you need to do.
#import the Subview in your header file, and add an instance of it to your class.
#import "Subview.h"
and inside of your #interface's {}'s,
Subview *mySubview;
In the viewDidLoad class for your main view controller, around the bottom, add something like:
mySubview = [[Subview alloc] init];
[self.view addSubview:mySubview];
[mySubview release];
First line will allocate a new "Subview" for you, second line will add this to your view so you get the stuff it has, and third line will release it. It's okay to release it here, because "self.view" will now be responsible for it, so it won't vanish.
Lastly you need to set the view up in the init method for Subview. In Subview.m, do something like:
- (id)init
{
if (self = [super init]) {
foo = [[UILabel alloc] init];
foo.text = #"Hello!";
[self addSubview:foo];
}
return self;
}
And I think that should take care of it. You also want to release foo in -dealloc for Subview but you probably know how to do that stuff already.
Without using Interface builder or xib files, what is the correct way to instantiate two classes which inherit from UIView such that they can switch between themselves using UIButtons located on the views themselves?
I think this involves setting up a UIViewController from the app delegate and adding two instances of my classes which implement UIView into the controller (perhaps from inside the controller?)
I'm also not sure how to raise events from UIButtons on the custom UIViews to switch the views. I suspect I would need to add a method to the view controller but I'm not sure how to get a reference to the view controller from inside the scope of my UIView.
Also, I'm wondering that,if the use of a UIViewController is necessary, should the switch method could be in the scope of the main app delegate?
Some code examples would be great!
Your main problem is that you don't conceptually understand the role of UIViewControllers versus UIViews. Most people don't when they first start out.
Views are stupid and ideally, they should be composed of generic objects. They contain virtually none of the logic of the interface. They do not know or care about the existence of other views. The only logic you put in views is logic that pertains to the immediate and generic functioning of the view itself, regardless of the data it displays or the state of other parts of the app. You seldom need to subclass UIView. This is why views can be completely configured in Interface builder without any code.
ViewControllers contain the logic of the interface and connect the interface to the data (but they do not contain or logically manipulate the data.) They are "intelligent" and highly customized. The viewControllers do understand the place of the view in the context of the app. The viewControllers load and configure the views either from nib or programmatically. The viewControllers control when the views are displayed or hidden and it what order. The viewControllers determine what action is taken in response to events and what data gets displayed where.
VictorB's example code shows how this is all done pragmatically. The important thing to note is that the viewController and view are entirely separate objects from two entirely separate classes. There is no overlap and no need to subclass UIView. All the customization is in the controller.
All this is because of the MVC design patter. It decouples the interface from the data model, making them both modular and independent of each other. This makes it easy to design, debug, and reuse each independent module.
If you want to get it done in code, here is an example I just drummed up using lazy loaded UI elements. I'm only making one button here and swapping it between whichever view is active. It's slightly awkward, but it reduces the amount of code necessary to demonstrate this.
I've created two UIViews to represent your custom classes, one with a blue background and one with a red. The button swaps between the two. If you have a unique button already in each of your custom views, you just need to either expose those buttons as properties of your UIView subclasses so your view controller can access them, or add the view controller as a target for the button's action from within your UIView's loading code.
I've tested this code in my simulator and it seems to work fine, but you really should try to understand what's going on here so you can implement it yourself.
ToggleViewController.h:
#import <UIKit/UIKit.h>
#interface ToggleViewController : UIViewController {
UIView *firstView;
UIView *secondView;
UIButton *button;
}
- (void)addButton;
- (void)toggleViews;
#property (nonatomic, readonly) UIView* firstView;
#property (nonatomic, readonly) UIView* secondView;
#property (nonatomic, readonly) UIButton* button;
#end
ToggleViewController.m:
#import "ToggleViewController.h"
#implementation ToggleViewController
// assign view to view controller
- (void)loadView {
self.view = self.firstView;
}
// make sure button is added when view is shown
- (void)viewWillAppear:(BOOL)animated {
[self addButton];
}
// add the button to the center of the view
- (void)addButton {
[self.view addSubview:self.button];
button.frame = CGRectMake(0,0,150,44);
button.center = self.view.center;
}
// to toggle views, remove button from old view, swap views, then add button again
- (void)toggleViews {
[self.button removeFromSuperview];
self.view = (self.view == self.firstView) ? self.secondView : self.firstView;
[self addButton];
}
// generate first view on access
- (UIView *)firstView {
if (firstView == nil) {
firstView = [[UIView alloc] initWithFrame:CGRectZero];
firstView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
firstView.backgroundColor = [UIColor redColor];
}
return firstView;
}
// generate second view on access
- (UIView *)secondView {
if (secondView == nil) {
secondView = [[UIView alloc] initWithFrame:CGRectZero];
secondView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
secondView.backgroundColor = [UIColor blueColor];
}
return secondView;
}
// generate button on access
- (UIButton *)button {
if (button == nil) {
// create button
button = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
// set title
[button setTitle:#"Toggle Views"
forState:UIControlStateNormal];
// set self as a target for the "touch up inside" event of the button
[button addTarget:self
action:#selector(toggleViews)
forControlEvents:UIControlEventTouchUpInside];
}
return button;
}
// clean up
- (void)dealloc {
[button release];
[secondView release];
[firstView release];
[super dealloc];
}
#end
Use Interface Builder. It's there for a reason.