I don't fully understand sendSubviewToBack. Please, see the code - ios

You can see my screenshot of xib's file.
I do some manipulation in viewDidLoad
-(void) viewDidLoad
{
[smallView removeFromSuperview];
[bigView sendSubviewToBack:smallView];
}
Will the hierarchy change from view->bigView->smallView to view->smallView->bigView?

No the hierarchy will not be affected. The documentation says:
sendSubviewToBack:
Moves the specified subview so that it appears behind its siblings.
It changes the order of painting subviews. If you have two views which are overlapping and have the same superview then you will be able to define which of that two views should be in front.

Related

Issue while using viewDidLayoutSubviews

I have heard that viewDidLayoutSubviews is the best place to alter the layout when we have used constraints.
So I jumped to viewDidLayoutSubviews
I have created three UIViews and SubViewed them on SuperView. I am not mentioning the code for frame calculation.
[self.view addSubview:imgMiddleCircle];
[self.view addSubview:imgFirstCircle];
[self.view addSubview:imgLastCircle];
Using this piece of I am adding these circles.
Now when I run my code In viewDidLayoutSubviews I get following screens:
And when I switch to viewWillLayoutSubviews I am getting this on screen:
Why I am getting extra two circles in viewDidLayoutSubviews even I am creating three circles.
And why in viewWillLayout gives the correct Output.
You should code for the fact that viewDidLayoutSubviews is called multiple types.
Ideally addSubview: should be happening in a place like viewDidLoad where you are sure it is only called once.
You can create a flag to avoid calling addSubview: multiples types (not my personal choice)
Otherwise, try to move your set up code to viewDidLoad and force the view to render itself before doing your calculation:
[yourView setNeedsLayout];
[yourView layoutIfNeeded];
Per Apple Documentation,
When the bounds change for a view controller's view, the view
adjusts the positions of its subviews and then the system calls this
method. However, this method being called does not indicate that the
individual layouts of the view's subviews have been adjusted. Each
subview is responsible for adjusting its own layout.
Your view controller can override this method to make changes after
the view lays out its subviews. The default implementation of this
method does nothing.
So, essentially, viewDidLayoutSubiews gets called multiple times during the creation of your viewController including in cases like rotating the device, scrolling etc. So, you should be really careful with the code you add to this method, because it might be executed multiple times as well. Use this method to reposition your sub-views and not to add/remove them.
Take a look at this blog for more details.

Add a subview from within and below another subview

In an iOS app, I am adding a subview to my main view with:
[self.view addSubview:firstUIImageSubview];
However in the subview class firstUIImageSubview, I am creating and adding another subView (secondUIImageSubview) that I would like to put below the first subview with:
[self insertSubview:secondUIImageSubview belowSubview:self];
My problem is that the second subview is displayed above the first subview when I want to have it below. How is it possible to achieve that ? Thanks.
This should do the trick.
[self.superview insertSubview:secondUIImageSubview atIndex:0];
When you use insertSubview:belowSubview: it places the subview in regards to other subviews that particular object manages.
[self insertSubview:secondUIImageSubview belowSubview:self];
Doesn't make much sense. Although self is a UIView (or a subclass) it still should never manage itself as a subview. Therefore
[self insertSubview:secondUIImageSubview belowSubview:firstUIImageSubview];
is probably what you want. But remember this will only place the secondUIImageSubview below firstUIImageSubview in terms of its Z-Index (it's depth on the screen). If you want it to be physically placed below firstUIImageSubview (IE it's XY coordinate) then you need to set it's position using subview's frame or setting its origin instead (by manipulating it's center or anchor points for instance).

Storyboards, removing subviews & adding them again later with the design constrains preserved

Ok, I have two complex (UI-wise) UIViewControllers. They share a [super] viewcontroller between them.
Some subviews (buttons, labels, uiviews) are to appear and disappear based on a series of remote events.
What I want to do is (well, already have), design the UI for both inside one of my storyboards, and then, in viewWillLayoutSubviews or viewDidLayoutSubviews, initially remove some visual elements from the view.
BUT, these are to be added again later during the lifecycle of the viewcontroller.
so, how can I design my view, initially [subview removeFromSuperview]; and then add these elements with the constrains I added during design
Also, correct me if I'm wrong, but since I'm going to be removingFrom and addSubview, I will have to make each of these IBOutlets strong (otherwise they will be lost after removeFromSubview, right?)
anyhow, any help / pointers are appreciated.
You have to save the constraints manually by looping through the superview's constraints (and the constraints of the view you're removing also, if they have size constraints). You can do that something like this,
-(void)saveConstraintsForView:(UIView *) viewToRemove {
for (NSLayoutConstraint *con in self.view.constraints) {
if ([con.firstItem isEqual:viewToRemove] || [con.secondItem isEqual:viewToRemove]) {
[self.constraintArray addObject:con];
}
}
}
And, yes, the IBOutlets for the views will have to be strong so they won't be deallocated when they are removed from their superview.

