Reordering UIView subviews
Asked Answered
D

4

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?

Devaluate answered 18/7, 2012 at 20:52 Comment(1)
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...Nymphalid
A
12

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];
Artiste answered 18/7, 2012 at 21:10 Comment(5)
calling [self removeFromSuperview] results in self being dealloc'd because once it's removed from its superview, nothing is owning it anymore.Devaluate
Perhaps you could delegate this to the superview (and/or the view controller in charge of this hierarchy) instead?Uhland
@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!Eckmann
You're right: shouldn't respond to programming answers while multitasking!Devaluate
Can you provide another solution for the case when ARC is enabled? I tried with the "AntiARCRetain" define from here (https://mcmap.net/q/528666/-manual-retain-with-arc), but to no success.Axilla
W
12

There is no need to remove from superview:

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

Woeful answered 7/7, 2014 at 23:44 Comment(0)
F
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)
Facia answered 1/5, 2018 at 10:24 Comment(0)
T
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];
}
Tuscarora answered 3/6, 2016 at 22:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.