itotsev
itotsev

Reputation: 121

How to draw a border around the content of a UIScrollView?

I'm working on a document viewer. The document is displayed inside a UIScrollView so that it can be scrolled and zoomed. I need to draw a border around the document in order to separate it visually from the background of the UIScrollView. The border must not be zoomed together with the document -- it should maintain a constant thickness regardless of the zoom scale.

My current setup consists of a UIScrollView with two UIView children -- one for the document, and one for the border. I've overriden viewForZoomingInScrollView: to return the document view. I've also overridden layoutSubviews to center the document view (in case it's smaller than the UIScrollView) and then resize and position the border view behind it so that it looks like a frame. This works OK when the user is scrolling and zooming manually. But when I use zoomToRect:animated: to zoom programatically, layoutSubviews is called before the animation starts and my border view gets resized immediately with the document view catching up a bit later.

Clarification: The border needs to be tightly fitting around the document view and not around the UIScrollView itself.

Upvotes: 2

Views: 5345

Answers (4)

itotsev
itotsev

Reputation: 121

Finally, I was able to fix the problem with animated zooming. My setup is the same as described in the question. I just added some code to my layoutSubview implementation in order to detect any running UIScrollView animation and match it with a similar animation for resizing the border.

Here is the code:

- (void)layoutSubviews
{
  [super layoutSubviews];

  // _pageView displays the document
  // _borderView represents the border around it

  . . .


  // _pageView is now centered -- we have to move/resize _borderView
  // layers backing a view are not implicitly animated
  // the following two lines work just fine if we don't need animation
  _borderView.layer.bounds = CGRectMake(0.0, 0.0, frameToCenter.size.width + 2.0 * 15.0, frameToCenter.size.height + 2.0 * 15.0);
  _borderView.layer.position = _pageView.center;

  if (_pageView.layer.animationKeys.count > 0)
  {
    // UIScrollView is animating its content (_pageView)
    // so we need to setup a matching animation for _borderView

    [CATransaction begin];

    CAAnimation *animation = [_pageView.layer animationForKey:[_pageView.layer.animationKeys lastObject]];
    CFTimeInterval beginTime = animation.beginTime;
    CFTimeInterval duration = animation.duration;

    if (beginTime != 0.0) // 0.0 means the animation starts now
    {
      CFTimeInterval currentTime = [_pageView.layer convertTime:CACurrentMediaTime() fromLayer:nil];
      duration = MAX(beginTime + duration - currentTime, 0.0);
    }

    [CATransaction setAnimationDuration:duration];
    [CATransaction setAnimationTimingFunction:animation.timingFunction];

    // calculate the initial state for _borderView animation from _pageView presentation layer
    CGPoint presentationPos = [_pageView.layer.presentationLayer position];
    CGRect presentationBounds = [_pageView.layer.presentationLayer frame];
    presentationBounds.origin = CGPointZero;
    presentationBounds.size.width += 2.0 * 15.0;
    presentationBounds.size.height += 2.0 * 15.0;

    CABasicAnimation *boundsAnim = [CABasicAnimation animationWithKeyPath:@"bounds"];
    boundsAnim.fromValue = [NSValue valueWithCGRect:presentationBounds];
    boundsAnim.toValue = [NSValue valueWithCGRect:_borderView.layer.bounds];
    [_borderView.layer addAnimation:boundsAnim forKey:@"bounds"];

    CABasicAnimation *posAnim = [CABasicAnimation animationWithKeyPath:@"position"];
    posAnim.fromValue = [NSValue valueWithCGPoint:presentationPos];
    posAnim.toValue = [NSValue valueWithCGPoint:_borderView.layer.position];
    [_borderView.layer addAnimation:posAnim forKey:@"position"];

    [CATransaction commit];
  }

}

It looks hacky but it works. I wish I didn't have to reverse engineer UIScrollView in order to make a simple border look good during animation...

Upvotes: 1

Bhavin
Bhavin

Reputation: 27225

Sample Code :

yourScrollView.layer.cornerRadius=8.0f;
yourScrollView.layer.masksToBounds=YES;
yourScrollView.layer.borderColor=[[UIColor redColor]CGColor];
yourScrollView.layer.borderWidth= 1.0f;

Don't Forget : #Import <QuartzCore/QuartzCore.h>

Upvotes: 3

Vasu
Vasu

Reputation: 896

First you need to Import QuartzCore framework to your App. then import that .h file on which class where you want to set the border. like this.

#import <QuartzCore/QuartzCore.h>

Setup for Border.

ScrollView.layer.cornerRadius=5.0f;
ScrollView.layer.masksToBounds=YES;
ScrollView.layer.borderColor=[[UIColor redColor]CGColor];
ScrollView.layer.borderWidth= 4.0f;

check this one really helpful to you.

Upvotes: 1

Meet
Meet

Reputation: 4934

Import QuartzCore framework:

#import <QuartzCore/QuartzCore.h>

Now Add color to its view:

[scrollViewObj.layer setBorderColor:[[UIColor redColor] CGColor]];

[scrollViewObj.layer setBorderWidth:2.0f];

Upvotes: 0

Related Questions