I am trying to remove an image from an existing view using the code below:
-(void) deleteImage:(int)imageID{
//[self.imageArray removeObjectAtIndex:imageID];
//remove the image from the screen
for (UIView* view in self.view.subviews) {
if ([view isKindOfClass:[UIImageView class]] && [view tag] == imageID) {
//could be a bug here with the re-Ordering of the array (could add a helper method to reset all the tags on screen when this is called
NSLog(#"view %#", view);
//[self.imageArray removeObjectAtIndex:imageID];
[view removeFromSuperview];
}
}
}
The NSLog outputs the following:
view <UIImageView: 0x18b8d7b0; frame = (0 0; 768 2016); autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x18b8d890>>
The issue i have is that it appears to be getting the ImageView but it doesn't remove it.
Since you're trying to modify the user interface, you must be doing it on the Main thread. Therefore, do the following:
dispatch_async(dispatch_get_main_queue(), ^{
[view removeFromSuperview];
});
First of all, when you ask a view for its subviews, you get a copy of the subviews array, so fiddling with the copy array doesn't do you any good.
Secondly, in principle, you should be getting a crash when you try to remove the subview, because you are using fast enumeration, which prohibits mutating the collection while in the enumeration.
Lastly, the correct method of removing a subview of a view is to use the removeFromSuperview method, which implies that you do need to keep a reference to the image view in question.
Essentially, you should use the methods provided in the UIView section titled 'Managing the View Hierarchy', and not to be grubbing around with the actual subview arrays themselves. That's a recipe for potentially getting the subviews hierarchy into an inconsistent state.
Related
I have a button hooked up in the storyboard to a method onButtonPress. In that method I call [pressedButton removeFromSuperview] but the view is not removed. I have even tried [_scrollView setNeedsDisplay]; and [_scrollView setNeedsLayout]; with no luck. I am assuming this is a restriction on being able to remove the button I have pressed. Is there a way I can signal to the view controller to call a method in the future to remove this button?
you can just hide it,
- (IBAction)celebritiesButtonPressed:(id)sender {
self.button.alpha = 0;
//[self.peopleButton removeFromSuperview]; //as you intend
}
Nevermind, actually. I was making a silly mistake.
The view I was pressing is part of a custom table I implemented to avoid the tableview in the scrollview. What I was doing was removing all the table cells from the table then re-adding them with the exception of the one that was clicked. Except right before I looped through the array which held the views and called [view removeFromSuperview], I removed the view I pressed from that array. Therefore, it was never calling that method.
So I was doing
[_taskViewCells removeObject:t];
for (id taskViewCell in _taskViewsCells) {
[taskViewCell removeFromSuperview];
// Remove all the task views
}
Pretty silly...
I'm currently coding a iOS apps that contains a scrollview in the mainview. When the user want to reset the scrollview I'm running this method:
-(void)clearFlightTimer{
// Removing object (button, label and image) from subview
// *** KNOWN BUG WHEN REMOVING UIImageView OBJECT IT'S ALSO REMOVING THE SCROLLBAR IMAGE ***
for(UIView *subview in [self.flightViewScrollView subviews]) {
if([subview isKindOfClass:[UIButton class]] || [subview isKindOfClass:[UILabel class]] || [subview isKindOfClass:[UIImageView class]]) {
[subview removeFromSuperview];
} else {
// Do nothing - not a UIButton or subclass instance
}
}
// Reloading the View
[self viewDidLoad];
[self viewWillAppear:(YES)];
}
The issue is that the scrollbar seem to be part of UIImageView class and this method remove it. How could I keep the scrollbar image when removing all object from this subview? Is there a way to code the scrollbar indicator back programmatically?
Thanks!
Hope that I'm clear!
The simplest and most fool proof way is probably to add an intermediate subview of the scroll view that contains your actual content. This new container view's subviews can be iterated over and removed as you need. Alternatively, if you know the scroll view is in an empty state, you might be able to iterate over subviews of the scrollview and store all image views that exist when empty - those views will be the ones you shouldn't mess with.
I've got an UIScrollView with inside, some views loaded from a xib file.
The UIScrollView loads only three Views. The current, the left one and the right one.
For exemple, I have one view at the left and one view at the right of the current View. If I scroll to the right, the UIScrollView will delete the view to the left, scroll to the right to the new current View and load the new view to the right of the new current View.
In addition, I have a button outside the UIScrollView. When I click on it, it changes the background color of the current view displayed on the UIScrollView.
It works well but sometimes, I don't know why, when I click on the button to change the background color of the view is well changed, but the view is not refresh so the user can't see the change of background color.
The UIScrollView:
container = [[UIScrollView alloc] initWithFrame:frame];
[container setBackgroundColor:[UIColor clearColor]];
[container setCanCancelContentTouches:NO];
[container setClipsToBounds:NO];
[container setShowsHorizontalScrollIndicator:NO];
[container setPagingEnabled:YES];
The method call when I click on the button to change the background color of the current view
- (void)menuColor:(MenuPickerViewController *)controller didPickOption:(UIView *)button
{
// Get the object containing the data of the product linked with the view.
MyProduct *product = (MyProduct *)[MyProduct getProduct:[_slider getCurrentContentDisplayed]];
// Get the the superview of the button sender to have an access for the attributes of this button (color selected, ...)
ColorButtonMenu *colorView = (ColorButtonMenu *)[button superview];
// Get the current UIView displayed in the UIScrollView
MyView *myView = (MyView *)[self sliderGetViewWithID:[_slider getCurrentContentDisplayed] FromSlider:_slider];
// I check with debugger, the color is well setted
product.color = colorView.color;
// "border" is a view in my xib that I want change its background color.
IFPrint(#"myView.border backgorund color before: %#\n", myView.border.backgroundColor.description);
[myView.border setBackgroundColor:[Utilities colorFromHexString:colorView.color]];
[myView.border setNeedsDisplay];
IFPrint(#"myView.border backgorund color after: %#\n", myView.border.backgroundColor.description);
IFPrint(#"=== DEBUG ===\n");
IFPrint(#"isMainThread ? : %i\n", [NSThread isMainThread]); // Always return YES
IFPrint(#"myView: %#\n", myView); // Always return the address of a valid pointer
IFPrint(#"myView border: %#\n", myView.border); // Always return the address of a valid pointer
IFPrint(#"=============\n\n");
}
So, as you can see at the end of the method, I tried to call method setNeedsDisplay on the view loaded from a xib and the other view inside "border" but nothings work.
Moreover, my method is always called on the main thread.
Any suppositions ?
Thanks !
Edit:
Obviously, I have tested if the view return by sliderGetViewWithID is the correct view. All the attributes are well set. In my opinion it's truly a refresh problem.
are you sure the view you're trying to set background color for is actually visible? I guess it might be offscreen so you see nothing happening.
You might want to trap that event by finding the intersection between scrollview's bounds and myView's frame. if there's no intersection, that means myView is not actually visible.
so the code is:
BOOL intersects = CGRectIntersectsRect(scrollview.bounds, myView.frame);
if(intersects == NO)
{
NSLog(#"myView is offscreen");
}
Problem solved :
After setting the background color of the view. I remove the view from the UIScrollView and I re add it inside the UIScrollView. It's a bit tricky but it works good !
[myView removeFromSuperView];
[myScrollView addSubview:myView];
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?
There is no need to remove from superview:
[self.superview insertSubview:self atIndex:zPosition];
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];
[ 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)
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];
}
I'm trying to update every frame for an allocated UIButton when the device's orientation is updated. Right now I have a for loop that's supposed to check for every UIButton in the view and update the frame of the UIButton. The for loop is not working though, can anyone tell me why?
Thanks in advance!
for(UIView* view in theTable.subviews) {
if([view isMemberOfClass:[UIButton class]]) {
NSLog(#"UIButton found");
view.frame = CGRectMake(430, 10, 23, 24);
}
}
Try calling [view setNeedsDisplay]; after changing the frame. From the documentation:
You can use this method or the
setNeedsDisplayInRect: to notify the
system that your view’s contents need
to be redrawn. This method makes a
note of the request and returns
immediately. The view is not actually
redrawn until the next drawing cycle,
at which point all invalidated views
are updated.