Master iOS Pagination: Build Custom QiPageMenuView & QiPageContentView
This article explains how to create a flexible paginated interface in iOS using the custom QiPageMenuView and QiPageContentView components, covering implementation analysis, key properties, code examples, and two usage patterns that enable decoupled menu and content views for seamless scrolling and navigation.
Implementation Overview
As an iOS developer, pagination controllers are frequently used for news‑style homepages. The article summarizes the key points for building a paginated UI in iOS.
Analysis of the Effect
The effect consists of two parts: QiPageMenuView (a UIScrollView‑based menu) and QiPageContentView (a UIPageViewController‑based content area). QiPageMenuView offers customizable properties such as item width, margins, fonts, colors, underline, and automatic resizing. QiPageContentView encapsulates UIPageViewController to avoid duplicated code and allows independent reuse.
QiPageMenuView Implementation
The header declares the interface and a series of @property definitions for appearance and layout. The core scrolling logic is implemented in scrollToPageItem:, which calculates the target offset based on the selected item's frame and adjusts the scroll view.
- (void)scrollToPageItem:(QiPageItem *)pageItem {
[self refreshUnderLineViewPosition:pageItem];
if (self.contentSize.width <= self.width) { return; }
CGRect originalRect = pageItem.frame;
CGRect convertRect = [self convertRect:originalRect toView:self.superview];
CGFloat targetX;
CGFloat realMidX = CGRectGetMinX(originalRect) + CGRectGetWidth(originalRect) / 2;
if (CGRectGetMidX(convertRect) < CGRectGetMidX(self.frame)) {
if (realMidX > CGRectGetMidX(self.frame)) {
targetX = realMidX - CGRectGetMidX(self.frame);
} else {
targetX = 0;
}
} else {
if (realMidX + CGRectGetMidX(self.frame) < self.contentSize.width) {
targetX = realMidX - CGRectGetMidX(self.frame);
} else {
targetX = self.contentSize.width - CGRectGetMaxX(self.frame);
}
}
[self setContentOffset:CGPointMake(targetX, 0) animated:YES];
}Two usage patterns are shown: (1) initializing with a dataSource dictionary to customize colors, fonts, margins, etc.; (2) creating the view and then setting properties directly.
// Example of custom dataSource
NSDictionary *dataSource = @{
QiPageMenuViewNormalTitleColor : [UIColor blackColor],
QiPageMenuViewSelectedTitleColor : [UIColor redColor],
QiPageMenuViewTitleFont : [UIFont systemFontOfSize:14],
// ... other properties
};
QiPageMenuView *menuView = [[QiPageMenuView alloc] initWithFrame:CGRectMake(0,0,self.view.width,50)
titles:@[@"Message",@"Event",@"Broadcast"]
dataSource:dataSource];
menuView.backgroundColor = [UIColor orangeColor];
[self.view addSubview:menuView];QiPageContentView Implementation
The header defines the interface, a UIPageViewController property, an array of child view controllers, and block/delegate callbacks for scroll events.
@interface QiPageContentView : UIView <UIPageViewControllerDelegate, UIPageViewControllerDataSource, UIScrollViewDelegate>
@property (nonatomic, strong) UIPageViewController *pageViewController;
@property (nonatomic, strong) NSArray *controllerArray; // child controllers
@property (nonatomic, copy) void (^pageContentViewDidScroll)(NSInteger currentIndex, NSInteger beforeIndex, QiPageContentView *pageView);
@property (nonatomic, weak) id<QiPageContentViewDelegate> contentViewDelegate;
- (instancetype)initWithFrame:(CGRect)frame childViewController:(NSArray *)childViewControllers;
- (void)setPageContentShouldScrollToIndex:(NSInteger)index beforeIndex:(NSInteger)beforeIndex;
@endScrolling to a specific page is performed by setting the UIPageViewController’s view controllers with animation.
Interaction Between Menu and Content
Both components define delegate protocols. QiPageMenuView notifies when an item is clicked via the pageItemClicked block or the pageMenuViewDidClickedIndex:beforeIndex: delegate method. QiPageContentView notifies scroll completion via the pageContentViewDidScroll block or the pageContentViewDidScrollToIndex:beforeIndex: delegate method. By assigning the callbacks, the two views stay synchronized while remaining loosely coupled.
Combined Usage Example
A typical setup creates a menu view, adds it to the controller’s view, creates a content view positioned below the menu, and links the callbacks so that selecting a menu item scrolls the content view and vice‑versa.
QiPageMenuView *menuView = [[QiPageMenuView alloc] initWithFrame:CGRectMake(0,0,self.view.width,50)
titles:@[@"System",@"Event",@"Broadcast",@"Latest",@"Hot"]
dataSource:dataSource];
[self.view addSubview:menuView];
QiPageContentView *contentView = [[QiPageContentView alloc] initWithFrame:CGRectMake(0, menuView.bottom+10, self.view.width, self.view.height - menuView.bottom - 98)
childViewController:@[vc0, vc1, vc2, vc3, vc4]];
[self.view addSubview:contentView];
menuView.pageItemClicked = ^(NSInteger clickedIndex, NSInteger beforeIndex, QiPageMenuView *menu) {
NSLog(@"Clicked: before %ld now %ld", beforeIndex, clickedIndex);
[contentView setPageContentShouldScrollToIndex:clickedIndex beforIndex:beforeIndex];
};
contentView.pageContentViewDidScroll = ^(NSInteger currentIndex, NSInteger beforeIndex, QiPageContentView *view) {
menuView.pageScrolledIndex = currentIndex;
NSLog(@"Scrolled: before %ld now %ld", beforeIndex, currentIndex);
};The decoupled design allows QiPageMenuView and QiPageContentView to be reused independently in different projects.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
360 Zhihui Cloud Developer
360 Zhihui Cloud is an enterprise open service platform that aims to "aggregate data value and empower an intelligent future," leveraging 360's extensive product and technology resources to deliver platform services to customers.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
