JXPhotoBrowser Documentation

repository·master·Indexed 23 days ago

https://github.com/jiongxing/photobrowser

A lightweight, customizable iOS image and video browser built on UIKit and UICollectionView. JXPhotoBrowser supports pinch-to-zoom, pull-to-dismiss, and auto-play, remaining agnostic of data models and image loading libraries. It requires iOS 12.0+ and Swift 5.4+. Key features include JXPhotoBrowserViewController for full-screen or embedded banner modes, support for custom cells via JXPhotoBrowserCellProtocol, and the ability to add UI overlays.

Tokens
7.7K
Snippets
17
Records
43
Agent score
79%

What's inside JXPhotoBrowser

  1. Understand the JXPhotoBrowser Architecture

    master

    JXPhotoBrowser is designed as a lightweight browser kernel that decouples data management from UI rendering. It uses a protocol-driven approach to allow the host application to provide data, media loading, and custom UI elements.

    Core Responsibilities of the Kernel:

    1. Pagination & Reuse: Uses UICollectionView to manage paging and cell reuse.
    2. Scaling & Centering: Uses UIScrollView to manage single-page zooming and centering.
    3. Transitions: Uses independent animators to manage presentation and dismissal.
    4. Abstraction: Uses protocols to delegate Cell implementation, Overlays, thumbnail sources, and media content to the business layer.

    Key Architectural Layers:

    • Host/Business Layer: Provides data and implements the JXPhotoBrowserDelegate.
    • JXPhotoBrowserViewController: The central controller managing the UICollectionView, transition animators, and the Overlay plugin system.
    • Cell Layer: Implements JXPhotoBrowserCellProtocol (e.g., JXZoomImageCell, JXImageCell, or custom video cells).
  2. How the page display lifecycle works

    master

    Understanding the sequence of events from a user interaction to media rendering:

    1. Trigger: Host page detects a thumbnail click.
    2. Initialization: A JXPhotoBrowserViewController is created.
    3. Configuration: Set delegate, initialIndex, scrollDirection, and transitionType.
    4. Presentation: Call present(from:).
    5. Animation: The transition animator takes over.
    6. Layout: The browser sets up the collectionView.
    7. Navigation: The controller scrolls to the initial virtual index.
    8. Cell Provision: The delegate provides the required Cell.
    9. Media Loading: Business logic populates media content within the willDisplay lifecycle method.

    Key Note: The browser does not distinguish between media types (local, network, video, etc.). Media loading should be managed by the host in willDisplay and didEndDisplaying.

  3. Quick Start: Implement JXPhotoBrowserViewController

    master

    To use the photo browser, instantiate JXPhotoBrowserViewController, configure its properties, and implement the JXPhotoBrowserDelegate to provide data and cells. The library does not include a built-in image loading library; you must set the image in the cell using your preferred library (e.g., Kingfisher, SDWebImage).

    import JXPhotoBrowser
    
    let browser = JXPhotoBrowserViewController()
    browser.delegate = self
    browser.initialIndex = indexPath.item
    browser.transitionType = .zoom
    browser.isLoopingEnabled = true
    browser.addOverlay(JXPageIndicatorOverlay())
    browser.present(from: self)
    extension ViewController: JXPhotoBrowserDelegate {
        func numberOfItems(in browser: JXPhotoBrowserViewController) -> Int {
            items.count
        }
    
        func photoBrowser(
            _ browser: JXPhotoBrowserViewController,
            cellForItemAt index: Int,
            at indexPath: IndexPath
        ) -> JXPhotoBrowserAnyCell {
            browser.dequeueReusableCell(
                withReuseIdentifier: JXZoomImageCell.reuseIdentifier,
                for: indexPath
            )
        }
    
        func photoBrowser(
            _ browser: JXPhotoBrowserViewController,
            willDisplay cell: JXPhotoBrowserAnyCell,
            at index: Int
        ) {
            guard let cell = cell as? JXZoomImageCell else { return }
            // Use your preferred image loading library to set cell.imageView.image
        }
    
        func photoBrowser(
            _ browser: JXPhotoBrowserViewController,
            thumbnailViewAt index: Int
        ) -> UIView? {
            let indexPath = IndexPath(item: index, section: 0)
            return collectionView.cellForItem(at: indexPath)?.contentView
        }
    }
  4. Use JXPhotoBrowser as an Embedded Banner

    master

    Because the kernel does not assume a full-screen context, you can reuse JXPhotoBrowserViewController to create an embedded PhotoBannerView.

    Configuration Steps:

    1. Set transitionType = .none to disable full-screen transitions.
    2. Register JXImageCell for lightweight rendering.
    3. Enable looping and auto-rotation.
    4. Load a page indicator using the Overlay mechanism.
    5. Embed the browser's view as a subview in your existing layout.
  5. Create custom cells using JXPhotoBrowserCellProtocol

    master

    To reuse all zoom and gesture behaviors, inherit from JXZoomImageCell. If you implement the protocol directly on a UICollectionViewCell, you must ensure the browser property is a weak reference to avoid retain cycles.

    All cells use the full page size of the browser. You must register your custom cell and dequeue it within the delegate methods.

    // 1. Define the custom cell
    final class MediaCell: UICollectionViewCell, JXPhotoBrowserCellProtocol {
        static let reuseIdentifier = "MediaCell"
        weak var browser: JXPhotoBrowserViewController?
        let imageView = UIImageView()
        var transitionImageView: UIImageView? { imageView }
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            contentView.addSubview(imageView)
        }
    
        required init?(coder: NSCoder) {
            super.init(coder: coder)
            contentView.addSubview(imageView)
        }
    }
    
    // 2. Register the cell with the browser
    browser.register(MediaCell.self, forReuseIdentifier: MediaCell.reuseIdentifier)
  6. Save images to Photo Library

    master

    JXPhotoBrowser does not include built-in saving functionality. You must implement this yourself using standard iOS APIs:

    • iOS 14+: Use the .addOnly permission.
    • iOS 12/13: Use the legacy authorization interface.
    • iPad Support: When presenting an ActionSheet, you must set the popoverPresentationController.sourceView and sourceRect to avoid crashes.
  7. Best practices for reusing JXPhotoBrowser in business logic

    master

    To maintain a stable kernel boundary, follow these recommendations when integrating JXPhotoBrowser as a component:

    1. Image Browsing: Prioritize reusing JXZoomImageCell.
    2. Simple Banners: Use JXImageCell for pure display/carousel needs.
    3. Custom Media (Video, etc.): Extend the browser by implementing custom Cells.
    4. UI Overlays: Load page numbers, buttons, and text via the Overlay mechanism.
    5. Resource Management: Manage image loading, caching, and task cancellation within the host's willDisplay and didEndDisplaying methods.
    6. SwiftUI Integration: Use the PhotoBrowserPresenter or a custom bridge layer to encapsulate the entry point.
  8. Add and manage Overlays

    master

    Overlays (like page indicators) can be added to the browser using addOverlay(_:).

    • JXPageIndicatorOverlay is a built-in option.
    • Adding the same instance multiple times will not result in duplicates.
    • If an overlay is added to a new browser instance, it is automatically removed from its previous host.
    let indicator = JXPageIndicatorOverlay()
    indicator.position = .bottom(padding: 20)
    indicator.hidesForSinglePage = true
    browser.addOverlay(indicator)
  9. Use JXPhotoBrowser in Embedded Banner Mode

    master

    To use the browser as an embedded banner (e.g., inside a scroll view), you must use standard View Controller containment and disable the pull-to-dismiss gesture.

    Configuration for Banner Mode:

    • Set transitionType to .none.
    • Set isDismissGestureEnabled to false.
    • Enable isAutoPlayEnabled and set autoPlayInterval if desired.

    Lifecycle Management:

    • To Add: Use addChild(browser), add browser.view to your container, set constraints, and call browser.didMove(toParent: self).
    • To Remove: Call willMove(toParent: nil), remove the view, and then call removeFromParent().
    let browser = JXPhotoBrowserViewController()
    browser.delegate = self
    browser.transitionType = .none
    browser.isDismissGestureEnabled = false
    browser.autoPlayInterval = 3
    browser.isAutoPlayEnabled = true
    
    addChild(browser)
    containerView.addSubview(browser.view)
    // Add constraints to browser.view
    browser.didMove(toParent: self)