I am having trouble understanding how layering is performed within Xcode. I have a .xib with a view, that view has 6 UIImageView 's which each contain a different image. I have created IBOutlets for each of these so I can change the image at runtime.
I now want to change the order the UIImageViews are drawn at runtime, so imageA at the back is now drawn on top of everything else. How can I change the layer order? I originally tried removing all the subviews and using [self.view insertSubview: atIndex: ] to see if I could change the draw order that way but my code crashes.
Can anyone tell me the correct method for doing this please?
Thanks in advanced,
Elliott
i guess you are looking for these UIView methods:
1:
exchangeSubviewAtIndex:withSubviewAtIndex:
Exchanges the subviews at the specified indices.
- (void)exchangeSubviewAtIndex:(NSInteger)index1 withSubviewAtIndex:(NSInteger)index2
2:
bringSubviewToFront:
Moves the specified subview so that it appears on top of its siblings.
- (void)bringSubviewToFront:(UIView *)view
3:
sendSubviewToBack:
Moves the specified subview so that it appears behind its siblings.
- (void)sendSubviewToBack:(UIView *)view
with these methods you can exchange the subviews order to get one UIView appear over one other in their superview... if that's what you were asking....
A crash may be because when you remove the image from its parent, its retain count goes to zero and is deallocated.
If you just want to swap two views, see - (void)exchangeSubviewAtIndex:(NSInteger)index1 withSubviewAtIndex:(NSInteger)index2 on UIView. If you want to do a more complicated exchange, you can remove and readd, just make sure you retain the view you remove (ARC notwithstanding).
Related
I am working on a project. I have a UIView that has several subviews. I need to know how to mask only certain subviews to the parent view, or mask all but one particular view. Is there some way to add refinement checks to the maskToBounds property? Any direction or suggestion would be greatly appreciated.
What do you mean by masking?
If you want to hide particular view, you could create a standalone additional view and change its frame to whichever view you want to hide. Then bring this standalone view to front of the view you want to hide, you could add it using insersubview :abovesubview and variants of it. For more on it, see this.
This obviously requires that you are able to access them all using specific outlets, or through tags that you know of from subviews array, to allow conditional masking.
If you would simply want to hide it instead of "masking" with some other content, your obvious choices are:
set it's hidden property to YES.
set it's alpha property to 0.0 (or anything for fade out effect)
masking means autoresizing mask if it is yes means follow this
UIView *customView = [[UIView alloc] initWithFrame:frame];
[customView setAutoresizingMask:UIViewAutoresizingFlexibleWidth |UIViewAutoresizingFlexibleHeight];
follow this link may be useful to you UIView autoresizingMask - Interface Builder to Code - Programmatically create struts and springs - Swift or Objective-C?
I have 4 UIView (each has a visible part on the screen) and I want them to be draggable.
I am adding them just like any other view on a controller with
addSubview:dragViewTop
addSubview:dragViewBottom
addSubview:dragViewRight
addSubview:dragViewLeft
My problem is that if I drag one view on top of another, even if I change it's layer.zPosition, I am able to drag one that is supposed to be beneath.
That behavior is given (I think, not sure) by the order of the added views.
I want that the view I am dragging stays on top. Any ideas?
I hope I made myself clear.
You need to work with the superview of those views to deal with the order of the views, not their layers' zPosition property.
Use the following methods of the superview to arrange the depth of the subviews. It seems you probably need only the first method, though.
- (void)bringSubviewToFront(UIView *)view
- (void)sendSubviewToBack(UIView *)view
- (void)exchangeSubviewAtIndex:(NSInteger)index1 withSubviewAtIndex:(NSInteger)index2
To bring view to top:
[view.superview bringSubviewToFront:view];
(note: this is the front of the subviews of view.superview so views that are in front of the superview will stay in front)
I've got a detail view with various labels and such providing information about a place (address, phone, etc.). The information provided is taller than an iPhone screen so they're all in a UIScrollView (itself inside a UIView) that allows you to swipe up and down to see everything.
I also have an MKMapView inside the scrollview. When it's not attached to anything in Interface Builder it moves up and down with the scrollview, as it should, staying in it's correct relative position to the other controls in the scrollview. You can play with the map, zooming and panning it, etc. and it shows your current location by default.
However, as soon as I hook it to an MKMapView variable in IB, the mapview no longer scrolls with the scrollview. Instead it literally just sits in the position it's originally displayed in (bottom of the view, with a little of the map hidden below the bottom of the view) and the scrollview scrolls up and down behind it.
What's happening here? I've tried changing a bunch of the mapview's and scrollview's properties in IB, but it has no effect. The only thing I haven't tried is just creating the mapview entirely in code, but that doesn't seem like an obvious solution.
EDIT: Sorry to everyone about the expired bounty. I got hung up in other areas of the project and couldn't get back here until now. I didn't know it would expire.
FURTHER EDIT: Well, I figured out my problem. For reasons completely unknown to me I had
[self.view addSubview:mapView];
in the viewcontoller's ViewDidLoad. Once it was hooked up then that line of code would (obviously) make the map a subview of my of view, effectively yanking it out of the scrollview.
Stupid mistake, sorry to have wasted your time (and the bounty). I'll delete this question after I think the answerers have had a chance to see the result.
Looking like as you are using the ScrollView,you need to scrolling facility in your DetailView.
Instead of using the ScrollView ,I had an alternative of this ....
You can try your hard luck by using the TableView instead of ScrollView.
Just take all the labels and mapView in a single View and then put that view in the header of the TableView.
like this :
UITableView
--> View
------>All Labels // Inside the singleView
------>MKMApView // At bottom of the View
Still You can play with the map, zooming and panning it, etc. and it will show your current location by default.
Hope this alternative can solve your problem.......
All the Best
If hooking up an outlet in IB is breaking an otherwise working view, you might be able to try this to locate the view at runtime:
- (UIView *) findClass:(Class) aClass inView:(UIView *) aSuperview {
for ( UIView *view in aSuperview.subviews ) {
if ( [view isKindOfClass: aClass] ) break;
if ( ( view = [self findClass: aClass inView: aSuperview] ) ) break;
}
return view;
}
- (void) viewDidLoad {
MkMapView *map = [self findClass: [MkMapView class] inView: self.view];
}
I figured out my problem. For reasons completely unknown to me I had
[self.view addSubview:mapView];
in the viewcontoller's ViewDidLoad. Once it was hooked up then that line of code would (obviously) make the map a subview of my of view, effectively yanking it out of the scrollview.
Do you have setContentSize property set to the content's size in the viewDidLoad method of the UIViewController?
I have a variable sized array of UIImageViews that I'd like to add to my main View. So I loop through the array, adding each UIImageView to the scene as follows:
for (UIImageView *nextImageView in myImageArray]
[self.view addSubview:nextImageView];
(I also configure their frames individually but have left that out here)
However, I later want to remove these UIImageViews. But how can I access them? I'm aware of the tag property, and that I could tag each one as I add it, and then remove views of the same tag, however I'm also adding and removing other elements to the view which messes up the index, and it doesn't seem like a very foolproof solution.
I thought of having a single UIView property in my class, and adding the array of UIImageViews as subviews of this main view, like so:
for (UIImageView *nextImageView in myImageArray]
[holderView addSubview:nextImageView];
Then I can remove all the images by calling:
[holderView removeFromSuperview];
However, I'm not sure how to arrange the UIImageViews within the UIView. If I try to configure their frames individually, they don't appear. So at the minute they just draw over the top of each other. Ideally I'd like them to appear in a stack. Any ideas how I might do that?
Thanks :)
I believe you could iterate the array of UIImageViews and call removeFromSuperview
for (UIImageView *nextImageView in myImageArray]
[nextImageView removeFromSuperview];
Hope this helps.
I'm trying to switch from two-stage rotation to one-stage rotation (to avoid the console warning, and because Apple recommend doing so because one-stage is faster).
However I can't figure out how to get the new size of my view (taking into account the navigation bar, status bar, etc) early enough to perform the update of my UI during the animation (rather than simply snapping the items to their new positions at the end as many applications seem to do, which results in a big "jerk" right at the end of the animation).
The sizes I get in the willAnimateRotationToInterfaceOrientation:duration: method are (perhaps obviously) the old sizes.
I can see I should be able calculate it by hand, working out the current bar heights, then inferring the new view frame size by deducted those from the rotated screen dimensions? (which isn't that difficult to do, though is perhaps fragile as it assumes the navigation bar, status bar, etc will be the same height in both orientations, and you'd have to manually take account of the toolbar being different heights in portrait vs landscape - I just want to make sure I've not missed a more straightforward or common way.)
Any feedback on approaches other people have taken would be great!
Thanks
Joseph
I've had the most success using my view's layoutSubviews method for autorotations. When layoutSubviews gets called during an orientation change, the view's bounds are already set to what they will be at the conclusion of the rotation. You can also at this time query the status bar, navigation bar, and toolbar for their sizes and get the correct post-rotation values (although the status bar width and height may be swapped--I just take the smaller value as the height, works great). Using that information you can lay out subviews and they will then be animated as part of the rotation.
It can be annoying to create a UIView subclass for every situation where you want to do this, so I created a special subclass of UIView called DelegatingView, which looks like this:
#protocol DelegatingViewDelegate
- (void)layoutSubviewsForView:(UIView*)view;
#end
#interface DelegatingView : UIView {
id<DelegatingViewDelegate> _delegate;
}
#property (nonatomic,assign) id<DelegatingViewDelegate> delegate;
#end
#implementation DelegatingView
#synthesize delegate = _delegate;
- (void)layoutSubviews {
[super layoutSubviews];
[_delegate layoutSubviewsForView:self];
}
#end
I use this as my base view, add subviews to it, and set my UIViewController as the delegate. Then I can layout subviews from my view controller, having access to the current orientation and so on.
Which OS version are you targeting? Under OS 4.0 (which is what I did a quick test in), [view bounds] within willAnimateRotationToInterfaceOrientation:duration: returns the new, post-rotation bounds.
If you are not seeing that, I’d suggest double-checking that your view has the appropriate auto resize mask set.
If you have complex layout requirements in the view of your view controller, it is worth it to create a subclass of UIView and perform your layout code in -layoutSubviews (the correct place to do view layout). As Hilton Campbell pointed out, if you use this method for layout, you can check frame sizes and subview relationships and set their new positions appropriately.
If your view controller's view has a simple set of subviews, then you should probably just set their autoresizingMask properties appropriately, and let them animate themselves automagically.
I tried two methods to adjust view's size.
1.) notify child views to adjust their size after -(void)viewWillAppear;
The drawback is that it will be executed each time when viewWillAppear.
ex:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self notifyChildViewWillAppear:animated];
}
at child view
- (void)notifyChildViewWillAppear:(BOOL)animated
{
// at this time, the size of superview's bounds
//equals the content view's size
// (no navigationBar's height and toolbar's height.
self.frame = [self superview].bounds;
}
2.) Calculate the necessary size by hand.
I calculate as viewDidLoad.
The sizes of navigationBar and toolBar can be derived by following code.
self.navagationController.navigationBar.view.frame.size.height;
self.tabBarController.tabBar.frame.size.height;