From 1b3c0d45c7b6e618c531f087c518a712c0df706f Mon Sep 17 00:00:00 2001
From: Mustafa Merza <mustafa.merza95@gmail.com>
Date: Wed, 14 Aug 2024 10:23:37 +0300
Subject: [PATCH] - Added displaying scanned items as a grid in documents
 preview screen.

---
 MiniScanner.xcodeproj/project.pbxproj         |   4 +
 MiniScanner/Extensions/UIImage+Images.swift   |   1 +
 .../DocumentPreview.storyboard                |  52 +++++--
 .../DocumentPreviewViewController.swift       | 128 +++++++++++++++++-
 .../DocumentPreview/PreviewCellLayout.swift   |  32 +++++
 .../layout-images-ic.imageset/Contents.json   |  12 ++
 .../layout-images-ic.svg                      |   3 +
 7 files changed, 215 insertions(+), 17 deletions(-)
 create mode 100644 MiniScanner/Modules/DocumentPreview/PreviewCellLayout.swift
 create mode 100644 MiniScanner/Supporting Files/Icons.xcassets/layout-images-ic.imageset/Contents.json
 create mode 100644 MiniScanner/Supporting Files/Icons.xcassets/layout-images-ic.imageset/layout-images-ic.svg

diff --git a/MiniScanner.xcodeproj/project.pbxproj b/MiniScanner.xcodeproj/project.pbxproj
index 6645f8d..9f08366 100644
--- a/MiniScanner.xcodeproj/project.pbxproj
+++ b/MiniScanner.xcodeproj/project.pbxproj
@@ -266,6 +266,7 @@
 		67A2AF8F2C6B9B5400039F30 /* View+CustomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A2AF8E2C6B9B5400039F30 /* View+CustomSheet.swift */; };
 		67A2AF922C6B9B8700039F30 /* View+iOS15Sheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A2AF912C6B9B8700039F30 /* View+iOS15Sheet.swift */; };
 		67A2AF962C6BADAA00039F30 /* UserSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A2AF952C6BADAA00039F30 /* UserSettings.swift */; };
+		67A2AF982C6BC4AE00039F30 /* PreviewCellLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A2AF972C6BC4AE00039F30 /* PreviewCellLayout.swift */; };
 		67D714B52C5161A30065E6F4 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 67D714B42C5161A30065E6F4 /* Images.xcassets */; };
 		67E6A1962C64DEB400A77F29 /* ScannedItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E6A1952C64DEB400A77F29 /* ScannedItemType.swift */; };
 		67E6A1982C65151F00A77F29 /* ImageCompressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E6A1972C65151F00A77F29 /* ImageCompressView.swift */; };
@@ -580,6 +581,7 @@
 		67A2AF8E2C6B9B5400039F30 /* View+CustomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+CustomSheet.swift"; sourceTree = "<group>"; };
 		67A2AF912C6B9B8700039F30 /* View+iOS15Sheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+iOS15Sheet.swift"; sourceTree = "<group>"; };
 		67A2AF952C6BADAA00039F30 /* UserSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettings.swift; sourceTree = "<group>"; };
+		67A2AF972C6BC4AE00039F30 /* PreviewCellLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewCellLayout.swift; sourceTree = "<group>"; };
 		67D714B42C5161A30065E6F4 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
 		67E6A1952C64DEB400A77F29 /* ScannedItemType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScannedItemType.swift; sourceTree = "<group>"; };
 		67E6A1972C65151F00A77F29 /* ImageCompressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCompressView.swift; sourceTree = "<group>"; };
@@ -1640,6 +1642,7 @@
 				EC8A9B26254DE91B00F9AF99 /* DocumentPreviewViewController.swift */,
 				EC702521254DF13200BE1958 /* PencilKitViewController.swift */,
 				677E65F52C5F9E6E0039E2C5 /* ScannedItemPagerViewCell.swift */,
+				67A2AF972C6BC4AE00039F30 /* PreviewCellLayout.swift */,
 			);
 			path = DocumentPreview;
 			sourceTree = "<group>";
