Finding the View with Center X-Coordinate Match inUIScrollView Scrolling

Understanding UIScrollView Scrolling and Frame Coordinates

When working with UIScrollView in iOS, it’s essential to understand how scrolling affects view coordinates. A UIScrollView can have multiple content views arranged horizontally or vertically within its frame. These content views are often nested inside other views, which can be used as anchors to calculate the scrolling center point.

The Problem and Requirements

You’re given a UIScrollView with several content views aligned horizontally. You want to find the view that contains the center x-coordinate of the scrollview’s frame (not its content view’s frame) as it scrolls. To achieve this, you’ll need to use the - (void)scrollViewDidScroll:(UIScrollView *)scrollView delegate method and perform some calculations.

The Solution Overview

To solve this problem, we’ll follow these steps:

  1. Calculate the start-point of a given frame in reference to another view using the convertPoint:toView: method.
  2. Use the - (void)scrollViewDidScroll:(UIScrollView *)scrollView delegate method to access the scrollview’s content offset and scrolling center point.
  3. Iterate through all content views and calculate their frame coordinates relative to the anchor view (usually the scrollview).
  4. Find the view whose frame coordinate matches the scrollview’s scrolling center x-coordinate.

Calculating Frame Coordinates

First, let’s understand how we can get the start-point of a frame in reference to another view using convertPoint:toView:. This method converts a point from one view’s coordinate system to another’s.

CGPoint origin = [myChildView convertPoint:CGPointMake(myChildView.bounds.origin.x, myChildView.bounds.origin.y) toView:[self scrollView]];

In the above code:

  • myChildView is the view for which we want to calculate its coordinates relative to the anchor view (the scrollview).
  • [self scrollView] is the anchor view (usually the scrollview).

By using convertPoint:toView:, we’re essentially “re-basing” the child view’s origin point in terms of its parent coordinate system.

Accessing ScrollView Content Offset and Scrolling Center Point

To get the content offset and scrolling center point, you can access these properties directly on the scrollview object within your delegate methods.

CGFloat offset = scrollView.contentOffset.x;
CGFloat center = scrollView.contentOffset.x + scrollView.frame.size.width / 2.0f;

Here:

  • scrollView.contentOffset.x is the x-coordinate of the content offset, which represents how far the scrollview has scrolled horizontally.
  • scrollView.contentOffset.x gives us the same value as above but using a different method for clarity.

Finding the Matching View

Now that we have the scrolling center point and can calculate frame coordinates relative to the anchor view (the scrollview), let’s iterate through all content views to find the one whose frame coordinate matches the scrolling center x-coordinate.

for (UIView *childView in self.scrollView.subviews) {
    // Skip the scrollview itself to avoid infinite loop.
    if ([childView isKindOfClass:[UIScrollView class]]) {
        continue;
    }
    
    CGPoint childOrigin = [childView convertPoint:CGPointMake(childView.bounds.origin.x, childView.bounds.origin.y) toView:[self scrollView]];
    CGFloat frameX = childOrigin.x + childView.frame.size.width / 2.0f;
    
    if (abs(offset - frameX) < 1e-6f) {
        // The frame coordinate of the current view matches the scrolling center point.
        break;
    }
}

Here:

  • self.scrollView.subviews returns an array containing all subviews of the scrollview, including its content views and potentially other UI elements.
  • We skip the scrollview itself to avoid infinite recursion or logic loops.
  • Within each child view, we calculate its coordinates relative to the anchor view (the scrollview) using convertPoint:toView:. This gives us its position in the global coordinate system.
  • We then find the frame’s x-coordinate by offsetting this point’s x-coordinate with half the width of the child view (childOrigin.x + childView.frame.size.width / 2.0f). This aligns it with the center point of the frame.
  • By iterating through all content views, we compare each one’s matching frame coordinate against our calculated scrolling center point offset.

Full-Example Implementation

Here is an example implementation that puts everything together:

#import <UIKit/UIKit.h>

@interface ViewController () <UIScrollViewDelegate>
@property (nonatomic, strong) UIScrollView *scrollView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // Create a scrollview and add it to the view hierarchy.
    self.scrollView = [[UIScrollView alloc] init];
    self.scrollView.frame = CGRectMake(0.0f, 0.0f, 300.0f, 200.0f);
    self.scrollView.delegate = self;
    [self.view addSubview:self.scrollView];
    
    // Create content views and add them to the scrollview.
    for (int i = 0; i < 10; i++) {
        UIView *childView = [[UIView alloc] init];
        childView.frame = CGRectMake(i * 30.0f, 50.0f, 20.0f, 200.0f);
        self.scrollView.addSubview(childView);
    }
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    // Calculate the scrolling center point's x-coordinate.
    CGFloat offset = scrollView.contentOffset.x;
    CGFloat center = scrollView.contentOffset.x + scrollView.frame.size.width / 2.0f;
    
    // Find the matching view by iterating through its subviews and comparing frame coordinates.
    for (UIView *childView in self.scrollView.subviews) {
        if ([childView isKindOfClass:[UIScrollView class]]) continue;
        
        CGPoint childOrigin = [childView convertPoint:CGPointMake(childView.bounds.origin.x, childView.bounds.origin.y) toView:self.scrollView];
        CGFloat frameX = childOrigin.x + childView.frame.size.width / 2.0f;
        
        if (fabs(offset - frameX) < 1e-6f) {
            // The matching view's frame coordinate matches the scrolling center point's x-coordinate.
            NSLog(@"Matching View Found: %@",NSStringFromClass([childView class]));
            break;
        }
    }
}

@end

In this implementation, a UIScrollView is added to the view hierarchy with multiple content views nested inside it. When the scrollview scrolls, the - (void)scrollViewDidScroll:(UIScrollView *)scrollView delegate method calculates its center point’s x-coordinate and iterates through all subviews to find the one whose frame coordinate matches the scrolling center point.

By following these steps, you can accurately identify which view contains the center x-coordinate of your UIScrollView.


Last modified on 2023-07-07