├── .DS_Store ├── README.md └── UIKitApp ├── .DS_Store ├── UIKitApp.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── nitinkumar.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── nitinkumar.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist └── UIKitApp ├── .DS_Store ├── AppDelegate └── AppDelegate.swift ├── Extensions ├── Foundation │ ├── Date+Extension.swift │ ├── Int+Extension.swift │ └── String+Extension.swift └── UIKit │ ├── UIButton+Extension.swift │ ├── UICollectionView+Extension.swift │ ├── UIColor+Extension.swift │ ├── UIImageView+Extension.swift │ ├── UILabel+Extension.swift │ ├── UIStackView+Extension.swift │ ├── UITableView+Extension.swift │ └── UIView+Extension.swift ├── Managers └── CommonUtility.swift ├── Modules ├── ChatUI │ ├── ChatHeaderDateLabel.swift │ ├── ChatMessageDataSource.swift │ ├── ChatMessageTableCell.swift │ └── ChatMessageViewController.swift ├── Common │ └── BaseViewController.swift ├── Download Video │ └── DownloadVideoViewController.swift ├── GitHub Followers │ ├── GitHubFollowersController.swift │ ├── GitHubFollowersDataSource.swift │ ├── GitHubProfileCollectionCell.swift │ └── GitHubProfilePreviewController.swift ├── Home │ ├── HomeControllerDataSource.swift │ ├── HomeListItemTableCell.swift │ ├── HomeViewController.swift │ └── home_list.json └── Photo Gallery │ ├── Photo Gallery │ ├── PhotoGalleryViewController.swift │ └── PhotoItemCollectionCell.swift │ └── Photo Grid │ ├── PhotoGridCollectionCell.swift │ ├── PhotosGridDataSource.swift │ └── PhotosGridViewController.swift ├── Resources ├── .DS_Store ├── Assets.xcassets │ ├── .DS_Store │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── Photo Gallery │ │ ├── Contents.json │ │ ├── image0.imageset │ │ │ ├── Contents.json │ │ │ └── image0.jpg │ │ ├── image1.imageset │ │ │ ├── Contents.json │ │ │ └── image1.jpg │ │ ├── image10.imageset │ │ │ ├── Contents.json │ │ │ └── image10.jpg │ │ ├── image11.imageset │ │ │ ├── Contents.json │ │ │ └── image11.jpg │ │ ├── image12.imageset │ │ │ ├── Contents.json │ │ │ └── image12.jpg │ │ ├── image13.imageset │ │ │ ├── Contents.json │ │ │ └── image13.jpg │ │ ├── image14.imageset │ │ │ ├── Contents.json │ │ │ └── image14.jpg │ │ ├── image15.imageset │ │ │ ├── Contents.json │ │ │ └── image15.jpg │ │ ├── image16.imageset │ │ │ ├── Contents.json │ │ │ └── image16.jpg │ │ ├── image17.imageset │ │ │ ├── Contents.json │ │ │ └── image17.jpg │ │ ├── image18.imageset │ │ │ ├── Contents.json │ │ │ └── image18.jpg │ │ ├── image19.imageset │ │ │ ├── Contents.json │ │ │ └── image19.jpg │ │ ├── image2.imageset │ │ │ ├── Contents.json │ │ │ └── image2.jpg │ │ ├── image20.imageset │ │ │ ├── Contents.json │ │ │ └── image20.jpg │ │ ├── image21.imageset │ │ │ ├── Contents.json │ │ │ └── image21.jpg │ │ ├── image22.imageset │ │ │ ├── 01w.jpg │ │ │ └── Contents.json │ │ ├── image3.imageset │ │ │ ├── Contents.json │ │ │ └── image3.jpg │ │ ├── image4.imageset │ │ │ ├── Contents.json │ │ │ └── image4.jpg │ │ ├── image5.imageset │ │ │ ├── Contents.json │ │ │ └── image5.jpg │ │ ├── image6.imageset │ │ │ ├── Contents.json │ │ │ └── image6.jpg │ │ ├── image7.imageset │ │ │ ├── Contents.json │ │ │ └── image7.jpg │ │ ├── image8.imageset │ │ │ ├── Contents.json │ │ │ └── image8.jpg │ │ └── image9.imageset │ │ │ ├── Contents.json │ │ │ └── image9.jpg │ ├── Profiles │ │ ├── 13m.imageset │ │ │ ├── 13m.jpg │ │ │ └── Contents.json │ │ ├── 14m.imageset │ │ │ ├── 14m.jpg │ │ │ └── Contents.json │ │ ├── 15w.imageset │ │ │ ├── 15w.jpg │ │ │ └── Contents.json │ │ ├── 16w.imageset │ │ │ ├── 16w.jpg │ │ │ └── Contents.json │ │ ├── 17w.imageset │ │ │ ├── 17w.jpg │ │ │ └── Contents.json │ │ ├── 18w.imageset │ │ │ ├── 18w.jpg │ │ │ └── Contents.json │ │ ├── 19m.imageset │ │ │ ├── 19m.jpg │ │ │ └── Contents.json │ │ ├── 20w.imageset │ │ │ ├── 20w.jpg │ │ │ └── Contents.json │ │ ├── 21w.imageset │ │ │ ├── 21w.jpg │ │ │ └── Contents.json │ │ ├── 22w.imageset │ │ │ ├── 22w.jpg │ │ │ └── Contents.json │ │ ├── 23w.imageset │ │ │ ├── 23w.jpg │ │ │ └── Contents.json │ │ ├── 24w.imageset │ │ │ ├── 24w.jpg │ │ │ └── Contents.json │ │ ├── 25w.imageset │ │ │ ├── 25w.jpg │ │ │ └── Contents.json │ │ ├── 26m.imageset │ │ │ ├── 26m.jpg │ │ │ └── Contents.json │ │ ├── 27m.imageset │ │ │ ├── 27m.jpg │ │ │ └── Contents.json │ │ ├── 28m.imageset │ │ │ ├── 28m.jpg │ │ │ └── Contents.json │ │ ├── 29w.imageset │ │ │ ├── 29w.jpg │ │ │ └── Contents.json │ │ ├── 30w.imageset │ │ │ ├── 30w.jpg │ │ │ └── Contents.json │ │ ├── 31m.imageset │ │ │ ├── 31m.jpg │ │ │ └── Contents.json │ │ ├── 32m.imageset │ │ │ ├── 32m.jpg │ │ │ └── Contents.json │ │ ├── 33w.imageset │ │ │ ├── 33w.jpg │ │ │ └── Contents.json │ │ ├── 34m.imageset │ │ │ ├── 34m.jpg │ │ │ └── Contents.json │ │ ├── 35w.imageset │ │ │ ├── 35w.jpg │ │ │ └── Contents.json │ │ ├── 36m.imageset │ │ │ ├── 36m.jpg │ │ │ └── Contents.json │ │ ├── 37w.imageset │ │ │ ├── 37w.jpg │ │ │ └── Contents.json │ │ ├── 38w.imageset │ │ │ ├── 38w.jpg │ │ │ └── Contents.json │ │ ├── 39w.imageset │ │ │ ├── 39w.jpg │ │ │ └── Contents.json │ │ ├── 40w.imageset │ │ │ ├── 40w.jpg │ │ │ └── Contents.json │ │ ├── 41w.imageset │ │ │ ├── 41w.jpg │ │ │ └── Contents.json │ │ ├── 42w.imageset │ │ │ ├── 42w.jpg │ │ │ └── Contents.json │ │ ├── 43w.imageset │ │ │ ├── 43w.jpg │ │ │ └── Contents.json │ │ ├── 44w.imageset │ │ │ ├── 44w.jpg │ │ │ └── Contents.json │ │ ├── 45m.imageset │ │ │ ├── 45m.jpg │ │ │ └── Contents.json │ │ ├── 46w.imageset │ │ │ ├── 46w.jpg │ │ │ └── Contents.json │ │ ├── 47w.imageset │ │ │ ├── 47w.jpg │ │ │ └── Contents.json │ │ ├── 48m.imageset │ │ │ ├── 48m.jpg │ │ │ └── Contents.json │ │ ├── 49w.imageset │ │ │ ├── 49w.jpg │ │ │ └── Contents.json │ │ ├── 50w.imageset │ │ │ ├── 50w.jpg │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── profile_1.imageset │ │ │ ├── Contents.json │ │ │ └── profile_1.jpg │ │ ├── profile_10.imageset │ │ │ ├── Contents.json │ │ │ └── profile_10.jpg │ │ ├── profile_11.imageset │ │ │ ├── Contents.json │ │ │ └── profile_11.jpg │ │ ├── profile_12.imageset │ │ │ ├── Contents.json │ │ │ └── profile_12.jpg │ │ ├── profile_2.imageset │ │ │ ├── Contents.json │ │ │ └── profile_2.jpg │ │ ├── profile_3.imageset │ │ │ ├── Contents.json │ │ │ └── profile_3.jpg │ │ ├── profile_4.imageset │ │ │ ├── Contents.json │ │ │ └── profile_4.jpg │ │ ├── profile_5.imageset │ │ │ ├── Contents.json │ │ │ └── profile_5.jpg │ │ ├── profile_6.imageset │ │ │ ├── Contents.json │ │ │ └── profile_6.jpg │ │ ├── profile_7.imageset │ │ │ ├── Contents.json │ │ │ └── profile_7.jpg │ │ ├── profile_8.imageset │ │ │ ├── Contents.json │ │ │ └── profile_8.jpg │ │ └── profile_9.imageset │ │ │ ├── Contents.json │ │ │ └── profile_9.jpg │ └── image_whats_app_bg.imageset │ │ ├── 5a11bb1d77ee734b4f16ad3b2d6bc189.png │ │ ├── 9dd007a70745d203b24aa6d6d0c0b1a1.png │ │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard └── Info.plist └── Views ├── BaseCollectionCell.swift └── BaseTableCell.swift /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ExploringUIKit 2 | 3 |
4 | In this series, you will explore different classes and API provided for iOS apps. 5 |
6 | 7 | 8 | ### Titbit 1 - UICollectionView with UIContextMenu 9 | 10 | Using UICollectionView, you will create a grid layout and use UIContextMenu to display a quick custom preview. A great way to enhance user experience in your apps is by implementing this feature. 11 | 12 |

13 | 14 |

15 | 16 | **Learning stack:** 17 | 18 | - How to create a grid layout with equal spacing around UICollectionView items? 19 | - How to show a custom preview using UIContextMenu? 20 | - How do I view menu items and submenu items along with a quick preview? 21 | - How to programmatically add constraints without a library? 22 | - How to programmatically create UIs for an app? 23 | 24 |
25 | 26 | ### Titbit 2 - Chat Messages using UITableView 27 | 28 | Using UITableView, it is easy to create user interface for chat messages. Programmatically creating the chat messages UI using UITableView gives you fine-grained control over the appearance and behavior of the interface. You can customize the table view cells, handle user interactions, and implement features like message threading, timestamps, and multimedia attachments according to your requirements. 29 | 30 |

31 | 32 |