@@ -2025,6 +2028,7 @@
 				677E65C12C5A15F40039E2C5 /* ScanSessionEntity+CoreDataProperties.swift in Sources */,
 				677E65C22C5A15F40039E2C5 /* FolderEntity+CoreDataClass.swift in Sources */,
 				677E65C32C5A15F40039E2C5 /* FolderEntity+CoreDataProperties.swift in Sources */,
+				67A2AF982C6BC4AE00039F30 /* PreviewCellLayout.swift in Sources */,
 				672C464A2C47BD8800497EF0 /* String+StringKeys.swift in Sources */,
 				677E65EB2C5A36AE0039E2C5 /* UpdateFolderUseCase.swift in Sources */,
 				53014F962C11A8E80071CE39 /* ScannerViewController.swift in Sources */,
diff --git a/MiniScanner/Extensions/UIImage+Images.swift b/MiniScanner/Extensions/UIImage+Images.swift
index d05180e..20b0fd7 100644
--- a/MiniScanner/Extensions/UIImage+Images.swift
+++ b/MiniScanner/Extensions/UIImage+Images.swift
@@ -27,6 +27,7 @@ extension UIImage {
     static let search = UIImage(resource: .searchIc)
     static let layoutGrid = UIImage(resource: .layoutGridIc)
     static let layoutList = UIImage(resource: .layoutListIc)
+    static let layoutImages = UIImage(resource: .layoutImagesIc)
     
     static let folderOpen = UIImage(resource: .folderOpenIc)
     static let gallery = UIImage(resource: .galleryIc)
diff --git a/MiniScanner/Modules/DocumentPreview/DocumentPreview.storyboard b/MiniScanner/Modules/DocumentPreview/DocumentPreview.storyboard
index c38382f..e94452b 100644
--- a/MiniScanner/Modules/DocumentPreview/DocumentPreview.storyboard
+++ b/MiniScanner/Modules/DocumentPreview/DocumentPreview.storyboard
@@ -27,8 +27,27 @@
                             <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="YIu-mV-Xt3" userLabel="Top Section">
                                 <rect key="frame" x="16" y="104" width="382" height="34"/>
                                 <subviews>
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="g4H-6z-yTr" userLabel="Layout Button">
+                                        <rect key="frame" x="31.5" y="4.5" width="25" height="25"/>
+                                        <constraints>
+                                            <constraint firstAttribute="width" secondItem="g4H-6z-yTr" secondAttribute="height" multiplier="1:1" id="w1c-RR-goA"/>
+                                        </constraints>
+                                        <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                        <state key="normal" image="layout-grid-ic"/>
+                                        <connections>
+                                            <action selector="layoutTapped:" destination="lAb-mo-IO6" eventType="touchUpInside" id="0Kj-dY-bkQ"/>
+                                        </connections>
+                                    </button>
+                                    <button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="R2E-Ce-wdD">
+                                        <rect key="frame" x="0.0" y="5.5" width="23.5" height="23.5"/>
+                                        <constraints>
+                                            <constraint firstAttribute="width" secondItem="R2E-Ce-wdD" secondAttribute="height" id="4FN-TU-J5g"/>
+                                        </constraints>
+                                        <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                        <state key="normal" image="ellipsis" catalog="system"/>
+                                    </button>
                                     <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="TqR-ZS-2Pv">
-                                        <rect key="frame" x="0.0" y="0.0" width="99.5" height="34"/>
+                                        <rect key="frame" x="220.5" y="0.0" width="99.5" height="34"/>
                                         <subviews>
                                             <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aMM-Hs-if2">
                                                 <rect key="frame" x="4" y="0.0" width="41.5" height="34"/>
@@ -56,15 +75,6 @@
                                             <constraint firstItem="aMM-Hs-if2" firstAttribute="leading" secondItem="TqR-ZS-2Pv" secondAttribute="leading" constant="4" id="xfJ-nL-Idd"/>
                                         </constraints>
                                     </view>
-                                    <button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="R2E-Ce-wdD">
-                                        <rect key="frame" x="0.0" y="-6.5" width="47.5" height="47.5"/>
-                                        <constraints>
-                                            <constraint firstAttribute="width" secondItem="R2E-Ce-wdD" secondAttribute="height" id="4FN-TU-J5g"/>
-                                        </constraints>
-                                        <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
-                                        <state key="normal" image="ellipsis" catalog="system"/>
-                                        <buttonConfiguration key="configuration" style="plain" image="ellipsis" catalog="system"/>
-                                    </button>
                                     <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yWg-kQ-A5X">
                                         <rect key="frame" x="336" y="0.0" width="46" height="34"/>
                                         <state key="normal" title="Button"/>
@@ -84,21 +94,34 @@
                                 <constraints>
                                     <constraint firstItem="Jpc-O0-8Id" firstAttribute="top" secondItem="YIu-mV-Xt3" secondAttribute="top" id="1mG-M0-gNq"/>
                                     <constraint firstItem="R2E-Ce-wdD" firstAttribute="leading" secondItem="YIu-mV-Xt3" secondAttribute="leading" id="9we-Pv-599"/>
+                                    <constraint firstItem="g4H-6z-yTr" firstAttribute="leading" secondItem="R2E-Ce-wdD" secondAttribute="trailing" constant="8" id="GtU-Pb-Hxs"/>
                                     <constraint firstAttribute="bottom" secondItem="TqR-ZS-2Pv" secondAttribute="bottom" id="ZUQ-Ag-eOW"/>
                                     <constraint firstItem="Jpc-O0-8Id" firstAttribute="centerX" secondItem="YIu-mV-Xt3" secondAttribute="centerX" id="att-Ib-oBi"/>
                                     <constraint firstItem="yWg-kQ-A5X" firstAttribute="centerY" secondItem="YIu-mV-Xt3" secondAttribute="centerY" id="cPj-8z-trd"/>
+                                    <constraint firstItem="g4H-6z-yTr" firstAttribute="centerY" secondItem="YIu-mV-Xt3" secondAttribute="centerY" id="cVc-Gu-Hzk"/>
                                     <constraint firstItem="R2E-Ce-wdD" firstAttribute="centerY" secondItem="YIu-mV-Xt3" secondAttribute="centerY" id="eQy-LQ-RwM"/>
                                     <constraint firstItem="TqR-ZS-2Pv" firstAttribute="top" secondItem="YIu-mV-Xt3" secondAttribute="top" id="g8Q-cU-fNM"/>
                                     <constraint firstItem="yWg-kQ-A5X" firstAttribute="trailing" secondItem="YIu-mV-Xt3" secondAttribute="trailing" id="h3Q-dw-S3L"/>
-                                    <constraint firstItem="TqR-ZS-2Pv" firstAttribute="leading" secondItem="YIu-mV-Xt3" secondAttribute="leading" id="ioq-C9-2iC"/>
                                     <constraint firstItem="yWg-kQ-A5X" firstAttribute="top" secondItem="YIu-mV-Xt3" secondAttribute="top" id="kyG-zt-er0"/>
                                     <constraint firstAttribute="bottom" secondItem="Jpc-O0-8Id" secondAttribute="bottom" id="kzj-Z3-1lw"/>
+                                    <constraint firstItem="yWg-kQ-A5X" firstAttribute="leading" secondItem="TqR-ZS-2Pv" secondAttribute="trailing" constant="16" id="szf-Uv-3UE"/>
                                 </constraints>
                             </view>
                             <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="HkK-Fc-Rgz" customClass="FSPagerView" customModule="MiniScanner" customModuleProvider="target">
                                 <rect key="frame" x="0.0" y="138" width="414" height="543"/>
                                 <color key="backgroundColor" systemColor="systemBackgroundColor"/>
                             </view>
+                            <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="jFy-zp-lQZ">
+                                <rect key="frame" x="0.0" y="154" width="414" height="511"/>
+                                <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <collectionViewFlowLayout key="collectionViewLayout" automaticEstimatedItemSize="YES" minimumLineSpacing="10" minimumInteritemSpacing="10" id="ofj-ZA-k7O">
+                                    <size key="itemSize" width="128" height="128"/>
+                                    <size key="headerReferenceSize" width="0.0" height="0.0"/>
+                                    <size key="footerReferenceSize" width="0.0" height="0.0"/>
+                                    <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
+                                </collectionViewFlowLayout>
+                                <cells/>
+                            </collectionView>
                             <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="YNN-5t-yKF" userLabel="Page Index View">
                                 <rect key="frame" x="123.5" y="681" width="167" height="40"/>
                                 <subviews>
@@ -330,13 +353,17 @@
                             <constraint firstItem="YIu-mV-Xt3" firstAttribute="top" secondItem="4bJ-Eb-Q72" secondAttribute="top" constant="12" id="M1U-Fa-vUM"/>
                             <constraint firstItem="oyQ-Gx-pj4" firstAttribute="leading" secondItem="4bJ-Eb-Q72" secondAttribute="leading" id="Mwk-iJ-rhi"/>
                             <constraint firstAttribute="trailing" secondItem="7cK-Hy-SJm" secondAttribute="trailing" id="OYo-8j-emp"/>
+                            <constraint firstItem="jFy-zp-lQZ" firstAttribute="leading" secondItem="4bJ-Eb-Q72" secondAttribute="leading" id="Ugj-5f-hn5"/>
                             <constraint firstItem="4bJ-Eb-Q72" firstAttribute="trailing" secondItem="HkK-Fc-Rgz" secondAttribute="trailing" id="d8m-yC-rcc"/>
                             <constraint firstItem="RBy-RI-NPW" firstAttribute="bottom" secondItem="oyQ-Gx-pj4" secondAttribute="top" id="f17-ly-k6b"/>
+                            <constraint firstItem="YNN-5t-yKF" firstAttribute="top" secondItem="jFy-zp-lQZ" secondAttribute="bottom" constant="16" id="g7o-EH-YzN"/>
                             <constraint firstItem="HkK-Fc-Rgz" firstAttribute="leading" secondItem="4bJ-Eb-Q72" secondAttribute="leading" id="lmq-dV-1Xb"/>
                             <constraint firstItem="HkK-Fc-Rgz" firstAttribute="top" secondItem="YIu-mV-Xt3" secondAttribute="bottom" id="r9f-lx-Udl"/>
                             <constraint firstItem="4bJ-Eb-Q72" firstAttribute="trailing" secondItem="RBy-RI-NPW" secondAttribute="trailing" id="sxU-bk-A97"/>
                             <constraint firstItem="4bJ-Eb-Q72" firstAttribute="trailing" secondItem="YIu-mV-Xt3" secondAttribute="trailing" constant="16" id="tDh-4W-Iol"/>
+                            <constraint firstItem="4bJ-Eb-Q72" firstAttribute="trailing" secondItem="jFy-zp-lQZ" secondAttribute="trailing" id="xPk-Qg-p5S"/>
                             <constraint firstItem="RBy-RI-NPW" firstAttribute="leading" secondItem="4bJ-Eb-Q72" secondAttribute="leading" id="ze4-pg-QcJ"/>
+                            <constraint firstItem="jFy-zp-lQZ" firstAttribute="top" secondItem="YIu-mV-Xt3" secondAttribute="bottom" constant="16" id="zma-rp-RtQ"/>
                         </constraints>
                     </view>
                     <navigationItem key="navigationItem" id="Nf0-zM-NqU">
@@ -345,9 +372,11 @@
                     <connections>
                         <outlet property="clearSelectionButton" destination="SHr-Ym-2En" id="mOk-0b-t3s"/>
                         <outlet property="closeButton" destination="yWg-kQ-A5X" id="2CJ-QC-mvV"/>
+                        <outlet property="collectionView" destination="jFy-zp-lQZ" id="IoW-dj-fcR"/>
                         <outlet property="currentPageLabel" destination="AEe-Tw-owM" id="iwi-YD-CJm"/>
                         <outlet property="directShareLabel" destination="GR8-HI-C1V" id="586-VP-rx9"/>
                         <outlet property="extraButton" destination="R2E-Ce-wdD" id="gCe-Dd-88D"/>
+                        <outlet property="layoutButton" destination="g4H-6z-yTr" id="eR3-5s-JPn"/>
                         <outlet property="nextPageButton" destination="cVY-eB-Oiw" id="anB-qz-F19"/>
                         <outlet property="pagerView" destination="HkK-Fc-Rgz" id="FYA-r7-Pxm"/>
                         <outlet property="previousPageButton" destination="zeo-0x-xpI" id="ef5-6G-8Bd"/>
@@ -390,6 +419,7 @@
         <image name="edit-ic" width="24" height="23"/>
         <image name="ellipsis" catalog="system" width="128" height="37"/>
         <image name="gmail" width="48" height="48"/>
+        <image name="layout-grid-ic" width="24" height="25"/>
         <image name="more-img" width="48" height="48"/>
         <image name="print-img" width="49" height="48"/>
         <image name="printer" catalog="system" width="128" height="111"/>
diff --git a/MiniScanner/Modules/DocumentPreview/DocumentPreviewViewController.swift b/MiniScanner/Modules/DocumentPreview/DocumentPreviewViewController.swift
index f64676d..7fbffae 100644
--- a/MiniScanner/Modules/DocumentPreview/DocumentPreviewViewController.swift
+++ b/MiniScanner/Modules/DocumentPreview/DocumentPreviewViewController.swift
@@ -15,6 +15,9 @@ final class DocumentPreviewViewController: UIViewController, ScanSessionSharable
     
     struct Constants {
         static let reuseIdentifier = String(describing: DocumentPreviewViewController.self)
+        
+        static let cellIdentifier = String(describing: ScannedItemPagerViewCell.self)
+        
         static let storyboardName = "DocumentPreview"
     }
     
@@ -26,9 +29,12 @@ final class DocumentPreviewViewController: UIViewController, ScanSessionSharable
     }
     
     @IBOutlet private weak var pagerView: FSPagerView!
