10

In my app I am trying bring a subview to front, then put it back to its original layer position later. The code should be pretty simple:

To bring the subview to front (inside my custom UIView class):

[self.superview bringSubviewToFront:self];

Easy. I store the original z position in an instance variable called, you guessed it, zPosition. So, the line before -bringSubviewToFront: is:

zPosition = [self.superview.subviews indexOfObject:self];

So, all of the code I use to bring my subview to front is:

zPosition = [self.superview.subviews indexOfObject:self];
[self.superview bringSubviewToFront:self];

This works as it should. The problem is when I try to put the subview back where it was. I'm simply doing this:

[self.superview exchangeSubviewAtIndex:zPosition withSubviewAtIndex:
    [self.superview.subviews indexOfObject:self]];

Using this code, if I have two subviews, this is what happens:

Let's say I have view A and view B. View A is above view B. I tap view B, it comes to the front. I tap view B again (it should go back to where it was), and nothing happens, so it's now on view A. If I now tap view A, it comes to the front, but when I tap it again (so it should go back to its original z position: below view B), all of its sibling views disappear!

Does anyone see what could be causing this problem?

eric.mitchell
  • 8,817
  • 12
  • 54
  • 92
  • Perhaps you can start with a dummy, empty subview on top. Then you swap back and forth with that subview. Not the most elegant, I know... – Nicolas Miari Jul 18 '12 at 21:13

4 Answers4

12

There is no need to remove from superview:

[self.superview insertSubview:self atIndex:zPosition];

david72
  • 7,151
  • 3
  • 37
  • 59
11

exchangeSubviewAtIndex may well put the view back in the right place, but it will also swap another view on top, which wont be what you started with. You might need to do something like this instead of exchangeSubviewAtIndex :

[self retain];
UIView *superview = self.superview;
[self removeFromSuperview];
[superview insertSubview:self atIndex:zPosition];
[self release];
AW101
  • 1,620
  • 14
  • 15
  • calling `[self removeFromSuperview]` results in `self` being dealloc'd because once it's removed from its superview, nothing is owning it anymore. – eric.mitchell Jul 18 '12 at 22:32
  • Perhaps you could delegate this to the superview (and/or the view controller in charge of this hierarchy) instead? – Husker Jeff Jul 18 '12 at 23:52
  • @Rickay how could the `self` be deallocated during the main thread is running inside the one of object's methods? the iOS does not cut out the tree until he is on top of the tree. :) it would be very inconsistence and dangerous behaviour of the iOS, don't you think? it would be causes hundreds of unwanted crashes... the thread's run loop keep the current object alive during the progress! – holex Jul 19 '12 at 08:13
  • You're right: shouldn't respond to programming answers while multitasking! – eric.mitchell Jul 19 '12 at 16:50
  • Can you provide another solution for the case when ARC is enabled? I tried with the "AntiARCRetain" define from here (http://stackoverflow.com/questions/7792622/manual-retain-with-arc), but to no success. – Eduard Feicho Mar 31 '15 at 17:56
2

[ Swift solution ]

As other guys said, there is no need to remove and re-add your subviews.

Instead I've found that the most convenient method is:

superView.insertSubview(subviewYouWantToReorder, aboveSubview: subviewWhichShouldBeBelow)
Ondřej Korol
  • 594
  • 6
  • 19
0

This question and answers were very helpful to me.

I had the requirement to place an overlay between the viewstack which views are above and below the overlay, and i wanted to keep it dynamic. That is, a view can tell it is hidden or not.

I used the following algorithm to reorder the views. Thanks to AW101 below for the "No need to remove view".

Here is my algorithm:

- (void) insertOverlay {

    // Remember above- and belowcounter
    int belowpos = 0, abovepos = 0;

    // Controller mainview
    UIView *mainview = [self currentMainView];

    // Iterate all direct mainview subviews
    for (UIView* view in mainview.subviews) {
        if ([self isAboveOverlay:view]) {
            // Re-insert as aboveview
            [mainview insertSubview:view atIndex:belowpos + (abovepos++)];
        }
        else {
            // Re-insert as belowview
            [mainview insertSubview:view atIndex:belowpos++];
        }
    }

    // Put overlay in between above and below.
    [mainview insertSubview:_overlay atIndex:belowpos];
}
gizmore
  • 88
  • 7