Create an animation like that of an NSPopOver - cocoa

I'm working at a custom Window object that is displayed as child in a parent Window.
For this object I'd like to create an animation like that of an NSPopover.
My first idea is to create a Screenshot of the child Window, than animate it using Core Animation and finally showing the real Window.
Before begging the implementation I would like to know if exists a better method and what you think about my solution.

It's not trivial. Here's how I do it:
#interface ZoomWindow : NSWindow
{
CGFloat animationTimeMultiplier;
}
#property (nonatomic, readwrite, assign) CGFloat animationTimeMultiplier;
#end
#implementation ZoomWindow
#synthesize animationTimeMultiplier;
- (NSTimeInterval)animationResizeTime: (NSRect)newWindowFrame
{
float multiplier = animationTimeMultiplier;
if (([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) != 0) {
multiplier *= 10;
}
return [super animationResizeTime: newWindowFrame] * multiplier;
}
#end
#implementation NSWindow (PecuniaAdditions)
- (ZoomWindow*)createZoomWindowWithRect: (NSRect)rect
{
// Code mostly from http://www.noodlesoft.com/blog/2007/06/30/animation-in-the-time-of-tiger-part-1/
// Copyright 2007 Noodlesoft, L.L.C.. All rights reserved.
// The code is provided under the MIT license.
// The code has been extended to support layer-backed views. However, only the top view is
// considered here. The code might not produce the desired output if only a subview has its layer
// set. So better set it on the top view (which should cover most cases).
NSImageView *imageView;
NSImage *image;
NSRect frame;
BOOL isOneShot;
frame = [self frame];
isOneShot = [self isOneShot];
if (isOneShot) {
[self setOneShot: NO];
}
BOOL hasLayer = [[self contentView] wantsLayer];
if ([self windowNumber] <= 0) // <= 0 if hidden
{
// We need to temporarily switch off the backing layer of the content view or we get
// context errors on the second or following runs of this code.
[[self contentView] setWantsLayer: NO];
// Force window device. Kinda crufty but I don't see a visible flash
// when doing this. May be a timing thing wrt the vertical refresh.
[self orderBack: self];
[self orderOut: self];
[[self contentView] setWantsLayer: hasLayer];
}
// Capture the window into an off-screen bitmap.
image = [[NSImage alloc] initWithSize: frame.size];
[[self contentView] lockFocus];
NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithFocusedViewRect: NSMakeRect(0.0, 0.0, frame.size.width, frame.size.height)];
[[self contentView] unlockFocus];
[image addRepresentation: rep];
// If the content view is layer-backed the above initWithFocusedViewRect call won't get the content
// of the view (seems it doesn't work for CALayers). So we need a second call that captures the
// CALayer content and copies it over the captured image (compositing so the window frame and its content).
if (hasLayer)
{
NSRect contentFrame = [[self contentView] bounds];
int bitmapBytesPerRow = 4 * contentFrame.size.width;
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
CGContextRef context = CGBitmapContextCreate (NULL,
contentFrame.size.width,
contentFrame.size.height,
8,
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colorSpace);
[[[self contentView] layer] renderInContext: context];
CGImageRef img = CGBitmapContextCreateImage(context);
CFRelease(context);
NSImage *subImage = [[NSImage alloc] initWithCGImage: img size: contentFrame.size];
CFRelease(img);
[image lockFocus];
[subImage drawAtPoint: NSMakePoint(0, 0)
fromRect: NSMakeRect(0, 0, contentFrame.size.width, contentFrame.size.height)
operation: NSCompositeCopy
fraction: 1];
[image unlockFocus];
}
ZoomWindow *zoomWindow = [[ZoomWindow alloc] initWithContentRect: rect
styleMask: NSBorderlessWindowMask
backing: NSBackingStoreBuffered
defer: NO];
zoomWindow.animationTimeMultiplier = 0.3;
[zoomWindow setBackgroundColor: [NSColor colorWithDeviceWhite: 0.0 alpha: 0.0]];
[zoomWindow setHasShadow: [self hasShadow]];
[zoomWindow setLevel: [self level]];
[zoomWindow setOpaque: NO];
[zoomWindow setReleasedWhenClosed: NO];
[zoomWindow useOptimizedDrawing: YES];
imageView = [[NSImageView alloc] initWithFrame: [zoomWindow contentRectForFrameRect: frame]];
[imageView setImage: image];
[imageView setImageFrameStyle: NSImageFrameNone];
[imageView setImageScaling: NSScaleToFit];
[imageView setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
[zoomWindow setContentView: imageView];
[self setOneShot: isOneShot];
return zoomWindow;
}
- (void)fadeIn
{
[self setAlphaValue: 0.f];
[self orderFront: nil];
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration: 0.3];
[[self animator] setAlphaValue: 1.f];
[NSAnimationContext endGrouping];
}
- (void)zoomInWithOvershot: (NSRect)overshotFrame withFade: (BOOL)fade makeKey: (BOOL)makeKey
{
[self setAlphaValue: 0];
NSRect frame = [self frame];
ZoomWindow *zoomWindow = [self createZoomWindowWithRect: frame];
zoomWindow.alphaValue = 0;
[zoomWindow orderFront: self];
NSDictionary *windowResize = #{NSViewAnimationTargetKey: zoomWindow,
NSViewAnimationEndFrameKey: [NSValue valueWithRect: overshotFrame],
NSViewAnimationEffectKey: NSViewAnimationFadeInEffect};
NSArray *animations = #[windowResize];
NSViewAnimation *animation = [[NSViewAnimation alloc] initWithViewAnimations: animations];
[animation setAnimationBlockingMode: NSAnimationBlocking];
[animation setAnimationCurve: NSAnimationEaseIn];
[animation setDuration: 0.2];
[animation startAnimation];
zoomWindow.animationTimeMultiplier = 0.5;
[zoomWindow setFrame: frame display: YES animate: YES];
[self setAlphaValue: 1];
if (makeKey) {
[self makeKeyAndOrderFront: self];
} else {
[self orderFront: self];
}
[zoomWindow close];
}
This is implemented in an NSWindow category. So you can call:
- (void)zoomInWithOvershot: (NSRect)overshotFrame withFade: (BOOL)fade makeKey: (BOOL)makeKey
on any NSWindow.
I should add that I haven't been able to get both animations to run at the same time (fade and size), but the effect is quite similar to how NSPopover does it. Maybe someone else can fix the animation issue.
Needless to say this code works on 10.6 too where you don't have NSPopover (that's why I have written it in the first place).