+    @IBOutlet weak var collectionView: UICollectionView!
+    
     @IBOutlet weak var directShareLabel: UILabel!
     @IBOutlet weak var titleLabel: UILabel!
     @IBOutlet weak var closeButton: UIButton!
+    @IBOutlet weak var layoutButton: UIButton!
     
     @IBOutlet weak var currentPageLabel: UILabel!
     @IBOutlet weak var nextPageButton: UIButton!
@@ -70,6 +76,17 @@ final class DocumentPreviewViewController: UIViewController, ScanSessionSharable
     private var renameAlertController: UIAlertController?
     private var renameFileName: String?
     
+    private var layoutType: PreviewCellLayout = .pager {
+        didSet {
+            switch layoutType {
+            case .pager:
+                displayPager()
+            case .grid:
+                displayGrid()
+            }
+        }
+    }
+    
     lazy var activityIndicator: UIActivityIndicatorView = {
         let activityIndicator = UIActivityIndicatorView(style: .large)
         activityIndicator.color = .gray
@@ -129,18 +146,24 @@ final class DocumentPreviewViewController: UIViewController, ScanSessionSharable
         
         updateCurrentPageLabel()
         
+        layoutButton.tintColor = .mainText
+        
         shareType = userSettings.defaultFileType.shareType
         
+        layoutType = .pager
+        
         setupMenu()
         
         setupExtraButtonMenu()
         
         setupPagerView()
+        
+        setupCollectionView()
     }
     
     private func setupPagerView() {
         
-        pagerView.register(ScannedItemPagerViewCell.self, forCellWithReuseIdentifier: "cell")
+        pagerView.register(ScannedItemPagerViewCell.self, forCellWithReuseIdentifier: Constants.cellIdentifier)
         
         pagerView.transformer = FSPagerViewTransformer(type: .linear)
         
@@ -156,6 +179,24 @@ final class DocumentPreviewViewController: UIViewController, ScanSessionSharable
         pagerView.backgroundColor = .clear
     }
     
