scrollRangeToVisible doesn't update contentOffset until a refresh - xcode

I am calling scrollRangeToVisible on a UITextView to scroll to the top. I then ask for its contentOffset, and this comes back as (0,0).
However, after a screen refresh, the contentOffset comes back as (0,8) - probably something to do with the font of the text. And the (0,8) is the correct value as it works for the rest of my app if I hard-code it in.
My problem is that I need to get the (0,8) as soon as I have made the call to scrollRangeToVisible. Perhaps it is not there because it needs that screen refresh - if so, how can I force a refresh and return immediately to do my further processing.
Alternatively, it may be animating the scrollRangeToVisible, and therefore the animation is right at the start when I get contentOffset. So how do I stop the animation?
NSRange start = NSMakeRange(0,1);
[self.recipe_view scrollRangeToVisible:start];
start_scroll = self.recipe_view.contentOffset;

Related

Intercept ScrollView zoom before it bounces back

I want to receive a callback when a ScrollView's zooming view is scaled below a threshold so that I can dismiss the view controller. I'm currently using the bouncesZoom property which allows the zooming view to be scaled below the minimumZoomScale and snap back when the user ends the gesture.
I considered using scrollViewDidEndZooming with passes me a scale parameter. The problem is that this callback is triggered after the zooming view bounces back to minimumZoomScale. Therefore, the dismissal transition would include the bounce back and that isn't the desired effect.
I considered setting the bouncesZoom to NO when the zoomScale dips below the threshold. But that has the effect of snapping the zooming view instantly to the minimumZoomScale.
I also tried setting the minimumZoomScale to the current zoomScale once it dips below the threshold. This solves the bounce back problem because the zoomScale is now equal to the minimumZoomScale and so the bounce back is imperceptible. This has a visual glitch as well when I make that assignment. I'm not sure it is safe to set the minimumZoomScale to the current zoomScale while zooming. So this approach isn't ideal either.
What can I do to hook into the gesture end event and avoid the bounce back?

-[NSButton setImage:] only redraws the first time it's called