iOS UIImageView Layering question

I am sure this question has been answered in numerous places but I have no idea what keyword to search for to answer this question. If anyone can link me to the answer that'd be great.
I'm not sure how to control which UIImageViews (which are all subviewed to the same view) cover the others visually. In other words, if I subview a UIImageView to the same location as another UIImageView, the 2nd one that was added is always covering the first one. I'm not sure how to do this either in the UI editor or in code. So far I've been adding them in the order I want them to show up but I can't always do that. Especially if I need to set UIImageView that's an IBOutlet and I need it to be on top of all the UIImageViews that I add programmatically.
Can someone show me how to do this programmatically and possibly how to set values in the UI editor? Thanks.
UIView has several APIs to deal with these issues:
When adding a subview:
insertSubview:aboveSubview:
insertSubview:atIndex:
insertSubview:belowSubview:
Moving subviews around in their z-ordering:
bringSubviewToFront:
sendSubviewToBack:
exchangeSubviewAtIndex:withSubviewAtIndex:
More details on all of these can be found in the UIView documentation (of which UIImageView is a subclass.)
This is done at the UIView layer and is not specific to UIImageViews.
Views can be programmatically 'relayered' with the following UIView methods:
– addSubview:
– bringSubviewToFront:
– sendSubviewToBack:
– removeFromSuperview
– insertSubview:atIndex:
– insertSubview:aboveSubview:
– insertSubview:belowSubview:
– exchangeSubviewAtIndex:withSubviewAtIndex:
To do it to the XIB in Interface Builder, reorder the views listed on this view, bottom row is the frontmost object:

Passing swipe touches from UIView to underlying UIScrollView for proper scrolling

I have a situation similar to these two posts (1907297 AND 689684) and to describe my situation most concisely, I present this text/graphical layout (similar to what you'd see in IB, dots used to enforce indent levels)
UIView (MainView: 320x460)
. .UIScrollView (ScrollView: 320x460)
. .UIView (OverlayView: 320x40)
. . . .UIButton (ArbitraryButton1)
. . . .UILabel (ArbitraryLabel1)
. . . .UILabel (ArbitraryLabel2)
The goal here is for the OverlayView to serve as a unified, transparent container to position and display some arbitrary buttons/labels on top of the ScrollView. These buttons/labels should remain stationary while the content in the ScrollView beneath moves with user swipes. The buttons/labels may sometimes be hidden/unhidden/scaled in unison (with animation) which is what makes it handy to have them all grouped in the single OverlayView.
The trouble is that, while taps on the OverlayView seem to transmit right through to the underlying ScrollView just nicely, swiping motions have no effect. I can detect/intercept the swipes by overriding the
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
method in the OverlayView, however I haven't yet found a way to properly pass those along to the ScrollView in a way that makes it scroll. Evidently the touchesMoved method is not what UIScrollView uses to detect/interpret swipes?
All the other similar posts I've researched have either found a different solution that wouldn't work in my case or have just gone unsolved. I've also seen mention of employing touchesShouldBegin / touchesShouldCancel though I don't grasp how that would be implemented. Anyhow, still hopeful that there's some insight from the community that can allow me to come up with an elegant solution for this - any sample code would be fantastic.
Thanks in advance, Joel.
P.S. - I should also mention that I need to make this compatible with iOS 3.0 so I think trying to use UIGestureRecognizers is out.
Here's an easier solutions that worked well for me:
In the OverlayView (the view on top of UIScrollView), make the width and height both 0 (so that the view is no longer technically on top of the scrollview) and set clipsToBounds = NO (so that the contents of the OverlayView still show up on top of the scrollview). It worked like a charm for me.
self.OverlayView.clipsToBounds = NO;
CGRect frame = self.OverlayView.frame;
self.OverlayView.frame = CGRectMake(frame.origin.x, frame.origin.y, 0, 0);
Note that if OverlayView contains interactive controls (like the button above) then they will no longer work. You'll need to move it into it's own view above the UIScrollView.
How about, at runtime in viewDidLoad you take the buttons out of the container view and place them in the view as subviews directly (and get rid of the container view)? Then there's no container view to intercept swipes but you can still use a view to group things in IB.
Also potentially you could put the container view in as a subview of the scroll view instead, and in the scroll view delegate keep re-positioning the view whenever the user scrolls. That would seem to have a high potential for being jittery but may be worth a try.
Also if the containing view is a visual container and you need to see it, you could instead use a CALayer that was placed in the superview on top of the CALayer for rendering the scroll view, since CALayers have nothing to do with input and would not each touches.
You should subclass UIScrollView and override the touchesShouldCancelInContentView: method
-(BOOL)touchesShouldCancelInContentView:(UIView *)view
{
if ([view isKindOfClass:[UIButton class]]) {//or whatever class you want to be able to scroll in
return YES;
}
if ([view isKindOfClass:[UIControl class]]) {
return NO;
}
return YES;
}

Resources