I've created a UIView programmatically that embeds several UIControls (UIButtons, UISwitchs and UILabels mainly). I set in the -(id)initWithFrame: of this class the background color.
I have created a message to add the UIControls in the view in order to put inside of my custom view. It's something like that:
-(void) CreateGuiObjects
{
UIView *container = self;
//create uiswitch
mOnOffSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(10, 20, 0, 0)];
mOnOffSwitch.translatesAutoresizingMaskIntoConstraints = NO; //used for constraint testing
//add parent view (self)
[container addSubview: mOnOffSwitch];
/*
other stuff like above
*/
}
then in my view controller there is an event handler for an external button; the action is to add the above custom view in an empty UIView created with Storyboard Interface Builder in Xcode.
the code is like the following:
-(void)CreateButton
{
MyCustomView *view = [MyCustomView alloc] initWithFrame:CGRectMake(20,20,300,200)];
[self.view addSubview: view];
//now call my create method
[view CreateGuiObjects];
}
now, the problem is that it draws the controls, but it seems to position them in a different place...i set the frame origin for the container view in (20,20) and then put the switch in (10,20) where this point is relative to the custom view origin. Instead of that position the view seem to be far away from that position and the second problem is that the background color (gray) set in the initWithFrame is totally ignored.
If i remove every call to addSubview inside the CreateGuiObjects, it draws the empty view with the correct background color and in the correct position.
Edit:
if remove `mOnOffSwitch.translatesAutoresizingMaskIntoConstraints = NO; it works fine...but if i put again it doesn't work. Need to understand deeply the meaning of this property.
Could someone can help me? i think it is a silly question but i'm new to iOS development :(
thanks in advice
Method translatesAutoresizingMaskIntoConstraints = YES means that the UIView is using Auto Layout.
The fundamental building block in Auto Layout is the constraint.
Constraints express rules for the layout of elements in your
interface; for example, you can create a constraint that specifies an
element’s width, or its horizontal distance from another element. You
add and remove constraints, or change the properties of constraints,
to affect the layout of your interface.
When calculating the runtime positions of elements in a user
interface, the Auto Layout system considers all constraints at the
same time, and sets positions in such a way that best satisfies all of
the constraints.
read more about Auto Layout Concepts
If you don't know how to use Auto Layout I would recommend you to turn it off.
Related
I had an UIViewControllerA created with xib for view reusability.
In the xib I simply added another red view to it and set its constraints to top,leading,trailing, and bottom space to red view's superview (which is my A's view).
Add A to another UIViewController B as B's ChildViewController by doing so:
// UIViewControllerB.m
UIViewControllerA *A = [UIViewControllerA alloc] initWithNibName:#"UIViewControllerA" bundle: nil]];
[A.view setFrame:CGRectMake(0,0, self.view.size.width, self.view.size.height/2)];
[self addChildViewController:A];
[self.view addSubview:A.view];
[self.A didMoveToParentViewController:self];
Nothing red or whatever showed up.
I did try [self.A.view setTranslatesAutoresizingMaskIntoConstraints:NO]; though
that seemed to make my constraints useless and actually did not bring up the red view.
Do I have to code the whole thing (UI elements & constraints creation) when I'm working with xib and need some extra view with autolayout?
Thanks for any advice!
The problem is this line:
[A.view setFrame:CGRectMake(0,0, self.view.size.width, self.view.size.height/2)];
Instead, give this view constraints.
When you insert a set of views that uses autolayout into a set of views that uses autolayout, you must use autolayout to position it.
Note: You don't have to check up the library to understand the question, I just brought it up for reference.
I implemented DIDatePicker in xcode. The way I inserted it in a view is: I inserted a UIView to the viewController with auto-layout, then at the Identity inspector I set the class to DIDatepicker.
The problem is, DIDatepicker doesn't resize to the view's size. I was thinking a possible solution may be, to set DIDatePicker as the view's class in viewDidLoad, though I don't know how to do that.
My question is: if my theory is right, how can I set DIDatePicker as the view's class. If what I said is not true, how can I make DIDatePicker to resize with the view?
DIDatePicker has a collectionView; here is how it's defined:
UICollectionViewFlowLayout *collectionViewLayout = [[UICollectionViewFlowLayout alloc] init];
[collectionViewLayout setItemSize:CGSizeMake(kDIDatepickerItemWidth, CGRectGetHeight(self.bounds))];
[collectionViewLayout setSectionInset:UIEdgeInsetsMake(0, kDIDatepickerSpaceBetweenItems, 0, kDIDatepickerSpaceBetweenItems)];
[collectionViewLayout setMinimumLineSpacing:kDIDatepickerSpaceBetweenItems];
_datesCollectionView = [[UICollectionView alloc] initWithFrame:self.bounds collectionViewLayout:collectionViewLayout];
so I had the same exact problem as you.
The problem is that when you build DIDatepicker it builds its own collection view based on the view's original frame rather than the frame after auto-layout takes place.
The way to circumvent this problem is by not making the storyboard UIView of class DIDatepicker. Instead, just use a normal UIView and then create a DIDatepicker instance with the same frame as the UIView in the ViewDidLoad Method.
Here is an example. datePickerView is the UIView from the storyboard.
datepicker = DIDatepicker(frame: CGRectMake(datePickerView.frame.origin.x, datePickerView.frame.origin.y , self.view.frame.width, datePickerView.frame.height));
self.view.addSubview(datepicker!);
If that doesn't work try changing the frame and adding it to the datePickerView as a subview instead of the main view.
Hope this helps!
I'm not totally sure if your "theory" about setting the main view to DIDatepicker is correct, but you should be able to test it by selecting the default "View" that is in the View Controller and setting a Custom Class on that. You shouldn't need to add a UIView first, just use the one that comes with the View Controller.
I have a view with some UI components and a button on it, upon touch of a button I want to show a half view with some textfields on it overlapping the initial view, the initial view should be visible partly , the overlapping view will cover only half screen from bottom. Is this possible ?
I don't have any code as I am unable to figure out what it needs to be done, as we show any view it covers the entire screen.
Thanks
there are several ways you can do this, here are two:
1) add a popover controller that gets displayed on your button press:
here's some apple documentation on popovers: https://developer.apple.com/Library/ios/documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/Popovers.html
2) add the new view as a subview to your UIViewController
PROGRAMICALLY:
in the viewDidLoad function you can do the following to initialize the halfScreenView
GLfloat topOffset = self.view.frame.size.height/2;
UIView halfScreenView = [[UIView alloc] initWithFrame: CGRectMake(0, topOffset , [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - topOffset)
[self.view addSubview:halfScreenView];
-more logic might be needed if you support Landscape orientation, you can always re-assign the location of the view with halfScreenView.frame.origin and halfScreenView.frame.size
-initially you can have this view be hidden
halfScreenView.hidden = YES;
-when you click the button it will show the overlaying view:
halfScreenView.hidden = NO;
USING STORYBOARD:
you can also set up your overlaying view in the storyboard if you have one
-drag a UIView into your UIViewController and set it up where you want it to be located
-initialize the view to be hidden by checking the hidden box in the attribute inspector
-add the view as a property to your view
-manage when to show this view with self.halfScreenView.hidden
-this technique allows you to customize what is inside the new view within the storyboard which is nice
FOR BOTH:
-be careful with layers, you don't want your view to show up behind the one you already present. In the storyboard the last thing inserted goes on top. With in the code you can always access/change the views z position with halfScreenView.layer.zPosition (higher z values are on top)
First create a new class subclassing UIViewController called SecondView (or whatever you want), then design the UI the way you want (in its .xib file)
Then go to your main view controller's file and make an IBAction for that button.
In that method, write:
SecondView* second = [[SecondView alloc] initWithFrame:CGRectMake(0, [[UIScreen mainScreen] bounds].size.height/2, height, width);
[self.view addSubview:second.view];
This will add it to the bottom half of the screen. Put your own parameters for its height and width. When you want to dismiss the view, you can do this inside your SecondView class
[self.view removeFromSuperview];
You can deal with the textFields from within the SecondView class and have them communicate with your other view by doing the following in SecondView.h
#property IBOutlet UITextField* textField;
Hope this helps!
Yes, assuming you are using Interface Builder, go ahead and build the overlapping view and hook up all of the IBOutlets and IBActions. Say this view is called myView. Set myView.hidden = YES and myView.enabled = NO. This hides and disables myView so that it isn't even there from the user's perspective. In the appropriate button's IBAction, change the hidden and enabled properties to YES. That will make the view visible and active again.
I had this question when/where to create and initialize views that are created programatically, so I hope some discussions here will shed more light on this topic for me.
This slide:
says: "not to initialize something based on the geometry of the view in viewDidLoad" and suggests viewDidAppear.
Imagine my view controller has view. I want to add 10 dynamic UIButtons to it.
Shall I put the code like below to the viewDidAppear?
-(void) viewDidAppear:(BOOL)animated
{
...
UIButton *button1 = [[UIButton alloc] initWithFrame: rect1];
[self.view addSubview: button1];
UIButton *button2 = [[UIButton alloc] initWithFrame: rect2];
[self.view addSubview: button2];
...
}
But this creates the buttons each time the view is shown. Is it what we want?
On the other hand if I put the code in viewDidLoad slide suggest not to initialize geometry of these views there.
Or shall we create buttons in viewDidLoad and set their frames in viewDidAppear?
What approach do you usually take?
But this creates the buttons each time the view is shown. It's true.
So the best thing you can do is to add a boolean (lets name it isLaunched). You set it to FALSE in the method -(void)viewDidLoad
Then add a if condition in your -(void)viewDidAppear where you perform creation of buttons (or other stuff) and set the boolean to true at the end.
You should have something like that :
-(void)viewDidLoad
{
//some settings
isLaunched = FALSE;
}
-(void)viewDidAppear:(BOOL)animated
{
if(!isLaunched)
{
//creating and adding buttons
isLaunched = TRUE;
}
}
zbMax (and now Amar) offered good solutions to implement the view creations in viewDidAppear: I will provide the rational for doing this (over viewDidLoad).
It is pretty simple actually. In viewDidLoad none of the views are actually setup yet, so any attempt to set/create frames or bounds will be extremely inconsistent. Struts and springs (or autolayout) will take effect after this method which will create additional changes to your views. viewDidAppear: is the correct place to do this because you can now rely on existing views and setting frames.
Reason for not playing with the geometry in viewDidLoad is because view is still in the memory and not on the window. Once the view is put on the window, then you can specify geometry. That happens when viewDidAppear is called for your controller.
As recommended, you should do all the initialisation in viewDidLoad as this is one time task and need not be repeated. Hold references to the added subviews and give them appropriate frame in viewDidAppear.
When you are dealing with custom UIView and its subviews, layoutSubviews is the method you need to override in the custom view in order to rearrange the geometry of its subviews.
Hope that helps!
I have a UIView subclass that programmatically creates and adds a number of subviews. However, I want to give users of the class the ability to place new subviews over it (and its subviews) using addSubview and ideally from within Interface Builder as well.
What is the best way to do this?
Now, I suppose it would be acceptable to only add the views programmatically using addSubview, but I'm not sure how I would go about this either.
NOTE: I've tried adding additional subviews using interface builder, but they're hidden once the custom UIView subclass I refered to above creates its other views programmatically, presumably since they're added last.
Just drag a generic UIView into a nib in Interface Builder and set its class to your custom subclass. You can still do everything you could with a plain old UIView, including drag-dropping subviews.
You won't be able to see what the custom subview actually looks like in Interface Builder; it will just be a gray rectangle. You could have (I think) done this in Xcode 3 with IB plugins, but, at least for the time being, Xcode 4 does not support such plugins.
EDIT:
To address the OP's follow-up, you need to manage z-ordering programmatically. Probably the easiest thing to do is to leave your code largely as-is, but instead of doing something like:
UIView *mySubview1 = ...;
UIView *mySubview2 = ...;
UIView *mysubview3 = ...;
[self addSubview:mySubview1];
[self addSubview:mySubview2];
[self addsubview:mySubview3];
(which, as you noted, may well layer the subviews on top of the user's subviews)
Do something like:
UIView *container = [[UIView alloc] initWithFrame:[self bounds]];
UIView *mySubview1 = ...;
UIView *mySubview2 = ...;
UIView *mysubview3 = ...;
[container addSubview:mySubview1];
[container addSubview:mySubview2];
[container addsubview:mySubview3];
[self addSubview:container];
[self sendSubviewToBack:container];
[container release];
You could alternatively use [self insertSubview:container atIndex:0] but I think that adding and moving to the back is a little clearer during a rapid skim of the code.
The end result is that you don't have to mess with the existing z-ordering of your subviews; by putting them in a single, easily-managed container, you can move the whole collection to wherever you would like in the subview hierarchy.