This is a question that follows on this SO Question.
What I'm trying to do is, I have a map with some cities on it. When you tap on a city. The city changes of color. (see image below).
So first I did the following. When I tap a city I executed this part of code.
if (pointIsNearPath28){
if([self.subviews containsObject:_imgNiuewerkerken]) {
NSLog(#"Remove");
[_imgNiuewerkerken removeFromSuperview];
[arrCities removeObject:[NSNumber numberWithInt:28]];
}else{
NSLog(#"add");
[self addSubview:_imgNiuewerkerken];
[arrCities addObject:[NSNumber numberWithInt:28]];
}
}
This went good till a certain point. If i had for example selected 20 cities and I selected one more. It crashes.
So I thought that it has a memory leak somewhere but it doesn't.
What I've tried next is to add all the imageViews at the same time to my view in my DrawRect. But then it also crashes.
So I think that that is going to be the problem. That I want to add to much images as subviews
Related
I've got a UIPageViewController set up paging my ImageViewController.
The ImageViewController contains a UIScrollView with a UIImageView inside. Nothing else.
I'm testing at the moment with 3 "items" in my datasource for the UIPageViewController (i.e. three pages).
It all works fine and I can scroll and zoom and then page for about 30 seconds and then suddenly I get this warning...
*** Assertion failure in -[_UIQueuingScrollView _didScrollWithAnimation:force:], /SourceCache/UIKit/UIKit-2372/_UIQueuingScrollView.m:778
I've got no idea where to start debugging it though as it doesn't point to any of my code and there isn't any of my code in the stack or anything.
Can someone give me a pointer as to where to start debugging this.
EDIT
I've done a bit more testing. It seems to happen if the scrollView is decelerating (i.e. after a flick) and then I try to transition the PageViewController to another ViewController as the scrollview is still moving.
The app crashes about 20% of the way through the transition to the next page.
EDIT 2
The error seems to stop on the line _cache_getImp (not sure if that's a capital i or lowercase L).
EDIT 3
This gets better. I just downloaded Apple's PhotoScroller sample app to see if they got round the problem a different way. Well, no, they didn't. The sample app crashes in exactly the same way mine does! You have to zoom and scroll and transition pages at the same time to make it more likely to crash but it happens on it's own too it just might take longer to happen.
Came up with a solution! In my case I have a UIPageViewController with UIPageViewControllerTransitionStyleScroll as well as next buttons that allow the user to advance through my viewpager by tapping. I am getting this crash when the user presses a next button and drags around slightly before releasing their finger (still within the frame of the button). It appears that the dragging within the button is interfering with UIPageViewController's pan gesture recognizer in this case, and that's what causes the crash.
While it's pretty unlikely that the user will get in this state, I've come up with a relatively simple solution the prevents my app from crashing if it does happen. I have a boolean that represents if the user is in a valid state to move to the next screen and set it to YES on touch down, then to NO if the user drags anywhere inside my button. Then, on touchUp (nextPressed) I check the boolean before moving my UIPageViewController programatically.
- (IBAction)touchDown:(id)sender
{
self.shouldAdvanceToNextScreen = YES;
}
- (IBAction)touchDragInside:(id)sender
{
self.shouldAdvanceToNextScreen = NO;
}
- (IBAction)nextPressed:(id)sender
{
if (self.shouldAdvanceToNextScreen) {
UIViewController *initialViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"TutorialScreen2"];
NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
}
}
The downside is that nothing will happen even though the user still released their finger within the button frame. However, I prefer this over a crash and see this as a pretty rare edge case regardless. I'd expect the user would just tap again - this time without a tap & drag - and move forward successfully.
I'd welcome any ideas on taking this a step further and preventing the clash between the touch drag and the UIPageViewController altogether.
Have you tried disabling bouncing in the UIScrollView? That worked for me and solved my other problem too alluded to in my comment above.
I have been fighting with this all day.
My Conclusions:
If you have a scrollview as the showing ViewController and you are delegate of the scrolls: you're in trouble. Even with the PageViewController configured with Horizontal scrolling, a vertical scrolling on your view will trigger an event. -> this does not cause trouble if: you scroll back to the top of your view before (Not sure how to fix this).
There are good StackOverflow threads like this one.
Basically the solution is:
1 [yourView setViewControllers:yourControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
2 Use UIPageViewControllerTransitionStylePageCurl as the transition Style.
This fixed most of my problems.
If someone has a solution for the delegation problems with the scrolling, will be most wellcome
I had a similar problem. My setup was a UIPageViewController and I was loading view controllers with an UIImageView inside. When interacting while the UIPageViewController was scrolling I got the same crash log.
I fixed it by creating a UILongPressGestureRecognizer and add to the UIPageViewController's scroll view.
Created my own subclass of UIPageViewController (Solution is specific for my case but can easily used as a generic solution)
Find the inner UIScrollView of the UIPageViewController
- (UIScrollView *)findScrollView
{
UIScrollView *scrollView;
for (id subview in self.view.subviews)
{
if ([subview isKindOfClass:UIScrollView.class])
{
scrollView = subview;
break;
}
}
return scrollView;
}
Add the long tap gesture recognizer to the inner scroll view and point its action to a method or nil
/**
* On tap-hold the page view controller crashes as soon as it pages to a new view controller.
* Setting a long press gesture to ignore the hold.
*/
- (void)listenForLongPressGestureOnScrollView:(UIScrollView *)scrollView
{
UILongPressGestureRecognizer *longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:nil];
[longPressGestureRecognizer setMinimumPressDuration:0.5];
[scrollView addGestureRecognizer:longPressGestureRecognizer];
}
It surely looks like a bug in iOS 8 and 9.
(and the "answers" down below attempt to work around that)
Feel free to file a ticket on bugreport.apple.com
Do be sure to specify how to crash PhotoScroller
Include all iOS versions you've seen it crash on
To date I've seen:
Assertion failure in -[XXX.TrackingPageViewController queuingScrollView:didEndManualScroll:toRevealView:direction:animated:didFinish:didComplete:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit/UIKit-3512.60.12/UIPageViewController.m:2028
Assertion failure in -[_UIQueuingScrollView _didScrollWithAnimation:force:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit/UIKit-3512.60.12/_UIQueuingScrollView.m:785
(lldb)
I'll keep the list updated (Despite the downvoting, however massive).
Stay tuned.
It does not look like our bug.
UPD 2016 09 27 (on Xcode 8 now):
Assertion failure in -[XXX.TrackingPageViewController queuingScrollView:didEndManualScroll:toRevealView:direction:animated:didFinish:didComplete:], /SourceCache/UIKit/UIKit-3347.44.2.2/UIPageViewController.m:1875
awesome
I'll try to explain my self, I have ContactsViewController that shows a table view with a list of contacts (the model is an array of Contact objects), each cell display an image of a contact.
Currently what I do to populate the cell's UIImageView is this:
1. I override the Contact image property getter -
- (UIImage *)contactImage
{
if (!_contactImage) {
_contactImage = [[UIImage imageNamed:#"placeHolder.png"] retain];
[self asyncDownloadContactImageFromServer];
}
return _contactImage;
}
Then when I finish downloading the image I set it to the contactImage property and I post a ContactUpdatedImageNotification.
My ContactsViewController then get this notification and reload the cell of this contact, this will set the downloaded image to the cell's imageView.
The result of this is good async fetching of the images without blocking the UI while the user scroll the table view.
BUT there is something small that bothers me, when the a user scroll the table view and reveal new cells the new cell's image get download as expected but the cell's imageView is not updated with the new downloaded image till the user pick up his finger.
I supposed that I need to do something in another thread to make this effect, but I don't know how?
The image is not updated until the user stops scrolling due the code being executed in the default runloop, which gets delayed until scrolling finishes. This other question deals with the difference between the runloops, NSDefaultRunLoopMode vs NSRunLoopCommonModes and it precisely recommends not updating the images while scrolling since that can introduce jerkiness in the scrolling itself if you are not careful.
Also, now that you know about the existence of these runloop modes you will be able to find much more information about them in the xcode documentation or internet.
Hey Eyal visit following url...you will get answer and as well sample code...
tableview with different cell with different images
Hope this will help you...
This is the flow of my app. 1st view -> 2nd View -> 3rd View
On 3rd view, when I click on any row of tableView one UIView gets displayed, which has one textField which accepts only numbers. For this I have implemented UIKeyboardWillShowNotification and displayed a UIButton for 'dot' button on the down-left corner of keyboard (For this I have created two images and sets that image to UIButton object).
My problem is, After using this custom keyboard(for 2-3 times), when I redirect form 3rd view to 1st View, This UIButton (with dot image) is appearing on 1st view. I have used default keyboard at there but this image not getting away.
While moving from 3rd view to 1st view, I m removing Observer for the keyboard notification which I have registered earlier & also I m checking wether,
if ([dotButton retainCount] > 0) {
[dotButton release];
dotButton = nil;
}
I have allocated dot button only once in viewDidLoad.
I m using popToRootViewController method to go back to 1st view from 3rd view.
I dont want to display this dot button on my 1st view. How can I do this.
Follow this steps
1) First make the doneButton an instance varible of your class, this will help u maintain the reference to the button
2) Add this code at the beginning of ur keyboardWillShow:(NSNotification *)note method
if(dotButton){
[dotButton removeFromSuperview];
dotButton = nil;
}
and one more thing implement UIKeyboardWillHideNotification method with NSNotificationCenter and perfrom step 2 over there.
I assume when you created the dotButton, you are calling addSubview: to put it on the screen.
When you want to remove it, you need to remove by calling [dotButton removeFromSuperview]. If you just release it, it will still be retained by the view that is containing it.
Finally, you should NEVER be calling retainCount unless you are debugging something. I've been writing Objective-C code for years and I have NEVER used retainCount, even when I was doing weird runtime stuff.
The rule is simple. If you need an object to stick around, you call retain. When you are done with it, you call release. If somebody else has retained it that's none of your business.
I have an iPad App which shows a whole load of information and related images. For instance: I can access to the section “Events”, from which I can choose one. Every single one has different dates or side events. Once I choose the side event, I charge some small views, which contain the title of the news; a small image as a preview and a short text in a ViewController (which has a ScrollView). This previews can be a lot. There are no leaks due to my code (I analized with Instruments and the only ones I can find are due to NSXMLParser bug), but I have noticed that the live bytes don’t slow down, ever, they only grow. Every time I have to update the content of the ScrollView I take care of inserting the following code:
if ( bannerVideo ) {
[banner release];
banner = nil;
}
Do you by any chance know what could be the reason for such a constant growth?
SOLVED
I added the ViewController "bannerVideo" as a subview to the main ViewController. This causes the retainCount to increase by one. So, when I asked for a new view:
if ( something ) {
[[bannerVideo view] removeFromSuperview];
[bannerVideo release];
bannerVideo = nil;
}
Yes, the main access to the heap area for bannerVideo was released, but it was still retained by the main ViewController View.
I've got a UIButton called tagButton and a UIScrollView called tagsView.
Inside my -(void)renderTags method, I do
[[self.tagsView subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
...to clear the decks, then I walk through a pair of arrays called allTags and activeTags, alternately creating "lit" and "unlit" buttons for each tag the user has ever used in the app. So far so good. But when I add the newly created UIButton to my view, thusly:
[self.tagsView addSubview:tagButton];
[tagButton release];
...the first time, nothing happens on the view, the tag button doesn't show up. The second time I put that same string in (which fires -toggleTag rather than -createTag, but ends up in the same rendering method), I get the console message modifying layer that is being finalized = 0x82b3ec0.
If I comment out [tagButton release], though, everything works. So, what gives? I thought adding a subview to a superview made the superview retain it. I'm not going to deal with this button again except to tell it to remove itself next time I render my tag list, so it seems like the view controller ought to release it.
Your problem is probably that you arent retaining tagButton to begin with, therefore there is no need to release it...hope that helps