I am doing my first steps creating applications in iOS, so maybe I am asking a dumb question.
In a View Controller, I put some buttons and text fields to make different actions. To improve the organization, I want to put several backgrounds with borders to delimitate the areas where the buttons and text fields are placed.
I tried putting a text label without text and color background, and move my objects over it, but I don't know why the layer is placed over the buttons and text fields. Then, I tried modifying the text label's opacity, but it's totally unsatisfactory.
label1.layer.cornerRadius = 5.0;
label1.layer.masksToBounds = YES;
label1.layer.borderColor = [UIColor lightGrayColor].CGColor;
label1.layer.borderWidth = 1.0;
label1.layer.opacity = 0.1;
label1.layer.backgroundColor = [UIColor whiteColor].CGColor;
Can help me anyone to find a better way to do this or improve my code?
1) you could just use a normal UIView instead of a label with no text (same difference just more correct)
2) in the story board the order in which the UI elements are on the left bar is important, it is basically the draw order of the elements, the ones at the top are drawn first, therefore things below it will be drawn over
You can try in Interface Builder to put multiple UIImageView objects and set your backgrounds as desired from Attributes Inspector and watch out how you put your elements in your UIView. The ones below are drawn after the ones at the top.
If I understood you right, ...
Make yourself familiar with the logic behind the subview hierarchy and the drawing sequence of siblings.
In your interface builder/storyboard you could align the views as followed:
UIView (the default view that is connected to the view controller's view property)
\--UIView (This is a background to group UI Items as buttons or Lables
\-- UILabel
\-- UIButton
\--UIView (the next background)
\-- UILabel
\-- UIButton
When it comes to coorindates then you will notice that the coordinates of each view are relative to its subview. So the UILabel and UIButton might have the same coordinates but each of them will be located within its very subview.
Another option would be:
UIView (the default view that is connected to the view controller's view property)
\--UIView (Background)
\--UIButton
\--UILable
\--UIView (Background)
\--UIButton
\--UILable
Or even
UIView (the default view that is connected to the view controller's view property)
\--UIView (Background)
\--UIView (Background)
\--UIButton
\--UILable
\--UIButton
\--UILable
In that case all UI elemets share the same coordinate space. In that case you need to care for a proper layout.
This one is would not work:
UIView (the default view that is connected to the view controller's view property)
--UIButton
--UILable
--UIButton
--UILable
--UIView (Background)
--UIView (Background)
Even if all coordinates etc. are identical with those of the examples above, the two background views would hide the other UI elements just because they are drawn after the others. And the last one wins the space.
Related
For a MacOS app, I have a Window, containing an NSView; into that view, I want to add a subview with a constant size and height.
When loading the subview programmatically by [myView addSubview:mySubview], I want the NSView *myView that is hosting the subview to change in size so it accomodates the subview, and the window to change in size accordingly; so that the edges of the NSView inside that Window keep the same distance to their surroundings in the Window as before. How do I achieve that most efficiently and which properties do I have to specify in IB to make that work? Do I have to adjust the size of myView and of the Window programmatically by hand or can I achieve this in a more beautiful way?
There are multiple ways to achieve this.
A simple one is to set autoresizingMask the value(s) you want.
The mask you can see in Interface Builder are represented by predefined numbers (NSAutoresizingMaskOptions) that you will combine with bit operation
view.autoresizingMask = NSViewMaxXMargin | NSViewMaxYMargin;
which is simmilar to Autoresizing like in this screenshot of IB
The checkmark on Layout Translates Mask Into Constraints has to be made, either in IB or programmatically so they are used as constraints.
view.translatesAutoresizingMaskIntoConstraints = YES;
The relative positioning to its enclosing superview is defined when the view is instanced with -initWithFrame: with the given frame or with the values set in IB when it creates an instance and inits the UI element via -initWithCoder: .
Be aware this does not stop the autolayout mechanism of IB to warn you that your desired coordinates, sizes and constraints are maybe clashing with constraints.
As suggested by #Willeke, I needed to understand and apply Autolayout. To make it work in IB, I set the autoresizingMask of my subview to stick to all for sides and automatically adjust width and height. Even though it can be done completely in IB, I think programmatically this would be
subview.autoresizingMask = NSViewWidthSizable | NSViewHeightSizeable;
As pointed out by #Ol Sen in his answer, Translates Mask Into Constraints also has to be activated.
To arrange the elements inside that subview that is added programmatically as described in the opening post, I rely on nested stackviews and resize them instead of resizing the parent.
The only problem left is to correctly adjust the frame of the subview to match the parent view before adding it. If this step is left out, the contraints the autoresizing mask of the subview is translated into when adding it will result in the correct resizing behaviour, but wrong margins. The essential code looks like this:
MySubViewController *subViewController = [[MySubViewController alloc] init];
subViewController.view.frame = superView.bounds; // Correct the margins
[superView addSubview:subViewController.view];
I am developing in Cocoa, and I am currently having problems with filling the background of a NSWindowController.
I understand that subclassing is the way forward if you want to customise your cocoa app. So I created a custom NSView named whiteView and added this view as a subview to my windowController's contentView; however, there are some issues with completely filling the background of the window. Can anyone explain how I can have the color cover the complete surface area of the window's frame pls. Thank you
These are the results that I have so far.
1) This is the window when I leave it as it is, notice the white color only having covered half of the window.
2)Here is the same window again when I adjust the window far to the right and bottom. The white screen seems to stretch enough so that it covers the elements.
This is how I create the custom view
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
[[NSColor whiteColor] set];
NSRectFill([self bounds]);
}
And this how I achieve plaster the view onto my window.
WhiteView *whiteBackgroundView = [[WhiteView alloc] initWithFrame:self.window.frame];
[self.window.contentView addSubview:whiteBackgroundView positioned:NSWindowBelow relativeTo:self.window.contentView];
What do I need to do to correctly allow for my window's background to be fully covered in white?
First, the simple solution is to use -[NSWindow setBackgroundColor:] to just set the window's background color. No need for a view.
If you're still interested in how to fix the view-based approach, probably what's wrong is that you haven't set the autoresizing mask of the view to make it follow the changes in the window size. For example, you could do [whiteBackgroundView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable].
However, you could also set the whiteBackgroundView as the window's contentView rather than as a subview of it. The window's content view is always kept at the size necessary to fill the window's content rect. All of the other views of your window would be subviews of the white background view. In my opinion, this is better than making it a sibling that just happens to be at the back. Using relative ordering among siblings views to achieve a particular rendering order is a hack.
Finally, there's no reason to invoke super's implementation in your -drawRect: if the superclass is NSView itself. NSView doesn't do any drawing in its -drawRect:. Also, your subclass takes over full responsibility for the entire drawn contents of its bounds, so you'd overdraw whatever super had drawn, anyway. (Also, you need only fill dirtyRect rather than [self bounds].)
While you're at it, since your class fills its bounds, you should override -isOpaque to return YES for optimization.
Update: regarding the frame of the view: if it's not going to be the window's content view, then you want to set its frame to be its prospective superview's bounds. So, you should have used self.window.contentView.bounds if you wanted whiteBackgroundView to fill the content view.
More generally, if you want the content rect of a window, you would do [window contentRectForFrameRect:window.frame]. But if a view is going to be a window's content view, there's no need to set its frame to anything in particular. It will be resized automatically.
Update 2:
To transfer the view hierarchy from the original content view to the new content view (when you're making the white background view the content view):
NSArray* subviews = [self.window.contentView.subviews copy];
[subviews makeObjectsPerformSelector:#selector(removeFromSuperview)];
[whiteBackgroundView setSubviews:subviews];
[subviews release];
(Written for manual retain-release. If using ARC, just drop the -release invocation.)
Regarding the frame to use, as mentioned in the first update: keep in mind that the view's frame should be expressed in the coordinate system of its superview. So, as I said, self.window.contentView.bounds would work if you're putting the new view into the content view. The window's frame and content rect are in screen coordinates. They would be completely incorrect for positioning a view.
I have a background image that is added to UIScrollView with zoom enabled. I am trying to add additional UIImage's on top of the background image that zoom together with the background image?
The idea is to have a background with multiple playing cards were the user can either see the whole playing field with small playing cards or zoom in the whole background including the playing cards for better views on the playing cards.
Your UIScrollview should contain a single zoomable UIView subview. That subview should contain your background UIImageView and all of the card UIImageViews as it's own subviews (if you add the background image first, it will be layered at the back of the resulting composite view)
The containing UIView that is the immediate subview of your scrollview should be the return value to the UIScrollviewDelegate method
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
You can manage your view hierarchy through a UIView's subviews property, which is an NSArray of views. They are layered in the order of the array, so the first view on the array will be at the back when the views are composited for display.
Well you could use the scrollviewdidzoom delegate method to notify you when the scroll view has been zoomed in and out.
From there you could do a number of things like present new UIImages at a specific zoom level using the scrollView.zoomScale property.
Let me know if this helps.
I have perfectly working UIPageControl. I would like to add paging by swipe, and as I understood, it is done by UIScrollView.
But all tutorials are done with images, I want to have controls (labels, buttons) repeating on every page.
Because UIScrollControl is working the way that it must have set its width * pages count, does it mean, that controls can be placed only in code, not in IB?
Should I place labels and buttons directly on UIScrollView?
Thanks
If you have a View Controller designed using IB with all its buttons and labels, then it is possible to add that View Controller as a subview of your UIScrollView as such:
[scrollView addSubview:controller.view];
scrollView being your UIScrollView and controller being your IB designed View Controller.
You can achieve repeating controls with pagecontroll by using IB by ordering the objects appropriately. Here's what I have:
Drag the UIScrollview to the ViewController and also the drag the Page Controller and other controls also to the ViewController but not onto the UIScrollView. Keep them separate. The objects on the bottom of the IB list of objects shows up at the top of the view stack. (So when you swipe new pages the controls dont move and isn't covered by the UIScrollView) I also group the various controls by group selecting them and then use the "embed with view" menu item so that in IB I have two groups, the controls and the UIScrollView. Makes it neater and easier to manage. As for changing labels, I haven't tried it but I've seen tutorials where you can have iboutlets linked to changing value of pagecontroller and then update the labels in uiscrollview appropriately.
I'm not that great with Core Graphics, but I am drawing text on the screen to my CGContext. I am doing this immediately after I add a standard, opaque UIView to my user interface.
Does anyone know why the text I draw after I add my UIView is still at the "bottom" of the user interface?
Thanks in advance.
iOS, like OS X, uses a compositing window manager. Adding and removing UIViews sets their position in the view hierarchy; when and how they're drawn is managed separately. There is no guaranteed relation between when a view is added and when it'll be drawn, and no reason to guarantee one. The content of a view is cached and composited as required from that copy.
If you want to do custom drawing, create a custom UIView subclass, add it to the hierarchy according to where you want it to appear and do your drawing in drawRect: or one of the other override points if you want to render off thread.