├── Simulator Screen Shot - iPhone 6 - 2017-10-05 at 12.05.43.png
├── Simulator Screen Shot - iPhone 6 - 2017-10-05 at 12.06.03.png
├── Simulator Screen Shot - iPhone 6 - 2017-10-05 at 12.06.09.png
├── Simulator Screen Shot - iPhone 6 - 2017-10-05 at 12.06.19.png
├── photosApp2.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── muskan.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
├── xcuserdata
│ └── muskan.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── project.pbxproj
├── README.md
└── photosApp2
├── Info.plist
├── Base.lproj
└── LaunchScreen.storyboard
├── Assets.xcassets
└── AppIcon.appiconset
│ └── Contents.json
├── AppDelegate.swift
├── ImagePreviewVC.swift
└── ViewController.swift
/Simulator Screen Shot - iPhone 6 - 2017-10-05 at 12.05.43.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Akhilendra/photosAppiOS/HEAD/Simulator Screen Shot - iPhone 6 - 2017-10-05 at 12.05.43.png
--------------------------------------------------------------------------------
/Simulator Screen Shot - iPhone 6 - 2017-10-05 at 12.06.03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Akhilendra/photosAppiOS/HEAD/Simulator Screen Shot - iPhone 6 - 2017-10-05 at 12.06.03.png
--------------------------------------------------------------------------------
/Simulator Screen Shot - iPhone 6 - 2017-10-05 at 12.06.09.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Akhilendra/photosAppiOS/HEAD/Simulator Screen Shot - iPhone 6 - 2017-10-05 at 12.06.09.png
--------------------------------------------------------------------------------
/Simulator Screen Shot - iPhone 6 - 2017-10-05 at 12.06.19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Akhilendra/photosAppiOS/HEAD/Simulator Screen Shot - iPhone 6 - 2017-10-05 at 12.06.19.png
--------------------------------------------------------------------------------
/photosApp2.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/photosApp2.xcodeproj/project.xcworkspace/xcuserdata/muskan.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Akhilendra/photosAppiOS/HEAD/photosApp2.xcodeproj/project.xcworkspace/xcuserdata/muskan.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/photosApp2.xcodeproj/xcuserdata/muskan.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | photosApp2.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # photosAppiOS
2 | This is the source code for my photo gallery app tutorial on Youtube https://youtu.be/QS2mWk3fAWc
3 |
4 |
5 |
6 |       
7 |
8 |
9 |
10 |       
11 |
12 |
13 |
--------------------------------------------------------------------------------
/photosApp2/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSPhotoLibraryUsageDescription
6 | This app requires access to the photo library.
7 | CFBundleDevelopmentRegion
8 | $(DEVELOPMENT_LANGUAGE)
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/photosApp2/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 |
--------------------------------------------------------------------------------
/photosApp2/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | }
88 | ],
89 | "info" : {
90 | "version" : 1,
91 | "author" : "xcode"
92 | }
93 | }
--------------------------------------------------------------------------------
/photosApp2/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // photosApp2
4 | //
5 | // Created by Muskan on 10/4/17.
6 | // Copyright © 2017 akhil. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Photos
11 |
12 | @UIApplicationMain
13 | class AppDelegate: UIResponder, UIApplicationDelegate {
14 |
15 | var window: UIWindow?
16 |
17 |
18 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
19 | // Override point for customization after application launch.
20 |
21 | let photos = PHPhotoLibrary.authorizationStatus()
22 | if photos == .notDetermined {
23 | PHPhotoLibrary.requestAuthorization({status in
24 | if status == .authorized{
25 | self.gotoVC()
26 | } else {
27 | let alert = UIAlertController(title: "Photos Access Denied", message: "App needs access to photos library.", preferredStyle: .alert)
28 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
29 | self.window?.rootViewController?.present(alert, animated: true, completion: nil)
30 | }
31 | })
32 | } else if photos == .authorized {
33 | gotoVC()
34 | }
35 |
36 | return true
37 | }
38 |
39 | func gotoVC() {
40 | DispatchQueue.main.async(execute: { () -> Void in
41 | self.window = UIWindow(frame: UIScreen.main.bounds)
42 | if let window = self.window {
43 | window.backgroundColor = UIColor.white
44 |
45 | let nav = UINavigationController()
46 | let mainView = ViewController()
47 | nav.viewControllers = [mainView]
48 | window.rootViewController = nav
49 | window.makeKeyAndVisible()
50 | }
51 | })
52 | }
53 |
54 | func applicationWillResignActive(_ application: UIApplication) {
55 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
56 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
57 | }
58 |
59 | func applicationDidEnterBackground(_ application: UIApplication) {
60 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
61 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
62 | }
63 |
64 | func applicationWillEnterForeground(_ application: UIApplication) {
65 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
66 | }
67 |
68 | func applicationDidBecomeActive(_ application: UIApplication) {
69 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
70 | }
71 |
72 | func applicationWillTerminate(_ application: UIApplication) {
73 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
74 | }
75 |
76 |
77 | }
78 |
79 |
--------------------------------------------------------------------------------
/photosApp2/ImagePreviewVC.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImagePreviewVC.swift
3 | // photosApp2
4 | //
5 | // Created by Muskan on 10/4/17.
6 | // Copyright © 2017 akhil. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ImagePreviewVC: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
12 |
13 | var myCollectionView: UICollectionView!
14 | var imgArray = [UIImage]()
15 | var passedContentOffset = IndexPath()
16 |
17 | override func viewDidLoad() {
18 | super.viewDidLoad()
19 |
20 | // Do any additional setup after loading the view.
21 |
22 | self.view.backgroundColor=UIColor.black
23 |
24 | let layout = UICollectionViewFlowLayout()
25 | layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
26 | layout.minimumInteritemSpacing=0
27 | layout.minimumLineSpacing=0
28 | layout.scrollDirection = .horizontal
29 |
30 | myCollectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
31 | myCollectionView.delegate=self
32 | myCollectionView.dataSource=self
33 | myCollectionView.register(ImagePreviewFullViewCell.self, forCellWithReuseIdentifier: "Cell")
34 | myCollectionView.isPagingEnabled = true
35 | myCollectionView.scrollToItem(at: passedContentOffset, at: .left, animated: true)
36 |
37 | self.view.addSubview(myCollectionView)
38 |
39 | myCollectionView.autoresizingMask = UIViewAutoresizing(rawValue: UIViewAutoresizing.RawValue(UInt8(UIViewAutoresizing.flexibleWidth.rawValue) | UInt8(UIViewAutoresizing.flexibleHeight.rawValue)))
40 | }
41 |
42 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
43 | return imgArray.count
44 | }
45 |
46 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
47 | let cell=collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! ImagePreviewFullViewCell
48 | cell.imgView.image=imgArray[indexPath.row]
49 | return cell
50 | }
51 |
52 | override func viewWillLayoutSubviews() {
53 | super.viewWillLayoutSubviews()
54 |
55 | guard let flowLayout = myCollectionView.collectionViewLayout as? UICollectionViewFlowLayout else { return }
56 |
57 | flowLayout.itemSize = myCollectionView.frame.size
58 |
59 | flowLayout.invalidateLayout()
60 |
61 | myCollectionView.collectionViewLayout.invalidateLayout()
62 | }
63 |
64 | override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
65 | super.viewWillTransition(to: size, with: coordinator)
66 | let offset = myCollectionView.contentOffset
67 | let width = myCollectionView.bounds.size.width
68 |
69 | let index = round(offset.x / width)
70 | let newOffset = CGPoint(x: index * size.width, y: offset.y)
71 |
72 | myCollectionView.setContentOffset(newOffset, animated: false)
73 |
74 | coordinator.animate(alongsideTransition: { (context) in
75 | self.myCollectionView.reloadData()
76 |
77 | self.myCollectionView.setContentOffset(newOffset, animated: false)
78 | }, completion: nil)
79 | }
80 |
81 | }
82 |
83 |
84 | class ImagePreviewFullViewCell: UICollectionViewCell, UIScrollViewDelegate {
85 |
86 | var scrollImg: UIScrollView!
87 | var imgView: UIImageView!
88 |
89 | override init(frame: CGRect) {
90 | super.init(frame: frame)
91 |
92 | scrollImg = UIScrollView()
93 | scrollImg.delegate = self
94 | scrollImg.alwaysBounceVertical = false
95 | scrollImg.alwaysBounceHorizontal = false
96 | scrollImg.showsVerticalScrollIndicator = true
97 | scrollImg.flashScrollIndicators()
98 |
99 | scrollImg.minimumZoomScale = 1.0
100 | scrollImg.maximumZoomScale = 4.0
101 |
102 | let doubleTapGest = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTapScrollView(recognizer:)))
103 | doubleTapGest.numberOfTapsRequired = 2
104 | scrollImg.addGestureRecognizer(doubleTapGest)
105 |
106 | self.addSubview(scrollImg)
107 |
108 | imgView = UIImageView()
109 | imgView.image = UIImage(named: "user3")
110 | scrollImg.addSubview(imgView!)
111 | imgView.contentMode = .scaleAspectFit
112 | }
113 |
114 | @objc func handleDoubleTapScrollView(recognizer: UITapGestureRecognizer) {
115 | if scrollImg.zoomScale == 1 {
116 | scrollImg.zoom(to: zoomRectForScale(scale: scrollImg.maximumZoomScale, center: recognizer.location(in: recognizer.view)), animated: true)
117 | } else {
118 | scrollImg.setZoomScale(1, animated: true)
119 | }
120 | }
121 |
122 | func zoomRectForScale(scale: CGFloat, center: CGPoint) -> CGRect {
123 | var zoomRect = CGRect.zero
124 | zoomRect.size.height = imgView.frame.size.height / scale
125 | zoomRect.size.width = imgView.frame.size.width / scale
126 | let newCenter = imgView.convert(center, from: scrollImg)
127 | zoomRect.origin.x = newCenter.x - (zoomRect.size.width / 2.0)
128 | zoomRect.origin.y = newCenter.y - (zoomRect.size.height / 2.0)
129 | return zoomRect
130 | }
131 |
132 | func viewForZooming(in scrollView: UIScrollView) -> UIView? {
133 | return self.imgView
134 | }
135 |
136 | override func layoutSubviews() {
137 | super.layoutSubviews()
138 | scrollImg.frame = self.bounds
139 | imgView.frame = self.bounds
140 | }
141 |
142 | override func prepareForReuse() {
143 | super.prepareForReuse()
144 | scrollImg.setZoomScale(1, animated: true)
145 | }
146 |
147 | required init?(coder aDecoder: NSCoder) {
148 | fatalError("init(coder:) has not been implemented")
149 | }
150 | }
151 |
152 |
--------------------------------------------------------------------------------
/photosApp2/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // photosApp2
4 | //
5 | // Created by Muskan on 10/4/17.
6 | // Copyright © 2017 akhil. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Photos
11 |
12 | class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UINavigationControllerDelegate {
13 |
14 | var myCollectionView: UICollectionView!
15 | var imageArray=[UIImage]()
16 |
17 | override func viewDidLoad() {
18 | super.viewDidLoad()
19 | // Do any additional setup after loading the view, typically from a nib.
20 |
21 | self.title = "Photos"
22 |
23 | let layout = UICollectionViewFlowLayout()
24 |
25 | myCollectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
26 | myCollectionView.delegate=self
27 | myCollectionView.dataSource=self
28 | myCollectionView.register(PhotoItemCell.self, forCellWithReuseIdentifier: "Cell")
29 | myCollectionView.backgroundColor=UIColor.white
30 | self.view.addSubview(myCollectionView)
31 |
32 | myCollectionView.autoresizingMask = UIViewAutoresizing(rawValue: UIViewAutoresizing.RawValue(UInt8(UIViewAutoresizing.flexibleWidth.rawValue) | UInt8(UIViewAutoresizing.flexibleHeight.rawValue)))
33 |
34 | grabPhotos()
35 | }
36 |
37 | //MARK: CollectionView
38 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
39 | return imageArray.count
40 | }
41 |
42 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
43 | let cell=collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! PhotoItemCell
44 | cell.img.image=imageArray[indexPath.item]
45 | return cell
46 | }
47 |
48 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
49 | print(indexPath)
50 | let vc=ImagePreviewVC()
51 | vc.imgArray = self.imageArray
52 | vc.passedContentOffset = indexPath
53 | self.navigationController?.pushViewController(vc, animated: true)
54 | }
55 |
56 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
57 | let width = collectionView.frame.width
58 | // if UIDevice.current.orientation.isPortrait {
59 | // return CGSize(width: width/4 - 1, height: width/4 - 1)
60 | // } else {
61 | // return CGSize(width: width/6 - 1, height: width/6 - 1)
62 | // }
63 | if DeviceInfo.Orientation.isPortrait {
64 | return CGSize(width: width/4 - 1, height: width/4 - 1)
65 | } else {
66 | return CGSize(width: width/6 - 1, height: width/6 - 1)
67 | }
68 | }
69 |
70 | override func viewWillLayoutSubviews() {
71 | super.viewWillLayoutSubviews()
72 | myCollectionView.collectionViewLayout.invalidateLayout()
73 | }
74 |
75 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
76 | return 1.0
77 | }
78 |
79 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
80 | return 1.0
81 | }
82 |
83 | //MARK: grab photos
84 | func grabPhotos(){
85 | imageArray = []
86 |
87 | DispatchQueue.global(qos: .background).async {
88 | print("This is run on the background queue")
89 | let imgManager=PHImageManager.default()
90 |
91 | let requestOptions=PHImageRequestOptions()
92 | requestOptions.isSynchronous=true
93 | requestOptions.deliveryMode = .highQualityFormat
94 |
95 | let fetchOptions=PHFetchOptions()
96 | fetchOptions.sortDescriptors=[NSSortDescriptor(key:"creationDate", ascending: false)]
97 |
98 | let fetchResult: PHFetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)
99 | print(fetchResult)
100 | print(fetchResult.count)
101 | if fetchResult.count > 0 {
102 | for i in 0..