Related

Label/Text in Xcode Appears Blurry

I am trying to make a preferences view with lots of text. I cannot figure out why the text is blurry when i run the project, even though the interface builder is very sharp and clear. Here is a picture. [1]: http://i.stack.imgur.com/EQl6D.png
here is the code for the views:
#implementation SKMainWindowController
-(NSRect)newFrameForNewContentView:(NSView*)view {
NSWindow *window = [self window];
NSRect newFrameRect = [window frameRectForContentRect:[view frame]];
NSRect oldFrameRect = [window frame];
NSSize newSize = newFrameRect.size;
NSSize oldSize = oldFrameRect.size;
NSRect frame = [window frame];
frame.size = newSize;
frame.origin.y -= (newSize.height - oldSize.height);
return frame;
}
-(NSView *)viewForTag:(int)tag {
NSView *view = nil;
switch (tag) {
case 0: default:
view = welcome;
break;
case 1:
view = status;
break;
case 2:
view = power;
break;
case 3:
view = preferences;
break;
case 4:
view = about;
break;
}
return view;
}
- (BOOL)validateToolbarItem:(NSToolbarItem *)item {
if ([item tag] == currentViewTag) return NO;
else return YES;
}
-(void)awakeFromNib {
[[self window] setContentSize:[welcome frame].size];
[[[self window] contentView] addSubview:welcome];
[[[self window] contentView] setWantsLayer:NO];
}
-(IBAction)switchWelcome:(id)sender {
int tag = [sender tag];
NSView *view = [self viewForTag:tag];
NSView *previousView = [self viewForTag:currentViewTag];
currentViewTag = tag;
NSRect newFrame = [self newFrameForNewContentView:view];
[NSAnimationContext beginGrouping];
if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask)
[[NSAnimationContext currentContext] setDuration:1.0];
[[[[self window] contentView] animator] replaceSubview:previousView with:view];
[[[self window] animator] setFrame:newFrame display:YES];
[NSAnimationContext endGrouping];
[welcomeButton setState:NSOnState];
[statsButton setState:NSOffState];
[powerButton setState:NSOffState];
}
I've tried changing the text fields into code by doing things like [textField setStringValue:#""];, but with no luck it is still blurry. Thanks for any help. PS: This is for cocoa, not iOS.
Found the answer, ended up being the stupid visual effect view not cooperating. Simple fix was to set the visual effect view to within the window, and set as inactive. (although there is now no point in having a visual effect view.)