I'm working with a view-based NSTableView that uses a custom cell view with several controls in it. One of the controls is an image-only NSButton that either shows a checkmark image or no image at all. Additionally, when I mouse over one of these buttons that has no image, I would like a "faded" version of the checkmark image to be displayed while the mouse is inside the button.
I've gotten the code set up using NSTrackingArea to respond to -mouseEntered: and -mouseExited: events, calling -setImage: on the NSButton to show the faded checkmark while the mouse is inside the button, then set the image back to nil when the mouse exits.
As far as I can tell, all that code seems to be working as expected, but while the button will update and show the checkmark image the first time the mouse enters the view, then disappear when it exits, moving the mouse back over the button a second time does not cause the button to redraw for some reason, leaving it always blank after the first redraw no matter how many times the image gets set.
This is a summary of the relevant code:
- (void)refreshActiveButton
{
self.activeButton.image = self.activeImage;
}
- (void)mouseEntered:(NSEvent *)theEvent
{
NSLog(#"mouse entered %#", self.listItem.name);
self.mouseOverActiveButton = YES;
[self refreshActiveButton];
}
- (void)mouseExited:(NSEvent *)theEvent
{
NSLog(#"mouse exited %#", self.listItem.name);
self.mouseOverActiveButton = NO;
[self refreshActiveButton];
}
- (NSImage*)activeImage
{
NSLog(#"returning new active image");
if (self.listItem.isActive)
return [NSImage imageNamed:#"checkmark16"];
else if (self.mouseOverActiveButton)
return fadedCheckmark;
else
return nil;
}
The button is set up in a .xib file as a "Momentary Push In" (I've also tried "Momentary Change" - same behavior) with no title displayed and no border.
Running in the debugger, I've been able to confirm that:
The -mouseEntered: and -mouseExited: methods do continue to get called when moving the mouse over the button, even when the display doesn't update.
The correct image is being assigned to the button, and is returned back from the button instance after being set. (either the fadedCheckmark image when the mouse is inside, or nil when the mouse is outside)
I tried calling -setNeedsDisplay:YES on the button after assigning the image, as well as setting a layerContentRedrawPolicy of NSViewLayerContentsRedrawOnSetNeedsDisplay (the entire table view is layer-backed) with no change in behavior. Edit: also tried disabling layer backing altogether, with no change.
I feel like there must be something obvious staring me in the face that I'm missing, but if anyone can clue me in, I'd appreciate it.
The short answer: it's a bug! I've filed it with Apple as radar 20774731, and on Open Radar at http://openradar.appspot.com/radar?id=4957762287042560
The basic sequence is, if you set the button's image to something, then set it back to nil, all subsequent setImage: calls have no effect on the button, forever as far as I can tell. I did forget to mention in the original post that this is all on 10.10.3. I did find some interesting stuff when debugging though.
It appears that once you set an image on the NSButton, after an additional pass of the run loop, it gains an NSImageView as a subview, which is presumably doing the actual drawing of the image. Apple has said they are moving NSCell towards deprecation, so I guess this is the first step, with relieving NSButtonCell of the responsibility of actually drawing the image.
So, the first time you set the image, the image gets set correctly on both the NSButton and the NSImageView. When the image gets set back to nil, they both get their images set back to nil as well. However, when you set the image a second time, while the NSButton's own image property does return back the new image, the underlying NSImageView's image property remains nil. So that might explain why it's not drawing anything.
Ultimately what I ended up doing was using a plain NSImageView with a transparent NSButton right on top of it. I don't get the highlighting you get when you click on a button, but I can live with that.

How to stop NSScrollView's animation when removing a cell

I'm currently making an OS X app with an NSScrollView. The scroll view's cells have a small "X" in them that allows me to remove them one at a time. However, whenever a cell is removed, the lower cells get pushed up to fill the space via a short animation. I'm wondering how to disable this animation so that the lower cells are instantly pushed up as soon as the cell is removed. Any help is appreciated!
Edit: The contents of the scroll view are maintained via an NSArrayController, so removing clicking the "x" sends a notification to an observing delegate which in turn calls:
-(void)alertRemoved:(NSNotification*)notification{
// Random code
...
[eventArrayController removeObject:alert];
}

Infinite UIScrollView in both direction

I would like to create an infinite scrollView (like a slot machine), but without paging. When the user scrolls down, it's easy i just have to increase the contentSize and the scrollView scroll endlessly :
- (void)scrollViewDidScroll:(UIScrollView *)theScrollView {
theScrollView.contentSize = CGSizeMake(45, theScrollView.contentSize.height+45);
}
But how can i create the same effect when the user scrolls upward ? I tried to play with the contentInset but then the contentOfsset doesn't get updated and i end up having weird behaviour.
Do you have any idea how i could achieve that ?
I needed the same, so I created this: http://dev.doukasd.com/2011/04/infinite-scrolling-dial-control-for-ios/
Have a look at the video, I believe it's what you're looking for. Source code is included.
I have developed this kind scroll view. It can scroll infinite.
You can check on github: https://github.com/quangrubify/InfiniteUITableView
I think you should give us more details about the issue. What content do you want the user to see when he is scrolling upwards? You increase the contentSize in scrollViewDidScroll method, but you are not checking the contentOffset, so the contentWill be bigger whenever the user scrolls the scrollView (either way, even horizontal if allowed). Since the contentOffset is already at 0, the user cant scroll upwards because there is nothing that the scroll view can show.
I dont know the content of your scrollView, but I have implemented infinite scrolling horizontally. For details, see: https://stackoverflow.com/a/12856174/936957
PS: Do not use "magic numbers", this is a better alternative:
theScrollView.contentSize = CGSizeMake(theScrollView.contentSize.x, theScrollView.contentSize.height+45);
//Or theScrollView.frame.size.width alternatively

How can I trigger Core Animation on an animator proxy during a call to resizeSubviewsWithOldSize?

I have some NSViews that I'm putting in one of two layouts depending on the size of my window.
I'm adjusting the layout when the relevant superview receives the resizeSubviewsWithOldSize method.
This works, but I'd like to animate the change. So naturally I tried calling the animator proxy when I set the new frames, but the animation won't run while the user is still dragging. If I release the mouse before the animation is scheduled to be done I can see the tail end of the animation, but nothing until then. I tried making sure kCATransactionDisableActions was set to NO, but that didn't help.
Is it possible to start a new animation and actually have it run during the resize?
I don't think you can do this easily because CA's animations are run via a timer and the timer won't fire during the runloop modes that are active while the user is dragging.
If you can control the runloop as the user is dragging, play around with the runloop modes. That'll make it work. I don't think you can change it on the CA side.
This really isn't an answer, but I would advise against animating anything while dragging to resize a window. The screen is already animating (from the window moving) - further animations are likely going to be visually confusing and extraneous.
CoreAnimation effects are best used to move from one known state to another - for example, when a preference window is resizing to accompany a new pane's contents, and you know both the old and new sizes, or when you are fading an object in or out (or both). Doing animation while the window is resizing is going to be visually confusing and make it harder for the user to focus on getting the size of the window where they want it to be.

Resources