Đặt vấn đề
Trong quá trình xây dựng ứng dụng, chắc hẳn chúng ta sẽ gặp UI/UX như thế này
Để giải quyết bài toán này, đa phần mọi người sẽ chọn cách đưa một CollectionView vào trong một CollectionView. Cách tiếp cận này rất tốt, giải quyết được nhiều trường hợp. Tuy nhiên nếu các bạn xử lý không khéo về Layout cũng như quản lý Reuse của CollectionView, sẽ gây ra nhiều Bugs khó chịu về UI. Trong bài viết hôm nay, mình sẽ giới thiệu một cách tiếp cận khác để giải quyết vấn đề trên, đó là sử dụng UIPageViewController.
Giới thiệu UIPageViewController
UIPageViewController là một Component thuộc UIKit. UIPageViewController có thể hiểu là một mảng các ViewController và tại một thời điểm sẽ chỉ hiển thị 1 Controller (chính xác là view của ViewController đó, nhưng trong bài viết này mình xin phép gọi luôn là Controlller cho ngắn gọn và các Controller này có thể chuyển qua lại (sang trái, phải) theo các hiệu ứng transition khác nhau trong lúc khởi tạo.
UIPageViewController có thể sử dụng trong nhiều trường hợp. Tuy nhiên theo mình thấy, khi tìm kiếm về cách sử dụng UIPageViewController lại không ra nhiều tài liệu. Swift cũng không hỗ trợ nhiều cho components này. Nếu bạn implement UIPageViewController mặc định của Apple sẽ thấy rõ điều đó. UIPageViewControllerDelegate cũng chỉ có 2 hàm được gọi khi view của một controller chuẩn bị transition sang view của controller tiếp theo trong Pages.
Để tiện sử dụng cũng như mở rộng tiện ích cho UIPageViewController, chúng ta sẽ phải thêm một số hàm Custom. Phần tiếp theo của bài viết mình sẽ đi vào từng bước cụ thể.
Custom UIPageViewController
Đầu tiên mình muốn nhận được sự kiện khi nào Controller hiển thị trên Page thay đổi kèm Index tương ứng. Ở trên mình đã nói đến 2 hàm trong Delegate mặc định nhưng 2 hàm này không phải lúc nào cũng được gọi. Do đó, mình sẽ tạo một Delegate để bắt được sự kiện này.
1 2 3 4 | <span class="token keyword">protocol</span> <span class="token builtin">CustomPageViewControllerDelegate</span><span class="token punctuation">:</span> <span class="token builtin">AnyObject</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">pageViewController</span><span class="token punctuation">(</span>pageViewController<span class="token punctuation">:</span> <span class="token builtin">CustomPageViewController</span><span class="token punctuation">,</span> didUpdatePageIndex index<span class="token punctuation">:</span> <span class="token builtin">Int</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Tạo ra class CustomPageViewController kế thừa UIPageViewController
1 2 3 4 5 6 | <span class="token keyword">class</span> <span class="token class-name">CustomPageViewController</span><span class="token punctuation">:</span> <span class="token builtin">UIPageViewController</span> <span class="token punctuation">{</span> <span class="token keyword">override</span> <span class="token keyword">func</span> <span class="token function">viewDidLoad</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">viewDidLoad</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Mình sẽ khai báo thuộc tính controllers chứa một mảng Controller chính là những Controller được hiển thị trên UIPageViewController và một delegate thuộc kiểu CustomPageViewControllerDelegate mình đã định nghĩa ở trên. Nhớ để weak để tránh Retain Cycle.
1 2 3 4 5 6 7 8 9 | <span class="token keyword">class</span> <span class="token class-name">CustomPageViewController</span><span class="token punctuation">:</span> <span class="token builtin">UIPageViewController</span> <span class="token punctuation">{</span> <span class="token keyword">weak</span> <span class="token keyword">var</span> customDelegate<span class="token punctuation">:</span> <span class="token builtin">CustomPageViewControllerDelegate</span><span class="token operator">?</span> <span class="token keyword">var</span> controllers <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token builtin">UIViewController</span><span class="token punctuation">]</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">override</span> <span class="token keyword">func</span> <span class="token function">viewDidLoad</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">viewDidLoad</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Cơ chế của UIPageViewController tại một thời điểm set một Controller, và tạo sẵn Controller trước (Sẽ hiển thị khi vuốt sang trái) và Controller sau (Sẽ hiển thị khi vuốt sang phải) của Controller đó. Do đó, khi cho CustomPageViewController conform UIPageViewControllerDataSource, ta sẽ phải khai báo 2 hàm
1 2 3 4 5 6 7 8 | <span class="token keyword">extension</span> <span class="token builtin">CustomPageViewController</span><span class="token punctuation">:</span> <span class="token builtin">UIPageViewControllerDataSource</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">pageViewController</span><span class="token punctuation">(</span><span class="token number">_</span><span class="token punctuation">:</span> <span class="token builtin">UIPageViewController</span><span class="token punctuation">,</span> viewControllerBefore viewController<span class="token punctuation">:</span> <span class="token builtin">UIViewController</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">UIViewController</span><span class="token operator">?</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">pageViewController</span><span class="token punctuation">(</span><span class="token number">_</span><span class="token punctuation">:</span> <span class="token builtin">UIPageViewController</span><span class="token punctuation">,</span> viewControllerAfter viewController<span class="token punctuation">:</span> <span class="token builtin">UIViewController</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">UIViewController</span><span class="token operator">?</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Bổ sung đoạn code sau vào 2 hàm đó. Logic của đoạn code này tương đối đơn giản, ta sẽ kiểm tra xem Controller được hiển thị đang ở index bao nhiêu. Lấy indext trừ đi 1 để ra index của Controller trước đó (previousController) trong mảng controlles, nếu index hợp lệ thì trả ra element tương ứng, còn không thì trả về nil. Tương tự với logic tìm afterController.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <span class="token keyword">extension</span> <span class="token builtin">CustomPageViewController</span><span class="token punctuation">:</span> <span class="token builtin">UIPageViewControllerDataSource</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">pageViewController</span><span class="token punctuation">(</span><span class="token number">_</span><span class="token punctuation">:</span> <span class="token builtin">UIPageViewController</span><span class="token punctuation">,</span> viewControllerBefore viewController<span class="token punctuation">:</span> <span class="token builtin">UIViewController</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">UIViewController</span><span class="token operator">?</span> <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> controllerIndex <span class="token operator">=</span> controllers<span class="token punctuation">.</span><span class="token function">firstIndex</span><span class="token punctuation">(</span>of<span class="token punctuation">:</span> viewController<span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token constant">nil</span> <span class="token punctuation">}</span> <span class="token keyword">let</span> previousIndex <span class="token operator">=</span> controllerIndex <span class="token operator">-</span> <span class="token number">1</span> <span class="token keyword">if</span> previousIndex <span class="token operator">>=</span> <span class="token number">0</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> controllers<span class="token punctuation">[</span>previousIndex<span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token constant">nil</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">pageViewController</span><span class="token punctuation">(</span><span class="token number">_</span><span class="token punctuation">:</span> <span class="token builtin">UIPageViewController</span><span class="token punctuation">,</span> viewControllerAfter viewController<span class="token punctuation">:</span> <span class="token builtin">UIViewController</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">UIViewController</span><span class="token operator">?</span> <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> controllerIndex <span class="token operator">=</span> controllers<span class="token punctuation">.</span><span class="token function">firstIndex</span><span class="token punctuation">(</span>of<span class="token punctuation">:</span> viewController<span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token constant">nil</span> <span class="token punctuation">}</span> <span class="token keyword">let</span> nextIndex <span class="token operator">=</span> controllerIndex <span class="token operator">+</span> <span class="token number">1</span> <span class="token keyword">if</span> nextIndex <span class="token operator"><</span> controllers<span class="token punctuation">.</span><span class="token builtin">count</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> controllers<span class="token punctuation">[</span>nextIndex<span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token constant">nil</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Khai báo hàm notifyfNewIndex() để trigger cho delegate khi Controller mới được hiển thị, index thay đổi.
1 2 3 4 5 6 7 | <span class="token keyword">private</span> <span class="token keyword">func</span> <span class="token function">notifyfNewIndex</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token keyword">let</span> firstViewController <span class="token operator">=</span> viewControllers<span class="token operator">?</span><span class="token punctuation">.</span><span class="token builtin">first</span><span class="token punctuation">,</span> <span class="token keyword">let</span> index <span class="token operator">=</span> controllers<span class="token punctuation">.</span><span class="token function">firstIndex</span><span class="token punctuation">(</span>of<span class="token punctuation">:</span> firstViewController<span class="token punctuation">)</span> <span class="token punctuation">{</span> customDelegate<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">pageViewController</span><span class="token punctuation">(</span>pageViewController<span class="token punctuation">:</span> <span class="token keyword">self</span><span class="token punctuation">,</span> didUpdatePageIndex<span class="token punctuation">:</span> index<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Khai báo hàm scrollToViewController. Mục đich cảu hàm này được gọi trong trường hợp chúng ta muốn chỉ định hiển thị một ViewController nào đó. Khi đó ta phải gọi hàm setViewControlles của UIPageViewController. Hàm này chúng ta sẽ không sử dụng trực tiếp, nên sẽ khai báo Private.
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">private</span> <span class="token keyword">func</span> <span class="token function">scrollToViewController</span><span class="token punctuation">(</span>viewController<span class="token punctuation">:</span> <span class="token builtin">UIViewController</span><span class="token punctuation">,</span> direction<span class="token punctuation">:</span> <span class="token builtin">UIPageViewController</span><span class="token punctuation">.</span><span class="token builtin">NavigationDirection</span> <span class="token operator">=</span> <span class="token punctuation">.</span>forward<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">setViewControllers</span><span class="token punctuation">(</span><span class="token punctuation">[</span>viewController<span class="token punctuation">]</span><span class="token punctuation">,</span> direction<span class="token punctuation">:</span> direction<span class="token punctuation">,</span> animated<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> completion<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token number">_</span> <span class="token keyword">in</span> <span class="token keyword">self</span><span class="token punctuation">.</span><span class="token function">notifyfNewIndex</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Các Controller của chúng ta nằm trong một mảng với các index tương ứng, trong quá trình sử dụng chúng ta muốn truy cập tới viewController cụ thể thông qua index. Do đó ta sẽ khai báo hàm func scrollToViewController(index newIndex: Int)
viewControllers?.first trả về Controller đang được hiển thị. Tìm index tương ứng của firstViewController đó trong mảng controllers. So sánh newIndex so với index vừa tìm được để quyết định hướng scroll là forward hay reverse. Sau đó gọi hàm scrollToViewController vừa khai báo ở đoạn trên để set lại controller được hiển thị
1 2 3 4 5 6 7 8 9 | <span class="token keyword">func</span> <span class="token function">scrollToViewController</span><span class="token punctuation">(</span>index newIndex<span class="token punctuation">:</span> <span class="token builtin">Int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token keyword">let</span> firstViewController <span class="token operator">=</span> viewControllers<span class="token operator">?</span><span class="token punctuation">.</span><span class="token builtin">first</span><span class="token punctuation">,</span> <span class="token keyword">let</span> currentIndex <span class="token operator">=</span> controllers<span class="token punctuation">.</span><span class="token function">firstIndex</span><span class="token punctuation">(</span>of<span class="token punctuation">:</span> firstViewController<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> direction<span class="token punctuation">:</span> <span class="token builtin">UIPageViewController</span><span class="token punctuation">.</span><span class="token builtin">NavigationDirection</span> <span class="token operator">=</span> newIndex <span class="token operator">>=</span> currentIndex <span class="token operator">?</span> <span class="token punctuation">.</span>forward <span class="token punctuation">:</span> <span class="token punctuation">.</span><span class="token builtin">reverse</span> <span class="token keyword">let</span> nextViewController <span class="token operator">=</span> controllers<span class="token punctuation">[</span>newIndex<span class="token punctuation">]</span> <span class="token function">scrollToViewController</span><span class="token punctuation">(</span>viewController<span class="token punctuation">:</span> nextViewController<span class="token punctuation">,</span> direction<span class="token punctuation">:</span> direction<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Conform UIPageViewControllerDelegate. Và khai báo đoạn mã sau. Hàm này sẽ được gọi khi viewController được gọi khi ta vuốt tuần tự giữa các Controller. Nhưng sẽ không đi vào khi ta gọi hàm setViewController của UIPageViewController. Do đó ta cần xử lý cả 2 trường hợp, gọi hàm notifyfNewIndex() để delegate bắt sự kiện.
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">extension</span> <span class="token builtin">CustomPageViewController</span><span class="token punctuation">:</span> <span class="token builtin">UIPageViewControllerDelegate</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">pageViewController</span><span class="token punctuation">(</span><span class="token number">_</span><span class="token punctuation">:</span> <span class="token builtin">UIPageViewController</span><span class="token punctuation">,</span> didFinishAnimating <span class="token number">_</span><span class="token punctuation">:</span> <span class="token builtin">Bool</span><span class="token punctuation">,</span> previousViewControllers <span class="token number">_</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token builtin">UIViewController</span><span class="token punctuation">]</span><span class="token punctuation">,</span> transitionCompleted <span class="token number">_</span><span class="token punctuation">:</span> <span class="token builtin">Bool</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">notifyfNewIndex</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Bổ sung một số hàm trong viewDidLoad. Chúng ta cần set controller mặc định hiển thị cho PageViewController, mình sẽ chọn là controller đầu tiên trong mảng.
1 2 3 4 5 6 7 8 9 | <span class="token keyword">override</span> <span class="token keyword">func</span> <span class="token function">viewDidLoad</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">viewDidLoad</span><span class="token punctuation">(</span><span class="token punctuation">)</span> dataSource <span class="token operator">=</span> <span class="token keyword">self</span> delegate <span class="token operator">=</span> <span class="token keyword">self</span> <span class="token keyword">if</span> <span class="token keyword">let</span> initialViewController <span class="token operator">=</span> controllers<span class="token punctuation">.</span><span class="token builtin">first</span> <span class="token punctuation">{</span> <span class="token function">scrollToViewController</span><span class="token punctuation">(</span>viewController<span class="token punctuation">:</span> initialViewController<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Sử dụng CustomPageViewController
Kịch bản ở đây sẽ giống như ví dụ ở đầu bài viết. Do đó mình sẽ tạo 1 Controller đơn giản gồm 1 CollectionView và random Color hiển thị
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <span class="token keyword">class</span> <span class="token class-name">ColorViewController</span><span class="token punctuation">:</span> <span class="token builtin">UIViewController</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">var</span> collectionView<span class="token punctuation">:</span> <span class="token builtin">UICollectionView</span><span class="token operator">!</span> <span class="token keyword">private</span> <span class="token keyword">let</span> randomColors<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token builtin">UIColor</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">.</span>red<span class="token punctuation">,</span> <span class="token punctuation">.</span>yellow<span class="token punctuation">,</span> <span class="token punctuation">.</span>blue<span class="token punctuation">,</span> <span class="token punctuation">.</span>orange<span class="token punctuation">,</span> <span class="token punctuation">.</span>purple<span class="token punctuation">,</span> <span class="token punctuation">.</span>systemPink<span class="token punctuation">,</span> <span class="token punctuation">.</span>brown<span class="token punctuation">]</span> <span class="token keyword">override</span> <span class="token keyword">func</span> <span class="token function">viewDidLoad</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">configView</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">private</span> <span class="token keyword">func</span> <span class="token function">configView</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> layout <span class="token operator">=</span> <span class="token function">UICollectionViewFlowLayout</span><span class="token punctuation">(</span><span class="token punctuation">)</span> layout<span class="token punctuation">.</span>itemSize <span class="token operator">=</span> <span class="token function">CGSize</span><span class="token punctuation">(</span>width<span class="token punctuation">:</span> <span class="token number">70</span><span class="token punctuation">,</span> height<span class="token punctuation">:</span> <span class="token number">70</span><span class="token punctuation">)</span> layout<span class="token punctuation">.</span>minimumLineSpacing <span class="token operator">=</span> <span class="token number">15</span> layout<span class="token punctuation">.</span>minimumInteritemSpacing <span class="token operator">=</span> <span class="token number">15</span> layout<span class="token punctuation">.</span>sectionInset <span class="token operator">=</span> <span class="token function">UIEdgeInsets</span><span class="token punctuation">(</span>top<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token keyword">left</span><span class="token punctuation">:</span> <span class="token number">15</span><span class="token punctuation">,</span> bottom<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token keyword">right</span><span class="token punctuation">:</span> <span class="token number">15</span><span class="token punctuation">)</span> collectionView <span class="token operator">=</span> <span class="token function">UICollectionView</span><span class="token punctuation">(</span>frame<span class="token punctuation">:</span> <span class="token punctuation">.</span>zero<span class="token punctuation">,</span> collectionViewLayout<span class="token punctuation">:</span> layout<span class="token punctuation">)</span> view<span class="token punctuation">.</span><span class="token function">addSubview</span><span class="token punctuation">(</span>collectionView<span class="token punctuation">)</span> collectionView<span class="token punctuation">.</span>snp<span class="token punctuation">.</span>makeConstraints <span class="token punctuation">{</span> $<span class="token number">0</span><span class="token punctuation">.</span>edges<span class="token punctuation">.</span><span class="token function">equalToSuperview</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> collectionView<span class="token punctuation">.</span>backgroundColor <span class="token operator">=</span> <span class="token punctuation">.</span>clear collectionView<span class="token punctuation">.</span>dataSource <span class="token operator">=</span> <span class="token keyword">self</span> collectionView<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token builtin">UICollectionViewCell</span><span class="token punctuation">.</span><span class="token keyword">self</span><span class="token punctuation">,</span> forCellWithReuseIdentifier<span class="token punctuation">:</span> <span class="token string">"Cell"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">ColorViewController</span><span class="token punctuation">:</span> <span class="token builtin">UICollectionViewDataSource</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">collectionView</span><span class="token punctuation">(</span><span class="token number">_</span> collectionView<span class="token punctuation">:</span> <span class="token builtin">UICollectionView</span><span class="token punctuation">,</span> numberOfItemsInSection section<span class="token punctuation">:</span> <span class="token builtin">Int</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">Int</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token number">20</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">collectionView</span><span class="token punctuation">(</span><span class="token number">_</span> collectionView<span class="token punctuation">:</span> <span class="token builtin">UICollectionView</span><span class="token punctuation">,</span> cellForItemAt indexPath<span class="token punctuation">:</span> <span class="token builtin">IndexPath</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">UICollectionViewCell</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> cell <span class="token operator">=</span> collectionView<span class="token punctuation">.</span><span class="token function">dequeueReusableCell</span><span class="token punctuation">(</span>withReuseIdentifier<span class="token punctuation">:</span> <span class="token string">"Cell"</span><span class="token punctuation">,</span> <span class="token keyword">for</span><span class="token punctuation">:</span> indexPath<span class="token punctuation">)</span> cell<span class="token punctuation">.</span>contentView<span class="token punctuation">.</span>backgroundColor <span class="token operator">=</span> randomColors<span class="token punctuation">.</span><span class="token function">randomElement</span><span class="token punctuation">(</span><span class="token punctuation">)</span> cell<span class="token punctuation">.</span>contentView<span class="token punctuation">.</span>cornerRadius <span class="token operator">=</span> <span class="token number">13</span> <span class="token keyword">return</span> cell <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Bây giời mình sẽ tạo một CustomPageViewController gồm một mảng ColorViewController vừa tạo ở trên trong ViewController. Mình sẽ khởi tạo từ trong Code pageViewController vì khi kéo trực tiếp từ Storyboard, Swift sẽ mặc định transitionStyle là dạng .pageCurl
1 2 3 4 5 6 7 8 9 10 | pageViewController <span class="token operator">=</span> <span class="token function">CustomPageViewController</span><span class="token punctuation">(</span>transitionStyle<span class="token punctuation">:</span> <span class="token punctuation">.</span>scroll<span class="token punctuation">,</span> navigationOrientation<span class="token punctuation">:</span> <span class="token punctuation">.</span>horizontal<span class="token punctuation">,</span> options<span class="token punctuation">:</span> <span class="token constant">nil</span><span class="token punctuation">)</span> pageViewController<span class="token punctuation">.</span>customDelegate <span class="token operator">=</span> <span class="token keyword">self</span> pageViewController<span class="token punctuation">.</span>controllers <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token function">ColorViewController</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">ColorViewController</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">ColorViewController</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">]</span> <span class="token function">addChild</span><span class="token punctuation">(</span>pageViewController<span class="token punctuation">)</span> contentView<span class="token punctuation">.</span><span class="token function">addSubview</span><span class="token punctuation">(</span>pageViewController<span class="token punctuation">.</span>view<span class="token punctuation">)</span> pageViewController<span class="token punctuation">.</span>view<span class="token punctuation">.</span>snp<span class="token punctuation">.</span>makeConstraints <span class="token punctuation">{</span> $<span class="token number">0</span><span class="token punctuation">.</span>top<span class="token punctuation">.</span>leading<span class="token punctuation">.</span><span class="token function">equalToSuperview</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">offset</span><span class="token punctuation">(</span><span class="token number">15</span><span class="token punctuation">)</span> $<span class="token number">0</span><span class="token punctuation">.</span>trailing<span class="token punctuation">.</span>bottom<span class="token punctuation">.</span><span class="token function">equalToSuperview</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">inset</span><span class="token punctuation">(</span><span class="token number">15</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Tiếp đến mình sẽ cho ViewController conform CustomPageViewControllerDelegate
1 2 3 4 5 6 | <span class="token keyword">extension</span> <span class="token builtin">ViewController</span><span class="token punctuation">:</span> <span class="token builtin">CustomPageViewControllerDelegate</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">pageViewController</span><span class="token punctuation">(</span>pageViewController<span class="token punctuation">:</span> <span class="token builtin">CustomPageViewController</span><span class="token punctuation">,</span> didUpdatePageIndex index<span class="token punctuation">:</span> <span class="token builtin">Int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> pageLabel<span class="token punctuation">.</span>text <span class="token operator">=</span> <span class="token string">"Trang <span class="token interpolation"><span class="token delimiter variable">(</span>index <span class="token operator">+</span> <span class="token number">1</span><span class="token delimiter variable">)</span></span>"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Build và Run Project mình sẽ thu được kết quả như ví dụ ở đầu bài viết
Source Code
https://github.com/buixuanhuy5798/DemoCustomPageViewController