How do I implement frameForAlignmentRect:/alignmentRectForFrame: such that the frame outside the alignment rect encapsulates those of subviews?

So I now have my Auto Layout-based container working, for the most part. On 10.8 (I need to run on 10.7 and newer), I see this:
Notice how the sides of the NSProgressIndicator and NSPopUpButton are clipped.
After some experimentation, I found that overriding alignmentRectInsets and returning 50 pixels of insets on all sides shows no clipping:
In both cases, the controls are bound to the left and right edges of the container view alignment rect with H:|[view]|. I imagine this will happen on other versions of OS X too, but it's most noticeable here (and as of writing I only have access to 10.8 and 10.10 installs).
Now, using alignment rect insets of 50 pixels on each side sounds wrong. I don't think there'd be any control that would need more than 50 pixels, but I'd rather do these correctly. So my question is: How do I implement the alignmentRectForFrame: and frameForAlignmentRect: selectors to properly account for the frames and alignment rects of the subviews?
Right now, I'm thinking to force a layout and then observe the frames and alignment rects of each subview, assuming that alignment rect (0, 0) of my last subview (the subviews are arranged linearly) will be at alignment rect (0, 0) of the container view. But I'm not sure if this approach is sufficient to handle all cases, and I'm not sure if I can invert the operation in the same way that these two selectors require. Subtraction, maybe?
If what I described above is the solution, could I do that with alignmentRectInsets, or must the insets returned by that method never change during the lifetime of the view?
Or is the second screenshot showing a scenario that Interface Builder won't reproduce, and thus I assume is "wrong" from a guidelines standpoint?
In the sample program below, start without a command-line argument to simulate the first screenshot, and start with an argument to simulate the second screenshot. Check the Spaced checkbox to add spacing to the views.
Thanks!
// 17 august 2015
#import <Cocoa/Cocoa.h>
BOOL useInsets = NO;
#interface ContainerView : NSView
#end
#implementation ContainerView
- (NSEdgeInsets)alignmentRectInsets
{
if (useInsets)
return NSEdgeInsetsMake(50, 50, 50, 50);
return [super alignmentRectInsets];
}
#end
NSWindow *mainwin;
NSView *containerView;
NSProgressIndicator *progressbar;
NSPopUpButton *popupbutton;
NSButton *checkbox;
void addConstraints(NSView *view, NSString *constraint, NSDictionary *views)
{
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:constraint
options:0
metrics:nil
views:views];
[view addConstraints:constraints];
}
void relayout(BOOL spaced)
{
[containerView removeConstraints:[containerView constraints]];
NSDictionary *views = #{
#"pbar": progressbar,
#"pbutton": popupbutton,
#"checkbox": checkbox,
};
NSString *vconstraint = #"V:|[pbar][pbutton][checkbox]|";
if (spaced)
vconstraint = #"V:|[pbar]-[pbutton]-[checkbox]|";
addConstraints(containerView, vconstraint, views);
addConstraints(containerView, #"H:|[pbar]|", views);
addConstraints(containerView, #"H:|[pbutton]|", views);
addConstraints(containerView, #"H:|[checkbox]|", views);
NSView *contentView = [mainwin contentView];
[contentView removeConstraints:[contentView constraints]];
NSString *base = #":|[view]|";
if (spaced)
base = #":|-[view]-|";
views = #{
#"view": containerView,
};
addConstraints(contentView, [#"H" stringByAppendingString:base], views);
addConstraints(contentView, [#"V" stringByAppendingString:base], views);
}
#interface appDelegate : NSObject<NSApplicationDelegate>
#end
#implementation appDelegate
- (IBAction)onChecked:(id)sender
{
relayout([checkbox state] == NSOnState);
}
- (void)applicationDidFinishLaunching:(NSNotification *)note
{
mainwin = [[NSWindow alloc]
initWithContentRect:NSMakeRect(0, 0, 320, 240)
styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)
backing:NSBackingStoreBuffered
defer:YES];
NSView *contentView = [mainwin contentView];
containerView = [[ContainerView alloc] initWithFrame:NSZeroRect];
[containerView setTranslatesAutoresizingMaskIntoConstraints:NO];
progressbar = [[NSProgressIndicator alloc] initWithFrame:NSZeroRect];
[progressbar setControlSize:NSRegularControlSize];
[progressbar setBezeled:YES];
[progressbar setStyle:NSProgressIndicatorBarStyle];
[progressbar setIndeterminate:NO];
[progressbar setTranslatesAutoresizingMaskIntoConstraints:NO];
[containerView addSubview:progressbar];
popupbutton = [[NSPopUpButton alloc] initWithFrame:NSZeroRect];
[popupbutton setPreferredEdge:NSMinYEdge];
NSPopUpButtonCell *pbcell = (NSPopUpButtonCell *) [popupbutton cell];
[pbcell setArrowPosition:NSPopUpArrowAtBottom];
[popupbutton addItemWithTitle:#"Item 1"];
[popupbutton addItemWithTitle:#"Item 2"];
[popupbutton setTranslatesAutoresizingMaskIntoConstraints:NO];
[containerView addSubview:popupbutton];
checkbox = [[NSButton alloc] initWithFrame:NSZeroRect];
[checkbox setTitle:#"Spaced"];
[checkbox setButtonType:NSSwitchButton];
[checkbox setBordered:NO];
[checkbox setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]]];
[checkbox setTarget:self];
[checkbox setAction:#selector(onChecked:)];
[checkbox setTranslatesAutoresizingMaskIntoConstraints:NO];
[containerView addSubview:checkbox];
[contentView addSubview:containerView];
relayout(NO);
[mainwin cascadeTopLeftFromPoint:NSMakePoint(20, 20)];
[mainwin makeKeyAndOrderFront:mainwin];
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app
{
return YES;
}
#end
int main(int argc, char *argv[])
{
useInsets = (argc > 1);
NSApplication *app = [NSApplication sharedApplication];
[app setActivationPolicy:NSApplicationActivationPolicyRegular];
[app setDelegate:[appDelegate new]];
[app run];
return 0;
}

Animate image from array with animation style

I have 3 images in my array for this sample code it just animate without any style .I want to animate like fade in fade out style.
NSArray *imarray = [[NSArray alloc] initWithObjects:
[UIImage imageWithContentsOfFile:[NSString stringWithFormat:#"%#/lady1_open.png", path]],
[UIImage imageWithContentsOfFile:[NSString stringWithFormat:#"%#/lady1_open2.png", path]],
[UIImage imageWithContentsOfFile:[NSString stringWithFormat:#"%#/lady1_open3.png", path]],
nil];
bingo_girl.animationImages=imarray;
bingo_girl.animationDuration=5;
bingo_girl.animationRepeatCount=0;
[bingo_girl.layer addAnimation:transition forKey:nil];
[bingo_girl startAnimating];
You may animate images with transition manually for example using this category
#implementation UIImageView (AnimateTransition)
- (void) assignImage:(UIImage *)image withTransition:(NSString *)withTransition withDirection:(NSString *)withDirection{
if(image == nil)
{
[self setImage:nil];
return;
}
if ([UIImagePNGRepresentation(self.image) isEqualToData:UIImagePNGRepresentation(image)]) {
return;
}
CATransition *animation = [CATransition animation];
animation.duration = 0.2;
animation.type = withDirection;
animation.subtype = withDirection;
[[self layer] addAnimation:animation forKey:#"imageFade"];
[self setImage:image];
}
#end

pan gesture of UIImageView interfers the scroll

I want to implement the functionality of pan gesture to a imageview, this imageview is a subview of a scroll view. the problem is when i try to scroll the scrollview the pan gesture of the imageview gets recognized, and the scrollview does not scroll.
please help me with this.
this is the code for initializing the pan to the uiimageview:
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(move:)];
[panRecognizer setMinimumNumberOfTouches:1];
[panRecognizer setMaximumNumberOfTouches:1];
[panRecognizer setDelegate:self];
[[[scrollView subviews] objectAtIndex:i] addGestureRecognizer:panRecognizer];
the imageview has the user set to "YES"
and the action for it
-(void)move:(UIPanGestureRecognizer *)sender
{
[[[[[sender view] superview] superview] superview] bringSubviewToFront:sender.view];
if([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateBegan) {
if ([[[sender view] superview] superview] == itemView) // adds the imageview in the item scroll
{
int tag = [[sender view] tag] / 100;
UIScrollView *tempScroll = (UIScrollView *)[itemView viewWithTag:-tag];
CGRect frame = CGRectFromString([itemFrameDict objectForKey:[NSString stringWithFormat:#"%i",[[sender view] tag]]]);
CGAffineTransform transform = temp.transform;
UIImageView *tempImageView = [[UIImageView alloc] initWithImage: [(UIImageView *) [sender view] image]];
tempImageView.tag = [[sender view] tag];
//NSLog(#"[sender view] superview] : %#", [[sender view] superview]);
tempImageView.frame = frame;
[tempImageView setTransform:transform];
[tempImageView setFrame:frame];
[tempImageView setUserInteractionEnabled:YES];
[tempScroll addSubview:tempImageView];
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(move:)];
[panRecognizer setMinimumNumberOfTouches:1];
[panRecognizer setMaximumNumberOfTouches:1];
[panRecognizer setDelegate:self];
[tempImageView addGestureRecognizer:panRecognizer];
UITapGestureRecognizer *singleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
[singleTapGestureRecognizer setNumberOfTapsRequired:1];
[tempImageView addGestureRecognizer:singleTapGestureRecognizer];
}
}
if([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateChanged)
{
CGPoint translatedPoint = [sender locationInView:[[sender view] superview]] ;
newTranslatedPoint = [[[sender view] superview] convertPoint:translatedPoint toView:self.view];
if ([sender view].frame.size.width == 35 && [sender view].frame.size.height == 35)
[[sender view] sizeToFit];
[sender view].center = CGPointMake(newTranslatedPoint.x, newTranslatedPoint.y); //startFrame;
[self.view addSubview:[sender view]];
}
if([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateEnded)
{
CGPoint newPoint = [[[sender view] superview] convertPoint:newTranslatedPoint toView:itemView];
if (newPoint.y < 0)// || !viewUp)
{
//NSLog(#" baseView");
[baseView addSubview:[sender view]];
UITapGestureRecognizer *doubleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleTap:)];
[doubleTapGestureRecognizer setNumberOfTapsRequired:2];
[[sender view] addGestureRecognizer:doubleTapGestureRecognizer];
UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(scale:)];
[pinchRecognizer setDelegate:self];
[[sender view] addGestureRecognizer:pinchRecognizer];
UIRotationGestureRecognizer *rotationRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotate:)];
[rotationRecognizer setDelegate:self];
[[sender view] addGestureRecognizer:rotationRecognizer];
}
else if (newPoint.y > 0)// && viewUp )
{
//NSLog(#" itemView");
int tag = [[sender view] tag] / 100;
UIScrollView *tempScroll = (UIScrollView *)[itemView viewWithTag:-tag];
CGRect frame = CGRectFromString([itemFrameDict objectForKey:[NSString stringWithFormat:#"%i",[[sender view] tag]]]);
CGAffineTransform transform = temp.transform;
[[sender view] setTransform:transform];
[[sender view] setFrame:frame];
[tempScroll addSubview:[sender view]];
}
}
}
I'm not sure I understand what you're trying to achieve exactly by using a UIPanGestureRecognizer on a UIImageView that is already in a UIScrollView. But from your description, it sounds like you want the UIScrollView to do it's thing and for the UIPanGestureRecognizer to not block that. I believe you need to implement this delegate method for your UIPanGestureRecognizer:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
Please read the UIGestureRecognizerDelegate Protocol reference in regards to allowing simultaneous gesture recognition. According to the documentation:
The default implementation returns NO—no two gestures can be
recognized simultaneously.
/* EDIT */
From UIPanGestureRecognizer - Only vertical or horizontal:
Implement just one direction for a UIPanGestureRecognizer. This is for a vertical pan:
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)panGestureRecognizer {
CGPoint translation = [panGestureRecognizer translationInView:someView];
return fabs(translation.y) > fabs(translation.x);
}

Custom scroll view - image outside the frame

I would like to make an infinite scrolling view for iPad. The scrolling works, only when I add pictures to my customized scrollview class, it appears out of the view's frame too (so I have view, which has a UIScrollView on it. This scroll view is connected with my CustomScroll.h :UIScrollView and CustomScroll.m files). Why do the images appear outside the view's frame (the view's frame is set in nib file to 320x420)?
CustomScroll.h:UIScrollView
A part from CustomScroll.m file:
- (id)initWithCoder:(NSCoder *)aDecoder{
if ((self = [super initWithCoder:aDecoder])){
self.contentSize = CGSizeMake(2000, 416);
[self setShowsVerticalScrollIndicator:NO];
for (int i=1; i<=4; i++){
[self addImageAtPosition:i];
}
//todo: to replace with the method: addFirstElementToTheEnd
UIImageView *image1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image1.jpg"]];
[image1 setFrame:CGRectMake(5*320, 0, 320, 416)];
[self addSubview:image1];
[image1 release];
//todo: to replace with the method: addLastElementToTheBeginning
UIImageView *image2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image4.jpg"]];
[image2 setFrame:CGRectMake(0*320, 0, 320, 416)];
[self addSubview:image2];
[image2 release];
}
return self;
}
- (void)addImageAtPosition:(NSInteger)position{
UIImageView *tempImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:#"image%d.jpg", position]]];
[tempImage setFrame:CGRectMake((position * 320), 0, 320, 416)];
[self addSubview:tempImage];
[tempImage release];
}
- (void)recenterIfNecessary{
CGFloat currentOffsetX = [self contentOffset].x;
if (currentOffsetX > 1600.00){
self.contentOffset = CGPointMake(320, [self contentOffset].y );
}
if (currentOffsetX < 320.00){
self.contentOffset = CGPointMake(5*320, [self contentOffset].y);
}
}
- (void) layoutSubviews{
[super layoutSubviews];
[self recenterIfNecessary];
}
TY.

Resources