+    private func setupCollectionView() {
+        
+        let layout = UICollectionViewFlowLayout()
+        
+        layout.scrollDirection = .vertical
+        layout.minimumLineSpacing = 10
+        layout.minimumInteritemSpacing = 10
+        
+        collectionView.collectionViewLayout = layout
+        
+        collectionView.register(ScannedItemPagerViewCell.self, forCellWithReuseIdentifier: Constants.cellIdentifier)
+        
+        collectionView.delegate = self
+        collectionView.dataSource = self
+        
+        collectionView.backgroundColor = .mainBackground
+    }
+    
     private func setupExtraButtonMenu() {
         
         let move = UIAction(title: .move.localized) { [self] _ in
@@ -183,7 +224,7 @@ final class DocumentPreviewViewController: UIViewController, ScanSessionSharable
         
         updateSelectedImagesLabel()
         
-        pagerView.reloadData()
+        refreshContainer()
     }
     
     private func updateCurrentPageIndex(index: Int) {
@@ -206,7 +247,6 @@ final class DocumentPreviewViewController: UIViewController, ScanSessionSharable
     private func updateSelectedImagesLabel() {
         
         selectedPagesView.isHidden = selectedPagesCount == 0
-        extraButton.isHidden = selectedPagesCount != 0
         
         selectedPagesLabel.set(text: String(selectedPagesCount))
     }
@@ -214,6 +254,40 @@ final class DocumentPreviewViewController: UIViewController, ScanSessionSharable
     private func handleShareTypeChanged() {
         shareTypeLabel.set(localized: shareType.displayName)
     }
+    
+    private func changeLayout(to layout: PreviewCellLayout) {
+        self.layoutType = layout
+    }
+    
+    private func displayPager() {
+        pagerView.isHidden = false
+        collectionView.isHidden = true
+    }
+    
+    private func displayGrid() {
+        pagerView.isHidden = true
+        collectionView.isHidden = false
+    }
+    
+    private func refreshContainer() {
+        
+        switch layoutType {
+        case .pager:
+            pagerView.reloadData()
+        case .grid:
+            collectionView.reloadData()
+        }
+    }
+    
+    private func refreshContainer(at index: Int) {
+        
+        switch layoutType {
+        case .pager:
+            pagerView.reloadData(at: index)
+        case .grid:
+            collectionView.reloadItems(at: [IndexPath(row: index, section: 0)])
+        }
+    }
 }
 
 extension DocumentPreviewViewController: FSPagerViewDelegate, FSPagerViewDataSource {
@@ -224,7 +298,7 @@ extension DocumentPreviewViewController: FSPagerViewDelegate, FSPagerViewDataSou
     
     func pagerView(_ pagerView: FSPagerView, cellForItemAt index: Int) -> FSPagerViewCell {
         
-        let cell = pagerView.dequeueReusableCell(withReuseIdentifier: "cell", at: index) as! ScannedItemPagerViewCell
+        let cell = pagerView.dequeueReusableCell(withReuseIdentifier: Constants.cellIdentifier, at: index) as! ScannedItemPagerViewCell
         
         let scannedItem = session.scannedItems[index]
         
@@ -239,6 +313,41 @@ extension DocumentPreviewViewController: FSPagerViewDelegate, FSPagerViewDataSou
     }
 }
 
+// MARK: - Collection view data source and delegate
+
+extension DocumentPreviewViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
+    
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
+        
+        let itemHeight = 200.0
+        
+        let itemWidth = collectionView.frame.width / 2 - (5 + 32)
+        
+        return CGSize(width: itemWidth, height: itemHeight)
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
+        UIEdgeInsets(top: 0, left: 32, bottom: 32, right: 32)
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        session.itemsCount
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        
+        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Constants.cellIdentifier, for: indexPath) as? ScannedItemPagerViewCell else { return UICollectionViewCell() }
+        
+        let index = indexPath.row
+        
+        let scannedItem = session.scannedItems[index]
+        
+        cell.configure(with: scannedItem, at: index, delegate: self)
+        
+        return cell
+    }
+}
+
 extension DocumentPreviewViewController: ScannedItemPagerViewCellDelegate {
     
     func select(at index: Int) {
@@ -247,7 +356,7 @@ extension DocumentPreviewViewController: ScannedItemPagerViewCellDelegate {
         
         selectedPagesCount += session.scannedItems[index].isSelected ? 1 : -1
         
-        pagerView.reloadData(at: index)
+        refreshContainer(at: index)
         
         updateSelectedImagesLabel()
     }
@@ -427,6 +536,13 @@ extension DocumentPreviewViewController {
         clearSelection()
     }
     
+    @IBAction func layoutTapped(_ sender: Any) {
+        layoutType = layoutType.toggle()
+        layoutButton.setImage(layoutType.image, for: .normal)
+        
+        changeLayout(to: layoutType)
+    }
+    
     @IBAction func nextPageTapped(_ sender: UIButton) {
         
         if currentPageIndex < (session.itemsCount - 1) {
@@ -616,7 +732,7 @@ extension DocumentPreviewViewController {
             
             updateScanSessionUseCase.execute(with: session)
             
-            pagerView.reloadData()
+            refreshContainer()
             
             delegate?.sessionUpdated(session: session)
             
diff --git a/MiniScanner/Modules/DocumentPreview/PreviewCellLayout.swift b/MiniScanner/Modules/DocumentPreview/PreviewCellLayout.swift
new file mode 100644
index 0000000..92f3e4b
--- /dev/null
+++ b/MiniScanner/Modules/DocumentPreview/PreviewCellLayout.swift
@@ -0,0 +1,32 @@
+//
+//  PreviewCellLayout.swift
+//  MiniScanner
+//
+//  Created by Mustafa Merza on 8/13/24.
+//  Copyright © 2024 AppsNectar. All rights reserved.
+//
+
+import Foundation
+
+enum PreviewCellLayout {
+    case pager
+    case grid
+    
+    var image: UIImage {
+        switch self {
+        case .pager:
+                .layoutGrid
+        case .grid:
+                .layoutImages
+        }
+    }
+    
+    func toggle() -> Self {
+        switch self {
+        case .pager:
+                .grid
+        case .grid:
+                .pager
+        }
+    }
+}
diff --git a/MiniScanner/Supporting Files/Icons.xcassets/layout-images-ic.imageset/Contents.json b/MiniScanner/Supporting Files/Icons.xcassets/layout-images-ic.imageset/Contents.json
new file mode 100644
index 0000000..52445b5
--- /dev/null
+++ b/MiniScanner/Supporting Files/Icons.xcassets/layout-images-ic.imageset/Contents.json	
@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "filename" : "layout-images-ic.svg",
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/MiniScanner/Supporting Files/Icons.xcassets/layout-images-ic.imageset/layout-images-ic.svg b/MiniScanner/Supporting Files/Icons.xcassets/layout-images-ic.imageset/layout-images-ic.svg
new file mode 100644
index 0000000..b0d8a94
--- /dev/null
+++ b/MiniScanner/Supporting Files/Icons.xcassets/layout-images-ic.imageset/layout-images-ic.svg	
@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M18 22H4C3.46957 22 2.96086 21.7893 2.58579 21.4142C2.21071 21.0391 2 20.5304 2 20V6M22 13L20.704 11.704C20.252 11.2522 19.6391 10.9983 19 10.9983C18.3609 10.9983 17.748 11.2522 17.296 11.704L11 18M14 8C14 9.10457 13.1046 10 12 10C10.8954 10 10 9.10457 10 8C10 6.89543 10.8954 6 12 6C13.1046 6 14 6.89543 14 8ZM8 2H20C21.1046 2 22 2.89543 22 4V16C22 17.1046 21.1046 18 20 18H8C6.89543 18 6 17.1046 6 16V4C6 2.89543 6.89543 2 8 2Z" stroke="#1C1C1E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
-- 
GitLab