├── .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 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
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 |
--------------------------------------------------------------------------------