33 | 34 | **Learning stack:** 35 | 36 | - How to implement table cells to show received and sent messages using a single cell? 37 | - How to create an input view to enter the message? 38 | - How to show the input view along with the keyboard appearing and dismissing? 39 | - How to programmatically add constraints without a library? 40 | - How to format the date to display in a customized way? 41 | - How to sort and group messages according to date? 42 | 43 |
44 | 45 | ### Titbit 3 - Photo Gallery with Zoom in/out 46 | 47 | UICollectionView is a powerful component that allows you to display and manage a collection of items in a grid-like or custom layout. It provides a flexible foundation for creating various types of interfaces, including photo galleries. It's important to note that implementing zoom in/out functionality can involve more complex logic, such as managing zoom scales, calculating zoom levels, and handling gesture interactions. But, you can implement all of these features quickly by programmatically. 48 | 49 | https://github.com/nitin-agam/ExploringUIKit/assets/12906999/54d0ae7b-ad7a-4bae-b687-28cdf67beb75 50 | 51 | **Learning stack:** 52 | 53 | - How to create a grid layout with equal spacing around UICollectionView items? 54 | - Perform actions like Save, Share, Copy, and Delete operations on an image by showing the context menu. 55 | - Show a photo gallery with all photos and a selected index. 56 | - Implement a zoom in/out feature using UIScrollView. 57 | - Set the zoom scale according to the image size. 58 | 59 |
60 | 61 | ### Titbit 4 - Download Video with Progress 62 | 63 | In your apps, downloading a video (or other format) file and showing continuous progress with animation is a common task when dealing with media content or file transfers. To achieve this, you can use the URLSession class, along with a CAShapeLayer with an animation using CABasicAnimation to visualize the download progress. 64 | 65 | https://github.com/nitin-agam/ExploringUIKit/assets/12906999/5ab9e6ba-f2a0-42cb-9623-84e3854a9897 66 | 67 | **Learning stack:** 68 | 69 | - How to download a video (or any other file) from a URL using the URLSession class. 70 | - How to draw a circular ring shape layer using UIBezierPath and CAShapeLayer? 71 | - How to implement pulsing animation using CABasicAnimation? 72 | - How to show continuous downloading progress to the user? 73 | - How to save the video file in the photo gallery after downloading it? 74 | 75 | and many more... 76 | 77 |
78 | 79 | #### Connect with me 80 | 81 | Feel free to reach out if you have questions or if you want to contribute in any way: 82 | 83 | 👉 [Topmate](https://topmate.io/nitinagam17) (Connect 1:1 with me) 84 | 85 | 👉 [Instagram](https://www.instagram.com/ios_geeks16/) (Random updates) 86 | 87 | 👉 [Linkedin](https://www.linkedin.com/in/nitinagam/) (Connect with me) 88 | 89 | Don't forget to click on 'Star' button 🌟 90 | 91 | > If you find any issue, please create an issue by mentioning the project title. Additionally, you can create an issue for any project if you would like to request a feature. 92 | 93 |
94 | Happy Coding !! 95 | 96 | -------------------------------------------------------------------------------- /UIKitApp/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/.DS_Store -------------------------------------------------------------------------------- /UIKitApp/UIKitApp.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 56; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C7034EEA2A6AB42D008458FA /* DownloadVideoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7034EE92A6AB42D008458FA /* DownloadVideoViewController.swift */; }; 11 | C70C33A72A4B248D00F5B05C /* UIColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C70C33A62A4B248D00F5B05C /* UIColor+Extension.swift */; }; 12 | C70C33B92A4D855400F5B05C /* UIButton+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C70C33B82A4D855400F5B05C /* UIButton+Extension.swift */; }; 13 | C70C33BC2A4F1D2600F5B05C /* home_list.json in Resources */ = {isa = PBXBuildFile; fileRef = C70C33BB2A4F1D2600F5B05C /* home_list.json */; }; 14 | C70C33BF2A4F1EAF00F5B05C /* CommonUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C70C33BE2A4F1EAF00F5B05C /* CommonUtility.swift */; }; 15 | C70C33C22A4F20FB00F5B05C /* PhotosGridViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C70C33C12A4F20FB00F5B05C /* PhotosGridViewController.swift */; }; 16 | C70C33C62A4F213A00F5B05C /* PhotosGridDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C70C33C52A4F213A00F5B05C /* PhotosGridDataSource.swift */; }; 17 | C70C33CB2A4F2CEF00F5B05C /* PhotoGridCollectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C70C33CA2A4F2CEF00F5B05C /* PhotoGridCollectionCell.swift */; }; 18 | C70C33CF2A50110F00F5B05C /* PhotoGalleryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C70C33CE2A50110F00F5B05C /* PhotoGalleryViewController.swift */; }; 19 | C70C33D12A5011B600F5B05C /* PhotoItemCollectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C70C33D02A5011B600F5B05C /* PhotoItemCollectionCell.swift */; }; 20 | C73D8D282A49B01200A1997A /* UILabel+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C73D8D272A49B01200A1997A /* UILabel+Extension.swift */; }; 21 | C73D8D2A2A49B2EE00A1997A /* UIImageView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C73D8D292A49B2EE00A1997A /* UIImageView+Extension.swift */; }; 22 | C73D8D2D2A4A9FD000A1997A /* ChatMessageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C73D8D2C2A4A9FD000A1997A /* ChatMessageViewController.swift */; }; 23 | C73D8D2F2A4AA07A00A1997A /* ChatMessageTableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C73D8D2E2A4AA07A00A1997A /* ChatMessageTableCell.swift */; }; 24 | C73D8D312A4AA0A100A1997A /* ChatMessageDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C73D8D302A4AA0A100A1997A /* ChatMessageDataSource.swift */; }; 25 | C73D8D332A4AA14A00A1997A /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C73D8D322A4AA14A00A1997A /* String+Extension.swift */; }; 26 | C73D8D352A4AA16300A1997A /* Date+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C73D8D342A4AA16300A1997A /* Date+Extension.swift */; }; 27 | C73D8D372A4AA4EA00A1997A /* ChatHeaderDateLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C73D8D362A4AA4EA00A1997A /* ChatHeaderDateLabel.swift */; }; 28 | C75C8C782A4209F100B46DEF /* UICollectionView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75C8C772A4209F100B46DEF /* UICollectionView+Extension.swift */; }; 29 | C75C8C7A2A42BFA600B46DEF /* GitHubProfilePreviewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75C8C792A42BFA600B46DEF /* GitHubProfilePreviewController.swift */; }; 30 | C75C8C7C2A44498A00B46DEF /* UIStackView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75C8C7B2A44498A00B46DEF /* UIStackView+Extension.swift */; }; 31 | C75C8C7E2A449A9800B46DEF /* Int+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75C8C7D2A449A9800B46DEF /* Int+Extension.swift */; }; 32 | C7809DB12A40A1A200206C53 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7809DB02A40A1A200206C53 /* AppDelegate.swift */; }; 33 | C7809DBA2A40A1A400206C53 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C7809DB92A40A1A400206C53 /* Assets.xcassets */; }; 34 | C7809DBD2A40A1A400206C53 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C7809DBB2A40A1A400206C53 /* LaunchScreen.storyboard */; }; 35 | C7809DC92A40B66000206C53 /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7809DC82A40B66000206C53 /* HomeViewController.swift */; }; 36 | C7809DCB2A40B83800206C53 /* HomeControllerDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7809DCA2A40B83800206C53 /* HomeControllerDataSource.swift */; }; 37 | C7809DCE2A40B8F500206C53 /* GitHubFollowersController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7809DCD2A40B8F500206C53 /* GitHubFollowersController.swift */; }; 38 | C7809DD12A40B91200206C53 /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7809DD02A40B91200206C53 /* BaseViewController.swift */; }; 39 | C7809DD42A40BACD00206C53 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7809DD32A40BACD00206C53 /* UIView+Extension.swift */; }; 40 | C7809DD62A40BB4B00206C53 /* UITableView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7809DD52A40BB4B00206C53 /* UITableView+Extension.swift */; }; 41 | C7809DD92A40BBC600206C53 /* BaseTableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7809DD82A40BBC600206C53 /* BaseTableCell.swift */; }; 42 | C7809DDB2A40BC4200206C53 /* HomeListItemTableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7809DDA2A40BC4200206C53 /* HomeListItemTableCell.swift */; }; 43 | C7809DDD2A41E74000206C53 /* BaseCollectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7809DDC2A41E74000206C53 /* BaseCollectionCell.swift */; }; 44 | C7809DDF2A41E79300206C53 /* GitHubProfileCollectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7809DDE2A41E79300206C53 /* GitHubProfileCollectionCell.swift */; }; 45 | C7809DE12A42087800206C53 /* GitHubFollowersDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7809DE02A42087800206C53 /* GitHubFollowersDataSource.swift */; }; 46 | /* End PBXBuildFile section */ 47 | 48 | /* Begin PBXFileReference section */ 49 | C7034EE92A6AB42D008458FA /* DownloadVideoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadVideoViewController.swift; sourceTree = ""; }; 50 | C70C33A62A4B248D00F5B05C /* UIColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Extension.swift"; sourceTree = ""; }; 51 | C70C33B82A4D855400F5B05C /* UIButton+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+Extension.swift"; sourceTree = ""; }; 52 | C70C33BB2A4F1D2600F5B05C /* home_list.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = home_list.json; sourceTree = ""; }; 53 | C70C33BE2A4F1EAF00F5B05C /* CommonUtility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonUtility.swift; sourceTree = ""; }; 54 | C70C33C12A4F20FB00F5B05C /* PhotosGridViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotosGridViewController.swift; sourceTree = ""; }; 55 | C70C33C52A4F213A00F5B05C /* PhotosGridDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotosGridDataSource.swift; sourceTree = ""; }; 56 | C70C33CA2A4F2CEF00F5B05C /* PhotoGridCollectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoGridCollectionCell.swift; sourceTree = ""; }; 57 | C70C33CE2A50110F00F5B05C /* PhotoGalleryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoGalleryViewController.swift; sourceTree = ""; }; 58 | C70C33D02A5011B600F5B05C /* PhotoItemCollectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoItemCollectionCell.swift; sourceTree = ""; }; 59 | C73D8D272A49B01200A1997A /* UILabel+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Extension.swift"; sourceTree = ""; }; 60 | C73D8D292A49B2EE00A1997A /* UIImageView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageView+Extension.swift"; sourceTree = ""; }; 61 | C73D8D2C2A4A9FD000A1997A /* ChatMessageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMessageViewController.swift; sourceTree = ""; }; 62 | C73D8D2E2A4AA07A00A1997A /* ChatMessageTableCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMessageTableCell.swift; sourceTree = ""; }; 63 | C73D8D302A4AA0A100A1997A /* ChatMessageDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMessageDataSource.swift; sourceTree = ""; }; 64 | C73D8D322A4AA14A00A1997A /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = ""; }; 65 | C73D8D342A4AA16300A1997A /* Date+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extension.swift"; sourceTree = ""; }; 66 | C73D8D362A4AA4EA00A1997A /* ChatHeaderDateLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatHeaderDateLabel.swift; sourceTree = ""; }; 67 | C75C8C772A4209F100B46DEF /* UICollectionView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionView+Extension.swift"; sourceTree = ""; }; 68 | C75C8C792A42BFA600B46DEF /* GitHubProfilePreviewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitHubProfilePreviewController.swift; sourceTree = ""; }; 69 | C75C8C7B2A44498A00B46DEF /* UIStackView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStackView+Extension.swift"; sourceTree = ""; }; 70 | C75C8C7D2A449A9800B46DEF /* Int+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Extension.swift"; sourceTree = ""; }; 71 | C7809DAD2A40A1A200206C53 /* UIKitApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UIKitApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 72 | C7809DB02A40A1A200206C53 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 73 | C7809DB92A40A1A400206C53 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 74 | C7809DBC2A40A1A400206C53 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 75 | C7809DBE2A40A1A400206C53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 76 | C7809DC82A40B66000206C53 /* HomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = ""; }; 77 | C7809DCA2A40B83800206C53 /* HomeControllerDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeControllerDataSource.swift; sourceTree = ""; }; 78 | C7809DCD2A40B8F500206C53 /* GitHubFollowersController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitHubFollowersController.swift; sourceTree = ""; }; 79 | C7809DD02A40B91200206C53 /* BaseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = ""; }; 80 | C7809DD32A40BACD00206C53 /* UIView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Extension.swift"; sourceTree = ""; }; 81 | C7809DD52A40BB4B00206C53 /* UITableView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+Extension.swift"; sourceTree = ""; }; 82 | C7809DD82A40BBC600206C53 /* BaseTableCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTableCell.swift; sourceTree = ""; }; 83 | C7809DDA2A40BC4200206C53 /* HomeListItemTableCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeListItemTableCell.swift; sourceTree = ""; }; 84 | C7809DDC2A41E74000206C53 /* BaseCollectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCollectionCell.swift; sourceTree = ""; }; 85 | C7809DDE2A41E79300206C53 /* GitHubProfileCollectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitHubProfileCollectionCell.swift; sourceTree = ""; }; 86 | C7809DE02A42087800206C53 /* GitHubFollowersDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitHubFollowersDataSource.swift; sourceTree = ""; }; 87 | /* End PBXFileReference section */ 88 | 89 | /* Begin PBXFrameworksBuildPhase section */ 90 | C7809DAA2A40A1A200206C53 /* Frameworks */ = { 91 | isa = PBXFrameworksBuildPhase; 92 | buildActionMask = 2147483647; 93 | files = ( 94 | ); 95 | runOnlyForDeploymentPostprocessing = 0; 96 | }; 97 | /* End PBXFrameworksBuildPhase section */ 98 | 99 | /* Begin PBXGroup section */ 100 | C7034EE82A6AB401008458FA /* Download Video */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | C7034EE92A6AB42D008458FA /* DownloadVideoViewController.swift */, 104 | ); 105 | path = "Download Video"; 106 | sourceTree = ""; 107 | }; 108 | C70C33A82A4B249800F5B05C /* UIKit */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | C7809DD32A40BACD00206C53 /* UIView+Extension.swift */, 112 | C7809DD52A40BB4B00206C53 /* UITableView+Extension.swift */, 113 | C75C8C772A4209F100B46DEF /* UICollectionView+Extension.swift */, 114 | C75C8C7B2A44498A00B46DEF /* UIStackView+Extension.swift */, 115 | C73D8D272A49B01200A1997A /* UILabel+Extension.swift */, 116 | C73D8D292A49B2EE00A1997A /* UIImageView+Extension.swift */, 117 | C70C33A62A4B248D00F5B05C /* UIColor+Extension.swift */, 118 | C70C33B82A4D855400F5B05C /* UIButton+Extension.swift */, 119 | ); 120 | path = UIKit; 121 | sourceTree = ""; 122 | }; 123 | C70C33A92A4B24A400F5B05C /* Foundation */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | C75C8C7D2A449A9800B46DEF /* Int+Extension.swift */, 127 | C73D8D322A4AA14A00A1997A /* String+Extension.swift */, 128 | C73D8D342A4AA16300A1997A /* Date+Extension.swift */, 129 | ); 130 | path = Foundation; 131 | sourceTree = ""; 132 | }; 133 | C70C33BD2A4F1E8E00F5B05C /* Managers */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | C70C33BE2A4F1EAF00F5B05C /* CommonUtility.swift */, 137 | ); 138 | path = Managers; 139 | sourceTree = ""; 140 | }; 141 | C70C33C02A4F20CF00F5B05C /* Photo Gallery */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | C70C33CC2A5010EA00F5B05C /* Photo Grid */, 145 | C70C33CD2A5010F600F5B05C /* Photo Gallery */, 146 | ); 147 | path = "Photo Gallery"; 148 | sourceTree = ""; 149 | }; 150 | C70C33CC2A5010EA00F5B05C /* Photo Grid */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | C70C33C12A4F20FB00F5B05C /* PhotosGridViewController.swift */, 154 | C70C33C52A4F213A00F5B05C /* PhotosGridDataSource.swift */, 155 | C70C33CA2A4F2CEF00F5B05C /* PhotoGridCollectionCell.swift */, 156 | ); 157 | path = "Photo Grid"; 158 | sourceTree = ""; 159 | }; 160 | C70C33CD2A5010F600F5B05C /* Photo Gallery */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | C70C33CE2A50110F00F5B05C /* PhotoGalleryViewController.swift */, 164 | C70C33D02A5011B600F5B05C /* PhotoItemCollectionCell.swift */, 165 | ); 166 | path = "Photo Gallery"; 167 | sourceTree = ""; 168 | }; 169 | C73D8D2B2A4A9FB800A1997A /* ChatUI */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | C73D8D2C2A4A9FD000A1997A /* ChatMessageViewController.swift */, 173 | C73D8D2E2A4AA07A00A1997A /* ChatMessageTableCell.swift */, 174 | C73D8D302A4AA0A100A1997A /* ChatMessageDataSource.swift */, 175 | C73D8D362A4AA4EA00A1997A /* ChatHeaderDateLabel.swift */, 176 | ); 177 | path = ChatUI; 178 | sourceTree = ""; 179 | }; 180 | C7809DA42A40A1A200206C53 = { 181 | isa = PBXGroup; 182 | children = ( 183 | C7809DAF2A40A1A200206C53 /* UIKitApp */, 184 | C7809DAE2A40A1A200206C53 /* Products */, 185 | ); 186 | sourceTree = ""; 187 | }; 188 | C7809DAE2A40A1A200206C53 /* Products */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | C7809DAD2A40A1A200206C53 /* UIKitApp.app */, 192 | ); 193 | name = Products; 194 | sourceTree = ""; 195 | }; 196 | C7809DAF2A40A1A200206C53 /* UIKitApp */ = { 197 | isa = PBXGroup; 198 | children = ( 199 | C70C33BD2A4F1E8E00F5B05C /* Managers */, 200 | C7809DD72A40BBB700206C53 /* Views */, 201 | C7809DD22A40BAAC00206C53 /* Extensions */, 202 | C7809DC42A40B53D00206C53 /* AppDelegate */, 203 | C7809DC62A40B63000206C53 /* Modules */, 204 | C7809DC52A40B55100206C53 /* Resources */, 205 | ); 206 | path = UIKitApp; 207 | sourceTree = ""; 208 | }; 209 | C7809DC42A40B53D00206C53 /* AppDelegate */ = { 210 | isa = PBXGroup; 211 | children = ( 212 | C7809DB02A40A1A200206C53 /* AppDelegate.swift */, 213 | ); 214 | path = AppDelegate; 215 | sourceTree = ""; 216 | }; 217 | C7809DC52A40B55100206C53 /* Resources */ = { 218 | isa = PBXGroup; 219 | children = ( 220 | C7809DBE2A40A1A400206C53 /* Info.plist */, 221 | C7809DB92A40A1A400206C53 /* Assets.xcassets */, 222 | C7809DBB2A40A1A400206C53 /* LaunchScreen.storyboard */, 223 | ); 224 | path = Resources; 225 | sourceTree = ""; 226 | }; 227 | C7809DC62A40B63000206C53 /* Modules */ = { 228 | isa = PBXGroup; 229 | children = ( 230 | C7809DCF2A40B90200206C53 /* Common */, 231 | C7809DC72A40B63F00206C53 /* Home */, 232 | C7809DCC2A40B8BB00206C53 /* GitHub Followers */, 233 | C73D8D2B2A4A9FB800A1997A /* ChatUI */, 234 | C70C33C02A4F20CF00F5B05C /* Photo Gallery */, 235 | C7034EE82A6AB401008458FA /* Download Video */, 236 | ); 237 | path = Modules; 238 | sourceTree = ""; 239 | }; 240 | C7809DC72A40B63F00206C53 /* Home */ = { 241 | isa = PBXGroup; 242 | children = ( 243 | C7809DC82A40B66000206C53 /* HomeViewController.swift */, 244 | C7809DCA2A40B83800206C53 /* HomeControllerDataSource.swift */, 245 | C7809DDA2A40BC4200206C53 /* HomeListItemTableCell.swift */, 246 | C70C33BB2A4F1D2600F5B05C /* home_list.json */, 247 | ); 248 | path = Home; 249 | sourceTree = ""; 250 | }; 251 | C7809DCC2A40B8BB00206C53 /* GitHub Followers */ = { 252 | isa = PBXGroup; 253 | children = ( 254 | C7809DCD2A40B8F500206C53 /* GitHubFollowersController.swift */, 255 | C7809DDE2A41E79300206C53 /* GitHubProfileCollectionCell.swift */, 256 | C7809DE02A42087800206C53 /* GitHubFollowersDataSource.swift */, 257 | C75C8C792A42BFA600B46DEF /* GitHubProfilePreviewController.swift */, 258 | ); 259 | path = "GitHub Followers"; 260 | sourceTree = ""; 261 | }; 262 | C7809DCF2A40B90200206C53 /* Common */ = { 263 | isa = PBXGroup; 264 | children = ( 265 | C7809DD02A40B91200206C53 /* BaseViewController.swift */, 266 | ); 267 | path = Common; 268 | sourceTree = ""; 269 | }; 270 | C7809DD22A40BAAC00206C53 /* Extensions */ = { 271 | isa = PBXGroup; 272 | children = ( 273 | C70C33A82A4B249800F5B05C /* UIKit */, 274 | C70C33A92A4B24A400F5B05C /* Foundation */, 275 | ); 276 | path = Extensions; 277 | sourceTree = ""; 278 | }; 279 | C7809DD72A40BBB700206C53 /* Views */ = { 280 | isa = PBXGroup; 281 | children = ( 282 | C7809DD82A40BBC600206C53 /* BaseTableCell.swift */, 283 | C7809DDC2A41E74000206C53 /* BaseCollectionCell.swift */, 284 | ); 285 | path = Views; 286 | sourceTree = ""; 287 | }; 288 | /* End PBXGroup section */ 289 | 290 | /* Begin PBXNativeTarget section */ 291 | C7809DAC2A40A1A200206C53 /* UIKitApp */ = { 292 | isa = PBXNativeTarget; 293 | buildConfigurationList = C7809DC12A40A1A400206C53 /* Build configuration list for PBXNativeTarget "UIKitApp" */; 294 | buildPhases = ( 295 | C7809DA92A40A1A200206C53 /* Sources */, 296 | C7809DAA2A40A1A200206C53 /* Frameworks */, 297 | C7809DAB2A40A1A200206C53 /* Resources */, 298 | ); 299 | buildRules = ( 300 | ); 301 | dependencies = ( 302 | ); 303 | name = UIKitApp; 304 | productName = UIKitApp; 305 | productReference = C7809DAD2A40A1A200206C53 /* UIKitApp.app */; 306 | productType = "com.apple.product-type.application"; 307 | }; 308 | /* End PBXNativeTarget section */ 309 | 310 | /* Begin PBXProject section */ 311 | C7809DA52A40A1A200206C53 /* Project object */ = { 312 | isa = PBXProject; 313 | attributes = { 314 | BuildIndependentTargetsInParallel = 1; 315 | LastSwiftUpdateCheck = 1430; 316 | LastUpgradeCheck = 1430; 317 | TargetAttributes = { 318 | C7809DAC2A40A1A200206C53 = { 319 | CreatedOnToolsVersion = 14.3; 320 | }; 321 | }; 322 | }; 323 | buildConfigurationList = C7809DA82A40A1A200206C53 /* Build configuration list for PBXProject "UIKitApp" */; 324 | compatibilityVersion = "Xcode 14.0"; 325 | developmentRegion = en; 326 | hasScannedForEncodings = 0; 327 | knownRegions = ( 328 | en, 329 | Base, 330 | ); 331 | mainGroup = C7809DA42A40A1A200206C53; 332 | productRefGroup = C7809DAE2A40A1A200206C53 /* Products */; 333 | projectDirPath = ""; 334 | projectRoot = ""; 335 | targets = ( 336 | C7809DAC2A40A1A200206C53 /* UIKitApp */, 337 | ); 338 | }; 339 | /* End PBXProject section */ 340 | 341 | /* Begin PBXResourcesBuildPhase section */ 342 | C7809DAB2A40A1A200206C53 /* Resources */ = { 343 | isa = PBXResourcesBuildPhase; 344 | buildActionMask = 2147483647; 345 | files = ( 346 | C7809DBD2A40A1A400206C53 /* LaunchScreen.storyboard in Resources */, 347 | C70C33BC2A4F1D2600F5B05C /* home_list.json in Resources */, 348 | C7809DBA2A40A1A400206C53 /* Assets.xcassets in Resources */, 349 | ); 350 | runOnlyForDeploymentPostprocessing = 0; 351 | }; 352 | /* End PBXResourcesBuildPhase section */ 353 | 354 | /* Begin PBXSourcesBuildPhase section */ 355 | C7809DA92A40A1A200206C53 /* Sources */ = { 356 | isa = PBXSourcesBuildPhase; 357 | buildActionMask = 2147483647; 358 | files = ( 359 | C73D8D282A49B01200A1997A /* UILabel+Extension.swift in Sources */, 360 | C7809DDD2A41E74000206C53 /* BaseCollectionCell.swift in Sources */, 361 | C75C8C7E2A449A9800B46DEF /* Int+Extension.swift in Sources */, 362 | C73D8D312A4AA0A100A1997A /* ChatMessageDataSource.swift in Sources */, 363 | C73D8D2D2A4A9FD000A1997A /* ChatMessageViewController.swift in Sources */, 364 | C75C8C7A2A42BFA600B46DEF /* GitHubProfilePreviewController.swift in Sources */, 365 | C73D8D352A4AA16300A1997A /* Date+Extension.swift in Sources */, 366 | C7809DD42A40BACD00206C53 /* UIView+Extension.swift in Sources */, 367 | C7809DD92A40BBC600206C53 /* BaseTableCell.swift in Sources */, 368 | C70C33A72A4B248D00F5B05C /* UIColor+Extension.swift in Sources */, 369 | C7809DD12A40B91200206C53 /* BaseViewController.swift in Sources */, 370 | C7809DD62A40BB4B00206C53 /* UITableView+Extension.swift in Sources */, 371 | C73D8D372A4AA4EA00A1997A /* ChatHeaderDateLabel.swift in Sources */, 372 | C7809DCE2A40B8F500206C53 /* GitHubFollowersController.swift in Sources */, 373 | C7034EEA2A6AB42D008458FA /* DownloadVideoViewController.swift in Sources */, 374 | C73D8D2F2A4AA07A00A1997A /* ChatMessageTableCell.swift in Sources */, 375 | C70C33B92A4D855400F5B05C /* UIButton+Extension.swift in Sources */, 376 | C73D8D332A4AA14A00A1997A /* String+Extension.swift in Sources */, 377 | C75C8C782A4209F100B46DEF /* UICollectionView+Extension.swift in Sources */, 378 | C70C33C62A4F213A00F5B05C /* PhotosGridDataSource.swift in Sources */, 379 | C70C33BF2A4F1EAF00F5B05C /* CommonUtility.swift in Sources */, 380 | C70C33CB2A4F2CEF00F5B05C /* PhotoGridCollectionCell.swift in Sources */, 381 | C75C8C7C2A44498A00B46DEF /* UIStackView+Extension.swift in Sources */, 382 | C70C33CF2A50110F00F5B05C /* PhotoGalleryViewController.swift in Sources */, 383 | C7809DE12A42087800206C53 /* GitHubFollowersDataSource.swift in Sources */, 384 | C73D8D2A2A49B2EE00A1997A /* UIImageView+Extension.swift in Sources */, 385 | C7809DDF2A41E79300206C53 /* GitHubProfileCollectionCell.swift in Sources */, 386 | C7809DB12A40A1A200206C53 /* AppDelegate.swift in Sources */, 387 | C7809DCB2A40B83800206C53 /* HomeControllerDataSource.swift in Sources */, 388 | C7809DC92A40B66000206C53 /* HomeViewController.swift in Sources */, 389 | C70C33D12A5011B600F5B05C /* PhotoItemCollectionCell.swift in Sources */, 390 | C7809DDB2A40BC4200206C53 /* HomeListItemTableCell.swift in Sources */, 391 | C70C33C22A4F20FB00F5B05C /* PhotosGridViewController.swift in Sources */, 392 | ); 393 | runOnlyForDeploymentPostprocessing = 0; 394 | }; 395 | /* End PBXSourcesBuildPhase section */ 396 | 397 | /* Begin PBXVariantGroup section */ 398 | C7809DBB2A40A1A400206C53 /* LaunchScreen.storyboard */ = { 399 | isa = PBXVariantGroup; 400 | children = ( 401 | C7809DBC2A40A1A400206C53 /* Base */, 402 | ); 403 | name = LaunchScreen.storyboard; 404 | sourceTree = ""; 405 | }; 406 | /* End PBXVariantGroup section */ 407 | 408 | /* Begin XCBuildConfiguration section */ 409 | C7809DBF2A40A1A400206C53 /* Debug */ = { 410 | isa = XCBuildConfiguration; 411 | buildSettings = { 412 | ALWAYS_SEARCH_USER_PATHS = NO; 413 | CLANG_ANALYZER_NONNULL = YES; 414 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 415 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 416 | CLANG_ENABLE_MODULES = YES; 417 | CLANG_ENABLE_OBJC_ARC = YES; 418 | CLANG_ENABLE_OBJC_WEAK = YES; 419 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 420 | CLANG_WARN_BOOL_CONVERSION = YES; 421 | CLANG_WARN_COMMA = YES; 422 | CLANG_WARN_CONSTANT_CONVERSION = YES; 423 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 424 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 425 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 426 | CLANG_WARN_EMPTY_BODY = YES; 427 | CLANG_WARN_ENUM_CONVERSION = YES; 428 | CLANG_WARN_INFINITE_RECURSION = YES; 429 | CLANG_WARN_INT_CONVERSION = YES; 430 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 431 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 432 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 433 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 434 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 435 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 436 | CLANG_WARN_STRICT_PROTOTYPES = YES; 437 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 438 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 439 | CLANG_WARN_UNREACHABLE_CODE = YES; 440 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 441 | COPY_PHASE_STRIP = NO; 442 | DEBUG_INFORMATION_FORMAT = dwarf; 443 | ENABLE_STRICT_OBJC_MSGSEND = YES; 444 | ENABLE_TESTABILITY = YES; 445 | GCC_C_LANGUAGE_STANDARD = gnu11; 446 | GCC_DYNAMIC_NO_PIC = NO; 447 | GCC_NO_COMMON_BLOCKS = YES; 448 | GCC_OPTIMIZATION_LEVEL = 0; 449 | GCC_PREPROCESSOR_DEFINITIONS = ( 450 | "DEBUG=1", 451 | "$(inherited)", 452 | ); 453 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 454 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 455 | GCC_WARN_UNDECLARED_SELECTOR = YES; 456 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 457 | GCC_WARN_UNUSED_FUNCTION = YES; 458 | GCC_WARN_UNUSED_VARIABLE = YES; 459 | IPHONEOS_DEPLOYMENT_TARGET = 16.4; 460 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 461 | MTL_FAST_MATH = YES; 462 | ONLY_ACTIVE_ARCH = YES; 463 | SDKROOT = iphoneos; 464 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 465 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 466 | }; 467 | name = Debug; 468 | }; 469 | C7809DC02A40A1A400206C53 /* Release */ = { 470 | isa = XCBuildConfiguration; 471 | buildSettings = { 472 | ALWAYS_SEARCH_USER_PATHS = NO; 473 | CLANG_ANALYZER_NONNULL = YES; 474 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 475 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 476 | CLANG_ENABLE_MODULES = YES; 477 | CLANG_ENABLE_OBJC_ARC = YES; 478 | CLANG_ENABLE_OBJC_WEAK = YES; 479 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 480 | CLANG_WARN_BOOL_CONVERSION = YES; 481 | CLANG_WARN_COMMA = YES; 482 | CLANG_WARN_CONSTANT_CONVERSION = YES; 483 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 484 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 485 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 486 | CLANG_WARN_EMPTY_BODY = YES; 487 | CLANG_WARN_ENUM_CONVERSION = YES; 488 | CLANG_WARN_INFINITE_RECURSION = YES; 489 | CLANG_WARN_INT_CONVERSION = YES; 490 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 491 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 492 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 493 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 494 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 495 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 496 | CLANG_WARN_STRICT_PROTOTYPES = YES; 497 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 498 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 499 | CLANG_WARN_UNREACHABLE_CODE = YES; 500 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 501 | COPY_PHASE_STRIP = NO; 502 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 503 | ENABLE_NS_ASSERTIONS = NO; 504 | ENABLE_STRICT_OBJC_MSGSEND = YES; 505 | GCC_C_LANGUAGE_STANDARD = gnu11; 506 | GCC_NO_COMMON_BLOCKS = YES; 507 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 508 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 509 | GCC_WARN_UNDECLARED_SELECTOR = YES; 510 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 511 | GCC_WARN_UNUSED_FUNCTION = YES; 512 | GCC_WARN_UNUSED_VARIABLE = YES; 513 | IPHONEOS_DEPLOYMENT_TARGET = 16.4; 514 | MTL_ENABLE_DEBUG_INFO = NO; 515 | MTL_FAST_MATH = YES; 516 | SDKROOT = iphoneos; 517 | SWIFT_COMPILATION_MODE = wholemodule; 518 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 519 | VALIDATE_PRODUCT = YES; 520 | }; 521 | name = Release; 522 | }; 523 | C7809DC22A40A1A400206C53 /* Debug */ = { 524 | isa = XCBuildConfiguration; 525 | buildSettings = { 526 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 527 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 528 | CODE_SIGN_STYLE = Automatic; 529 | CURRENT_PROJECT_VERSION = 1; 530 | DEVELOPMENT_TEAM = ""; 531 | GENERATE_INFOPLIST_FILE = YES; 532 | INFOPLIST_FILE = UIKitApp/Resources/Info.plist; 533 | INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "To save media files in photo library."; 534 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 535 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 536 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; 537 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; 538 | IPHONEOS_DEPLOYMENT_TARGET = 16.0; 539 | LD_RUNPATH_SEARCH_PATHS = ( 540 | "$(inherited)", 541 | "@executable_path/Frameworks", 542 | ); 543 | MARKETING_VERSION = 1.0; 544 | PRODUCT_BUNDLE_IDENTIFIER = com.nitin.uikitapp; 545 | PRODUCT_NAME = "$(TARGET_NAME)"; 546 | SWIFT_EMIT_LOC_STRINGS = YES; 547 | SWIFT_VERSION = 5.0; 548 | TARGETED_DEVICE_FAMILY = "1,2"; 549 | }; 550 | name = Debug; 551 | }; 552 | C7809DC32A40A1A400206C53 /* Release */ = { 553 | isa = XCBuildConfiguration; 554 | buildSettings = { 555 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 556 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 557 | CODE_SIGN_STYLE = Automatic; 558 | CURRENT_PROJECT_VERSION = 1; 559 | DEVELOPMENT_TEAM = ""; 560 | GENERATE_INFOPLIST_FILE = YES; 561 | INFOPLIST_FILE = UIKitApp/Resources/Info.plist; 562 | INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "To save media files in photo library."; 563 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 564 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 565 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; 566 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; 567 | IPHONEOS_DEPLOYMENT_TARGET = 16.0; 568 | LD_RUNPATH_SEARCH_PATHS = ( 569 | "$(inherited)", 570 | "@executable_path/Frameworks", 571 | ); 572 | MARKETING_VERSION = 1.0; 573 | PRODUCT_BUNDLE_IDENTIFIER = com.nitin.uikitapp; 574 | PRODUCT_NAME = "$(TARGET_NAME)"; 575 | SWIFT_EMIT_LOC_STRINGS = YES; 576 | SWIFT_VERSION = 5.0; 577 | TARGETED_DEVICE_FAMILY = "1,2"; 578 | }; 579 | name = Release; 580 | }; 581 | /* End XCBuildConfiguration section */ 582 | 583 | /* Begin XCConfigurationList section */ 584 | C7809DA82A40A1A200206C53 /* Build configuration list for PBXProject "UIKitApp" */ = { 585 | isa = XCConfigurationList; 586 | buildConfigurations = ( 587 | C7809DBF2A40A1A400206C53 /* Debug */, 588 | C7809DC02A40A1A400206C53 /* Release */, 589 | ); 590 | defaultConfigurationIsVisible = 0; 591 | defaultConfigurationName = Release; 592 | }; 593 | C7809DC12A40A1A400206C53 /* Build configuration list for PBXNativeTarget "UIKitApp" */ = { 594 | isa = XCConfigurationList; 595 | buildConfigurations = ( 596 | C7809DC22A40A1A400206C53 /* Debug */, 597 | C7809DC32A40A1A400206C53 /* Release */, 598 | ); 599 | defaultConfigurationIsVisible = 0; 600 | defaultConfigurationName = Release; 601 | }; 602 | /* End XCConfigurationList section */ 603 | }; 604 | rootObject = C7809DA52A40A1A200206C53 /* Project object */; 605 | } 606 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp.xcodeproj/project.xcworkspace/xcuserdata/nitinkumar.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp.xcodeproj/project.xcworkspace/xcuserdata/nitinkumar.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /UIKitApp/UIKitApp.xcodeproj/xcuserdata/nitinkumar.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp.xcodeproj/xcuserdata/nitinkumar.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | UIKitApp.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/.DS_Store -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/AppDelegate/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 19/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | 17 | let navigation = UINavigationController(rootViewController: HomeViewController()) 18 | window = UIWindow(frame: UIScreen.main.bounds) 19 | window?.rootViewController = navigation 20 | window?.makeKeyAndVisible() 21 | 22 | return true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Extensions/Foundation/Date+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Date+Extension.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 27/06/23. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Date { 11 | 12 | func asString() -> String { 13 | let dateFormatter = DateFormatter() 14 | dateFormatter.dateFormat = "dd/MM/yyyy" 15 | return dateFormatter.string(from: self) 16 | } 17 | 18 | func chatCenterDisplayTime() -> String { 19 | 20 | let dateString = self.asString() 21 | let dateFormatter = DateFormatter() 22 | dateFormatter.dateFormat = "dd/MM/yyyy" 23 | 24 | if let date = dateFormatter.date(from: dateString) { 25 | if date.isDateInToday { return "Today" } 26 | if date.timeInDays() == 1 { return "Yesterday" } 27 | dateFormatter.dateFormat = "d MMMM yyyy" 28 | return dateFormatter.string(from: date) 29 | } 30 | return dateString 31 | } 32 | 33 | func dateOnly() -> String { 34 | let dateFormatter = DateFormatter() 35 | dateFormatter.dateFormat = "dd/MM/yyyy" 36 | return dateFormatter.string(from: self) 37 | } 38 | 39 | var isDateInToday: Bool { 40 | return Calendar.autoupdatingCurrent.isDateInToday(self) 41 | } 42 | 43 | func timeInDays() -> Int { 44 | Calendar.current.dateComponents([.day], from: self, to: Date()).day ?? 0 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Extensions/Foundation/Int+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Int+Extension.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 22/06/23. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Int { 11 | 12 | // Converting the Int value in string format. Eg. 1234 -> 1.2K 13 | var formatWithAbbreviations: String { 14 | let number = Double(self) 15 | let thousand = number / 1000 16 | let million = number / 1000000 17 | if million >= 1.0 { 18 | return "\(round(million * 10) / 10)M" 19 | } else if thousand >= 1.0 { 20 | return "\(round(thousand * 10) / 10)K" 21 | } 22 | 23 | return "\(self)" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Extensions/Foundation/String+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+Extension.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 27/06/23. 6 | // 7 | 8 | import Foundation 9 | 10 | extension String { 11 | 12 | func asDate() -> Date { 13 | let dateFormatter = DateFormatter() 14 | dateFormatter.dateFormat = "dd/MM/yyyy" 15 | return dateFormatter.date(from: self) ?? Date() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Extensions/UIKit/UIButton+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton+Extension.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 29/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIButton { 11 | 12 | func addSystemImage(imageName: String, _ pointSize: CGFloat = 27.5, state: UIControl.State = .normal) { 13 | let configuration = UIImage.SymbolConfiguration(pointSize: pointSize) 14 | let image = UIImage(systemName: imageName, withConfiguration: configuration) 15 | setImage(image, for: state) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Extensions/UIKit/UICollectionView+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UICollectionView+Extension.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 20/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UICollectionView { 11 | 12 | func register(cell: T.Type) { 13 | register(T.self, forCellWithReuseIdentifier: T.reuseIdentifier) 14 | } 15 | 16 | func register(header name: T.Type) { 17 | register(T.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: T.reuseIdentifier) 18 | } 19 | 20 | func register(footer name: T.Type) { 21 | register(T.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: T.reuseIdentifier) 22 | } 23 | 24 | func dequeueReusableCell(with type: T.Type, for indexPath: IndexPath) -> T { 25 | guard let cell = dequeueReusableCell(withReuseIdentifier: T.reuseIdentifier, for: indexPath) as? T else { 26 | fatalError( 27 | "Couldn't find UICollectionViewCell for \(String(describing: T.reuseIdentifier)), make sure the cell is registered with collection view") 28 | } 29 | return cell 30 | } 31 | 32 | func dequeueReusableViewHeader(with type: T.Type, for indexPath: IndexPath,ofKind kind: String = UICollectionView.elementKindSectionHeader) -> T { 33 | return dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: T.reuseIdentifier, for: indexPath) as! T 34 | } 35 | 36 | func dequeueReusableViewFooter(with type: T.Type, for indexPath: IndexPath,ofKind kind: String = UICollectionView.elementKindSectionFooter ) -> T { 37 | return dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: T.reuseIdentifier, for: indexPath) as! T 38 | } 39 | 40 | func hasItemAtIndexPath(_ indexPath: IndexPath) -> Bool { 41 | return indexPath.section < self.numberOfSections && indexPath.row < self.numberOfItems(inSection: indexPath.section) 42 | } 43 | } 44 | 45 | extension UICollectionReusableView { 46 | static var reuseIdentifier: String { 47 | return String(describing: Self.self) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Extensions/UIKit/UIColor+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Extension.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 27/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | infix operator |: AdditionPrecedence 11 | 12 | public extension UIColor { 13 | 14 | // MARK: - Utility Functions 15 | static func | (lightMode: UIColor, darkMode: UIColor) -> UIColor { 16 | guard #available(iOS 13.0, *) else { return lightMode } 17 | return UIColor { (traitCollection) -> UIColor in 18 | return traitCollection.userInterfaceStyle == .light ? lightMode : darkMode 19 | } 20 | } 21 | 22 | convenience init(r: Int, g: Int, b: Int) { 23 | assert(r >= 0 && r <= 255, "Invalid red component") 24 | assert(g >= 0 && g <= 255, "Invalid green component") 25 | assert(b >= 0 && b <= 255, "Invalid blue component") 26 | self.init(red: CGFloat(r) / 255.0, green: CGFloat(g) / 255.0, blue: CGFloat(b) / 255.0, alpha: 1.0) 27 | } 28 | 29 | class func colorWithHex(_ hex: String) -> UIColor { 30 | 31 | var colorString: String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() 32 | 33 | // Remove prefix if contain # at 0th position. 34 | if colorString.hasPrefix("#") { colorString.remove(at: colorString.startIndex) } 35 | if colorString.count != 6 { return UIColor.gray } 36 | 37 | var rgbValue: UInt64 = 0 38 | Scanner(string: colorString).scanHexInt64(&rgbValue) 39 | 40 | return UIColor ( 41 | red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, 42 | green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, 43 | blue: CGFloat(rgbValue & 0x0000FF) / 255.0, 44 | alpha: CGFloat(1.0) 45 | ) 46 | } 47 | 48 | static var random: UIColor { 49 | let max = CGFloat(UInt32.max) 50 | let red = CGFloat(arc4random()) / max 51 | let green = CGFloat(arc4random()) / max 52 | let blue = CGFloat(arc4random()) / max 53 | return UIColor(red: red, green: green, blue: blue, alpha: 1.0) 54 | } 55 | 56 | static func colorHex(color: UIColor) -> String { 57 | let components = color.cgColor.components 58 | let r: CGFloat = components?[0] ?? 0.0 59 | let g: CGFloat = components?[1] ?? 0.0 60 | let b: CGFloat = components?[2] ?? 0.0 61 | return String.init(format: "#%02lX%02lX%02lX", lroundf(Float(r * 255)), lroundf(Float(g * 255)), lroundf(Float(b * 255))) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Extensions/UIKit/UIImageView+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageView+Extension.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 26/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIImageView { 11 | 12 | convenience init(mode: UIView.ContentMode = .scaleAspectFit, clipsToBounds: Bool = true) { 13 | self.init(frame: .zero) 14 | self.contentMode = mode 15 | self.clipsToBounds = clipsToBounds 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Extensions/UIKit/UILabel+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UILabel+Extension.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 26/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UILabel { 11 | 12 | convenience init(font: UIFont, text: String? = nil, textColor: UIColor = .label, lines: Int = 1, alignment: NSTextAlignment = .left) { 13 | self.init(frame: .zero) 14 | self.font = font 15 | self.text = text 16 | self.textColor = textColor 17 | self.numberOfLines = lines 18 | self.textAlignment = alignment 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Extensions/UIKit/UIStackView+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIStackView+Extension.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 22/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIStackView { 11 | 12 | convenience init(arrangeSubViews: [UIView], axis: NSLayoutConstraint.Axis ,spacing: CGFloat, distribution: UIStackView.Distribution) { 13 | self.init(arrangedSubviews: arrangeSubViews) 14 | self.axis = axis 15 | self.spacing = spacing 16 | self.distribution = distribution 17 | translatesAutoresizingMaskIntoConstraints = false 18 | } 19 | 20 | func addBackgroundColor(_ color: UIColor) { 21 | let subView = UIView(frame: bounds) 22 | subView.backgroundColor = color 23 | subView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 24 | insertSubview(subView, at: 0) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Extensions/UIKit/UITableView+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+Extension.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 19/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UITableView { 11 | 12 | func register(cell name: T.Type) { 13 | register(T.self, forCellReuseIdentifier: String(describing: name)) 14 | } 15 | 16 | func dequeueReusableCell(withClass name: T.Type, for indexPath: IndexPath) -> T { 17 | guard let cell = dequeueReusableCell(withIdentifier: String(describing: name), for: indexPath) as? T else { 18 | fatalError( 19 | "Couldn't find UITableViewCell for \(String(describing: name)), make sure the cell is registered with table view") 20 | } 21 | return cell 22 | } 23 | 24 | func numberOfRows() -> Int { 25 | var section = 0 26 | var rowCount = 0 27 | while section < numberOfSections { 28 | rowCount += numberOfRows(inSection: section) 29 | section += 1 30 | } 31 | return rowCount 32 | } 33 | 34 | func safeScrollToTop(animated: Bool) { 35 | guard numberOfSections > 0, numberOfRows() > 0 else { return } 36 | scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: animated) 37 | } 38 | 39 | func indexPathForLastRow(inSection section: Int) -> IndexPath? { 40 | guard numberOfSections > 0, section >= 0 else { return nil } 41 | guard numberOfRows(inSection: section) > 0 else { 42 | return IndexPath(row: 0, section: section) 43 | } 44 | return IndexPath(row: numberOfRows(inSection: section) - 1, section: section) 45 | } 46 | 47 | var lastSection: Int? { 48 | return numberOfSections > 0 ? numberOfSections - 1 : nil 49 | } 50 | 51 | var indexPathForLastRow: IndexPath? { 52 | guard let lastSection = lastSection else { return nil } 53 | return indexPathForLastRow(inSection: lastSection) 54 | } 55 | 56 | func safeScrollToBottom(animated: Bool) { 57 | guard numberOfSections > 0, numberOfRows() > 0 else { return } 58 | scrollToRow(at: self.indexPathForLastRow!, at: .bottom, animated: animated) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Extensions/UIKit/UIView+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Extension.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 19/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | struct AnchoredConstraints { 11 | var top, leading, bottom, trailing, width, height: NSLayoutConstraint? 12 | } 13 | 14 | extension UIView { 15 | 16 | func addSubviews(_ views: UIView...) { 17 | views.forEach{addSubview($0)} 18 | } 19 | } 20 | 21 | extension UIView { 22 | 23 | @discardableResult 24 | func makeConstraints(top: NSLayoutYAxisAnchor?, 25 | leading: NSLayoutXAxisAnchor?, 26 | trailing: NSLayoutXAxisAnchor?, 27 | bottom: NSLayoutYAxisAnchor?, 28 | topMargin: CGFloat, 29 | leftMargin: CGFloat, 30 | rightMargin: CGFloat, 31 | bottomMargin: CGFloat, 32 | width: CGFloat, 33 | height: CGFloat) -> AnchoredConstraints? { 34 | 35 | self.translatesAutoresizingMaskIntoConstraints = false 36 | var anchoredConstraints = AnchoredConstraints() 37 | 38 | if let top = top { 39 | anchoredConstraints.top = topAnchor.constraint(equalTo: top, constant: topMargin) 40 | } 41 | 42 | if let left = leading { 43 | anchoredConstraints.leading = leadingAnchor.constraint(equalTo: left, constant: leftMargin) 44 | } 45 | 46 | if let right = trailing { 47 | anchoredConstraints.trailing = trailingAnchor.constraint(equalTo: right, constant: -rightMargin) 48 | } 49 | 50 | if let bottom = bottom { 51 | anchoredConstraints.bottom = bottomAnchor.constraint(equalTo: bottom, constant: -bottomMargin) 52 | } 53 | 54 | if width != 0 { 55 | anchoredConstraints.width = widthAnchor.constraint(equalToConstant: width) 56 | } 57 | 58 | if height != 0 { 59 | anchoredConstraints.height = heightAnchor.constraint(equalToConstant: height) 60 | } 61 | 62 | [anchoredConstraints.top, anchoredConstraints.leading, anchoredConstraints.bottom, anchoredConstraints.trailing, anchoredConstraints.width, anchoredConstraints.height].forEach{ $0?.isActive = true } 63 | 64 | return anchoredConstraints 65 | } 66 | 67 | func makeEdgeConstraints(toView parentView: UIView, edgeMargin: CGFloat = 0) { 68 | self.translatesAutoresizingMaskIntoConstraints = false 69 | self.topAnchor.constraint(equalTo: parentView.topAnchor, constant: edgeMargin).isActive = true 70 | self.leftAnchor.constraint(equalTo: parentView.leftAnchor, constant: edgeMargin).isActive = true 71 | self.rightAnchor.constraint(equalTo: parentView.rightAnchor, constant: -edgeMargin).isActive = true 72 | self.bottomAnchor.constraint(equalTo: parentView.bottomAnchor, constant: -edgeMargin).isActive = true 73 | } 74 | 75 | func makeCenterConstraints(toView parentView: UIView) { 76 | self.translatesAutoresizingMaskIntoConstraints = false 77 | self.centerXAnchor.constraint(equalTo: parentView.centerXAnchor).isActive = true 78 | self.centerYAnchor.constraint(equalTo: parentView.centerYAnchor).isActive = true 79 | } 80 | 81 | func makeConstraints(centerX: NSLayoutXAxisAnchor?, centerY: NSLayoutYAxisAnchor?) { 82 | self.translatesAutoresizingMaskIntoConstraints = false 83 | if let centerX = centerX { 84 | self.centerXAnchor.constraint(equalTo: centerX).isActive = true 85 | } 86 | 87 | if let centerY = centerY { 88 | self.centerYAnchor.constraint(equalTo: centerY).isActive = true 89 | } 90 | } 91 | 92 | func makeConstraints(width: CGFloat, height: CGFloat) { 93 | self.translatesAutoresizingMaskIntoConstraints = false 94 | if width != 0 { self.widthAnchor.constraint(equalToConstant: width).isActive = true } 95 | if height != 0 { self.heightAnchor.constraint(equalToConstant: height).isActive = true } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Managers/CommonUtility.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommonUtility.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 30/06/23. 6 | // 7 | 8 | import Foundation 9 | 10 | class CommonUtility { 11 | 12 | class func readJSON(forName name: String) -> Data? { 13 | do { 14 | if let bundlePath = Bundle.main.path(forResource: name, ofType: "json") { 15 | return try String(contentsOfFile: bundlePath).data(using: .utf8) 16 | } 17 | } catch { 18 | print(error) 19 | } 20 | return nil 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Modules/ChatUI/ChatHeaderDateLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChatHeaderDateLabel.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 27/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | class ChatHeaderDateLabel: UILabel { 11 | 12 | override init(frame: CGRect) { 13 | super.init(frame: frame) 14 | textColor = .secondaryLabel 15 | translatesAutoresizingMaskIntoConstraints = false 16 | textAlignment = .center 17 | font = .systemFont(ofSize: 16) 18 | backgroundColor = .tertiarySystemGroupedBackground 19 | } 20 | 21 | required init?(coder: NSCoder) { 22 | fatalError("init(coder:) has not been implemented") 23 | } 24 | 25 | override var intrinsicContentSize: CGSize { 26 | let originalContentSize = super.intrinsicContentSize 27 | let height = originalContentSize.height + 12 28 | layer.cornerRadius = height / 2 29 | layer.masksToBounds = true 30 | return CGSize(width: originalContentSize.width + 20, height: height) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Modules/ChatUI/ChatMessageDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChatMessageDataSource.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 27/06/23. 6 | // 7 | 8 | import Foundation 9 | 10 | class ChatMessageDataSource { 11 | 12 | private var messages = [ 13 | ChatMessage(text: "How are you, Maria?", isIncoming: false, date: "01/06/2023".asDate()), 14 | ChatMessage(text: "I am fine, what about you?", isIncoming: true, date: "01/06/2023".asDate()), 15 | ChatMessage(text: "I am also fine, how are your studies going on?", isIncoming: false, date: "1/06/2023".asDate()), 16 | ChatMessage(text: "My studies are going well. You know that due to the pandemic classes are being conducted online.", isIncoming: true, date: "1/06/2023".asDate()), 17 | ChatMessage(text: "Yes, I am also studying through online classes.", isIncoming: false, date: "1/06/2023".asDate()), 18 | ChatMessage(text: "Have you realised that the classes are more enticing with the new ways of teaching? Our teachers have also become more creative with their approaches.", isIncoming: true, date: "27/06/2023".asDate()), 19 | ChatMessage(text: "I have seen that too. These online classes are very good for all of us. We learn something new every day even though there’s a pandemic.", isIncoming: false, date: "27/06/2023".asDate()), 20 | ChatMessage(text: "But I have some problems too with online classes.", isIncoming: true, date: "09/06/2023".asDate()), 21 | ChatMessage(text: "What problems are you facing during online classes?", isIncoming: false, date: "28/06/2023".asDate()), 22 | ChatMessage(text: "Even though I enjoy online classes. But I lost that concern at which we study in our class.", isIncoming: true, date: "10/06/2023".asDate()), 23 | ChatMessage(text: "Yes you are right, Some students lose focus if the teacher doesn’t look after them.", isIncoming: false, date: "10/06/2023".asDate()), 24 | ChatMessage(text: "Sure no problem, goodbye 😍😎", isIncoming: false, date: "29/06/2023".asDate()), 25 | ChatMessage(text: "Goodbye, it was nice talking to you.", isIncoming: true, date: "29/06/2023".asDate()), 26 | ChatMessage(text: "Same here, see you soon !!", isIncoming: false, date: "29/06/2023".asDate()) 27 | ] 28 | 29 | var messageGroups: [[ChatMessage]] = [] 30 | 31 | 32 | // grouping our messages inside this Dictionary, and returning data - (by DATE ) 33 | func groupMessagesByDate() { 34 | 35 | let groupedMessages = Dictionary(grouping: messages) { element in 36 | element.date 37 | } 38 | 39 | // provide a sorting for your keys somehow 40 | let sortedKeys = groupedMessages.keys.sorted() 41 | sortedKeys.forEach { key in 42 | if let value = groupedMessages[key] { 43 | messageGroups.append(value) 44 | } 45 | } 46 | } 47 | } 48 | 49 | struct ChatMessage { 50 | let text: String 51 | let isIncoming: Bool 52 | let date: Date 53 | } 54 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Modules/ChatUI/ChatMessageTableCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChatMessageTableCell.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 27/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | class ChatMessageTableCell: BaseTableCell { 11 | 12 | private let messageLabel = UILabel(font: .systemFont(ofSize: 17), lines: 0) 13 | private let bubbleBackgroundView = UIView() 14 | private var leadingConstraint: NSLayoutConstraint! 15 | private var trailingConstraint: NSLayoutConstraint! 16 | private let textMargin: CGFloat = 12 17 | 18 | var chatMessage: ChatMessage? { 19 | didSet { 20 | guard let chatMessage = chatMessage else { return } 21 | bubbleBackgroundView.backgroundColor = chatMessage.isIncoming ? .ChatMessage.receivedMessage : .ChatMessage.sentMessage 22 | messageLabel.textColor = .label 23 | 24 | let paragraph = NSMutableParagraphStyle() 25 | paragraph.lineSpacing = 6 26 | let attributedString = NSAttributedString(string: chatMessage.text, 27 | attributes: [.paragraphStyle: paragraph]) 28 | messageLabel.attributedText = attributedString 29 | 30 | if chatMessage.isIncoming { 31 | leadingConstraint?.isActive = true 32 | trailingConstraint?.isActive = false 33 | } else { 34 | leadingConstraint?.isActive = false 35 | trailingConstraint?.isActive = true 36 | } 37 | } 38 | } 39 | 40 | override func initialSetup() { 41 | super.initialSetup() 42 | 43 | bubbleBackgroundView.layer.cornerRadius = 12 44 | bubbleBackgroundView.translatesAutoresizingMaskIntoConstraints = false 45 | messageLabel.translatesAutoresizingMaskIntoConstraints = false 46 | 47 | addSubviews(bubbleBackgroundView, messageLabel) 48 | 49 | NSLayoutConstraint.activate([ 50 | messageLabel.topAnchor.constraint(equalTo: topAnchor, constant: textMargin), 51 | messageLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -(2 * textMargin)), 52 | messageLabel.widthAnchor.constraint(lessThanOrEqualToConstant: 280), 53 | 54 | bubbleBackgroundView.topAnchor.constraint(equalTo: messageLabel.topAnchor, constant: -textMargin), 55 | bubbleBackgroundView.leadingAnchor.constraint(equalTo: messageLabel.leadingAnchor, constant: -textMargin), 56 | bubbleBackgroundView.trailingAnchor.constraint(equalTo: messageLabel.trailingAnchor, constant: textMargin), 57 | bubbleBackgroundView.bottomAnchor.constraint(equalTo: messageLabel.bottomAnchor, constant: textMargin), 58 | ]) 59 | 60 | leadingConstraint = messageLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 2 * textMargin) 61 | leadingConstraint.isActive = false 62 | 63 | trailingConstraint = messageLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -(2 * textMargin)) 64 | trailingConstraint.isActive = false 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Modules/ChatUI/ChatMessageViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChatMessageViewController.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 27/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIColor { 11 | 12 | struct ChatMessage { 13 | static let sentMessage = UIColor.colorWithHex("#E7FDD7") | UIColor.colorWithHex("#205046") 14 | static let receivedMessage = UIColor.colorWithHex("#FFFFFF") | UIColor.colorWithHex("#363537") 15 | static let textViewBackground = UIColor.colorWithHex("#FDFFFF") | UIColor.colorWithHex("#1D1D1E") 16 | } 17 | } 18 | 19 | class ChatMessageViewController: BaseViewController { 20 | 21 | // MARK: - Properties 22 | private lazy var tableView: UITableView = { 23 | let tableView = UITableView() 24 | tableView.showsVerticalScrollIndicator = false 25 | tableView.backgroundColor = .clear 26 | tableView.keyboardDismissMode = .interactive 27 | tableView.register(cell: ChatMessageTableCell.self) 28 | tableView.separatorStyle = .none 29 | tableView.delegate = self 30 | tableView.dataSource = self 31 | tableView.translatesAutoresizingMaskIntoConstraints = false 32 | // tableView.contentInset = .init(top: 20, left: 0, bottom: 20, right: 0) 33 | return tableView 34 | }() 35 | 36 | private lazy var sendButton: UIButton = { 37 | let button = UIButton(type: .custom) 38 | button.addTarget(self, action: #selector(handleSendMessageClicked), for: .touchUpInside) 39 | button.addSystemImage(imageName: "arrow.up.circle", state: .disabled) 40 | button.addSystemImage(imageName: "arrow.up.circle.fill") 41 | button.translatesAutoresizingMaskIntoConstraints = false 42 | button.tintColor = .tertiaryLabel 43 | button.isEnabled = false 44 | return button 45 | }() 46 | 47 | private lazy var addMediaButton: UIButton = { 48 | let button = UIButton(type: .custom) 49 | button.addTarget(self, action: #selector(handleAddMediaClicked), for: .touchUpInside) 50 | button.translatesAutoresizingMaskIntoConstraints = false 51 | button.addSystemImage(imageName: "plus.circle") 52 | return button 53 | }() 54 | 55 | private lazy var messageTextView: UITextView = { 56 | let textView = UITextView() 57 | textView.font = .systemFont(ofSize: 17, weight: .regular) 58 | textView.textColor = UIColor.label 59 | textView.showsVerticalScrollIndicator = false 60 | textView.dataDetectorTypes = [] 61 | textView.backgroundColor = .ChatMessage.textViewBackground 62 | textView.autocapitalizationType = .sentences 63 | textView.layer.cornerRadius = 21.0 64 | textView.layer.borderColor = UIColor.quaternaryLabel.cgColor 65 | textView.layer.borderWidth = 1.0 66 | textView.delegate = self 67 | textView.textContainerInset = .init(top: 10, left: 10, bottom: 6, right: 10) 68 | textView.adjustsFontForContentSizeCategory = true 69 | textView.isScrollEnabled = false 70 | textView.translatesAutoresizingMaskIntoConstraints = false 71 | textView.autocorrectionType = .no 72 | return textView 73 | }() 74 | 75 | private let messageInputView: UIView = { 76 | let view = UIView() 77 | view.backgroundColor = .tertiarySystemGroupedBackground 78 | view.translatesAutoresizingMaskIntoConstraints = false 79 | return view 80 | }() 81 | 82 | private var textViewAttributes = (minHeight: CGFloat(42), maxHeight: CGFloat(120)) 83 | private var inputViewHeight: NSLayoutConstraint! 84 | private var inputViewBottom: NSLayoutConstraint! 85 | private let dataSource = ChatMessageDataSource() 86 | 87 | private let messageInputViewBackgroundColor = UIColor.black.withAlphaComponent(0.2) 88 | 89 | 90 | // MARK: - View LifeCycle 91 | override func viewDidLoad() { 92 | super.viewDidLoad() 93 | 94 | addNotificationObservers() 95 | initialSetup() 96 | dataSource.groupMessagesByDate() 97 | updateTableView(true) 98 | } 99 | 100 | 101 | // MARK: - Private Methods 102 | private func initialSetup() { 103 | 104 | view.backgroundColor = .tertiarySystemGroupedBackground 105 | 106 | let backgroundImage = UIImageView(image: UIImage(named: "image_whats_app_bg")) 107 | backgroundImage.contentMode = .scaleAspectFill 108 | backgroundImage.clipsToBounds = true 109 | tableView.backgroundView = backgroundImage 110 | 111 | let navigationBarAppearance = UINavigationBarAppearance() 112 | navigationBarAppearance.configureWithDefaultBackground() 113 | navigationController?.navigationBar.tintColor = .label 114 | navigationItem.scrollEdgeAppearance = navigationBarAppearance 115 | navigationItem.standardAppearance = navigationBarAppearance 116 | navigationItem.compactAppearance = navigationBarAppearance 117 | navigationController?.setNeedsStatusBarAppearanceUpdate() 118 | 119 | 120 | view.addSubviews(messageInputView, tableView) 121 | 122 | messageInputView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true 123 | messageInputView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true 124 | inputViewHeight = messageInputView.heightAnchor.constraint(equalToConstant: textViewAttributes.minHeight + 20) 125 | inputViewBottom = messageInputView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor) 126 | 127 | inputViewHeight.isActive = true 128 | inputViewBottom.isActive = true 129 | 130 | tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true 131 | tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true 132 | tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true 133 | tableView.bottomAnchor.constraint(equalTo: messageInputView.topAnchor).isActive = true 134 | 135 | messageInputView.addSubviews(sendButton, addMediaButton, messageTextView) 136 | 137 | sendButton.heightAnchor.constraint(equalToConstant: 40).isActive = true 138 | sendButton.widthAnchor.constraint(equalToConstant: 40).isActive = true 139 | sendButton.bottomAnchor.constraint(equalTo: messageInputView.bottomAnchor, constant: -10).isActive = true 140 | sendButton.trailingAnchor.constraint(equalTo: messageInputView.trailingAnchor, constant: -10).isActive = true 141 | 142 | addMediaButton.heightAnchor.constraint(equalToConstant: 40).isActive = true 143 | addMediaButton.widthAnchor.constraint(equalToConstant: 40).isActive = true 144 | addMediaButton.bottomAnchor.constraint(equalTo: sendButton.bottomAnchor).isActive = true 145 | addMediaButton.leadingAnchor.constraint(equalTo: messageInputView.leadingAnchor, constant: 10).isActive = true 146 | 147 | messageTextView.leadingAnchor.constraint(equalTo: addMediaButton.trailingAnchor, constant: 10).isActive = true 148 | messageTextView.trailingAnchor.constraint(equalTo: sendButton.leadingAnchor, constant: -10).isActive = true 149 | messageTextView.topAnchor.constraint(equalTo: messageInputView.topAnchor, constant: 10).isActive = true 150 | messageTextView.bottomAnchor.constraint(equalTo: sendButton.bottomAnchor, constant: 0).isActive = true 151 | } 152 | 153 | private func addNotificationObservers() { 154 | NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil) 155 | NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil) 156 | } 157 | 158 | private func updateTableView(_ shouldScrollToBottom: Bool = false) { 159 | DispatchQueue.main.async { 160 | self.tableView.reloadData() 161 | if shouldScrollToBottom { 162 | self.tableView.safeScrollToBottom(animated: false) 163 | } 164 | } 165 | } 166 | 167 | 168 | // MARK: - Messages Methods 169 | @objc private func handleSendMessageClicked() { 170 | guard messageTextView.text.isEmpty == false else { return } 171 | sendButton.isEnabled = false 172 | self.sendTextMessage(messageTextView.text) 173 | } 174 | 175 | @objc private func handleAddMediaClicked() { 176 | // open photo gallery to pick media message. 177 | } 178 | 179 | private func sendTextMessage(_ message: String) { 180 | messageTextView.text.removeAll() 181 | resetMessageTextViewIfNeeded() 182 | } 183 | } 184 | 185 | extension ChatMessageViewController: UITableViewDelegate, UITableViewDataSource { 186 | 187 | // MARK: - UITableView Methods 188 | func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { 189 | if let firstMessageInSection = dataSource.messageGroups[section].first { 190 | let dateLabel = ChatHeaderDateLabel() 191 | dateLabel.text = firstMessageInSection.date.chatCenterDisplayTime() 192 | 193 | let containerView = UIView() 194 | containerView.addSubview(dateLabel) 195 | dateLabel.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true 196 | dateLabel.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true 197 | 198 | return containerView 199 | } 200 | return nil 201 | } 202 | 203 | func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { 204 | 50 205 | } 206 | 207 | func numberOfSections(in tableView: UITableView) -> Int { 208 | dataSource.messageGroups.count 209 | } 210 | 211 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 212 | dataSource.messageGroups[section].count 213 | } 214 | 215 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 216 | let cell = tableView.dequeueReusableCell(withClass: ChatMessageTableCell.self, for: indexPath) 217 | let chatMessage = dataSource.messageGroups[indexPath.section][indexPath.row] 218 | cell.chatMessage = chatMessage 219 | return cell 220 | } 221 | } 222 | 223 | extension ChatMessageViewController: UITextViewDelegate { 224 | 225 | func textViewDidChange(_ textView: UITextView) { 226 | sendButton.isEnabled = !textView.text.isEmpty 227 | sendButton.tintColor = sendButton.isEnabled ? .systemBlue : .tertiaryLabel 228 | 229 | let estimatedTextHeight = messageTextView.sizeThatFits(CGSize(width: messageTextView.frame.width, height: .infinity)).height 230 | let newHeight: CGFloat! 231 | if estimatedTextHeight <= textViewAttributes.minHeight { 232 | messageTextView.isScrollEnabled = false 233 | newHeight = textViewAttributes.minHeight + 20 234 | } else if estimatedTextHeight <= textViewAttributes.maxHeight { 235 | newHeight = estimatedTextHeight + 30 236 | } else { 237 | messageTextView.isScrollEnabled = true 238 | newHeight = textViewAttributes.maxHeight + 20 239 | } 240 | 241 | guard inputViewHeight.constant != newHeight else { return } 242 | animateTextView(newHeight: newHeight) 243 | } 244 | 245 | func textViewDidEndEditing(_ textView: UITextView) { 246 | resetMessageTextViewIfNeeded() 247 | } 248 | 249 | private func resetMessageTextViewIfNeeded() { 250 | guard messageTextView.text.isEmpty else { return } 251 | 252 | if inputViewHeight.constant != textViewAttributes.minHeight { 253 | animateTextView(newHeight: textViewAttributes.minHeight + 20) 254 | } 255 | } 256 | 257 | private func animateTextView(newHeight: CGFloat) { 258 | inputViewHeight.constant = newHeight 259 | UIView.animate(withDuration: 0.15, 260 | delay: 0, 261 | options: [.allowUserInteraction], 262 | animations: { [weak self] in 263 | self?.view.layoutIfNeeded() 264 | }, completion: nil) 265 | } 266 | 267 | @objc func keyboardWillShow(notification: NSNotification) { 268 | if let userInfo = notification.userInfo { 269 | let keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue 270 | inputViewBottom.constant = -(keyboardFrame.height - (self.tabBarController?.tabBar.frame.height ?? 0)) 271 | UIView.animate(withDuration: 0, 272 | delay: 0, 273 | options: .curveEaseOut, 274 | animations: { 275 | self.view.layoutIfNeeded() 276 | self.tableView.safeScrollToBottom(animated: true) 277 | }, completion: nil) 278 | } 279 | } 280 | 281 | @objc func keyboardWillHide(notification: NSNotification) { 282 | if let userInfo = notification.userInfo { 283 | let keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue 284 | let isKeyboardHiding = notification.name == UIResponder.keyboardDidHideNotification 285 | inputViewBottom.constant = isKeyboardHiding ? keyboardFrame.height - view.safeAreaInsets.bottom : 0 286 | 287 | UIView.animate(withDuration: 0, 288 | delay: 0, 289 | options: .curveEaseOut, 290 | animations: { 291 | self.view.layoutIfNeeded() 292 | }, completion: nil) 293 | } 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Modules/Common/BaseViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewController.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 19/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | class BaseViewController: UIViewController { 11 | 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | initialSetup() 15 | } 16 | 17 | private func initialSetup() { 18 | view.backgroundColor = .systemBackground 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Modules/Download Video/DownloadVideoViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DownloadVideoViewController.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 21/07/23. 6 | // 7 | 8 | import UIKit 9 | import Photos 10 | 11 | extension UIColor { 12 | static let pulsingBackground = UIColor(r: 21, g: 22, b: 33) 13 | static let outlineStroke = UIColor(r: 234, g: 46, b: 111) 14 | static let trackStroke = UIColor(r: 56, g: 25, b: 49) 15 | static let pulsingFill = UIColor(r: 86, g: 30, b: 63) 16 | } 17 | 18 | class DownloadVideoViewController: UIViewController { 19 | 20 | // MARK: - Properties 21 | private let fileUrlString = "https://storage.googleapis.com/gtv-videos-bucket/sample/TearsOfSteel.mp4" 22 | private let percentageLabel = UILabel(font: UIFont.boldSystemFont(ofSize: 25), text: "Download", textColor: .white, alignment: .center) 23 | private var isDownloading = false 24 | private var circleShapeLayer: CAShapeLayer! 25 | private var pulsingLayer: CAShapeLayer! 26 | 27 | 28 | // MARK: - LifeCycle 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | initialSetup() 32 | setupCircles() 33 | } 34 | 35 | override func viewWillAppear(_ animated: Bool) { 36 | super.viewWillAppear(animated) 37 | 38 | // Adding notification to continue the pulsing animation when app will come in foreground from background. 39 | setupNotification() 40 | } 41 | 42 | override func viewWillDisappear(_ animated: Bool) { 43 | super.viewWillDisappear(animated) 44 | NotificationCenter.default.removeObserver(self) 45 | } 46 | 47 | 48 | // MARK: - Private Methods 49 | private func setupNotification() { 50 | NotificationCenter.default.addObserver(self, 51 | selector: #selector(handleEnterForeground), 52 | name: UIApplication.willEnterForegroundNotification, 53 | object: nil) 54 | } 55 | 56 | @objc private func handleEnterForeground() { 57 | pulsingLayerAnimate() 58 | } 59 | 60 | private func initialSetup() { 61 | view.backgroundColor = UIColor.pulsingBackground 62 | } 63 | 64 | private func createCircleShapeLayer(strokeColor: UIColor, fillColor: UIColor) -> CAShapeLayer { 65 | // Return a shape layer in circle shape. 66 | let circlePath = UIBezierPath(arcCenter: .zero, 67 | radius: 100, 68 | startAngle: 0, 69 | endAngle: 2 * CGFloat.pi, 70 | clockwise: true) 71 | let layer = CAShapeLayer() 72 | layer.path = circlePath.cgPath 73 | layer.strokeColor = strokeColor.cgColor 74 | layer.lineWidth = 15.0 75 | layer.fillColor = fillColor.cgColor 76 | layer.lineCap = .round 77 | layer.position = view.center 78 | return layer 79 | } 80 | 81 | private func setupCircles() { 82 | 83 | // Setup pulsing animate layer 84 | pulsingLayer = createCircleShapeLayer(strokeColor: UIColor.clear, 85 | fillColor: UIColor.pulsingFill.withAlphaComponent(0.3)) 86 | view.layer.addSublayer(pulsingLayer) 87 | pulsingLayerAnimate() 88 | 89 | 90 | // Setup track layer 91 | let trackLayer = createCircleShapeLayer(strokeColor: UIColor.trackStroke, 92 | fillColor: UIColor.pulsingBackground) 93 | view.layer.addSublayer(trackLayer) 94 | 95 | 96 | // Setup circle outline layer 97 | circleShapeLayer = createCircleShapeLayer(strokeColor: UIColor.outlineStroke, 98 | fillColor: UIColor.clear) 99 | circleShapeLayer.transform = CATransform3DMakeRotation(-CGFloat.pi / 2, 0, 0, 1) 100 | circleShapeLayer.strokeEnd = 0.0 101 | view.layer.addSublayer(circleShapeLayer) 102 | 103 | view.addGestureRecognizer(UITapGestureRecognizer(target: self, 104 | action: #selector(handleTapGesture))) 105 | 106 | // Setup percentage label 107 | setupPercentageLabel() 108 | } 109 | 110 | private func setupPercentageLabel() { 111 | view.addSubview(percentageLabel) 112 | percentageLabel.makeCenterConstraints(toView: self.view) 113 | } 114 | 115 | @objc private func handleTapGesture() { 116 | if isDownloading == false { 117 | beginDownloadFile() 118 | } 119 | } 120 | 121 | private func beginDownloadFile() { 122 | circleShapeLayer.strokeEnd = 0 123 | percentageLabel.text = "Starting..." 124 | 125 | let configuration = URLSessionConfiguration.default 126 | let urlSession = URLSession(configuration: configuration, 127 | delegate: self, 128 | delegateQueue: nil) 129 | 130 | if let url = URL(string: fileUrlString) { 131 | let downloadTask = urlSession.downloadTask(with: url) 132 | downloadTask.resume() 133 | self.isDownloading = true 134 | } 135 | } 136 | 137 | 138 | // MARK: - Animations 139 | private func pulsingLayerAnimate() { 140 | let pulseAnimation = CABasicAnimation(keyPath: "transform.scale") 141 | pulseAnimation.toValue = 1.4 142 | pulseAnimation.duration = 0.8 143 | pulseAnimation.autoreverses = true 144 | pulseAnimation.repeatCount = Float.infinity 145 | pulsingLayer.add(pulseAnimation, forKey: "pulsing") 146 | } 147 | 148 | private func drawCircleWithAnimation() { 149 | let animation = CABasicAnimation(keyPath: "strokeEnd") 150 | animation.toValue = 1 151 | animation.duration = 3.0 152 | animation.isRemovedOnCompletion = false 153 | animation.fillMode = .forwards 154 | circleShapeLayer.add(animation, forKey: "pulsingAnimation") 155 | } 156 | 157 | 158 | // MARK - Save Video 159 | private func saveVideo(from url: URL) { 160 | PHPhotoLibrary.shared().performChanges({ 161 | PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: url) 162 | }) { (success, error) in 163 | if success { 164 | self.showAlert(with: "Video saved to photo gallery successfully!") 165 | } else { 166 | self.showAlert(with: error?.localizedDescription ?? "Something is wrong") 167 | } 168 | } 169 | } 170 | 171 | private func showAlert(with message: String) { 172 | DispatchQueue.main.async { 173 | let alert = UIAlertController(title: "Status", message: message, preferredStyle: .alert) 174 | alert.addAction(UIAlertAction(title: "Okay", style: .default)) 175 | self.present(alert, animated: true) 176 | } 177 | } 178 | } 179 | 180 | extension DownloadVideoViewController: URLSessionDownloadDelegate { 181 | 182 | func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { 183 | let progress = CGFloat(totalBytesWritten) / CGFloat(totalBytesExpectedToWrite) 184 | let percentage = Int(progress * 100) 185 | DispatchQueue.main.async { 186 | self.percentageLabel.text = "\(percentage)%" 187 | self.circleShapeLayer.strokeEnd = progress 188 | 189 | if percentage == 100 { 190 | self.percentageLabel.text = "Downloaded" 191 | } 192 | } 193 | } 194 | 195 | func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { 196 | isDownloading = false 197 | 198 | guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return } 199 | let destinationURL = documentsDirectory.appendingPathComponent(downloadTask.originalRequest?.url?.lastPathComponent ?? "") 200 | try? FileManager.default.moveItem(at: location, to: destinationURL) 201 | saveVideo(from: destinationURL) 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Modules/GitHub Followers/GitHubFollowersController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GitHubFollowersController.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 19/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | class GitHubFollowersController: BaseViewController { 11 | 12 | // MARK: - Properties 13 | private lazy var collectionView: UICollectionView = { 14 | let layout = UICollectionViewFlowLayout() 15 | layout.scrollDirection = .vertical 16 | layout.minimumLineSpacing = gridSpacing 17 | layout.minimumInteritemSpacing = gridSpacing 18 | let width = floor((view.frame.width - gridSpacing * (numberOfItemsInRow + 1)) / numberOfItemsInRow) 19 | layout.itemSize = CGSize(width: width, height: width + 40) // adding margin to give extra height 20 | layout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) 21 | let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) 22 | collectionView.register(cell: GitHubProfileCollectionCell.self) 23 | collectionView.delegate = self 24 | collectionView.dataSource = self 25 | collectionView.showsVerticalScrollIndicator = false 26 | collectionView.backgroundColor = .clear 27 | return collectionView 28 | }() 29 | 30 | private let gridSpacing: CGFloat = 10 31 | private let numberOfItemsInRow: CGFloat = 3 32 | private let dataSource = GitHubFollowersDataSource() 33 | 34 | 35 | // MARK: - LifeCycle 36 | override func viewDidLoad() { 37 | super.viewDidLoad() 38 | initialSetup() 39 | } 40 | 41 | 42 | // MARK: - Private Methods 43 | private func initialSetup() { 44 | view.addSubview(collectionView) 45 | collectionView.makeEdgeConstraints(toView: view) 46 | } 47 | } 48 | 49 | extension GitHubFollowersController: UICollectionViewDelegate, UICollectionViewDataSource { 50 | 51 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 52 | dataSource.users.count 53 | } 54 | 55 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 56 | let cell = collectionView.dequeueReusableCell(with: GitHubProfileCollectionCell.self, for: indexPath) 57 | cell.configure(dataSource.users[indexPath.item]) 58 | return cell 59 | } 60 | 61 | func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { 62 | 63 | UIContextMenuConfiguration(identifier: nil, 64 | previewProvider: { self.makePreview(for: indexPath.item) }) { _ in 65 | 66 | let repositoriesAction = UIAction(title: "Repositories", image: UIImage(systemName: "filemenu.and.cursorarrow")) { _ in 67 | // handle click action here... 68 | } 69 | 70 | let starAction = UIAction(title: "Stars", image: UIImage(systemName: "star")) { _ in 71 | // handle click action here... 72 | } 73 | 74 | let achievementAction = UIAction(title: "Achievements", image: UIImage(systemName: "trophy")) { _ in 75 | // handle click action here... 76 | } 77 | 78 | let unfollowAction = UIAction(title: "Unfollow", image: UIImage(systemName: "xmark.bin.fill"), attributes: .destructive) { _ in 79 | // handle click action here... 80 | } 81 | 82 | 83 | let shareAction = UIAction(title: "Share Profile", image: UIImage(systemName: "square.and.arrow.up")) { _ in 84 | // handle click action here... 85 | } 86 | 87 | let copyAction = UIAction(title: "Copy URL", image: UIImage(systemName: "doc.on.doc")) { _ in 88 | // handle click action here... 89 | } 90 | 91 | // submenu 92 | let moreMenu = UIMenu(title: "More...", children: [shareAction, copyAction]) 93 | 94 | return UIMenu(title: "", image: nil, children: [repositoriesAction, starAction, achievementAction, unfollowAction, moreMenu]) 95 | } 96 | } 97 | 98 | private func makePreview(for index: Int) -> UIViewController { 99 | let profilePreviewController = GitHubProfilePreviewController() 100 | profilePreviewController.configure(with: dataSource.users[index]) 101 | 102 | // you can give size according to design's requirement. 103 | let preferredWidth = view.frame.width * 0.7 104 | profilePreviewController.preferredContentSize = .init(width: preferredWidth, height: preferredWidth + 100) // adding 100 for names and follow status views 105 | return profilePreviewController 106 | } 107 | 108 | func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) { 109 | 110 | // When the preview will be clicked, you can handle the click action here... 111 | 112 | animator.addCompletion { 113 | // you can perform an action on completion of animation after clicking the preview. 114 | } 115 | 116 | /* 117 | guard let identifier = configuration.identifier as? String else { return } 118 | print("Identifier: \(identifier)") 119 | 120 | // You can identify the cell for which preview has clicked by assigning a unique identifier while configuring UIContextMenuConfiguration in above method. For example, you can pass the indexPath (in string format) as an identifier. 121 | */ 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Modules/GitHub Followers/GitHubFollowersDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GitHubFollowersDataSource.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 20/06/23. 6 | // 7 | 8 | import Foundation 9 | 10 | class GitHubFollowersDataSource { 11 | 12 | private(set) var users: [GitHubUser] = [] 13 | 14 | init() { 15 | users.append(contentsOf: [ 16 | GitHubUser(profileImage: "profile_1", username: "alina", name: "Alina", followers: 34, following: 1200), 17 | GitHubUser(profileImage: "profile_2", username: "john", name: "Johny", followers: 34, following: 6507), 18 | GitHubUser(profileImage: "profile_3", username: "ritaahh", name: "Rita", followers: 34, following: 657), 19 | GitHubUser(profileImage: "profile_4", username: "tinamartin", name: "Tina", followers: 6784, following: 657), 20 | GitHubUser(profileImage: "profile_5", username: "milly", name: "Milly", followers: 304, following: 657), 21 | GitHubUser(profileImage: "profile_6", username: "Garcia", name: "Garcia", followers: 34, following: 657), 22 | GitHubUser(profileImage: "profile_7", username: "Smily", name: "Smily", followers: 34, following: 657), 23 | GitHubUser(profileImage: "profile_8", username: "Brown", name: "Brown", followers: 34, following: 657), 24 | GitHubUser(profileImage: "profile_9", username: "Catie", name: "Catie", followers: 34, following: 657), 25 | GitHubUser(profileImage: "profile_10", username: "Roney", name: "Roney", followers: 34, following: 657), 26 | GitHubUser(profileImage: "profile_11", username: "Michael", name: "Michael", followers: 34, following: 657), 27 | GitHubUser(profileImage: "profile_12", username: "William", name: "William", followers: 34, following: 657), 28 | GitHubUser(profileImage: "20w", username: "alina", name: "alina", followers: 34, following: 657), 29 | GitHubUser(profileImage: "21w", username: "Barbara", name: "Barbara", followers: 34, following: 657), 30 | GitHubUser(profileImage: "22w", username: "ritaahh", name: "rita", followers: 34, following: 657), 31 | GitHubUser(profileImage: "33w", username: "tinamartin", name: "tina r", followers: 34, following: 657), 32 | GitHubUser(profileImage: "34m", username: "Miller", name: "Miller", followers: 34, following: 657), 33 | GitHubUser(profileImage: "39w", username: "Garcia", name: "Garcia", followers: 34, following: 657), 34 | GitHubUser(profileImage: "32m", username: "Smith", name: "Smith", followers: 34, following: 657), 35 | GitHubUser(profileImage: "48m", username: "Brown", name: "Brown", followers: 34, following: 657), 36 | GitHubUser(profileImage: "36m", username: "Williams", name: "Williams", followers: 34, following: 657), 37 | GitHubUser(profileImage: "30w", username: "Barbara", name: "Barbara", followers: 34, following: 657), 38 | GitHubUser(profileImage: "28m", username: "Michael", name: "Michael", followers: 34, following: 657), 39 | GitHubUser(profileImage: "14m", username: "William", name: "William", followers: 34, following: 657) 40 | ]) 41 | } 42 | } 43 | 44 | struct GitHubUser { 45 | let profileImage: String 46 | let username: String 47 | let name: String 48 | let followers: Int 49 | let following: Int 50 | } 51 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Modules/GitHub Followers/GitHubProfileCollectionCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GitHubProfileCollectionCell.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 20/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | class GitHubProfileCollectionCell: BaseCollectionCell { 11 | 12 | private let profileImageView: UIImageView = { 13 | UIImageView(mode: .scaleAspectFill) 14 | }() 15 | 16 | private let usernameLabel: UILabel = { 17 | UILabel(font: .systemFont(ofSize: 16, weight: .semibold), alignment: .center) 18 | }() 19 | 20 | override func initialSetup() { 21 | super.initialSetup() 22 | 23 | backgroundColor = UIColor.tertiarySystemGroupedBackground 24 | layer.cornerRadius = 8 25 | layer.masksToBounds = true 26 | 27 | addSubviews(profileImageView, usernameLabel) 28 | 29 | profileImageView.makeConstraints(width: frame.width, height: frame.width) 30 | profileImageView.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true 31 | profileImageView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true 32 | 33 | usernameLabel.makeConstraints(top: profileImageView.bottomAnchor, leading: leadingAnchor, trailing: trailingAnchor, bottom: nil, topMargin: 10, leftMargin: 8, rightMargin: 8, bottomMargin: 0, width: 0, height: 0) 34 | } 35 | 36 | func configure(_ user: GitHubUser) { 37 | profileImageView.image = UIImage(named: user.profileImage) 38 | usernameLabel.text = "@" + user.username.lowercased() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Modules/GitHub Followers/GitHubProfilePreviewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GitHubProfilePreviewController.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 21/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | class GitHubProfilePreviewController: BaseViewController { 11 | 12 | // MARK: - Properties 13 | private let profileImageView: UIImageView = { 14 | UIImageView(mode: .scaleAspectFill) 15 | }() 16 | 17 | private let fullNameLabel: UILabel = { 18 | UILabel(font: .systemFont(ofSize: 20, weight: .semibold)) 19 | }() 20 | 21 | private let usernameLabel: UILabel = { 22 | UILabel(font: .systemFont(ofSize: 16, weight: .medium), textColor: .secondaryLabel) 23 | }() 24 | 25 | private let followersLabel: UILabel = { 26 | let label = UILabel(font: .systemFont(ofSize: 14, weight: .semibold), textColor: .secondaryLabel, alignment: .center) 27 | label.backgroundColor = .tertiaryLabel.withAlphaComponent(0.1) 28 | label.layer.cornerRadius = 8 29 | label.layer.masksToBounds = true 30 | return label 31 | }() 32 | 33 | private let followingLabel: UILabel = { 34 | let label = UILabel(font: .systemFont(ofSize: 14, weight: .semibold), textColor: .secondaryLabel, alignment: .center) 35 | label.backgroundColor = .tertiaryLabel.withAlphaComponent(0.1) 36 | label.layer.cornerRadius = 8 37 | label.layer.masksToBounds = true 38 | return label 39 | }() 40 | 41 | 42 | // MARK: - LifeCycle 43 | override func viewDidLoad() { 44 | super.viewDidLoad() 45 | initialSetup() 46 | } 47 | 48 | 49 | // MARK: - Private Methods 50 | private func initialSetup() { 51 | 52 | let followStatusStackView = UIStackView(arrangeSubViews: [followersLabel, followingLabel], 53 | axis: .horizontal, 54 | spacing: 12, 55 | distribution: .fillEqually) 56 | followStatusStackView.alignment = .fill 57 | 58 | view.addSubviews(followStatusStackView, fullNameLabel, usernameLabel, profileImageView) 59 | 60 | followStatusStackView.makeConstraints(top: nil, leading: view.leadingAnchor, trailing: view.trailingAnchor, bottom: view.safeAreaLayoutGuide.bottomAnchor, topMargin: 0, leftMargin: 12, rightMargin: 12, bottomMargin: 10, width: 0, height: 40) 61 | 62 | fullNameLabel.makeConstraints(top: nil, leading: view.leadingAnchor, trailing: nil, bottom: followStatusStackView.topAnchor, topMargin: 0, leftMargin: 12, rightMargin: 0, bottomMargin: 0, width: 0, height: 50) 63 | 64 | usernameLabel.makeConstraints(centerX: nil, centerY: fullNameLabel.centerYAnchor) 65 | usernameLabel.leftAnchor.constraint(equalTo: fullNameLabel.rightAnchor, constant: 5).isActive = true 66 | 67 | profileImageView.makeConstraints(top: view.safeAreaLayoutGuide.topAnchor, leading: view.leadingAnchor, trailing: view.trailingAnchor, bottom: fullNameLabel.topAnchor, topMargin: 0, leftMargin: 0, rightMargin: 0, bottomMargin: 0, width: 0, height: 0) 68 | } 69 | 70 | func configure(with user: GitHubUser) { 71 | profileImageView.image = UIImage(named: user.profileImage) 72 | usernameLabel.text = "@" + user.username.lowercased() 73 | fullNameLabel.text = user.name.capitalized 74 | 75 | let followersAttributedString = NSMutableAttributedString(string: "Followers ") 76 | followersAttributedString.append(NSAttributedString(string: user.followers.formatWithAbbreviations, attributes: [.foregroundColor: UIColor.label])) 77 | followersLabel.attributedText = followersAttributedString 78 | 79 | let followingAttributedString = NSMutableAttributedString(string: "Following ") 80 | followingAttributedString.append(NSAttributedString(string: user.following.formatWithAbbreviations, attributes: [.foregroundColor: UIColor.label])) 81 | followingLabel.attributedText = followingAttributedString 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Modules/Home/HomeControllerDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeControllerDataSource.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 19/06/23. 6 | // 7 | 8 | import Foundation 9 | 10 | class HomeControllerDataSource { 11 | 12 | private(set) var items: [ListItem] = [] 13 | 14 | init() { 15 | guard let homeListData = CommonUtility.readJSON(forName: "home_list") else { return } 16 | do { 17 | self.items = try JSONDecoder().decode([ListItem].self, from: homeListData) 18 | } catch { 19 | print("Something is wrong while decoding home list data.") 20 | } 21 | } 22 | } 23 | 24 | struct ListItem: Codable { 25 | 26 | let title: String 27 | let description: String 28 | let slug: String 29 | let navigationTitle: String 30 | 31 | var navigationDisplayTitle: String { 32 | "#" + navigationTitle 33 | } 34 | 35 | func listTitle(for index: Int) -> String { 36 | "TitBit \(index + 1) - " + slug 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Modules/Home/HomeListItemTableCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeListItemTableCell.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 19/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | class HomeListItemTableCell: BaseTableCell { 11 | 12 | private let slugLabel: UILabel = { 13 | UILabel(font: .systemFont(ofSize: 20, weight: .semibold), textColor: .systemBlue) 14 | }() 15 | 16 | private let titleLabel: UILabel = { 17 | UILabel(font: .systemFont(ofSize: 18, weight: .semibold)) 18 | }() 19 | 20 | private let descriptionLabel: UILabel = { 21 | UILabel(font: .systemFont(ofSize: 18), textColor: .secondaryLabel, lines: 0) 22 | }() 23 | 24 | override func initialSetup() { 25 | super.initialSetup() 26 | 27 | let contentStackView = UIStackView(arrangeSubViews: [slugLabel, titleLabel, descriptionLabel], 28 | axis: .vertical, 29 | spacing: 8, 30 | distribution: .fill) 31 | contentStackView.alignment = .top 32 | contentStackView.layer.cornerRadius = 10 33 | contentStackView.layer.masksToBounds = true 34 | contentStackView.addBackgroundColor(.tertiarySystemGroupedBackground) 35 | contentStackView.layoutMargins = .init(top: 10, left: 12, bottom: 10, right: 12) 36 | contentStackView.isLayoutMarginsRelativeArrangement = true 37 | 38 | addSubview(contentStackView) 39 | 40 | contentStackView.makeConstraints(top: topAnchor, leading: leadingAnchor, trailing: trailingAnchor, bottom: bottomAnchor, topMargin: 10, leftMargin: 15, rightMargin: 15, bottomMargin: 10, width: 0, height: 0) 41 | } 42 | 43 | func configure(_ item: ListItem, indexPath: IndexPath) { 44 | slugLabel.text = item.listTitle(for: indexPath.row) 45 | titleLabel.text = item.title 46 | descriptionLabel.text = item.description 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Modules/Home/HomeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeViewController.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 19/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | class HomeViewController: BaseViewController { 11 | 12 | // MARK: - Properties 13 | private lazy var tableView: UITableView = { 14 | let tableView = UITableView() 15 | tableView.backgroundColor = .clear 16 | tableView.delegate = self 17 | tableView.dataSource = self 18 | tableView.estimatedRowHeight = UITableView.automaticDimension 19 | tableView.register(cell: HomeListItemTableCell.self) 20 | tableView.separatorStyle = .none 21 | return tableView 22 | }() 23 | 24 | private let dataSource = HomeControllerDataSource() 25 | 26 | 27 | // MARK: - LifeCycle 28 | override func viewDidLoad() { 29 | super.viewDidLoad() 30 | initialSetup() 31 | } 32 | 33 | 34 | // MARK: - Private Methods 35 | private func initialSetup() { 36 | navigationItem.title = "#ExploringUIKit" 37 | view.addSubview(tableView) 38 | tableView.makeEdgeConstraints(toView: view) 39 | } 40 | } 41 | 42 | extension HomeViewController: UITableViewDelegate, UITableViewDataSource { 43 | 44 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 45 | dataSource.items.count 46 | } 47 | 48 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 49 | let cell = tableView.dequeueReusableCell(withClass: HomeListItemTableCell.self, for: indexPath) 50 | cell.configure(dataSource.items[indexPath.row], indexPath: indexPath) 51 | return cell 52 | } 53 | 54 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 55 | 56 | let selectedItem = dataSource.items[indexPath.row] 57 | var controller: UIViewController? 58 | 59 | switch indexPath.row { 60 | case 0: controller = GitHubFollowersController() 61 | case 1: controller = ChatMessageViewController() 62 | case 2: controller = PhotosGridViewController() 63 | case 3: controller = DownloadVideoViewController() 64 | default: break 65 | } 66 | 67 | guard let targetController = controller else { return } 68 | targetController.navigationItem.title = selectedItem.navigationDisplayTitle 69 | navigationController?.pushViewController(targetController, animated: true) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Modules/Home/home_list.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "UICollectionView with UIContextMenu", 4 | "description": "Explore about UIContextMenu by configuring actions, providing custom previews, sub-menus, and handling preview interactions.", 5 | "slug": "GitHub Followers", 6 | "navigationTitle": "GithubFollowers" 7 | }, 8 | { 9 | "title": "Chat Messages using UITableView", 10 | "description": "Explore about to create UI for chat messages using single cell and message input view programatically.", 11 | "slug": "Chat Messages", 12 | "navigationTitle": "ChatMessages" 13 | }, 14 | { 15 | "title": "Photo Gallery with Zoom in/out", 16 | "description": "Explore how to create photo grid view using collection view and show photo gallery with selected photo index.", 17 | "slug": "Photo Gallery", 18 | "navigationTitle": "PhotoGallery" 19 | }, 20 | { 21 | "title": "Download Video with Progress", 22 | "description": "Explore how to download a video file from a URL and show progress in percentage with animation. After downloaded, save it to the photo gallery.", 23 | "slug": "Download Progress", 24 | "navigationTitle": "DownloadProgress" 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Modules/Photo Gallery/Photo Gallery/PhotoGalleryViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhotoGalleryViewController.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 01/07/23. 6 | // 7 | 8 | import UIKit 9 | 10 | class PhotoGalleryViewController: BaseViewController { 11 | 12 | // MARK: - Properties 13 | private lazy var collectionView: UICollectionView = { 14 | let layout = UICollectionViewFlowLayout() 15 | layout.minimumLineSpacing = 0 16 | layout.minimumInteritemSpacing = 0 17 | layout.scrollDirection = .horizontal 18 | let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) 19 | collectionView.register(cell: PhotoItemCollectionCell.self) 20 | collectionView.delegate = self 21 | collectionView.dataSource = self 22 | collectionView.backgroundColor = .clear 23 | collectionView.showsHorizontalScrollIndicator = false 24 | collectionView.isPagingEnabled = true 25 | return collectionView 26 | }() 27 | 28 | private lazy var closeButton: UIButton = { 29 | let button = UIButton(type: .custom) 30 | button.addSystemImage(imageName: "xmark.circle.fill", 25) 31 | button.tintColor = .white 32 | button.addTarget(self, action: #selector(handleCloseTapped), for: .touchUpInside) 33 | return button 34 | }() 35 | 36 | private var photos: [PhotoItem] 37 | private var selectedIndex: Int = 0 38 | 39 | 40 | // MARK: - LifeCycle 41 | init(photos: [PhotoItem], selectedIndex: Int) { 42 | self.photos = photos 43 | self.selectedIndex = selectedIndex 44 | super.init(nibName: nil, bundle: nil) 45 | } 46 | 47 | required init?(coder: NSCoder) { 48 | fatalError("init(coder:) has not been implemented") 49 | } 50 | 51 | override func viewDidLoad() { 52 | super.viewDidLoad() 53 | initialSetup() 54 | } 55 | 56 | override func viewWillAppear(_ animated: Bool) { 57 | super.viewWillAppear(animated) 58 | 59 | // moving to selected index 60 | DispatchQueue.main.async { 61 | self.moveToIndexPath(IndexPath(item: self.selectedIndex, section: 0), animated: false) 62 | } 63 | } 64 | 65 | 66 | // MARK: - Private Methods 67 | private func initialSetup() { 68 | 69 | view.backgroundColor = .black 70 | 71 | view.addSubviews(collectionView, closeButton) 72 | 73 | collectionView.makeConstraints(top: view.topAnchor, leading: view.leadingAnchor, trailing: view.trailingAnchor, bottom: view.bottomAnchor, topMargin: 0, leftMargin: 0, rightMargin: 0, bottomMargin: 0, width: 0, height: 0) 74 | 75 | closeButton.makeConstraints(top: view.safeAreaLayoutGuide.topAnchor, leading: nil, trailing: view.trailingAnchor, bottom: nil, topMargin: 0, leftMargin: 0, rightMargin: 4, bottomMargin: 0, width: 50, height: 50) 76 | } 77 | 78 | private func moveToIndexPath(_ indexPath: IndexPath, animated: Bool = true) { 79 | if collectionView.hasItemAtIndexPath(indexPath) { 80 | collectionView.selectItem(at: indexPath, animated: animated, scrollPosition: .centeredHorizontally) 81 | } 82 | } 83 | 84 | @objc private func handleCloseTapped() { 85 | dismiss(animated: true) 86 | } 87 | } 88 | 89 | extension PhotoGalleryViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { 90 | 91 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 92 | photos.count 93 | } 94 | 95 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 96 | let cell = collectionView.dequeueReusableCell(with: PhotoItemCollectionCell.self, for: indexPath) 97 | cell.configure(photos[indexPath.item]) 98 | return cell 99 | } 100 | 101 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 102 | return collectionView.frame.size 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Modules/Photo Gallery/Photo Gallery/PhotoItemCollectionCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhotoItemCollectionCell.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 01/07/23. 6 | // 7 | 8 | import UIKit 9 | 10 | class PhotoItemCollectionCell: BaseCollectionCell, UIScrollViewDelegate, UIGestureRecognizerDelegate { 11 | 12 | // MARK: - Properties 13 | private lazy var zoomScrollView: UIScrollView = { 14 | let scrollView = UIScrollView(frame: .zero) 15 | scrollView.minimumZoomScale = 1.0 16 | scrollView.maximumZoomScale = 3.0 17 | scrollView.zoomScale = 1.0 18 | scrollView.delegate = self 19 | return scrollView 20 | }() 21 | 22 | private let previewImageView: UIImageView = { 23 | let imageView = UIImageView(frame: .zero) 24 | imageView.backgroundColor = .tertiaryLabel 25 | imageView.clipsToBounds = true 26 | imageView.isUserInteractionEnabled = true 27 | imageView.contentMode = .scaleAspectFit 28 | return imageView 29 | }() 30 | 31 | 32 | //MARK: - Initial Setup 33 | override func initialSetup() { 34 | super.initialSetup() 35 | 36 | contentView.addSubviews(zoomScrollView) 37 | zoomScrollView.makeEdgeConstraints(toView: contentView) 38 | 39 | zoomScrollView.addSubview(previewImageView) 40 | previewImageView.makeEdgeConstraints(toView: zoomScrollView) 41 | previewImageView.makeCenterConstraints(toView: zoomScrollView) 42 | 43 | let doubleTapGesture = UITapGestureRecognizer(target: self, action: #selector(doubleTapAction)) 44 | doubleTapGesture.numberOfTapsRequired = 2 45 | previewImageView.addGestureRecognizer(doubleTapGesture) 46 | } 47 | 48 | func configure(_ photoItem: PhotoItem) { 49 | guard let image = UIImage(named: photoItem.imageName) else { return } 50 | previewImageView.image = image 51 | previewImageView.backgroundColor = .clear 52 | } 53 | 54 | override func prepareForReuse() { 55 | super.prepareForReuse() 56 | previewImageView.image = nil 57 | zoomScrollView.setZoomScale(1.0, animated: false) 58 | } 59 | 60 | // MARK: - Other Methods 61 | func viewForZooming(in scrollView: UIScrollView) -> UIView? { 62 | previewImageView 63 | } 64 | 65 | @objc private func doubleTapAction(gesture: UITapGestureRecognizer) { 66 | if gesture.state == .ended { 67 | if zoomScrollView.zoomScale == 1 { 68 | zoomScrollView.setZoomScale(2.5, animated: true) 69 | } else { 70 | zoomScrollView.setZoomScale(1, animated: true) 71 | } 72 | } 73 | } 74 | 75 | func scrollViewDidZoom(_ scrollView: UIScrollView) { 76 | if scrollView.zoomScale > 1 { 77 | 78 | guard let image = previewImageView.image else { 79 | zoomScrollView.contentInset = .zero 80 | return 81 | } 82 | 83 | // width & height ratio 84 | let widthRatio = previewImageView.frame.width / image.size.width 85 | let heightRatio = previewImageView.frame.height / image.size.height 86 | 87 | // calculate new width and height from min ratio value 88 | let ratio = min(widthRatio, heightRatio) 89 | let newWidth = image.size.width * ratio 90 | let newHeight = image.size.height * ratio 91 | 92 | // calculate left and right edge inset value 93 | let leftEdgeExpression = newWidth * scrollView.zoomScale > previewImageView.frame.width 94 | let leftEdgeValue = 0.5 * (leftEdgeExpression ? newWidth - previewImageView.frame.width : (scrollView.frame.width - scrollView.contentSize.width)) 95 | 96 | // calculate top and bottom edge inset value 97 | let topEdgeExpression = newHeight * scrollView.zoomScale > previewImageView.frame.height 98 | let topEdgeValue = 0.5 * (topEdgeExpression ? newHeight - previewImageView.frame.height : (scrollView.frame.height - scrollView.contentSize.height)) 99 | 100 | // fixing the content inset 101 | zoomScrollView.contentInset = UIEdgeInsets(top: topEdgeValue, left: leftEdgeValue, bottom: topEdgeValue, right: leftEdgeValue) 102 | } else { 103 | zoomScrollView.contentInset = .zero 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Modules/Photo Gallery/Photo Grid/PhotoGridCollectionCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhotoGridCollectionCell.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 30/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | class PhotoGridCollectionCell: BaseCollectionCell { 11 | 12 | private let imageView: UIImageView = { 13 | UIImageView(mode: .scaleAspectFill) 14 | }() 15 | 16 | override func initialSetup() { 17 | super.initialSetup() 18 | addSubview(imageView) 19 | imageView.makeEdgeConstraints(toView: self) 20 | } 21 | 22 | func configure(_ photo: PhotoItem) { 23 | imageView.image = UIImage(named: photo.imageName) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Modules/Photo Gallery/Photo Grid/PhotosGridDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhotosGridDataSource.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 30/06/23. 6 | // 7 | 8 | import Foundation 9 | 10 | class PhotosGridDataSource { 11 | 12 | private(set) var photos: [PhotoItem] = [] 13 | 14 | init() { 15 | photos.append(contentsOf: [ 16 | PhotoItem(imageName: "image0"), 17 | PhotoItem(imageName: "image1"), 18 | PhotoItem(imageName: "image2"), 19 | PhotoItem(imageName: "image3"), 20 | PhotoItem(imageName: "image4"), 21 | PhotoItem(imageName: "image5"), 22 | PhotoItem(imageName: "image6"), 23 | PhotoItem(imageName: "image7"), 24 | PhotoItem(imageName: "image8"), 25 | PhotoItem(imageName: "image9"), 26 | PhotoItem(imageName: "image10"), 27 | PhotoItem(imageName: "image11"), 28 | PhotoItem(imageName: "image12"), 29 | PhotoItem(imageName: "image13"), 30 | PhotoItem(imageName: "image14"), 31 | PhotoItem(imageName: "image15"), 32 | PhotoItem(imageName: "image16"), 33 | PhotoItem(imageName: "image17"), 34 | PhotoItem(imageName: "image18"), 35 | PhotoItem(imageName: "image19"), 36 | PhotoItem(imageName: "image20"), 37 | PhotoItem(imageName: "image21"), 38 | PhotoItem(imageName: "image22"), 39 | ]) 40 | } 41 | 42 | func deleteAt(_ indexPath: IndexPath) { 43 | photos.remove(at: indexPath.item) 44 | } 45 | } 46 | 47 | struct PhotoItem { 48 | let imageName: String 49 | } 50 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Modules/Photo Gallery/Photo Grid/PhotosGridViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhotosGridViewController.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 30/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | class PhotosGridViewController: BaseViewController { 11 | 12 | // MARK: - Properties 13 | private lazy var collectionView: UICollectionView = { 14 | let layout = UICollectionViewFlowLayout() 15 | layout.scrollDirection = .vertical 16 | layout.minimumLineSpacing = gridSpacing 17 | layout.minimumInteritemSpacing = gridSpacing 18 | let width = floor((view.frame.width - gridSpacing * (numberOfItemsInRow - 1)) / numberOfItemsInRow) 19 | layout.itemSize = CGSize(width: width, height: width) 20 | let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) 21 | collectionView.register(cell: PhotoGridCollectionCell.self) 22 | collectionView.delegate = self 23 | collectionView.dataSource = self 24 | collectionView.showsVerticalScrollIndicator = false 25 | collectionView.backgroundColor = .clear 26 | return collectionView 27 | }() 28 | 29 | private let gridSpacing: CGFloat = 1.5 30 | private let numberOfItemsInRow: CGFloat = 3 31 | private let dataSource = PhotosGridDataSource() 32 | 33 | 34 | // MARK: - LifeCycle 35 | override func viewDidLoad() { 36 | super.viewDidLoad() 37 | initialSetup() 38 | } 39 | 40 | 41 | // MARK: - Private Methods 42 | private func initialSetup() { 43 | view.addSubview(collectionView) 44 | collectionView.makeEdgeConstraints(toView: view) 45 | } 46 | 47 | private func copyAction(for indexPath: IndexPath) { 48 | UIPasteboard.general.image = UIImage(named: self.dataSource.photos[indexPath.item].imageName) 49 | } 50 | 51 | private func shareAction(for indexPath: IndexPath) { 52 | guard let image = UIImage(named: self.dataSource.photos[indexPath.item].imageName) else { return } 53 | let items: [Any] = [image] 54 | let activityController = UIActivityViewController(activityItems: items, applicationActivities: nil) 55 | present(activityController, animated: true) 56 | } 57 | 58 | private func deleteAction(for indexPath: IndexPath) { 59 | dataSource.deleteAt(indexPath) 60 | collectionView.deleteItems(at: [indexPath]) 61 | } 62 | 63 | private func saveAction(for indexPath: IndexPath) { 64 | guard let image = UIImage(named: self.dataSource.photos[indexPath.item].imageName) else { return } 65 | UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil) 66 | } 67 | } 68 | 69 | extension PhotosGridViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { 70 | 71 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 72 | dataSource.photos.count 73 | } 74 | 75 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 76 | let cell = collectionView.dequeueReusableCell(with: PhotoGridCollectionCell.self, for: indexPath) 77 | cell.configure(dataSource.photos[indexPath.item]) 78 | return cell 79 | } 80 | 81 | func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { 82 | 83 | UIContextMenuConfiguration(identifier: nil, 84 | previewProvider: { self.makePreview(for: indexPath.item) }) { _ in 85 | 86 | let saveAction = UIAction(title: "Save", image: UIImage(systemName: "square.and.arrow.down")) { _ in 87 | self.saveAction(for: indexPath) 88 | } 89 | 90 | let copyAction = UIAction(title: "Copy", image: UIImage(systemName: "doc.on.doc")) { _ in 91 | self.copyAction(for: indexPath) 92 | } 93 | 94 | let shareAction = UIAction(title: "Share", image: UIImage(systemName: "square.and.arrow.up")) { _ in 95 | self.shareAction(for: indexPath) 96 | } 97 | 98 | let deleteAction = UIAction(title: "Delete", image: UIImage(systemName: "trash"), attributes: .destructive) { _ in 99 | self.deleteAction(for: indexPath) 100 | } 101 | 102 | return UIMenu(title: "", image: nil, children: [saveAction, copyAction, shareAction, deleteAction]) 103 | } 104 | } 105 | 106 | private func makePreview(for index: Int) -> UIViewController { 107 | 108 | // configure a preview controller. 109 | let previewController = UIViewController() 110 | 111 | guard let image = UIImage(named: dataSource.photos[index].imageName) else { return previewController } 112 | let imageView = UIImageView(image: image) 113 | imageView.translatesAutoresizingMaskIntoConstraints = false 114 | imageView.contentMode = .scaleAspectFit 115 | imageView.frame = .init(x: 0, y: 0, width: image.size.width, height: image.size.height) 116 | 117 | previewController.view = imageView 118 | previewController.preferredContentSize = imageView.frame.size 119 | return previewController 120 | } 121 | 122 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 123 | let controller = PhotoGalleryViewController(photos: dataSource.photos, selectedIndex: indexPath.item) 124 | controller.modalPresentationStyle = .overFullScreen 125 | present(controller, animated: true) 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/.DS_Store -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/.DS_Store -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image0.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image0.imageset/image0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image0.imageset/image0.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image1.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image1.imageset/image1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image1.imageset/image1.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image10.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image10.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image10.imageset/image10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image10.imageset/image10.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image11.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image11.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image11.imageset/image11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image11.imageset/image11.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image12.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image12.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image12.imageset/image12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image12.imageset/image12.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image13.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image13.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image13.imageset/image13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image13.imageset/image13.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image14.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image14.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image14.imageset/image14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image14.imageset/image14.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image15.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image15.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image15.imageset/image15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image15.imageset/image15.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image16.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image16.imageset/image16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image16.imageset/image16.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image17.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image17.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image17.imageset/image17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image17.imageset/image17.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image18.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image18.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image18.imageset/image18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image18.imageset/image18.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image19.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image19.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image19.imageset/image19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image19.imageset/image19.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image2.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image2.imageset/image2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image2.imageset/image2.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image20.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image20.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image20.imageset/image20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image20.imageset/image20.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image21.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image21.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image21.imageset/image21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image21.imageset/image21.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image22.imageset/01w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image22.imageset/01w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image22.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "01w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image3.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image3.imageset/image3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image3.imageset/image3.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image4.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image4.imageset/image4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image4.imageset/image4.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image5.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image5.imageset/image5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image5.imageset/image5.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image6.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image6.imageset/image6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image6.imageset/image6.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image7.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image7.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image7.imageset/image7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image7.imageset/image7.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image8.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image8.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image8.imageset/image8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image8.imageset/image8.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image9.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image9.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image9.imageset/image9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Photo Gallery/image9.imageset/image9.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/13m.imageset/13m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/13m.imageset/13m.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/13m.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "13m.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/14m.imageset/14m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/14m.imageset/14m.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/14m.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "14m.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/15w.imageset/15w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/15w.imageset/15w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/15w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "15w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/16w.imageset/16w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/16w.imageset/16w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/16w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "16w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/17w.imageset/17w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/17w.imageset/17w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/17w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "17w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/18w.imageset/18w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/18w.imageset/18w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/18w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "18w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/19m.imageset/19m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/19m.imageset/19m.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/19m.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "19m.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/20w.imageset/20w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/20w.imageset/20w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/20w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "20w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/21w.imageset/21w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/21w.imageset/21w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/21w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "21w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/22w.imageset/22w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/22w.imageset/22w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/22w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "22w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/23w.imageset/23w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/23w.imageset/23w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/23w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "23w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/24w.imageset/24w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/24w.imageset/24w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/24w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "24w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/25w.imageset/25w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/25w.imageset/25w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/25w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "25w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/26m.imageset/26m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/26m.imageset/26m.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/26m.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "26m.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/27m.imageset/27m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/27m.imageset/27m.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/27m.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "27m.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/28m.imageset/28m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/28m.imageset/28m.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/28m.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "28m.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/29w.imageset/29w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/29w.imageset/29w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/29w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "29w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/30w.imageset/30w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/30w.imageset/30w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/30w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "30w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/31m.imageset/31m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/31m.imageset/31m.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/31m.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "31m.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/32m.imageset/32m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/32m.imageset/32m.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/32m.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "32m.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/33w.imageset/33w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/33w.imageset/33w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/33w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "33w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/34m.imageset/34m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/34m.imageset/34m.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/34m.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "34m.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/35w.imageset/35w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/35w.imageset/35w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/35w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "35w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/36m.imageset/36m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/36m.imageset/36m.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/36m.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "36m.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/37w.imageset/37w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/37w.imageset/37w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/37w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "37w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/38w.imageset/38w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/38w.imageset/38w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/38w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "38w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/39w.imageset/39w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/39w.imageset/39w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/39w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "39w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/40w.imageset/40w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/40w.imageset/40w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/40w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "40w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/41w.imageset/41w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/41w.imageset/41w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/41w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "41w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/42w.imageset/42w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/42w.imageset/42w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/42w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "42w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/43w.imageset/43w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/43w.imageset/43w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/43w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "43w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/44w.imageset/44w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/44w.imageset/44w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/44w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "44w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/45m.imageset/45m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/45m.imageset/45m.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/45m.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "45m.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/46w.imageset/46w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/46w.imageset/46w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/46w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "46w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/47w.imageset/47w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/47w.imageset/47w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/47w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "47w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/48m.imageset/48m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/48m.imageset/48m.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/48m.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "48m.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/49w.imageset/49w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/49w.imageset/49w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/49w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "49w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/50w.imageset/50w.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/50w.imageset/50w.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/50w.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "50w.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "profile_1.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_1.imageset/profile_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_1.imageset/profile_1.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_10.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "profile_10.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_10.imageset/profile_10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_10.imageset/profile_10.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_11.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "profile_11.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_11.imageset/profile_11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_11.imageset/profile_11.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_12.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "profile_12.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_12.imageset/profile_12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_12.imageset/profile_12.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "profile_2.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_2.imageset/profile_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_2.imageset/profile_2.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "profile_3.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_3.imageset/profile_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_3.imageset/profile_3.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "profile_4.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_4.imageset/profile_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_4.imageset/profile_4.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "profile_5.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_5.imageset/profile_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_5.imageset/profile_5.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "profile_6.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_6.imageset/profile_6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_6.imageset/profile_6.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_7.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "profile_7.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_7.imageset/profile_7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_7.imageset/profile_7.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_8.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "profile_8.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_8.imageset/profile_8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_8.imageset/profile_8.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_9.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "profile_9.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_9.imageset/profile_9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/Profiles/profile_9.imageset/profile_9.jpg -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/image_whats_app_bg.imageset/5a11bb1d77ee734b4f16ad3b2d6bc189.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/image_whats_app_bg.imageset/5a11bb1d77ee734b4f16ad3b2d6bc189.png -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/image_whats_app_bg.imageset/9dd007a70745d203b24aa6d6d0c0b1a1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitin-agam/ExploringUIKit/9f9f1f4d9b059716307e6b91e13666135976332f/UIKitApp/UIKitApp/Resources/Assets.xcassets/image_whats_app_bg.imageset/9dd007a70745d203b24aa6d6d0c0b1a1.png -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Assets.xcassets/image_whats_app_bg.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "9dd007a70745d203b24aa6d6d0c0b1a1.png", 5 | "idiom" : "universal" 6 | }, 7 | { 8 | "appearances" : [ 9 | { 10 | "appearance" : "luminosity", 11 | "value" : "dark" 12 | } 13 | ], 14 | "filename" : "5a11bb1d77ee734b4f16ad3b2d6bc189.png", 15 | "idiom" : "universal" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Views/BaseCollectionCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseCollectionCell.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 20/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | class BaseCollectionCell: UICollectionViewCell { 11 | 12 | override init(frame: CGRect) { 13 | super.init(frame: frame) 14 | initialSetup() 15 | } 16 | 17 | required init?(coder: NSCoder) { 18 | fatalError("init(coder:) has not been implemented") 19 | } 20 | 21 | func initialSetup() { 22 | backgroundColor = .clear 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /UIKitApp/UIKitApp/Views/BaseTableCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseTableCell.swift 3 | // UIKitApp 4 | // 5 | // Created by Nitin Aggarwal on 19/06/23. 6 | // 7 | 8 | import UIKit 9 | 10 | class BaseTableCell: UITableViewCell { 11 | 12 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 13 | super.init(style: style, reuseIdentifier: reuseIdentifier) 14 | initialSetup() 15 | } 16 | 17 | required init?(coder: NSCoder) { 18 | fatalError("init(coder:) has not been implemented") 19 | } 20 | 21 | func initialSetup() { 22 | backgroundColor = .clear 23 | selectionStyle = .none 24 | } 25 | } 26 | --------------------------------------------------------------------------------