├── screen.png ├── FSImageViewer.bundle ├── en.lproj │ └── Localizable.strings ├── es.lproj │ └── Localizable.strings ├── fr.lproj │ └── Localizable.strings ├── it.lproj │ └── Localizable.strings ├── de.lproj │ └── Localizable.strings ├── nl.lproj │ └── Localizable.strings └── ru.lproj │ └── Localizable.strings ├── FSImageViewerDemo ├── de.lproj │ └── InfoPlist.strings ├── en.lproj │ └── InfoPlist.strings ├── es.lproj │ └── InfoPlist.strings ├── fr.lproj │ └── InfoPlist.strings ├── it.lproj │ └── InfoPlist.strings ├── nl.lproj │ └── InfoPlist.strings ├── ru.lproj │ └── InfoPlist.strings ├── Images.xcassets │ ├── AppIcon.appiconset │ │ ├── Icon-76.png │ │ ├── Icon-60@2x.png │ │ ├── Icon-60@3x.png │ │ ├── Icon-76@2x.png │ │ ├── Icon-Small.png │ │ ├── Icon-Small-40.png │ │ ├── Icon-Small@2x-1.png │ │ ├── Icon-Small@2x.png │ │ ├── Icon-Small@3x.png │ │ ├── Icon-Small-40@2x.png │ │ ├── Icon-Small-40@3x.png │ │ ├── Icon-Small-40@2x-1.png │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ ├── Default@2x.png │ │ ├── Default-568h@2x.png │ │ └── Contents.json ├── main.m ├── FSAppDelegate.h ├── FSImageViewerDemo-Prefix.pch ├── FSDemoViewController.h ├── FSImageViewerDemo-Info.plist ├── FSDemoViewController.xib ├── FSDemoViewController.m └── FSAppDelegate.m ├── FSImageViewerTests ├── en.lproj │ └── InfoPlist.strings ├── Testimage.jpg ├── ReferenceImages_64 │ ├── FSImageViewTests │ │ ├── testView@2x.png │ │ ├── testEmptyView@2x.png │ │ ├── testFailedView@2x.png │ │ ├── testViewWithBackgroundColor@2x.png │ │ └── testEmptyViewWithProgressViewColor@2x.png │ ├── FSPlaceholderImagesTests │ │ └── testView@2x.png │ └── FSImageTitleViewTests │ │ ├── testLongTitle@2x.png │ │ ├── testNormalTitle@2x.png │ │ └── testShortTitle@2x.png ├── FSImageViewerTests-Prefix.pch ├── FSImageViewerTests-Info.plist ├── FSTestImageHelper.h ├── FSTestImageHelper.m ├── FSPlaceholderImagesTests.m ├── FSBasicImageSourceTests.m ├── FSImageTitleViewTests.m └── FSImageViewTests.m ├── .gitignore ├── FSImageViewer.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── project.pbxproj ├── FSImageViewer.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── FSImageViewerDemo.xcscheme │ └── FSImageViewer.xcscheme ├── Podfile ├── .travis.yml ├── FSImageViewer.podspec ├── LICENSE ├── FSImageViewer ├── FSImageScrollView.h ├── FSImageViewer-Prefix.pch ├── FSPlaceholderImages.h ├── FSImageTitleView.h ├── FSTitleView.h ├── FSBasicImageSource.h ├── FSBasicImageSource.m ├── FSImage.h ├── FSImageViewer.h ├── FSImageSource.h ├── FSBasicImage.h ├── FSImageView.h ├── FSImageLoader.h ├── FSBasicImage.m ├── FSImageScrollView.m ├── FSImageViewerViewController.h ├── FSPlaceholderImages.m ├── FSImageLoader.m ├── FSImageTitleView.m ├── FSImageView.m └── FSImageViewerViewController.m └── README.md /screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/screen.png -------------------------------------------------------------------------------- /FSImageViewer.bundle/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "imageCounter" = "of"; 2 | "done" = "Done"; -------------------------------------------------------------------------------- /FSImageViewer.bundle/es.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "imageCounter" = "de"; 2 | "done" = "OK"; -------------------------------------------------------------------------------- /FSImageViewer.bundle/fr.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "imageCounter" = "de"; 2 | "done" = "Ok"; -------------------------------------------------------------------------------- /FSImageViewer.bundle/it.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "imageCounter" = "di"; 2 | "done" = "Fine"; -------------------------------------------------------------------------------- /FSImageViewer.bundle/de.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "imageCounter" = "von"; 2 | "done" = "Fertig"; -------------------------------------------------------------------------------- /FSImageViewer.bundle/nl.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "imageCounter" = "van"; 2 | "done" = "Gereed"; -------------------------------------------------------------------------------- /FSImageViewer.bundle/ru.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "imageCounter" = "из"; 2 | "done" = "Готово"; -------------------------------------------------------------------------------- /FSImageViewerDemo/de.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /FSImageViewerDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /FSImageViewerDemo/es.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /FSImageViewerDemo/fr.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /FSImageViewerDemo/it.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /FSImageViewerDemo/nl.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /FSImageViewerDemo/ru.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /FSImageViewerTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /FSImageViewerTests/Testimage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerTests/Testimage.jpg -------------------------------------------------------------------------------- /FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-Small.png -------------------------------------------------------------------------------- /FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-40.png -------------------------------------------------------------------------------- /FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png -------------------------------------------------------------------------------- /FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png -------------------------------------------------------------------------------- /FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png -------------------------------------------------------------------------------- /FSImageViewerDemo/Images.xcassets/LaunchImage.launchimage/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerDemo/Images.xcassets/LaunchImage.launchimage/Default@2x.png -------------------------------------------------------------------------------- /FSImageViewerTests/ReferenceImages_64/FSImageViewTests/testView@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerTests/ReferenceImages_64/FSImageViewTests/testView@2x.png -------------------------------------------------------------------------------- /FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png -------------------------------------------------------------------------------- /FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png -------------------------------------------------------------------------------- /FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-40@2x-1.png -------------------------------------------------------------------------------- /FSImageViewerDemo/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerDemo/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png -------------------------------------------------------------------------------- /FSImageViewerTests/ReferenceImages_64/FSImageViewTests/testEmptyView@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerTests/ReferenceImages_64/FSImageViewTests/testEmptyView@2x.png -------------------------------------------------------------------------------- /FSImageViewerTests/ReferenceImages_64/FSImageViewTests/testFailedView@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerTests/ReferenceImages_64/FSImageViewTests/testFailedView@2x.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Podfile.lock 2 | Pods 3 | FSImageViewer.xcodeproj/xcuserdata 4 | *.xccheckout 5 | FSImageViewer.xcodeproj/project.xcworkspace/xcuserdata 6 | FSImageViewer.xcworkspace/xcuserdata 7 | documentation 8 | -------------------------------------------------------------------------------- /FSImageViewerTests/ReferenceImages_64/FSPlaceholderImagesTests/testView@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerTests/ReferenceImages_64/FSPlaceholderImagesTests/testView@2x.png -------------------------------------------------------------------------------- /FSImageViewerTests/ReferenceImages_64/FSImageTitleViewTests/testLongTitle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerTests/ReferenceImages_64/FSImageTitleViewTests/testLongTitle@2x.png -------------------------------------------------------------------------------- /FSImageViewerTests/ReferenceImages_64/FSImageTitleViewTests/testNormalTitle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerTests/ReferenceImages_64/FSImageTitleViewTests/testNormalTitle@2x.png -------------------------------------------------------------------------------- /FSImageViewerTests/ReferenceImages_64/FSImageTitleViewTests/testShortTitle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerTests/ReferenceImages_64/FSImageTitleViewTests/testShortTitle@2x.png -------------------------------------------------------------------------------- /FSImageViewerTests/ReferenceImages_64/FSImageViewTests/testViewWithBackgroundColor@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerTests/ReferenceImages_64/FSImageViewTests/testViewWithBackgroundColor@2x.png -------------------------------------------------------------------------------- /FSImageViewerTests/ReferenceImages_64/FSImageViewTests/testEmptyViewWithProgressViewColor@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2on/FSImageViewer/HEAD/FSImageViewerTests/ReferenceImages_64/FSImageViewTests/testEmptyViewWithProgressViewColor@2x.png -------------------------------------------------------------------------------- /FSImageViewer.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /FSImageViewer.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /FSImageViewerTests/FSImageViewerTests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | #endif 14 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | 3 | platform :ios, '7.0' 4 | inhibit_all_warnings! 5 | 6 | pod 'AFNetworking', '~> 2.5' 7 | pod 'EGOCache', '~> 2.1' 8 | pod 'UAProgressView', '~> 0.1' 9 | 10 | target :FSImageViewerDemo, :exclusive => true do 11 | link_with ['FSImageViewer'] 12 | end 13 | 14 | target :FSImageViewerTests, :exclusive => true do 15 | pod 'OCMock', '~> 3.1' 16 | pod 'FBSnapshotTestCase/Core', '~> 2.0' 17 | end -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | osx_image: xcode7 2 | language: objective-c 3 | before_install: 4 | - echo "Update CocoaPods" 5 | - gem install cocoapods 6 | - pod --version 7 | - brew update 8 | - brew outdated xctool || brew upgrade xctool 9 | script: 10 | - xctool -workspace FSImageViewer.xcworkspace -scheme FSImageViewer -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 6' clean build test 11 | - pod --version 12 | - pod spec lint FSImageViewer.podspec -------------------------------------------------------------------------------- /FSImageViewerTests/FSImageViewerTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /FSImageViewer.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "FSImageViewer" 3 | s.version = "3.4" 4 | s.summary = "FSImageViewer is a photo viewer for iOS." 5 | s.homepage = "https://github.com/x2on/FSImageViewer" 6 | s.social_media_url = 'https://twitter.com/x2on' 7 | s.screenshot = 'https://raw.github.com/x2on/FSImageViewer/master/screen.png' 8 | s.license = { :type => 'MIT', :file => 'LICENSE' } 9 | s.author = { "Felix Schulze" => "code@felixschulze.de" } 10 | s.source = { 11 | :git => "https://github.com/x2on/FSImageViewer.git", 12 | :tag => s.version.to_s 13 | } 14 | 15 | s.platform = :ios, '7.0' 16 | s.requires_arc = true 17 | 18 | s.source_files = 'FSImageViewer/FS*.{h,m}' 19 | s.resources = 'FSImageViewer.bundle' 20 | 21 | s.framework = 'Foundation', 'UIKit', 'CoreGraphics', 'QuartzCore', 'Security', 'CFNetwork' 22 | 23 | s.dependency 'AFNetworking', '~> 2.5' 24 | s.dependency 'EGOCache', '~> 2.1' 25 | s.dependency 'UAProgressView', '~> 0.1' 26 | 27 | end 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Felix Schulze 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /FSImageViewer/FSImageScrollView.h: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import "FSImageViewer.h" 26 | 27 | @interface FSImageScrollView : UIScrollView 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /FSImageViewer/FSImageViewer-Prefix.pch: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #ifdef __OBJC__ 26 | #import 27 | #import 28 | #endif -------------------------------------------------------------------------------- /FSImageViewer/FSPlaceholderImages.h: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import 26 | 27 | @interface FSPlaceholderImages : NSObject 28 | 29 | + (UIImage * _Nonnull)errorImage; 30 | 31 | @end -------------------------------------------------------------------------------- /FSImageViewer/FSImageTitleView.h: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | #import "FSTitleView.h" 25 | 26 | @interface FSImageTitleView : UIView 27 | 28 | @property (strong, nonatomic) NSString *text; 29 | @property (assign, nonatomic) BOOL adjustsFontSizeToFitWidth; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /FSImageViewerTests/FSTestImageHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // FSTestImageHelper.h 3 | // FSImageViewer 4 | // 5 | // Created by Felix Schulze on 19.09.15. 6 | // Copyright © 2015 Felix Schulze. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | // 26 | 27 | #import 28 | #import 29 | 30 | @interface FSTestImageHelper : NSObject 31 | 32 | + (UIImage *)imageNamed:(NSString *)name; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /FSImageViewerDemo/main.m: -------------------------------------------------------------------------------- 1 | // FSImageViewerDemo 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import 26 | 27 | #import "FSAppDelegate.h" 28 | 29 | int main(int argc, char *argv[]) 30 | { 31 | @autoreleasepool { 32 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([FSAppDelegate class])); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /FSImageViewerDemo/FSAppDelegate.h: -------------------------------------------------------------------------------- 1 | // FSImageViewerDemo 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import 26 | 27 | @interface FSAppDelegate : UIResponder 28 | 29 | @property(strong, nonatomic) UIWindow *window; 30 | @property(strong, nonatomic) UINavigationController *navigationController; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /FSImageViewerDemo/FSImageViewerDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // FSImageViewerDemo 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import 26 | 27 | #ifndef __IPHONE_6_0 28 | #warning "This project uses features only available in iOS SDK 6.0 and later." 29 | #endif 30 | 31 | #ifdef __OBJC__ 32 | #import 33 | #import 34 | #endif 35 | -------------------------------------------------------------------------------- /FSImageViewerDemo/FSDemoViewController.h: -------------------------------------------------------------------------------- 1 | // FSImageViewerDemo 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import 26 | #import "FSImageViewerViewController.h" 27 | 28 | @interface FSDemoViewController : UIViewController 29 | 30 | @property(strong, nonatomic) FSImageViewerViewController *imageViewController; 31 | 32 | - (IBAction)openGallery; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /FSImageViewer/FSTitleView.h: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | /// FSTitleView represents an abstract UIView with a title and the image object. 26 | @protocol FSTitleView 27 | 28 | - (void)updateMetadata:(NSString*)text index:(NSInteger)index total:(NSInteger)total; 29 | - (void)hideView:(BOOL)value; 30 | - (BOOL)isHidden; 31 | - (void)adjustTextViewSize:(CGRect)imageViewControllerBounds; 32 | 33 | @end -------------------------------------------------------------------------------- /FSImageViewer/FSBasicImageSource.h: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 9/04/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import 26 | #import "FSImageSource.h" 27 | 28 | /// FSBasicImageSource is a standard implementation of FSImageSource. 29 | @interface FSBasicImageSource : NSObject 30 | 31 | /// @param images array of FSImage objects 32 | - (instancetype _Nonnull)initWithImages:(NSArray>* _Nonnull)images; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /FSImageViewerTests/FSTestImageHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // FSTestImageHelper.m 3 | // FSImageViewer 4 | // 5 | // Created by Felix Schulze on 19.09.15. 6 | // Copyright © 2015 Felix Schulze. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | // 26 | 27 | #import "FSTestImageHelper.h" 28 | 29 | @implementation FSTestImageHelper 30 | 31 | + (UIImage *)imageNamed:(NSString *)name { 32 | NSString *extension = name.pathExtension ? name.pathExtension : @"png"; 33 | NSString *fileName = name.stringByDeletingPathExtension; 34 | NSString *imagePath = [[NSBundle bundleForClass:[self class]] pathForResource:fileName ofType:extension]; 35 | return [UIImage imageWithContentsOfFile:imagePath]; 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /FSImageViewer/FSBasicImageSource.m: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 9/04/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import "FSBasicImageSource.h" 26 | 27 | @implementation FSBasicImageSource 28 | 29 | @synthesize images = _images; 30 | @synthesize numberOfImages = _numberOfImages; 31 | 32 | - (instancetype _Nonnull)initWithImages:(NSArray>* _Nonnull)images{ 33 | 34 | if (self = [super init]) { 35 | _images = images; 36 | _numberOfImages = [images count]; 37 | } 38 | 39 | return self; 40 | 41 | } 42 | 43 | - (id )objectAtIndexedSubscript:(NSUInteger)index { 44 | return _images[index]; 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /FSImageViewerDemo/FSImageViewerDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIcons 12 | 13 | CFBundleIcons~ipad 14 | 15 | CFBundleIdentifier 16 | $(PRODUCT_BUNDLE_IDENTIFIER) 17 | CFBundleInfoDictionaryVersion 18 | 6.0 19 | CFBundleName 20 | ${PRODUCT_NAME} 21 | CFBundlePackageType 22 | APPL 23 | CFBundleShortVersionString 24 | 1.0 25 | CFBundleSignature 26 | ???? 27 | CFBundleVersion 28 | 1.0 29 | LSRequiresIPhoneOS 30 | 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | NSAppTransportSecurity 49 | 50 | NSAllowsArbitraryLoads 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /FSImageViewer/FSImage.h: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | /// FSImage represents an abstract image object with URL, title and the image object. 26 | @protocol FSImage 27 | 28 | /// The remote image URL 29 | @property(strong, nonatomic, readonly) NSURL *URL; 30 | /// The title of the image - is displayed as overlay. 31 | @property(strong, nonatomic, readonly) NSString *title; 32 | /// The image size 33 | @property(assign, nonatomic) CGSize size; 34 | /// The UIImage representation - set directly if not from remote. 35 | @property(strong, nonatomic) UIImage *image; 36 | /// true if the image download failed. 37 | @property(assign, nonatomic, getter=didFail) BOOL failed; 38 | 39 | @end -------------------------------------------------------------------------------- /FSImageViewerTests/FSPlaceholderImagesTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // FSPlaceholderImagesTests.m 3 | // FSImageViewerTests 4 | // 5 | // Created by Felix Schulze on 01.06.14. 6 | // Copyright (c) 2014 Felix Schulze. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | // 26 | 27 | #import 28 | #import 29 | 30 | #import "FSPlaceholderImages.h" 31 | 32 | 33 | @interface FSPlaceholderImagesTests : FBSnapshotTestCase 34 | @end 35 | 36 | @implementation FSPlaceholderImagesTests 37 | 38 | - (void)setUp { 39 | [super setUp]; 40 | 41 | self.recordMode = NO; 42 | } 43 | 44 | 45 | - (void)testView { 46 | UIImageView *imageView = [[UIImageView alloc] initWithImage:[FSPlaceholderImages errorImage]]; 47 | FBSnapshotVerifyView(imageView, nil); 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /FSImageViewerDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-Small@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "29x29", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-Small@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "40x40", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-Small-40@2x.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "40x40", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-Small-40@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "60x60", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-60@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-60@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "29x29", 41 | "idiom" : "ipad", 42 | "filename" : "Icon-Small.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "29x29", 47 | "idiom" : "ipad", 48 | "filename" : "Icon-Small@2x-1.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "40x40", 53 | "idiom" : "ipad", 54 | "filename" : "Icon-Small-40.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "40x40", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-Small-40@2x-1.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "76x76", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-76.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "76x76", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-76@2x.png", 73 | "scale" : "2x" 74 | } 75 | ], 76 | "info" : { 77 | "version" : 1, 78 | "author" : "xcode" 79 | }, 80 | "properties" : { 81 | "pre-rendered" : true 82 | } 83 | } -------------------------------------------------------------------------------- /FSImageViewer/FSImageViewer.h: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import "FSImageViewerViewController.h" 26 | #import "FSImageScrollView.h" 27 | #import "FSImageView.h" 28 | #import "FSImageTitleView.h" 29 | #import "FSImageSource.h" 30 | #import "FSImage.h" 31 | #import "FSPlaceholderImages.h" 32 | 33 | #define kFSImageViewerToogleBarsNotificationKey @"FSImageViewerToggleBars" 34 | #define kFSImageViewerDidFinishedLoadingNotificationKey @"FSImageViewerDidFinishLoading" 35 | 36 | #ifndef FSImageViewerErrorPlaceholderImage 37 | #define FSImageViewerErrorPlaceholderImage [FSPlaceholderImages errorImage] 38 | #endif 39 | 40 | #ifndef kFSImageViewerImageGap 41 | #define kFSImageViewerImageGap 30 42 | #endif 43 | 44 | #ifndef kFSImageViewerZoomScale 45 | #define kFSImageViewerZoomScale 2.5 46 | #endif -------------------------------------------------------------------------------- /FSImageViewer/FSImageSource.h: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | @protocol FSImage; 26 | 27 | /// FSImageSource is the data source for the images. For normal usage you can use FSBasicImageSource 28 | @protocol FSImageSource 29 | 30 | /// The array contains all image objects. 31 | @property(strong, nonatomic, readonly) NSArray> *images; 32 | 33 | /// The number of the image objects are stored in the images array. 34 | @property(assign, nonatomic, readonly) NSInteger numberOfImages; 35 | 36 | /// Must return image at the given index - with object subscription you can use it like `imageSource[1]; 37 | /// @param index image index 38 | /// @return image at given index 39 | /// @warning index must be in the range of the images 40 | - (id )objectAtIndexedSubscript:(NSUInteger)index; 41 | 42 | @end 43 | 44 | 45 | -------------------------------------------------------------------------------- /FSImageViewerDemo/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "8.0", 8 | "subtype" : "736h", 9 | "scale" : "3x" 10 | }, 11 | { 12 | "orientation" : "landscape", 13 | "idiom" : "iphone", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "8.0", 16 | "subtype" : "736h", 17 | "scale" : "3x" 18 | }, 19 | { 20 | "orientation" : "portrait", 21 | "idiom" : "iphone", 22 | "extent" : "full-screen", 23 | "minimum-system-version" : "8.0", 24 | "subtype" : "667h", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "orientation" : "portrait", 29 | "idiom" : "iphone", 30 | "extent" : "full-screen", 31 | "minimum-system-version" : "7.0", 32 | "filename" : "Default@2x.png", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "extent" : "full-screen", 37 | "idiom" : "iphone", 38 | "subtype" : "retina4", 39 | "filename" : "Default-568h@2x.png", 40 | "minimum-system-version" : "7.0", 41 | "orientation" : "portrait", 42 | "scale" : "2x" 43 | }, 44 | { 45 | "orientation" : "portrait", 46 | "idiom" : "ipad", 47 | "extent" : "full-screen", 48 | "minimum-system-version" : "7.0", 49 | "scale" : "1x" 50 | }, 51 | { 52 | "orientation" : "landscape", 53 | "idiom" : "ipad", 54 | "extent" : "full-screen", 55 | "minimum-system-version" : "7.0", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "orientation" : "portrait", 60 | "idiom" : "ipad", 61 | "extent" : "full-screen", 62 | "minimum-system-version" : "7.0", 63 | "scale" : "2x" 64 | }, 65 | { 66 | "orientation" : "landscape", 67 | "idiom" : "ipad", 68 | "extent" : "full-screen", 69 | "minimum-system-version" : "7.0", 70 | "scale" : "2x" 71 | } 72 | ], 73 | "info" : { 74 | "version" : 1, 75 | "author" : "xcode" 76 | } 77 | } -------------------------------------------------------------------------------- /FSImageViewer/FSBasicImage.h: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 9/04/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import "FSImageViewer.h" 26 | 27 | /// FSBasicImage is a standard implementation of FSImage. It can download images from an URL or use an image. 28 | @interface FSBasicImage : NSObject 29 | 30 | /// @param URL remote image URL 31 | /// @param name title of the image 32 | - (instancetype _Nonnull)initWithImageURL:(NSURL * _Nonnull)URL name:(NSString * _Nullable)name; 33 | 34 | /// @param URL remote image URL 35 | - (instancetype _Nonnull)initWithImageURL:(NSURL * _Nonnull)URL; 36 | 37 | /// @param image an UIImage representation of the image 38 | - (instancetype _Nonnull)initWithImage:(UIImage * _Nonnull)image; 39 | 40 | /// @param image an UIImage representation of the image 41 | /// @param name title of the image 42 | - (instancetype _Nonnull)initWithImage:(UIImage * _Nonnull)image name:(NSString * _Nullable)name; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /FSImageViewer/FSImageView.h: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import 26 | #import 27 | #import "FSImageSource.h" 28 | #import "FSImageLoader.h" 29 | 30 | @class FSImageScrollView, FSImageTitleView; 31 | 32 | @interface FSImageView : UIView 33 | 34 | @property(strong, nonatomic) id image; 35 | @property(strong, nonatomic, readonly) UIImageView *imageView; 36 | @property(strong, nonatomic, readonly) FSImageScrollView *scrollView; 37 | @property(assign, nonatomic) BOOL loading; 38 | @property(assign, nonatomic) BOOL rotationEnabled; 39 | 40 | - (void)killScrollViewZoom; 41 | 42 | - (void)layoutScrollViewAnimated:(BOOL)animated; 43 | 44 | - (void)prepareForReuse; 45 | 46 | - (void)changeBackgroundColor:(UIColor *)color; 47 | 48 | - (void)changeProgressViewColor:(UIColor *)color; 49 | 50 | - (void)rotateToOrientation:(UIInterfaceOrientation)orientation; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /FSImageViewer/FSImageLoader.h: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import 26 | 27 | NS_ASSUME_NONNULL_BEGIN 28 | 29 | /// Download remote images with AFNetworking 30 | @interface FSImageLoader : NSObject 31 | 32 | /// Timeout for remote image downloading - Default is 30 seconds 33 | @property(assign, nonatomic) NSTimeInterval timeoutInterval; 34 | 35 | /// Singleton getter 36 | + (FSImageLoader *)sharedInstance; 37 | 38 | /// Download remote images from url 39 | /// @param url remote image url 40 | /// @param imageBlock block for image or error 41 | - (void)loadImageForURL:(NSURL * _Nonnull)url progress:(void (^)(float progress))progress image:(void (^)(UIImage * __nullable image, NSError * __nullable error))imageBlock; 42 | 43 | /// Cancel all image requests 44 | - (void)cancelAllRequests; 45 | 46 | /// Cancel image request 47 | /// @param url remote image url 48 | - (void)cancelRequestForUrl:(NSURL *)url; 49 | 50 | NS_ASSUME_NONNULL_END 51 | 52 | @end -------------------------------------------------------------------------------- /FSImageViewerDemo/FSDemoViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /FSImageViewer/FSBasicImage.m: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 9/04/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import "FSBasicImage.h" 26 | 27 | @implementation FSBasicImage 28 | 29 | @synthesize URL = _URL; 30 | @synthesize title = _title; 31 | @synthesize size = _size; 32 | @synthesize image = _image; 33 | @synthesize failed = _failed; 34 | 35 | - (instancetype)initWithImageURL:(NSURL *)aURL name:(NSString *)aName image:(UIImage *)aImage { 36 | self = [super init]; 37 | if (self) { 38 | _URL = aURL; 39 | _title = aName; 40 | self.image = aImage; 41 | 42 | } 43 | return self; 44 | } 45 | 46 | - (instancetype)initWithImageURL:(NSURL *)aURL name:(NSString *)aName { 47 | return [self initWithImageURL:aURL name:aName image:nil]; 48 | } 49 | 50 | - (instancetype)initWithImageURL:(NSURL *)aURL { 51 | return [self initWithImageURL:aURL name:nil image:nil]; 52 | } 53 | 54 | - (instancetype)initWithImage:(UIImage *)aImage { 55 | return [self initWithImageURL:nil name:nil image:aImage]; 56 | } 57 | 58 | - (instancetype)initWithImage:(UIImage *)aImage name:(NSString *)aName { 59 | return [self initWithImageURL:nil name:aName image:aImage]; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /FSImageViewerTests/FSBasicImageSourceTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // FSBasicImageSourceTests.m 3 | // FSImageViewer 4 | // 5 | // Created by Felix Schulze on 19.09.15. 6 | // Copyright © 2015 Felix Schulze. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | // 26 | 27 | #import "FSBasicImageSource.h" 28 | #import "FSImage.h" 29 | #import "FSBasicImage.h" 30 | 31 | @interface FSBasicImageSourceTests : XCTestCase 32 | 33 | @end 34 | 35 | @implementation FSBasicImageSourceTests 36 | 37 | - (void)testObjectSubscription { 38 | id imageMock = OCMProtocolMock(@protocol(FSImage)); 39 | NSArray> *basicImageSource = @[imageMock]; 40 | FSBasicImageSource *imageSource = [[FSBasicImageSource alloc] initWithImages:basicImageSource]; 41 | 42 | XCTAssertEqual(imageSource.numberOfImages, 1); 43 | XCTAssertEqual(imageSource[0], imageMock); 44 | } 45 | 46 | - (void)testObjectSubscriptionWithBasicImage { 47 | FSBasicImage *basicImage = [[FSBasicImage alloc] initWithImageURL:[NSURL URLWithString:@"http://example.com"]]; 48 | NSArray> *basicImageSource = @[basicImage]; 49 | FSBasicImageSource *imageSource = [[FSBasicImageSource alloc] initWithImages:basicImageSource]; 50 | 51 | XCTAssertEqual(imageSource.numberOfImages, 1); 52 | XCTAssertEqual(imageSource[0], basicImage); 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /FSImageViewer.xcworkspace/xcshareddata/xcschemes/FSImageViewerDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 67 | 68 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /FSImageViewerTests/FSImageTitleViewTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // FSImageViewerTests.m 3 | // FSImageViewerTests 4 | // 5 | // Created by Felix Schulze on 01.06.14. 6 | // Copyright (c) 2014 Felix Schulze. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | // 26 | 27 | #import "FSImageTitleView.h" 28 | 29 | @interface FSImageTitleViewTests : FBSnapshotTestCase 30 | @end 31 | 32 | @implementation FSImageTitleViewTests 33 | 34 | - (void)setUp { 35 | [super setUp]; 36 | 37 | self.recordMode = NO; 38 | } 39 | 40 | 41 | - (void)testShortTitle { 42 | FSImageTitleView *view = [[FSImageTitleView alloc] initWithFrame:CGRectMake(0, 0, 280, 40)]; 43 | view.text = @"Some title"; 44 | FBSnapshotVerifyView(view, nil); 45 | XCTAssertFalse(view.isHidden); 46 | } 47 | 48 | - (void)testNormalTitle { 49 | FSImageTitleView *view = [[FSImageTitleView alloc] initWithFrame:CGRectMake(0, 0, 280, 40)]; 50 | view.text = @"Some longer title with some lorem ipsum text"; 51 | FBSnapshotVerifyView(view, nil); 52 | XCTAssertFalse(view.isHidden); 53 | } 54 | 55 | - (void)testLongTitle { 56 | FSImageTitleView *view = [[FSImageTitleView alloc] initWithFrame:CGRectMake(0, 0, 280, 40)]; 57 | view.text = @"Some longer title with some lorem ipsum text and some other lorem ipsum and another lorem ipsum text"; 58 | FBSnapshotVerifyView(view, nil); 59 | XCTAssertFalse(view.isHidden); 60 | } 61 | 62 | - (void)testIsHiddenIfNoText { 63 | FSImageTitleView *view = [[FSImageTitleView alloc] initWithFrame:CGRectMake(0, 0, 280, 40)]; 64 | view.text = nil; 65 | XCTAssertTrue(view.isHidden); 66 | } 67 | 68 | - (void)testIsHiddenIfTextContainsOnlyWhitespace { 69 | FSImageTitleView *view = [[FSImageTitleView alloc] initWithFrame:CGRectMake(0, 0, 280, 40)]; 70 | view.text = @" "; 71 | XCTAssertTrue(view.isHidden); 72 | } 73 | 74 | @end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FSImageViewer [![Build Status](https://travis-ci.org/x2on/FSImageViewer.png)](https://travis-ci.org/x2on/FSImageViewer) [![Cocoa Pod Platform](https://img.shields.io/cocoapods/p/FSImageViewer.svg)](http://cocoadocs.org/docsets/FSImageViewer/) [![Cocoa Pod Version](https://img.shields.io/cocoapods/v/FSImageViewer.svg)](http://cocoadocs.org/docsets/FSImageViewer/) [![License](https://img.shields.io/cocoapods/l/FSImageViewer.svg)](http://opensource.org/licenses/MIT) 2 | 3 | ![Screenshot](https://raw.github.com/x2on/FSImageViewer/master/screen.png) 4 | 5 | FSImageViewer is a photo viewer (gallery) for iOS. 6 | 7 | It's initially based on [EGOPhotoViewer](https://raw.github.com/enormego/PhotoViewer), but completely refactored to use ARC, [AFNetworking 2.5+](https://github.com/AFNetworking/AFNetworking) for remote image downloads and [EGOCache 2.1](https://github.com/enormego/EGOCache) for image caching. 8 | 9 | If you must use AFNetworking 1.3 you can use the 1.x version of FSImageViewer (https://github.com/x2on/FSImageViewer/tree/1.x) 10 | 11 | ## Install 12 | Using [CocoaPods](http://cocoapods.org/): 13 | 14 | `pod 'FSImageViewer', '~> 3.4'` 15 | 16 | ## Basic usage 17 | 18 | Create your image objects: 19 | 20 | ```objc 21 | FSBasicImage *firstPhoto = [[FSBasicImage alloc] initWithImageURL:[NSURL URLWithString:@"http://example.com/1.jpg"] name:@"Photo 1"]; 22 | FSBasicImage *secondPhoto = [[FSBasicImage alloc] initWithImageURL:[NSURL URLWithString:@"http://example.com/2.jpg"] name:@"Photo 2"]; 23 | ``` 24 | 25 | And add them to the data source: 26 | 27 | ```objc 28 | FSBasicImageSource *photoSource = [[FSBasicImageSource alloc] initWithImages:@[firstPhoto, secondPhoto]]; 29 | ``` 30 | 31 | And create and show the view controller: 32 | ```objc 33 | FSImageViewerViewController *imageViewController = [[FSImageViewerViewController alloc] initWithImageSource:photoSource]; 34 | [self.navigationController pushViewController:imageViewController animated:YES]; 35 | ``` 36 | 37 | If you like to use a modal view controller: 38 | ```objc 39 | FSImageViewerViewController *imageViewController = [[FSImageViewerViewController alloc] initWithImageSource:photoSource]; 40 | UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:imageViewController]; 41 | [self.navigationController presentViewController:navigationController animated:YES completion:nil]; 42 | ``` 43 | 44 | ## Advanced usage 45 | 46 | You can also create your own image class by implementing the `FSImage` protocol and your own datasource by implementing the `FSImageSource` protocol. 47 | 48 | ## Demo 49 | 50 | The demo project uses [CocoaPods](http://cocoapods.org/) for dependency management. 51 | 52 | Install dependencies:`pod install` 53 | 54 | ## System support 55 | iOS 7.0+ is currently supported. 56 | 57 | If you must support iOS 5.0+ you can use the 1.x version of FSImageViewer. 58 | If you must support iOS 6.0+ you can use the 2.x version of FSImageViewer. 59 | 60 | ## License 61 | 62 | FSImageViewer is available under the MIT license. See the LICENSE file for more info. 63 | 64 | -------------------------------------------------------------------------------- /FSImageViewerTests/FSImageViewTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // FSImageViewTests.m 3 | // FSImageViewer 4 | // 5 | // Created by Felix Schulze on 19.09.15. 6 | // Copyright © 2015 Felix Schulze. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | // 26 | #import "FSImageView.h" 27 | #import "FSImage.h" 28 | #import "FSTestImageHelper.h" 29 | 30 | @interface FSImageViewTests : FBSnapshotTestCase 31 | 32 | @end 33 | 34 | @implementation FSImageViewTests 35 | 36 | - (void)setUp { 37 | [super setUp]; 38 | 39 | self.recordMode = NO; 40 | } 41 | 42 | - (void)testEmptyView { 43 | FSImageView *view = [[FSImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)]; 44 | FBSnapshotVerifyView(view, nil); 45 | } 46 | 47 | - (void)testEmptyViewWithProgressViewColor { 48 | FSImageView *view = [[FSImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)]; 49 | [view changeProgressViewColor:[UIColor greenColor]]; 50 | FBSnapshotVerifyView(view, nil); 51 | } 52 | 53 | - (void)testView { 54 | id mock = OCMProtocolMock(@protocol(FSImage)); 55 | OCMStub([mock image]).andReturn([FSTestImageHelper imageNamed:@"Testimage.jpg"]); 56 | FSImageView *view = [[FSImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)]; 57 | view.image = mock; 58 | FBSnapshotVerifyView(view, nil); 59 | } 60 | 61 | - (void)testViewWithBackgroundColor { 62 | id mock = OCMProtocolMock(@protocol(FSImage)); 63 | OCMStub([mock image]).andReturn([FSTestImageHelper imageNamed:@"Testimage.jpg"]); 64 | FSImageView *view = [[FSImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)]; 65 | view.image = mock; 66 | [view changeBackgroundColor:[UIColor greenColor]]; 67 | FBSnapshotVerifyView(view, nil); 68 | } 69 | 70 | - (void)testFailedView { 71 | id mock = OCMProtocolMock(@protocol(FSImage)); 72 | OCMStub([mock didFail]).andReturn(YES); 73 | FSImageView *view = [[FSImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)]; 74 | view.image = mock; 75 | FBSnapshotVerifyView(view, nil); 76 | } 77 | 78 | 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /FSImageViewerDemo/FSDemoViewController.m: -------------------------------------------------------------------------------- 1 | // FSImageViewerDemo 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import "FSDemoViewController.h" 26 | #import "FSBasicImage.h" 27 | #import "FSBasicImageSource.h" 28 | 29 | @implementation FSDemoViewController 30 | 31 | - (id)init { 32 | self = [super initWithNibName:@"FSDemoViewController" bundle:nil]; 33 | if (self) { 34 | } 35 | return self; 36 | } 37 | 38 | - (void)viewDidLoad { 39 | [super viewDidLoad]; 40 | } 41 | 42 | - (void)viewDidAppear:(BOOL)animated { 43 | [super viewDidAppear:animated]; 44 | 45 | if (!_imageViewController) { 46 | [self openGallery]; 47 | } 48 | } 49 | 50 | - (IBAction)openGallery { 51 | FSBasicImage *firstPhoto = [[FSBasicImage alloc] initWithImageURL:[NSURL URLWithString:@"http://farm8.staticflickr.com/7319/9668947331_3112b1fcca_b.jpg"] name:@"Photo by Brian Adamson"]; 52 | FSBasicImage *secondPhoto = [[FSBasicImage alloc] initWithImageURL:[NSURL URLWithString:@"http://farm9.staticflickr.com/8023/6971840814_68614eba26_b.jpg"] name:@"Photo by Ben Fredericson (http://farm9.staticflickr.com/8023/6971840814_68614eba26_b.jpg)"]; 53 | FSBasicImage *thirdPhoto = [[FSBasicImage alloc] initWithImageURL:[NSURL URLWithString:@"http://farm5.staticflickr.com/4094/4888398067_36e695177a_o.jpg"] name:@"Photo by digital cat (https://www.flickr.com/photos/14646075@N03/4888398067)"]; 54 | 55 | FSBasicImage *failingPhoto = [[FSBasicImage alloc] initWithImageURL:[NSURL URLWithString:@"http://example.com/1.jpg"] name:@"Failure image"]; 56 | 57 | FSBasicImageSource *photoSource = [[FSBasicImageSource alloc] initWithImages:@[firstPhoto, secondPhoto, thirdPhoto, failingPhoto]]; 58 | self.imageViewController = [[FSImageViewerViewController alloc] initWithImageSource:photoSource]; 59 | 60 | _imageViewController.delegate = self; 61 | 62 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { 63 | UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:_imageViewController]; 64 | [self.navigationController presentViewController:navigationController animated:YES completion:nil]; 65 | } 66 | else { 67 | [self.navigationController pushViewController:_imageViewController animated:YES]; 68 | } 69 | } 70 | 71 | - (void)imageViewerViewController:(FSImageViewerViewController *)imageViewerViewController didMoveToImageAtIndex:(NSInteger)index { 72 | NSLog(@"FSImageViewerViewController: %@ didMoveToImageAtIndex: %li",imageViewerViewController, (long)index); 73 | } 74 | @end 75 | -------------------------------------------------------------------------------- /FSImageViewerDemo/FSAppDelegate.m: -------------------------------------------------------------------------------- 1 | // FSImageViewerDemo 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import "FSAppDelegate.h" 26 | #import "FSDemoViewController.h" 27 | 28 | @implementation FSAppDelegate 29 | 30 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 31 | if (!getenv("UNIT_TEST")) { 32 | FSDemoViewController *viewController = [[FSDemoViewController alloc] init]; 33 | self.navigationController = [[UINavigationController alloc] initWithRootViewController:viewController]; 34 | } 35 | else { 36 | self.navigationController = [[UINavigationController alloc] initWithRootViewController:[[UIViewController alloc] init]]; 37 | } 38 | 39 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 40 | self.window.rootViewController = _navigationController; 41 | [self.window makeKeyAndVisible]; 42 | 43 | return YES; 44 | } 45 | 46 | - (void)applicationWillResignActive:(UIApplication *)application { 47 | // 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. 48 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 49 | } 50 | 51 | - (void)applicationDidEnterBackground:(UIApplication *)application { 52 | // 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. 53 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 54 | } 55 | 56 | - (void)applicationWillEnterForeground:(UIApplication *)application { 57 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 58 | } 59 | 60 | - (void)applicationDidBecomeActive:(UIApplication *)application { 61 | // 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. 62 | } 63 | 64 | - (void)applicationWillTerminate:(UIApplication *)application { 65 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /FSImageViewer.xcworkspace/xcshareddata/xcschemes/FSImageViewer.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 57 | 58 | 59 | 60 | 61 | 62 | 72 | 73 | 79 | 80 | 81 | 82 | 83 | 84 | 90 | 91 | 97 | 98 | 99 | 100 | 102 | 103 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /FSImageViewer/FSImageScrollView.m: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import "FSImageScrollView.h" 26 | 27 | @implementation FSImageScrollView 28 | 29 | - (instancetype)initWithFrame:(CGRect)frame { 30 | self = [super initWithFrame:frame]; 31 | if (self) { 32 | self.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin; 33 | self.scrollEnabled = NO; 34 | self.pagingEnabled = NO; 35 | self.clipsToBounds = NO; 36 | self.maximumZoomScale = 3.0f; 37 | self.minimumZoomScale = 1.0f; 38 | self.showsVerticalScrollIndicator = NO; 39 | self.showsHorizontalScrollIndicator = NO; 40 | self.bounces = YES; 41 | self.alwaysBounceVertical = NO; 42 | self.alwaysBounceHorizontal = NO; 43 | self.bouncesZoom = YES; 44 | self.scrollsToTop = NO; 45 | self.backgroundColor = [UIColor whiteColor]; 46 | self.decelerationRate = UIScrollViewDecelerationRateFast; 47 | } 48 | return self; 49 | } 50 | 51 | - (void)zoomRectWithCenter:(CGPoint)center{ 52 | 53 | if (self.zoomScale > 1.0f) { 54 | self.scrollEnabled = NO; 55 | [((FSImageView *)self.superview) killScrollViewZoom]; 56 | return; 57 | } 58 | self.scrollEnabled = YES; 59 | CGRect rect; 60 | rect.size = CGSizeMake(self.frame.size.width / kFSImageViewerZoomScale, self.frame.size.height / kFSImageViewerZoomScale); 61 | rect.origin.x = MAX((center.x - (rect.size.width / 2.0f)), 0.0f); 62 | rect.origin.y = MAX((center.y - (rect.size.height / 2.0f)), 0.0f); 63 | 64 | CGRect frame = [self.superview convertRect:self.frame toView:self.superview]; 65 | CGFloat borderX = frame.origin.x; 66 | CGFloat borderY = frame.origin.y; 67 | 68 | if (borderX > 0.0f && (center.x < borderX || center.x > self.frame.size.width - borderX)) { 69 | 70 | if (center.x < (self.frame.size.width / 2.0f)) { 71 | rect.origin.x += (borderX/kFSImageViewerZoomScale); 72 | } else { 73 | rect.origin.x -= ((borderX/kFSImageViewerZoomScale) + rect.size.width); 74 | } 75 | } 76 | 77 | if (borderY > 0.0f && (center.y < borderY || center.y > self.frame.size.height - borderY)) { 78 | if (center.y < (self.frame.size.height / 2.0f)) { 79 | rect.origin.y += (borderY/kFSImageViewerZoomScale); 80 | } else { 81 | rect.origin.y -= ((borderY/kFSImageViewerZoomScale) + rect.size.height); 82 | } 83 | } 84 | 85 | [self zoomToRect:rect animated:YES]; 86 | } 87 | 88 | - (void)toggleBars{ 89 | [[NSNotificationCenter defaultCenter] postNotificationName:kFSImageViewerToogleBarsNotificationKey object:nil]; 90 | } 91 | 92 | #pragma mark - Touches 93 | 94 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 95 | [super touchesBegan:touches withEvent:event]; 96 | } 97 | 98 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ 99 | [super touchesEnded:touches withEvent:event]; 100 | UITouch *touch = [touches anyObject]; 101 | 102 | if (touch.tapCount == 1) { 103 | [self performSelector:@selector(toggleBars) withObject:nil afterDelay:.2]; 104 | } else if (touch.tapCount == 2) { 105 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(toggleBars) object:nil]; 106 | [self zoomRectWithCenter:[[touches anyObject] locationInView:self]]; 107 | } 108 | } 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /FSImageViewer/FSImageViewerViewController.h: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | #import 25 | #import "FSImageViewer.h" 26 | #import "FSImageSource.h" 27 | #import "FSTitleView.h" 28 | 29 | NS_ASSUME_NONNULL_BEGIN 30 | 31 | @class FSImageViewerViewController; 32 | 33 | // Optional Delegate for getting current presented image index. 34 | @protocol FSImageViewerViewControllerDelegate 35 | 36 | @optional 37 | 38 | // Called if moved to the image at the given index. 39 | - (void)imageViewerViewController:(FSImageViewerViewController *)imageViewerViewController didMoveToImageAtIndex:(NSInteger)index; 40 | 41 | - (void)imageViewerViewController:(FSImageViewerViewController *)imageViewerViewController willDismissViewControllerAnimated:(BOOL)animated; 42 | - (void)imageViewerViewController:(FSImageViewerViewController *)imageViewerViewController didDismissViewControllerAnimated:(BOOL)animated; 43 | 44 | @end 45 | 46 | /// FSImageViewerViewController is an UIViewController which can present images. 47 | @interface FSImageViewerViewController : UIViewController 48 | 49 | /// @param imageSource image data source 50 | - (instancetype)initWithImageSource:(id )imageSource; 51 | 52 | /// @param imageSource image data source 53 | /// @param imageIndex the index of the first shown image 54 | - (instancetype)initWithImageSource:(id )imageSource imageIndex:(NSInteger)imageIndex; 55 | 56 | /// Image data source 57 | @property(strong, nonatomic, readonly) id imageSource; 58 | 59 | /// Title 60 | @property(strong, nonatomic, nullable) UIView* titleView; 61 | 62 | /// Optional Delegate 63 | @property(weak, nonatomic, nullable) id delegate; 64 | 65 | /// FSImageView array 66 | @property(strong, nonatomic, nullable) NSMutableArray *imageViews; 67 | 68 | /// Main scrollView 69 | @property(strong, nonatomic, nullable) UIScrollView *scrollView; 70 | 71 | /// Display a "x of y" images in the navigation title - Default is YES 72 | @property(assign, nonatomic) BOOL showNumberOfItemsInTitle; 73 | 74 | /// Disable image sharing - Default is NO 75 | @property(assign, nonatomic, getter = isSharingDisabled) BOOL sharingDisabled; 76 | 77 | /// Override rotation of images - Default is YES 78 | @property(assign, nonatomic, getter = isRotationEnabled) BOOL rotationEnabled; 79 | 80 | /// Override the background color when overlay is hidden - Default is black 81 | @property(strong, nonatomic, nullable) UIColor *backgroundColorHidden; 82 | 83 | /// Override the background color when overlay is visible - Default is white 84 | @property(strong, nonatomic, nullable) UIColor *backgroundColorVisible; 85 | 86 | /// Override the progressView color when overlay is hidden - Default is white 87 | @property(strong, nonatomic, nullable) UIColor *progressColorHidden; 88 | 89 | /// Override the progressView color when overlay is visible - Default is darkGrayColor 90 | @property(strong, nonatomic, nullable) UIColor *progressColorVisible; 91 | 92 | /// Used to add additional items to the "share" button 93 | @property(strong, nonatomic, nullable) NSArray* applicationActivities; 94 | 95 | /// Current index of the image displayed 96 | /// @return current index of the image displayed 97 | - (NSInteger)currentImageIndex; 98 | 99 | /// Move the FSImageView to the index 100 | /// @param index index move to 101 | /// @param animated should the movevement animated 102 | - (void)moveToImageAtIndex:(NSInteger)index animated:(BOOL)animated; 103 | 104 | NS_ASSUME_NONNULL_END 105 | 106 | @end -------------------------------------------------------------------------------- /FSImageViewer/FSPlaceholderImages.m: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import "FSPlaceholderImages.h" 26 | 27 | @implementation FSPlaceholderImages 28 | 29 | + (UIImage *)errorImage { 30 | UIImage *errorImage; 31 | UIGraphicsBeginImageContextWithOptions(CGSizeMake(320, 480), NO, [[UIScreen mainScreen] scale]); 32 | 33 | UIColor *signColor = [UIColor colorWithRed:0.333 green:0.333 blue:0.333 alpha:1]; 34 | UIColor *bangColor = [UIColor whiteColor]; 35 | 36 | UIBezierPath *signPath = [UIBezierPath bezierPath]; 37 | [signPath moveToPoint:CGPointMake(161.99, 183.24)]; 38 | [signPath addLineToPoint:CGPointMake(102.02, 281.82)]; 39 | [signPath addCurveToPoint:CGPointMake(102.02, 287.92) controlPoint1:CGPointMake(100.87, 283.71) controlPoint2:CGPointMake(100.87, 286.03)]; 40 | [signPath addCurveToPoint:CGPointMake(107.57, 290.95) controlPoint1:CGPointMake(103.15, 289.78) controlPoint2:CGPointMake(105.27, 290.95)]; 41 | [signPath addLineToPoint:CGPointMake(227.51, 290.95)]; 42 | [signPath addCurveToPoint:CGPointMake(233.06, 287.92) controlPoint1:CGPointMake(229.79, 290.95) controlPoint2:CGPointMake(231.91, 289.78)]; 43 | [signPath addCurveToPoint:CGPointMake(233.06, 281.82) controlPoint1:CGPointMake(234.19, 286.03) controlPoint2:CGPointMake(234.19, 283.71)]; 44 | [signPath addLineToPoint:CGPointMake(173.1, 183.24)]; 45 | [signPath addCurveToPoint:CGPointMake(167.54, 180.18) controlPoint1:CGPointMake(171.95, 181.36) controlPoint2:CGPointMake(169.82, 180.18)]; 46 | [signPath addCurveToPoint:CGPointMake(161.99, 183.24) controlPoint1:CGPointMake(165.26, 180.18) controlPoint2:CGPointMake(163.14, 181.36)]; 47 | [signPath closePath]; 48 | signPath.miterLimit = 4; 49 | 50 | [signColor setFill]; 51 | [signPath fill]; 52 | 53 | 54 | UIBezierPath *bangPath = [UIBezierPath bezierPath]; 55 | [bangPath moveToPoint:CGPointMake(164.98, 284.2)]; 56 | [bangPath addCurveToPoint:CGPointMake(159.93, 276.55) controlPoint1:CGPointMake(162.12, 282.83) controlPoint2:CGPointMake(159.93, 279.52)]; 57 | [bangPath addCurveToPoint:CGPointMake(171.15, 268.46) controlPoint1:CGPointMake(159.93, 271.03) controlPoint2:CGPointMake(165.7, 266.87)]; 58 | [bangPath addCurveToPoint:CGPointMake(177.3, 276.63) controlPoint1:CGPointMake(174.83, 269.53) controlPoint2:CGPointMake(177.33, 272.85)]; 59 | [bangPath addCurveToPoint:CGPointMake(173.05, 283.87) controlPoint1:CGPointMake(177.27, 279.84) controlPoint2:CGPointMake(175.88, 282.21)]; 60 | [bangPath addCurveToPoint:CGPointMake(164.98, 284.2) controlPoint1:CGPointMake(170.88, 285.14) controlPoint2:CGPointMake(167.25, 285.29)]; 61 | [bangPath closePath]; 62 | [bangPath moveToPoint:CGPointMake(165.63, 262.4)]; 63 | [bangPath addCurveToPoint:CGPointMake(163.76, 260.87) controlPoint1:CGPointMake(165.28, 262.2) controlPoint2:CGPointMake(164.43, 261.51)]; 64 | [bangPath addCurveToPoint:CGPointMake(159.88, 234.65) controlPoint1:CGPointMake(161.41, 258.63) controlPoint2:CGPointMake(161.41, 258.65)]; 65 | [bangPath addCurveToPoint:CGPointMake(159.09, 211.45) controlPoint1:CGPointMake(158.51, 213.31) controlPoint2:CGPointMake(158.5, 212.98)]; 66 | [bangPath addCurveToPoint:CGPointMake(168.22, 206.75) controlPoint1:CGPointMake(160.4, 208) controlPoint2:CGPointMake(163.33, 206.49)]; 67 | [bangPath addCurveToPoint:CGPointMake(175.26, 209.96) controlPoint1:CGPointMake(171.32, 206.91) controlPoint2:CGPointMake(173.71, 208)]; 68 | [bangPath addCurveToPoint:CGPointMake(176.87, 215.27) controlPoint1:CGPointMake(176.59, 211.64) controlPoint2:CGPointMake(176.88, 212.59)]; 69 | [bangPath addCurveToPoint:CGPointMake(174.75, 256.05) controlPoint1:CGPointMake(176.87, 218.18) controlPoint2:CGPointMake(175.02, 253.87)]; 70 | [bangPath addCurveToPoint:CGPointMake(170.29, 262.27) controlPoint1:CGPointMake(174.41, 258.92) controlPoint2:CGPointMake(172.74, 261.24)]; 71 | [bangPath addCurveToPoint:CGPointMake(165.63, 262.4) controlPoint1:CGPointMake(168.89, 262.85) controlPoint2:CGPointMake(166.58, 262.92)]; 72 | [bangPath closePath]; 73 | bangPath.miterLimit = 4; 74 | 75 | [bangColor setFill]; 76 | [bangPath fill]; 77 | 78 | errorImage = UIGraphicsGetImageFromCurrentImageContext(); 79 | UIGraphicsEndImageContext(); 80 | return errorImage; 81 | } 82 | @end -------------------------------------------------------------------------------- /FSImageViewer/FSImageLoader.m: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import 26 | #import 27 | #import "FSImageLoader.h" 28 | #import "AFHTTPRequestOperation.h" 29 | 30 | @implementation FSImageLoader { 31 | NSMutableArray *runningRequests; 32 | } 33 | 34 | + (FSImageLoader *)sharedInstance { 35 | static FSImageLoader *sharedInstance = nil; 36 | static dispatch_once_t onceToken = 0; 37 | dispatch_once(&onceToken, ^{ 38 | sharedInstance = [[FSImageLoader alloc] init]; 39 | }); 40 | return sharedInstance; 41 | } 42 | 43 | - (instancetype)init { 44 | self = [super init]; 45 | if (self) { 46 | self.timeoutInterval = 30.0; 47 | runningRequests = [[NSMutableArray alloc] init]; 48 | } 49 | 50 | return self; 51 | } 52 | 53 | - (void)dealloc { 54 | [self cancelAllRequests]; 55 | } 56 | 57 | - (void)cancelAllRequests { 58 | for (AFHTTPRequestOperation *imageRequestOperation in runningRequests) { 59 | [imageRequestOperation cancel]; 60 | } 61 | } 62 | 63 | - (void)cancelRequestForUrl:(NSURL *)aURL { 64 | for (AFHTTPRequestOperation *imageRequestOperation in runningRequests) { 65 | if ([imageRequestOperation.request.URL isEqual:aURL]) { 66 | [imageRequestOperation cancel]; 67 | break; 68 | } 69 | } 70 | } 71 | 72 | - (void)loadImageForURL:(NSURL *)aURL progress:(void (^)(float progress))progress image:(void (^)(UIImage *image, NSError *error))imageBlock { 73 | 74 | if (!aURL) { 75 | NSError *error = [NSError errorWithDomain:@"de.felixschulze.fsimageloader" code:412 userInfo:@{ 76 | NSLocalizedDescriptionKey : @"You must set a url" 77 | }]; 78 | imageBlock(nil, error); 79 | }; 80 | 81 | NSString *urlString = [[aURL absoluteString] copy]; 82 | NSData *data = [urlString dataUsingEncoding:NSUTF8StringEncoding]; 83 | uint8_t digest[CC_SHA1_DIGEST_LENGTH]; 84 | CC_SHA1(data.bytes, (CC_LONG)data.length, digest); 85 | NSMutableString *urlStringSha1 = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2]; 86 | for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) { 87 | [urlStringSha1 appendFormat:@"%02x", digest[i]]; 88 | } 89 | NSString *cacheKey = [NSString stringWithFormat:@"FSImageLoader-%@", [urlStringSha1 copy]]; 90 | UIImage *anImage = [[EGOCache globalCache] imageForKey:cacheKey]; 91 | 92 | if (!anImage) { 93 | // Deprecated cacheKey 94 | NSString *deprecatedCacheKey = [NSString stringWithFormat:@"FSImageLoader-%lu", (unsigned long) [[aURL description] hash]]; 95 | anImage = [[EGOCache globalCache] imageForKey:deprecatedCacheKey]; 96 | } 97 | 98 | 99 | if (anImage) { 100 | if (imageBlock) { 101 | imageBlock(anImage, nil); 102 | } 103 | } 104 | else { 105 | [self cancelRequestForUrl:aURL]; 106 | 107 | NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:aURL cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:_timeoutInterval]; 108 | AFHTTPRequestOperation *imageRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest]; 109 | imageRequestOperation.responseSerializer = [AFImageResponseSerializer serializer]; 110 | [runningRequests addObject:imageRequestOperation]; 111 | __weak AFHTTPRequestOperation *imageRequestOperationForBlock = imageRequestOperation; 112 | 113 | [imageRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { 114 | UIImage *image = responseObject; 115 | [[EGOCache globalCache] setImage:image forKey:cacheKey]; 116 | if (imageBlock) { 117 | imageBlock(image, nil); 118 | } 119 | [runningRequests removeObject:imageRequestOperationForBlock]; 120 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 121 | if (imageBlock) { 122 | imageBlock(nil, error); 123 | } 124 | [runningRequests removeObject:imageRequestOperationForBlock]; 125 | }]; 126 | 127 | [imageRequestOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) { 128 | if (progress) { 129 | progress((float)totalBytesRead / totalBytesExpectedToRead); 130 | } 131 | }]; 132 | 133 | [imageRequestOperation start]; 134 | } 135 | } 136 | 137 | @end -------------------------------------------------------------------------------- /FSImageViewer/FSImageTitleView.m: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import 26 | #import "FSImageTitleView.h" 27 | 28 | @implementation FSImageTitleView { 29 | UILabel *textLabel; 30 | BOOL hidden; 31 | } 32 | 33 | - (instancetype)initWithFrame:(CGRect)frame { 34 | if ((self = [super initWithFrame:frame])) { 35 | 36 | self.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.3f]; 37 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; 38 | 39 | textLabel = [[UILabel alloc] initWithFrame:CGRectMake(20.0f, 0.0f, self.frame.size.width - 40.0f, 40.0f)]; 40 | textLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; 41 | textLabel.numberOfLines = 0; 42 | textLabel.lineBreakMode = NSLineBreakByWordWrapping; 43 | textLabel.backgroundColor = [UIColor clearColor]; 44 | textLabel.textAlignment = NSTextAlignmentCenter; 45 | textLabel.textColor = [UIColor whiteColor]; 46 | textLabel.shadowColor = [UIColor blackColor]; 47 | textLabel.shadowOffset = CGSizeMake(0.0f, 1.0f); 48 | [self addSubview:textLabel]; 49 | } 50 | return self; 51 | } 52 | 53 | - (void)layoutSubviews { 54 | textLabel.frame = CGRectMake(20.0f, 0.0f, self.frame.size.width - 40.0f, 40.0f); 55 | [self setNeedsDisplay]; 56 | } 57 | 58 | - (void)drawRect:(CGRect)rect { 59 | CGContextRef ctx = UIGraphicsGetCurrentContext(); 60 | [[UIColor colorWithWhite:1.0f alpha:0.8f] setStroke]; 61 | CGContextMoveToPoint(ctx, 0.0f, 0.0f); 62 | CGContextAddLineToPoint(ctx, self.frame.size.width, 0.0f); 63 | CGContextStrokePath(ctx); 64 | } 65 | 66 | 67 | 68 | - (void)setText:(NSString *)text { 69 | _text = text; 70 | textLabel.font = nil; 71 | if (_text == nil || [_text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]].length == 0) { 72 | textLabel.text = nil; 73 | self.hidden = YES; 74 | } 75 | else { 76 | textLabel.text = _text; 77 | self.hidden = NO; 78 | if (!_adjustsFontSizeToFitWidth) { 79 | [self adjustFontSizeToFit]; 80 | } 81 | } 82 | } 83 | 84 | - (void)updateMetadata:(NSString *)text index:(NSInteger)index total:(NSInteger)total { 85 | self.text = text; 86 | } 87 | 88 | - (void) setAdjustsFontSizeToFitWidth:(BOOL)adjustsFontSizeToFitWidth { 89 | textLabel.adjustsFontSizeToFitWidth = adjustsFontSizeToFitWidth; 90 | _adjustsFontSizeToFitWidth = adjustsFontSizeToFitWidth; 91 | } 92 | 93 | - (void)hideView:(BOOL)value { 94 | if (hidden == value) { 95 | return; 96 | } 97 | 98 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { 99 | [UIView animateWithDuration:0.3f animations:^{ 100 | self.alpha = value ? 0.0f : 1.0f; 101 | }]; 102 | hidden = value; 103 | } 104 | else { 105 | if (value) { 106 | [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionCurveEaseIn animations:^{ 107 | self.frame = CGRectMake(0.0f, self.superview.frame.size.height, self.frame.size.width, self.frame.size.height); 108 | } completion:nil]; 109 | } 110 | else { 111 | [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionCurveEaseOut animations:^{ 112 | self.frame = CGRectMake(0.0f, self.superview.frame.size.height - self.frame.size.height, self.frame.size.width, self.frame.size.height); 113 | } completion:nil]; 114 | } 115 | } 116 | hidden = value; 117 | } 118 | 119 | - (void)setHidden:(BOOL)aHidden { 120 | hidden = aHidden; 121 | } 122 | 123 | - (BOOL)isHidden { 124 | return hidden; 125 | } 126 | 127 | - (void) adjustTextViewSize:(CGRect)imageViewControllerBounds { 128 | self.frame = CGRectMake(0.0f, imageViewControllerBounds.size.height - 40.0f, imageViewControllerBounds.size.width, 40.0f); 129 | } 130 | 131 | - (void)adjustFontSizeToFit { 132 | 133 | for (int i = textLabel.font.pointSize; i>textLabel.minimumScaleFactor * textLabel.font.pointSize; i--) { 134 | 135 | UIFont *font = [UIFont fontWithName:textLabel.font.fontName size:(CGFloat)i]; 136 | NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:textLabel.text attributes:@{NSFontAttributeName: font}]; 137 | 138 | CGRect rectSize = [attributedText boundingRectWithSize:CGSizeMake(self.frame.size.width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin context:nil]; 139 | 140 | if (rectSize.size.height <= self.frame.size.height) { 141 | textLabel.font = [UIFont fontWithName:textLabel.font.fontName size:(CGFloat)i]; 142 | break; 143 | } 144 | } 145 | } 146 | 147 | @end 148 | -------------------------------------------------------------------------------- /FSImageViewer/FSImageView.m: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | #import 25 | #import "FSImageView.h" 26 | #import "FSPlaceholderImages.h" 27 | #import "FSImageScrollView.h" 28 | 29 | #define ZOOM_VIEW_TAG 0x101 30 | #define MB_FILE_SIZE 1024*1024 31 | 32 | @interface RotateGesture : UIRotationGestureRecognizer { 33 | } 34 | @end 35 | 36 | @implementation RotateGesture 37 | - (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer *)gesture { 38 | return NO; 39 | } 40 | 41 | - (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer { 42 | return YES; 43 | } 44 | @end 45 | 46 | @interface FSImageView() 47 | 48 | @property (nonatomic, strong) UAProgressView *progressView; 49 | 50 | @end 51 | 52 | @implementation FSImageView { 53 | CGFloat beginRadians; 54 | } 55 | 56 | - (instancetype)initWithFrame:(CGRect)frame { 57 | if ((self = [super initWithFrame:frame])) { 58 | 59 | self.backgroundColor = [UIColor whiteColor]; 60 | self.userInteractionEnabled = NO; 61 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 62 | self.opaque = YES; 63 | self.rotationEnabled = YES; 64 | 65 | FSImageScrollView *scrollView = [[FSImageScrollView alloc] initWithFrame:self.bounds]; 66 | scrollView.backgroundColor = [UIColor whiteColor]; 67 | scrollView.opaque = YES; 68 | scrollView.delegate = self; 69 | [self addSubview:scrollView]; 70 | _scrollView = scrollView; 71 | 72 | UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.bounds]; 73 | imageView.opaque = YES; 74 | imageView.contentMode = UIViewContentModeScaleAspectFit; 75 | imageView.tag = ZOOM_VIEW_TAG; 76 | [_scrollView addSubview:imageView]; 77 | _imageView = imageView; 78 | 79 | self.progressView = [[UAProgressView alloc] initWithFrame:CGRectMake((CGRectGetWidth(self.frame) / 2) - 22.0f, CGRectGetHeight(self.frame) / 2 - 22.0f, 44.0f, 44.0f)]; 80 | 81 | _progressView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin; 82 | [self addSubview:_progressView]; 83 | 84 | RotateGesture *gesture = [[RotateGesture alloc] initWithTarget:self action:@selector(rotate:)]; 85 | [self addGestureRecognizer:gesture]; 86 | 87 | } 88 | return self; 89 | } 90 | 91 | - (void)dealloc { 92 | if (_image) { 93 | [[FSImageLoader sharedInstance] cancelRequestForUrl:self.image.URL]; 94 | } 95 | } 96 | 97 | 98 | - (void)layoutSubviews { 99 | [super layoutSubviews]; 100 | 101 | if (_scrollView.zoomScale == 1.0f) { 102 | [self layoutScrollViewAnimated:YES]; 103 | } 104 | 105 | } 106 | 107 | - (void)setImage:(id )aImage { 108 | 109 | if (!aImage) { 110 | return; 111 | } 112 | if ([aImage isEqual:_image]) { 113 | return; 114 | } 115 | if (_image != nil) { 116 | [[FSImageLoader sharedInstance] cancelRequestForUrl:_image.URL]; 117 | } 118 | 119 | _image = aImage; 120 | 121 | if (_image.image) { 122 | _imageView.image = _image.image; 123 | 124 | } 125 | else { 126 | 127 | if ([_image.URL isFileURL]) { 128 | 129 | NSError *error = nil; 130 | NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[_image.URL path] error:&error]; 131 | NSInteger fileSize = [[attributes objectForKey:NSFileSize] integerValue]; 132 | 133 | if (fileSize >= MB_FILE_SIZE) { 134 | _progressView.hidden = NO; 135 | [_progressView setProgress:0.5 animated:YES]; 136 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 137 | 138 | UIImage *image = nil; 139 | NSData *data = [NSData dataWithContentsOfURL:self.image.URL]; 140 | if (!data) { 141 | [self handleFailedImage]; 142 | } else { 143 | image = [UIImage imageWithData:data]; 144 | } 145 | 146 | dispatch_async(dispatch_get_main_queue(), ^{ 147 | _progressView.hidden = YES; 148 | if (image != nil) { 149 | [self setupImageViewWithImage:image]; 150 | } 151 | 152 | }); 153 | }); 154 | 155 | } 156 | else { 157 | self.imageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:self.image.URL]]; 158 | } 159 | 160 | } 161 | else { 162 | _progressView.hidden = NO; 163 | __weak FSImageView *weakSelf = self; 164 | [[FSImageLoader sharedInstance] loadImageForURL:_image.URL progress:^(float progress) { 165 | [weakSelf.progressView setProgress:progress animated:YES]; 166 | }image:^(UIImage *image, NSError *error) { 167 | __strong FSImageView *strongSelf = weakSelf; 168 | if (!error) { 169 | strongSelf.image.image = image; 170 | [strongSelf setupImageViewWithImage:image]; 171 | } 172 | else { 173 | [strongSelf handleFailedImage]; 174 | } 175 | }]; 176 | } 177 | 178 | } 179 | 180 | if (_imageView.image) { 181 | _progressView.hidden = YES; 182 | self.userInteractionEnabled = YES; 183 | _loading = NO; 184 | 185 | [[NSNotificationCenter defaultCenter] postNotificationName:kFSImageViewerDidFinishedLoadingNotificationKey object:@{ 186 | @"image" : self.image, 187 | @"failed" : @(NO) 188 | }]; 189 | 190 | } else { 191 | _loading = YES; 192 | self.userInteractionEnabled = NO; 193 | } 194 | [self layoutScrollViewAnimated:NO]; 195 | } 196 | 197 | - (void)setupImageViewWithImage:(UIImage *)aImage { 198 | if (!aImage) { 199 | return; 200 | } 201 | 202 | _loading = NO; 203 | _progressView.hidden = YES; 204 | _imageView.image = aImage; 205 | [self layoutScrollViewAnimated:NO]; 206 | 207 | [[self layer] addAnimation:[self fadeAnimation] forKey:@"opacity"]; 208 | self.userInteractionEnabled = YES; 209 | 210 | [[NSNotificationCenter defaultCenter] postNotificationName:kFSImageViewerDidFinishedLoadingNotificationKey object:@{ 211 | @"image" : self.image, 212 | @"failed" : @(NO) 213 | }]; 214 | } 215 | 216 | - (void)prepareForReuse { 217 | self.tag = -1; 218 | } 219 | 220 | - (void)changeBackgroundColor:(UIColor *)color { 221 | self.backgroundColor = color; 222 | self.imageView.backgroundColor = color; 223 | self.scrollView.backgroundColor = color; 224 | } 225 | 226 | - (void)changeProgressViewColor:(UIColor *)color { 227 | _progressView.tintColor = color; 228 | } 229 | 230 | 231 | - (void)handleFailedImage { 232 | 233 | _imageView.image = FSImageViewerErrorPlaceholderImage; 234 | _image.failed = YES; 235 | [self layoutScrollViewAnimated:NO]; 236 | self.userInteractionEnabled = NO; 237 | _progressView.hidden = YES; 238 | [[NSNotificationCenter defaultCenter] postNotificationName:kFSImageViewerDidFinishedLoadingNotificationKey object:@{ 239 | @"image" : self.image, 240 | @"failed" : @(YES) 241 | }]; 242 | } 243 | 244 | - (void)resetBackgroundColors { 245 | self.backgroundColor = [UIColor whiteColor]; 246 | self.superview.backgroundColor = self.backgroundColor; 247 | self.superview.superview.backgroundColor = self.backgroundColor; 248 | } 249 | 250 | 251 | #pragma mark - Layout 252 | 253 | - (void)rotateToOrientation:(UIInterfaceOrientation)orientation { 254 | 255 | if (self.scrollView.zoomScale > 1.0f) { 256 | 257 | CGFloat height, width; 258 | height = MIN(CGRectGetHeight(self.imageView.frame) + self.imageView.frame.origin.x, CGRectGetHeight(self.bounds)); 259 | width = MIN(CGRectGetWidth(self.imageView.frame) + self.imageView.frame.origin.y, CGRectGetWidth(self.bounds)); 260 | self.scrollView.frame = CGRectMake((self.bounds.size.width / 2) - (width / 2), (self.bounds.size.height / 2) - (height / 2), width, height); 261 | 262 | } else { 263 | 264 | [self layoutScrollViewAnimated:NO]; 265 | 266 | } 267 | } 268 | 269 | - (void)layoutScrollViewAnimated:(BOOL)animated { 270 | 271 | if (!_imageView.image) { 272 | return; 273 | } 274 | 275 | if (animated) { 276 | [UIView beginAnimations:nil context:NULL]; 277 | [UIView setAnimationDuration:0.0001]; 278 | } 279 | 280 | CGFloat hfactor = self.imageView.image.size.width / self.frame.size.width; 281 | CGFloat vfactor = self.imageView.image.size.height / self.frame.size.height; 282 | 283 | CGFloat factor = MAX(hfactor, vfactor); 284 | 285 | CGFloat newWidth = (int) (self.imageView.image.size.width / factor); 286 | CGFloat newHeight = (int) (self.imageView.image.size.height / factor); 287 | 288 | CGFloat leftOffset = (int) ((self.frame.size.width - newWidth) / 2); 289 | CGFloat topOffset = (int) ((self.frame.size.height - newHeight) / 2); 290 | 291 | self.scrollView.frame = CGRectMake(leftOffset, topOffset, newWidth, newHeight); 292 | self.scrollView.layer.position = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2); 293 | self.scrollView.contentSize = CGSizeMake(self.scrollView.bounds.size.width, self.scrollView.bounds.size.height); 294 | self.scrollView.contentOffset = CGPointMake(0.0f, 0.0f); 295 | self.imageView.frame = self.scrollView.bounds; 296 | 297 | if (animated) { 298 | [UIView commitAnimations]; 299 | } 300 | } 301 | 302 | #pragma mark - Animation 303 | 304 | - (CABasicAnimation *)fadeAnimation { 305 | 306 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 307 | animation.fromValue = [NSNumber numberWithFloat:0.0f]; 308 | animation.toValue = [NSNumber numberWithFloat:1.0f]; 309 | animation.duration = .3f; 310 | animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; 311 | 312 | return animation; 313 | } 314 | 315 | #pragma mark - UIScrollViewDelegate 316 | 317 | - (void)killZoomAnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context { 318 | 319 | if ([finished boolValue]) { 320 | 321 | [self.scrollView setZoomScale:1.0f animated:NO]; 322 | self.imageView.frame = self.scrollView.bounds; 323 | [self layoutScrollViewAnimated:NO]; 324 | 325 | } 326 | 327 | } 328 | 329 | - (void)killScrollViewZoom { 330 | 331 | if (!self.scrollView.zoomScale > 1.0f) return; 332 | 333 | if (!self.imageView.image) { 334 | return; 335 | } 336 | 337 | [UIView beginAnimations:nil context:NULL]; 338 | [UIView setAnimationDuration:0.3]; 339 | [UIView setAnimationDidStopSelector:@selector(killZoomAnimationDidStop:finished:context:)]; 340 | [UIView setAnimationDelegate:self]; 341 | 342 | 343 | CGFloat hfactor = self.imageView.image.size.width / self.frame.size.width; 344 | CGFloat vfactor = self.imageView.image.size.height / self.frame.size.height; 345 | 346 | CGFloat factor = MAX(hfactor, vfactor); 347 | 348 | CGFloat newWidth = (int) (self.imageView.image.size.width / factor); 349 | CGFloat newHeight = (int) (self.imageView.image.size.height / factor); 350 | 351 | CGFloat leftOffset = (int) ((self.frame.size.width - newWidth) / 2); 352 | CGFloat topOffset = (int) ((self.frame.size.height - newHeight) / 2); 353 | 354 | self.scrollView.frame = CGRectMake(leftOffset, topOffset, newWidth, newHeight); 355 | self.imageView.frame = self.scrollView.bounds; 356 | 357 | [UIView commitAnimations]; 358 | 359 | } 360 | 361 | - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { 362 | return [self.scrollView viewWithTag:ZOOM_VIEW_TAG]; 363 | } 364 | 365 | - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale { 366 | 367 | if (scrollView.zoomScale > 1.0f) { 368 | 369 | CGFloat height; 370 | CGFloat width; 371 | 372 | if (CGRectGetMaxX(self.imageView.frame) > self.bounds.size.width) { 373 | width = CGRectGetWidth(self.bounds); 374 | } else { 375 | width = CGRectGetMaxX(self.imageView.frame); 376 | } 377 | 378 | if (CGRectGetMaxY(self.imageView.frame) > self.bounds.size.height) { 379 | height = CGRectGetHeight(self.bounds); 380 | } else { 381 | height = CGRectGetMaxY(self.imageView.frame); 382 | } 383 | 384 | CGRect frame = self.scrollView.frame; 385 | self.scrollView.frame = CGRectMake((self.bounds.size.width / 2) - (width / 2), (self.bounds.size.height / 2) - (height / 2), width, height); 386 | self.scrollView.layer.position = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2); 387 | if (!CGRectEqualToRect(frame, self.scrollView.frame)) { 388 | 389 | CGFloat offsetY, offsetX; 390 | 391 | if (frame.origin.y < self.scrollView.frame.origin.y) { 392 | offsetY = self.scrollView.contentOffset.y - (self.scrollView.frame.origin.y - frame.origin.y); 393 | } else { 394 | offsetY = self.scrollView.contentOffset.y - (frame.origin.y - self.scrollView.frame.origin.y); 395 | } 396 | 397 | if (frame.origin.x < self.scrollView.frame.origin.x) { 398 | offsetX = self.scrollView.contentOffset.x - (self.scrollView.frame.origin.x - frame.origin.x); 399 | } else { 400 | offsetX = self.scrollView.contentOffset.x - (frame.origin.x - self.scrollView.frame.origin.x); 401 | } 402 | 403 | if (offsetY < 0) offsetY = 0; 404 | if (offsetX < 0) offsetX = 0; 405 | 406 | self.scrollView.contentOffset = CGPointMake(offsetX, offsetY); 407 | self.scrollView.scrollEnabled = YES; 408 | } 409 | 410 | } else { 411 | [self layoutScrollViewAnimated:YES]; 412 | self.scrollView.scrollEnabled = NO; 413 | } 414 | } 415 | 416 | #pragma mark - RotateGesture 417 | 418 | - (void)rotate:(UIRotationGestureRecognizer *)gesture { 419 | 420 | if (!_rotationEnabled) { 421 | return; 422 | } 423 | if (gesture.state == UIGestureRecognizerStateBegan) { 424 | [self.layer removeAllAnimations]; 425 | beginRadians = gesture.rotation; 426 | self.layer.transform = CATransform3DMakeRotation(beginRadians, 0.0f, 0.0f, 1.0f); 427 | 428 | } 429 | else if (gesture.state == UIGestureRecognizerStateChanged) { 430 | self.layer.transform = CATransform3DMakeRotation((beginRadians + gesture.rotation), 0.0f, 0.0f, 1.0f); 431 | } 432 | else { 433 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"]; 434 | animation.toValue = [NSValue valueWithCATransform3D:CATransform3DIdentity]; 435 | animation.duration = 0.3f; 436 | animation.removedOnCompletion = NO; 437 | animation.fillMode = kCAFillModeForwards; 438 | animation.delegate = self; 439 | [animation setValue:[NSNumber numberWithInt:202] forKey:@"AnimationType"]; 440 | [self.layer addAnimation:animation forKey:@"RotateAnimation"]; 441 | } 442 | } 443 | 444 | - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { 445 | 446 | if (flag) { 447 | if ([[anim valueForKey:@"AnimationType"] integerValue] == 101) { 448 | [self resetBackgroundColors]; 449 | } else if ([[anim valueForKey:@"AnimationType"] integerValue] == 202) { 450 | self.layer.transform = CATransform3DIdentity; 451 | } 452 | } 453 | } 454 | 455 | #pragma mark - Bars 456 | 457 | - (void)toggleBars { 458 | [[NSNotificationCenter defaultCenter] postNotificationName:kFSImageViewerToogleBarsNotificationKey object:nil]; 459 | } 460 | 461 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 462 | [super touchesEnded:touches withEvent:event]; 463 | UITouch *touch = [touches anyObject]; 464 | 465 | if (touch.tapCount == 1) { 466 | [self performSelector:@selector(toggleBars) withObject:nil afterDelay:.2]; 467 | } 468 | } 469 | 470 | 471 | @end 472 | -------------------------------------------------------------------------------- /FSImageViewer/FSImageViewerViewController.m: -------------------------------------------------------------------------------- 1 | // FSImageViewer 2 | // 3 | // Created by Felix Schulze on 8/26/2013. 4 | // Copyright 2013 Felix Schulze. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import "FSImageViewerViewController.h" 26 | #import "FSImageTitleView.h" 27 | 28 | @interface FSImageViewerViewController () 29 | 30 | @end 31 | 32 | @implementation FSImageViewerViewController { 33 | NSInteger pageIndex; 34 | NSInteger currentPageIndex; 35 | BOOL rotating; 36 | BOOL barsHidden; 37 | BOOL statusBarHidden; 38 | UIBarButtonItem *shareButton; 39 | } 40 | 41 | - (instancetype)initWithImageSource:(id )aImageSource { 42 | return [self initWithImageSource:aImageSource imageIndex:0]; 43 | } 44 | 45 | - (instancetype)initWithImageSource:(id )aImageSource imageIndex:(NSInteger)imageIndex { 46 | if ((self = [super init])) { 47 | 48 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(toggleBarsNotification:) name:kFSImageViewerToogleBarsNotificationKey object:nil]; 49 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(imageViewDidFinishLoading:) name:kFSImageViewerDidFinishedLoadingNotificationKey object:nil]; 50 | 51 | self.hidesBottomBarWhenPushed = YES; 52 | 53 | self.backgroundColorHidden = [UIColor blackColor]; 54 | self.backgroundColorVisible = [UIColor whiteColor]; 55 | 56 | self.progressColorHidden = [UIColor whiteColor]; 57 | self.progressColorVisible = [UIColor darkGrayColor]; 58 | 59 | _imageSource = aImageSource; 60 | pageIndex = imageIndex; 61 | currentPageIndex = imageIndex; 62 | 63 | self.sharingDisabled = NO; 64 | self.showNumberOfItemsInTitle = YES; 65 | self.rotationEnabled = YES; 66 | } 67 | return self; 68 | } 69 | 70 | - (void)dealloc { 71 | _scrollView.delegate = nil; 72 | [[FSImageLoader sharedInstance] cancelAllRequests]; 73 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 74 | } 75 | 76 | - (void)viewDidLoad { 77 | [super viewDidLoad]; 78 | 79 | if ([self respondsToSelector:@selector(setAutomaticallyAdjustsScrollViewInsets:)]) { 80 | self.automaticallyAdjustsScrollViewInsets = NO; 81 | } 82 | 83 | self.view.backgroundColor = self.backgroundColorVisible; 84 | 85 | if (!_scrollView) { 86 | self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds]; 87 | _scrollView.delegate = self; 88 | _scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; 89 | _scrollView.scrollEnabled = YES; 90 | _scrollView.multipleTouchEnabled = YES; 91 | _scrollView.directionalLockEnabled = YES; 92 | _scrollView.canCancelContentTouches = YES; 93 | _scrollView.delaysContentTouches = YES; 94 | _scrollView.clipsToBounds = YES; 95 | _scrollView.bounces = YES; 96 | _scrollView.alwaysBounceHorizontal = YES; 97 | _scrollView.pagingEnabled = YES; 98 | _scrollView.showsVerticalScrollIndicator = NO; 99 | _scrollView.showsHorizontalScrollIndicator = NO; 100 | _scrollView.backgroundColor = self.view.backgroundColor; 101 | [self.view addSubview:_scrollView]; 102 | } 103 | 104 | if (!_titleView) { 105 | [self setTitleView:[[FSImageTitleView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height, self.view.frame.size.width, 1)]]; 106 | } 107 | 108 | // load FSImageView lazy 109 | NSMutableArray *views = [[NSMutableArray alloc] init]; 110 | for (NSUInteger i = 0; i < [_imageSource numberOfImages]; i++) { 111 | [views addObject:[NSNull null]]; 112 | } 113 | self.imageViews = views; 114 | } 115 | 116 | - (void)setTitleView:(UIView *)titleView { 117 | if(_titleView) { 118 | [_titleView removeFromSuperview]; 119 | } 120 | _titleView = titleView; 121 | if (_titleView) { 122 | [self.view addSubview:_titleView]; 123 | } 124 | } 125 | 126 | - (void)viewWillAppear:(BOOL)animated { 127 | [super viewWillAppear:animated]; 128 | 129 | shareButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(share:)]; 130 | shareButton.enabled = NO; 131 | if (self.presentingViewController && (self.modalPresentationStyle == UIModalPresentationFullScreen)) { 132 | UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithTitle:[self localizedStringForKey:@"done" withDefault:@"Done"] style:UIBarButtonItemStyleDone target:self action:@selector(done:)]; 133 | self.navigationItem.rightBarButtonItem = doneButton; 134 | if (!_sharingDisabled) { 135 | self.navigationItem.leftBarButtonItem = shareButton; 136 | } 137 | } 138 | else { 139 | if (!_sharingDisabled) { 140 | self.navigationItem.rightBarButtonItem = shareButton; 141 | } 142 | } 143 | 144 | [self setupScrollViewContentSize]; 145 | [self moveToImageAtIndex:pageIndex animated:NO]; 146 | } 147 | 148 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { 149 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { 150 | return (interfaceOrientation == UIInterfaceOrientationLandscapeRight || interfaceOrientation == UIInterfaceOrientationLandscapeLeft); 151 | } 152 | 153 | return (UIInterfaceOrientationIsLandscape(interfaceOrientation) || interfaceOrientation == UIInterfaceOrientationPortrait); 154 | } 155 | 156 | - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { 157 | rotating = YES; 158 | 159 | if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) { 160 | CGRect rect = [[UIScreen mainScreen] bounds]; 161 | _scrollView.contentSize = CGSizeMake(rect.size.height * [_imageSource numberOfImages], rect.size.width); 162 | } 163 | 164 | NSInteger count = 0; 165 | for (FSImageView *view in _imageViews) { 166 | if ([view isKindOfClass:[FSImageView class]]) { 167 | if (count != pageIndex) { 168 | [view setHidden:YES]; 169 | } 170 | } 171 | count++; 172 | } 173 | } 174 | 175 | - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { 176 | for (FSImageView *view in _imageViews) { 177 | if ([view isKindOfClass:[FSImageView class]]) { 178 | [view rotateToOrientation:toInterfaceOrientation]; 179 | } 180 | } 181 | } 182 | 183 | - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { 184 | [self setupScrollViewContentSize]; 185 | [self moveToImageAtIndex:pageIndex animated:NO]; 186 | if (pageIndex < [_imageViews count]) 187 | [_scrollView scrollRectToVisible:((FSImageView *) [_imageViews objectAtIndex:(NSUInteger) pageIndex]).frame animated:YES]; 188 | 189 | for (FSImageView *view in self.imageViews) { 190 | if ([view isKindOfClass:[FSImageView class]]) { 191 | [view setHidden:NO]; 192 | } 193 | } 194 | rotating = NO; 195 | } 196 | 197 | - (void)done:(id)sender { 198 | if ([_delegate respondsToSelector:@selector(imageViewerViewController:willDismissViewControllerAnimated:)]) { 199 | [_delegate imageViewerViewController:self willDismissViewControllerAnimated:YES]; 200 | } 201 | 202 | [self dismissViewControllerAnimated:YES completion:^{ 203 | if ([_delegate respondsToSelector:@selector(imageViewerViewController:didDismissViewControllerAnimated:)]) { 204 | [_delegate imageViewerViewController:self didDismissViewControllerAnimated:YES]; 205 | } 206 | }]; 207 | } 208 | 209 | - (void)share:(id)sender { 210 | id currentImage = _imageSource[[self currentImageIndex]]; 211 | NSAssert(currentImage.image, @"The image must be loaded to share."); 212 | if (currentImage.image) { 213 | UIActivityViewController *controller = [[UIActivityViewController alloc] initWithActivityItems:@[currentImage.image] applicationActivities:_applicationActivities]; 214 | if([controller respondsToSelector:@selector(popoverPresentationController)]) { 215 | if (!controller.popoverPresentationController.barButtonItem) { 216 | controller.popoverPresentationController.barButtonItem = shareButton; 217 | } 218 | } 219 | [self presentViewController:controller animated:YES completion:nil]; 220 | } 221 | } 222 | 223 | - (NSInteger)currentImageIndex { 224 | return pageIndex; 225 | } 226 | 227 | #pragma mark - Bar/Caption Methods 228 | 229 | - (void)setStatusBarHidden:(BOOL)hidden { 230 | statusBarHidden = hidden; 231 | 232 | if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) { 233 | [self performSelector:@selector(setNeedsStatusBarAppearanceUpdate)]; 234 | } else { 235 | [[UIApplication sharedApplication] setStatusBarHidden:hidden withAnimation:UIStatusBarAnimationFade]; 236 | } 237 | 238 | } 239 | 240 | - (void)setBarsHidden:(BOOL)hidden animated:(BOOL)animated { 241 | if (hidden && barsHidden) { 242 | return; 243 | } 244 | 245 | [self setStatusBarHidden:hidden]; 246 | [self.navigationController setNavigationBarHidden:hidden animated:animated]; 247 | 248 | [UIView animateWithDuration:0.3 animations:^{ 249 | UIColor *backgroundColor = hidden ? _backgroundColorHidden : _backgroundColorVisible; 250 | self.view.backgroundColor = backgroundColor; 251 | self.scrollView.backgroundColor = backgroundColor; 252 | for (FSImageView *imageView in _imageViews) { 253 | if ([imageView isKindOfClass:[FSImageView class]]) { 254 | [imageView changeBackgroundColor:backgroundColor];; 255 | [imageView changeProgressViewColor:hidden ? _progressColorHidden : _progressColorVisible]; 256 | } 257 | } 258 | }]; 259 | 260 | [_titleView hideView:hidden]; 261 | 262 | barsHidden = hidden; 263 | } 264 | 265 | - (void)toggleBarsNotification:(NSNotification *)notification { 266 | [self setBarsHidden:!barsHidden animated:YES]; 267 | } 268 | 269 | #pragma mark - Image View 270 | 271 | - (void)imageViewDidFinishLoading:(NSNotification *)notification { 272 | if (notification == nil) { 273 | return; 274 | } 275 | 276 | NSInteger centerIndex = [self centerImageIndex]; 277 | if (centerIndex >= _imageSource.numberOfImages) { 278 | NSAssert(centerIndex < _imageSource.numberOfImages, @"centerIndex is out of bounds"); 279 | return; 280 | } 281 | 282 | if ([[notification object][@"image"] isEqual:_imageSource[centerIndex]]) { 283 | if ([[notification object][@"failed"] boolValue]) { 284 | if (barsHidden) { 285 | [self setBarsHidden:NO animated:YES]; 286 | } 287 | shareButton.enabled = NO; 288 | } 289 | else { 290 | shareButton.enabled = YES; 291 | } 292 | [self setViewState]; 293 | } 294 | } 295 | 296 | - (NSInteger)centerImageIndex { 297 | if (self.scrollView) { 298 | CGFloat pageWidth = self.scrollView.frame.size.width; 299 | NSInteger centerImageIndex = (NSInteger)(floor((self.scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1); 300 | if (centerImageIndex >= 0) { 301 | return centerImageIndex; 302 | } 303 | } 304 | return 0; 305 | } 306 | 307 | - (void)setViewState { 308 | 309 | if(_showNumberOfItemsInTitle) { 310 | NSInteger numberOfImages = [_imageSource numberOfImages]; 311 | if (numberOfImages > 1) { 312 | self.navigationItem.title = [NSString stringWithFormat:@"%i %@ %li", (int)pageIndex + 1, [self localizedStringForKey:@"imageCounter" withDefault:@"of"], (long)numberOfImages]; 313 | } else { 314 | self.title = @""; 315 | } 316 | } 317 | 318 | if (_titleView) { 319 | [_titleView updateMetadata:_imageSource[pageIndex].title index:pageIndex total:_imageSource.numberOfImages]; 320 | } 321 | 322 | } 323 | 324 | - (void)moveToImageAtIndex:(NSInteger)index animated:(BOOL)animated { 325 | if (index < [self.imageSource numberOfImages] && index >= 0) { 326 | 327 | BOOL sameIndex = (currentPageIndex == index); 328 | pageIndex = index; 329 | currentPageIndex = index; 330 | 331 | [self setViewState]; 332 | 333 | [self enqueueImageViewAtIndex:index]; 334 | 335 | [self loadScrollViewWithPage:index - 1]; 336 | [self loadScrollViewWithPage:index]; 337 | [self loadScrollViewWithPage:index + 1]; 338 | 339 | [self.scrollView scrollRectToVisible:((FSImageView *) [_imageViews objectAtIndex:(NSUInteger) index]).frame animated:animated]; 340 | 341 | if (_imageSource[pageIndex].failed) { 342 | [self setBarsHidden:NO animated:YES]; 343 | shareButton.enabled = NO; 344 | } 345 | else { 346 | if (pageIndex == [self currentImageIndex] && _imageSource[pageIndex].image) { 347 | shareButton.enabled = YES; 348 | if (!sameIndex && [_delegate respondsToSelector:@selector(imageViewerViewController:didMoveToImageAtIndex:)]) { 349 | [_delegate imageViewerViewController:self didMoveToImageAtIndex:pageIndex]; 350 | } 351 | } 352 | } 353 | 354 | if (index + 1 < [self.imageSource numberOfImages] && (NSNull *) [_imageViews objectAtIndex:(NSUInteger) (index + 1)] != [NSNull null]) { 355 | [((FSImageView *) [self.imageViews objectAtIndex:(NSUInteger) (index + 1)]) killScrollViewZoom]; 356 | } 357 | if (index - 1 >= 0 && (NSNull *) [self.imageViews objectAtIndex:(NSUInteger) (index - 1)] != [NSNull null]) { 358 | [((FSImageView *) [self.imageViews objectAtIndex:(NSUInteger) (index - 1)]) killScrollViewZoom]; 359 | } 360 | } 361 | 362 | } 363 | 364 | - (void)layoutScrollViewSubviews { 365 | 366 | NSInteger index = [self currentImageIndex]; 367 | 368 | for (NSInteger page = index - 1; page < index + 3; page++) { 369 | 370 | if (page >= 0 && page < [_imageSource numberOfImages]) { 371 | 372 | CGFloat originX = _scrollView.bounds.size.width * page; 373 | 374 | if (page < index) { 375 | originX -= kFSImageViewerImageGap; 376 | } 377 | if (page > index) { 378 | originX += kFSImageViewerImageGap; 379 | } 380 | 381 | if ([_imageViews objectAtIndex:(NSUInteger) page] == [NSNull null] || !((UIView *) [_imageViews objectAtIndex:(NSUInteger) page]).superview) { 382 | [self loadScrollViewWithPage:page]; 383 | } 384 | 385 | FSImageView *imageView = [_imageViews objectAtIndex:(NSUInteger) page]; 386 | CGRect newFrame = CGRectMake(originX, 0.0f, _scrollView.bounds.size.width, _scrollView.bounds.size.height); 387 | 388 | if (!CGRectEqualToRect(imageView.frame, newFrame)) { 389 | [UIView animateWithDuration:0.1 animations:^{ 390 | imageView.frame = newFrame; 391 | }]; 392 | } 393 | } 394 | } 395 | } 396 | 397 | - (void)setupScrollViewContentSize { 398 | 399 | CGSize contentSize = self.view.bounds.size; 400 | contentSize.width = (contentSize.width * [_imageSource numberOfImages]); 401 | 402 | if (!CGSizeEqualToSize(contentSize, self.scrollView.contentSize)) { 403 | self.scrollView.contentSize = contentSize; 404 | } 405 | 406 | if (![_titleView isHidden]) { 407 | [_titleView adjustTextViewSize:self.view.bounds]; 408 | } 409 | } 410 | 411 | - (void)enqueueImageViewAtIndex:(NSInteger)theIndex { 412 | 413 | NSInteger count = 0; 414 | for (FSImageView *view in _imageViews) { 415 | if ([view isKindOfClass:[FSImageView class]]) { 416 | if (count > theIndex + 1 || count < theIndex - 1) { 417 | [view prepareForReuse]; 418 | [view removeFromSuperview]; 419 | } else { 420 | view.tag = 0; 421 | } 422 | } 423 | count++; 424 | } 425 | } 426 | 427 | - (FSImageView *)dequeueImageView { 428 | 429 | NSInteger count = 0; 430 | for (FSImageView *view in self.imageViews) { 431 | if ([view isKindOfClass:[FSImageView class]]) { 432 | if (view.superview == nil) { 433 | view.tag = count; 434 | return view; 435 | } 436 | } 437 | count++; 438 | } 439 | return nil; 440 | } 441 | 442 | - (void)loadScrollViewWithPage:(NSInteger)page { 443 | 444 | if (page < 0) { 445 | return; 446 | } 447 | if (page >= [_imageSource numberOfImages]) { 448 | return; 449 | } 450 | 451 | FSImageView *imageView = [_imageViews objectAtIndex:(NSUInteger) page]; 452 | if ((NSNull *) imageView == [NSNull null]) { 453 | imageView = [self dequeueImageView]; 454 | if (imageView != nil) { 455 | [_imageViews exchangeObjectAtIndex:(NSUInteger) imageView.tag withObjectAtIndex:(NSUInteger) page]; 456 | imageView = [_imageViews objectAtIndex:(NSUInteger) page]; 457 | } 458 | } 459 | 460 | if (imageView == nil || (NSNull *) imageView == [NSNull null]) { 461 | imageView = [[FSImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, _scrollView.bounds.size.width, _scrollView.bounds.size.height)]; 462 | imageView.rotationEnabled = _rotationEnabled; 463 | UIColor *backgroundColor = barsHidden ? _backgroundColorHidden : _backgroundColorVisible; 464 | [imageView changeBackgroundColor:backgroundColor]; 465 | [imageView changeProgressViewColor:barsHidden ? _progressColorHidden : _progressColorVisible]; 466 | [_imageViews replaceObjectAtIndex:(NSUInteger) page withObject:imageView]; 467 | } 468 | 469 | imageView.image = _imageSource[page]; 470 | 471 | if (imageView.superview == nil) { 472 | [_scrollView addSubview:imageView]; 473 | } 474 | 475 | CGRect frame = _scrollView.frame; 476 | NSInteger centerPageIndex = pageIndex; 477 | CGFloat xOrigin = (frame.size.width * page); 478 | if (page > centerPageIndex) { 479 | xOrigin = (frame.size.width * page) + kFSImageViewerImageGap; 480 | } else if (page < centerPageIndex) { 481 | xOrigin = (frame.size.width * page) - kFSImageViewerImageGap; 482 | } 483 | 484 | frame.origin.x = xOrigin; 485 | frame.origin.y = 0; 486 | imageView.frame = frame; 487 | } 488 | 489 | #pragma mark - UIScrollViewDelegate 490 | 491 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 492 | NSInteger index = [self centerImageIndex]; 493 | if (index >= [_imageSource numberOfImages] || index < 0) { 494 | return; 495 | } 496 | 497 | if (pageIndex != index && !rotating) { 498 | pageIndex = index; 499 | [self setViewState]; 500 | 501 | if (![scrollView isTracking]) { 502 | [self layoutScrollViewSubviews]; 503 | } 504 | } 505 | } 506 | 507 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { 508 | NSInteger index = [self centerImageIndex]; 509 | if (index >= [_imageSource numberOfImages] || index < 0) { 510 | return; 511 | } 512 | 513 | [self moveToImageAtIndex:index animated:YES]; 514 | } 515 | 516 | - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView { 517 | [self layoutScrollViewSubviews]; 518 | } 519 | 520 | - (BOOL)prefersStatusBarHidden { 521 | return statusBarHidden; 522 | } 523 | 524 | #pragma mark - Localization Helper 525 | - (NSString *)localizedStringForKey:(NSString *)key withDefault:(NSString *)defaultString 526 | { 527 | static NSBundle *bundle = nil; 528 | if (bundle == nil) 529 | { 530 | NSString *bundlePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"FSImageViewer" ofType:@"bundle"]; 531 | bundle = [NSBundle bundleWithPath:bundlePath] ?: [NSBundle bundleForClass:[self class]]; 532 | for (NSString *language in [NSLocale preferredLanguages]) 533 | { 534 | if ([[bundle localizations] containsObject:language]) 535 | { 536 | bundlePath = [bundle pathForResource:language ofType:@"lproj"]; 537 | bundle = [NSBundle bundleWithPath:bundlePath]; 538 | break; 539 | } 540 | } 541 | } 542 | defaultString = [bundle localizedStringForKey:key value:defaultString table:nil]; 543 | return [[NSBundle bundleForClass:[self class]] localizedStringForKey:key value:defaultString table:nil]; 544 | } 545 | 546 | @end 547 | -------------------------------------------------------------------------------- /FSImageViewer.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 53EF70271A9A38B9000FBFE2 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 53EF70261A9A38B9000FBFE2 /* Images.xcassets */; }; 11 | 5AB0DA258592480FB4099411 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 746D153768E9481F8194ECCE /* libPods.a */; }; 12 | 69855A85F6C241808F6F609F /* libPods-FSImageViewerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3E4F4DB52EA04D489651AD23 /* libPods-FSImageViewerTests.a */; }; 13 | FD0FEFBB17D77F1C00470082 /* FSBasicImageSource.m in Sources */ = {isa = PBXBuildFile; fileRef = FD0FEFBA17D77F1C00470082 /* FSBasicImageSource.m */; }; 14 | FD0FEFBF17D781EF00470082 /* FSBasicImage.m in Sources */ = {isa = PBXBuildFile; fileRef = FD0FEFBE17D781EF00470082 /* FSBasicImage.m */; }; 15 | FD37CEC0181EBA5700606D81 /* FSImageViewer.bundle in Resources */ = {isa = PBXBuildFile; fileRef = FD37CEBF181EBA5700606D81 /* FSImageViewer.bundle */; }; 16 | FD89E29417CB99D900A41D91 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD89E29317CB99D900A41D91 /* Foundation.framework */; }; 17 | FD89E2A717CB99F000A41D91 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD89E2A617CB99F000A41D91 /* UIKit.framework */; }; 18 | FD89E2A817CB99F000A41D91 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD89E29317CB99D900A41D91 /* Foundation.framework */; }; 19 | FD89E2AA17CB99F000A41D91 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD89E2A917CB99F000A41D91 /* CoreGraphics.framework */; }; 20 | FD89E2B017CB99F000A41D91 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = FD89E2AE17CB99F000A41D91 /* InfoPlist.strings */; }; 21 | FD89E2B217CB99F000A41D91 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = FD89E2B117CB99F000A41D91 /* main.m */; }; 22 | FD89E2B617CB99F000A41D91 /* FSAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = FD89E2B517CB99F000A41D91 /* FSAppDelegate.m */; }; 23 | FD89E2CA17CB9A0700A41D91 /* FSImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = FD89E2C217CB9A0700A41D91 /* FSImageLoader.m */; }; 24 | FD89E2CB17CB9A0700A41D91 /* FSImageScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = FD89E2C417CB9A0700A41D91 /* FSImageScrollView.m */; }; 25 | FD89E2CC17CB9A0700A41D91 /* FSImageTitleView.m in Sources */ = {isa = PBXBuildFile; fileRef = FD89E2C717CB9A0700A41D91 /* FSImageTitleView.m */; }; 26 | FD89E2CD17CB9A0700A41D91 /* FSImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = FD89E2C917CB9A0700A41D91 /* FSImageView.m */; }; 27 | FD89E2D217CB9A0E00A41D91 /* FSImageViewerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FD89E2CF17CB9A0E00A41D91 /* FSImageViewerViewController.m */; }; 28 | FD89E2D317CB9A0E00A41D91 /* FSPlaceholderImages.m in Sources */ = {isa = PBXBuildFile; fileRef = FD89E2D117CB9A0E00A41D91 /* FSPlaceholderImages.m */; }; 29 | FD89E2DD17CB9A4400A41D91 /* FSDemoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FD89E2D917CB9A4400A41D91 /* FSDemoViewController.m */; }; 30 | FD89E2DE17CB9A4400A41D91 /* FSDemoViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = FD89E2DA17CB9A4400A41D91 /* FSDemoViewController.xib */; }; 31 | FD8B868C1BAD6F2400EA0D5D /* FSImageViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FD8B868B1BAD6F2400EA0D5D /* FSImageViewTests.m */; settings = {ASSET_TAGS = (); }; }; 32 | FD8B868E1BAD97DC00EA0D5D /* Testimage.jpg in Resources */ = {isa = PBXBuildFile; fileRef = FD8B868D1BAD97DC00EA0D5D /* Testimage.jpg */; settings = {ASSET_TAGS = (); }; }; 33 | FD8B86921BAD986B00EA0D5D /* FSTestImageHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = FD8B86911BAD986B00EA0D5D /* FSTestImageHelper.m */; settings = {ASSET_TAGS = (); }; }; 34 | FD8B86941BAD9C0B00EA0D5D /* FSBasicImageSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FD8B86931BAD9C0B00EA0D5D /* FSBasicImageSourceTests.m */; settings = {ASSET_TAGS = (); }; }; 35 | FDBD6630193B054F006D3BF6 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FDBD662F193B054F006D3BF6 /* XCTest.framework */; }; 36 | FDBD6631193B054F006D3BF6 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD89E29317CB99D900A41D91 /* Foundation.framework */; }; 37 | FDBD6632193B054F006D3BF6 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD89E2A617CB99F000A41D91 /* UIKit.framework */; }; 38 | FDBD6638193B054F006D3BF6 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = FDBD6636193B054F006D3BF6 /* InfoPlist.strings */; }; 39 | FDBD663A193B054F006D3BF6 /* FSImageTitleViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FDBD6639193B054F006D3BF6 /* FSImageTitleViewTests.m */; }; 40 | FDCB0B27193B956500F8B8A1 /* FSPlaceholderImagesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FDCB0B26193B956500F8B8A1 /* FSPlaceholderImagesTests.m */; }; 41 | FDD96CE417CB9B7400B66931 /* libFSImageViewer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FD89E29017CB99D900A41D91 /* libFSImageViewer.a */; }; 42 | FDD96CEB17CB9B9000B66931 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FDD96CE917CB9B8800B66931 /* CFNetwork.framework */; }; 43 | FDD96CEC17CB9B9100B66931 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FDD96CE717CB9B8500B66931 /* Security.framework */; }; 44 | FDD96CED17CB9B9300B66931 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FDD96CE517CB9B8000B66931 /* QuartzCore.framework */; }; 45 | /* End PBXBuildFile section */ 46 | 47 | /* Begin PBXContainerItemProxy section */ 48 | FDBD663C193B054F006D3BF6 /* PBXContainerItemProxy */ = { 49 | isa = PBXContainerItemProxy; 50 | containerPortal = FD89E28817CB99D900A41D91 /* Project object */; 51 | proxyType = 1; 52 | remoteGlobalIDString = FD89E28F17CB99D900A41D91; 53 | remoteInfo = FSImageViewer; 54 | }; 55 | FDD96CE217CB9B7000B66931 /* PBXContainerItemProxy */ = { 56 | isa = PBXContainerItemProxy; 57 | containerPortal = FD89E28817CB99D900A41D91 /* Project object */; 58 | proxyType = 1; 59 | remoteGlobalIDString = FD89E28F17CB99D900A41D91; 60 | remoteInfo = FSImageViewer; 61 | }; 62 | FDF817081BAC2C75001337B4 /* PBXContainerItemProxy */ = { 63 | isa = PBXContainerItemProxy; 64 | containerPortal = FD89E28817CB99D900A41D91 /* Project object */; 65 | proxyType = 1; 66 | remoteGlobalIDString = FD89E2A417CB99F000A41D91; 67 | remoteInfo = FSImageViewerDemo; 68 | }; 69 | /* End PBXContainerItemProxy section */ 70 | 71 | /* Begin PBXCopyFilesBuildPhase section */ 72 | FD89E28E17CB99D900A41D91 /* CopyFiles */ = { 73 | isa = PBXCopyFilesBuildPhase; 74 | buildActionMask = 2147483647; 75 | dstPath = "include/${PRODUCT_NAME}"; 76 | dstSubfolderSpec = 16; 77 | files = ( 78 | ); 79 | runOnlyForDeploymentPostprocessing = 0; 80 | }; 81 | /* End PBXCopyFilesBuildPhase section */ 82 | 83 | /* Begin PBXFileReference section */ 84 | 2926F8C4CE2E738802DA8060 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; 85 | 3E4F4DB52EA04D489651AD23 /* libPods-FSImageViewerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-FSImageViewerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 86 | 53EF70261A9A38B9000FBFE2 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 87 | 589F82D818EDE791007F654A /* FSTitleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTitleView.h; sourceTree = ""; }; 88 | 746D153768E9481F8194ECCE /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; 89 | 85A9CBC49B63FEB31F1448F5 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; 90 | EAB987CD32B1D53A7F4A3B72 /* Pods-FSImageViewerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FSImageViewerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-FSImageViewerTests/Pods-FSImageViewerTests.release.xcconfig"; sourceTree = ""; }; 91 | F438121ACBFC9888194F99FE /* Pods-FSImageViewerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FSImageViewerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-FSImageViewerTests/Pods-FSImageViewerTests.debug.xcconfig"; sourceTree = ""; }; 92 | FD0FEFB917D77F1C00470082 /* FSBasicImageSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSBasicImageSource.h; sourceTree = ""; }; 93 | FD0FEFBA17D77F1C00470082 /* FSBasicImageSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSBasicImageSource.m; sourceTree = ""; }; 94 | FD0FEFBD17D781EF00470082 /* FSBasicImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSBasicImage.h; sourceTree = ""; }; 95 | FD0FEFBE17D781EF00470082 /* FSBasicImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSBasicImage.m; sourceTree = ""; }; 96 | FD37CEBF181EBA5700606D81 /* FSImageViewer.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = FSImageViewer.bundle; sourceTree = SOURCE_ROOT; }; 97 | FD37CEC1181EBCDC00606D81 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = ""; }; 98 | FD37CEC2181EBCE000606D81 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; 99 | FD37CEC3181EBCEC00606D81 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = ""; }; 100 | FD37CEC4181EBCF400606D81 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = ""; }; 101 | FD37CEC5181EC01700606D81 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; }; 102 | FD37CEC6181EC01E00606D81 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = ""; }; 103 | FD89E29017CB99D900A41D91 /* libFSImageViewer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libFSImageViewer.a; sourceTree = BUILT_PRODUCTS_DIR; }; 104 | FD89E29317CB99D900A41D91 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 105 | FD89E29717CB99D900A41D91 /* FSImageViewer-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FSImageViewer-Prefix.pch"; sourceTree = ""; }; 106 | FD89E2A517CB99F000A41D91 /* FSImageViewerDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FSImageViewerDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 107 | FD89E2A617CB99F000A41D91 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 108 | FD89E2A917CB99F000A41D91 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 109 | FD89E2AD17CB99F000A41D91 /* FSImageViewerDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "FSImageViewerDemo-Info.plist"; sourceTree = ""; }; 110 | FD89E2AF17CB99F000A41D91 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 111 | FD89E2B117CB99F000A41D91 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 112 | FD89E2B317CB99F000A41D91 /* FSImageViewerDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FSImageViewerDemo-Prefix.pch"; sourceTree = ""; }; 113 | FD89E2B417CB99F000A41D91 /* FSAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSAppDelegate.h; sourceTree = ""; }; 114 | FD89E2B517CB99F000A41D91 /* FSAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSAppDelegate.m; sourceTree = ""; }; 115 | FD89E2C017CB9A0700A41D91 /* FSImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSImage.h; sourceTree = ""; }; 116 | FD89E2C117CB9A0700A41D91 /* FSImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSImageLoader.h; sourceTree = ""; }; 117 | FD89E2C217CB9A0700A41D91 /* FSImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSImageLoader.m; sourceTree = ""; }; 118 | FD89E2C317CB9A0700A41D91 /* FSImageScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSImageScrollView.h; sourceTree = ""; }; 119 | FD89E2C417CB9A0700A41D91 /* FSImageScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSImageScrollView.m; sourceTree = ""; }; 120 | FD89E2C517CB9A0700A41D91 /* FSImageSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSImageSource.h; sourceTree = ""; }; 121 | FD89E2C617CB9A0700A41D91 /* FSImageTitleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSImageTitleView.h; sourceTree = ""; }; 122 | FD89E2C717CB9A0700A41D91 /* FSImageTitleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSImageTitleView.m; sourceTree = ""; }; 123 | FD89E2C817CB9A0700A41D91 /* FSImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSImageView.h; sourceTree = ""; }; 124 | FD89E2C917CB9A0700A41D91 /* FSImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSImageView.m; sourceTree = ""; }; 125 | FD89E2CE17CB9A0E00A41D91 /* FSImageViewerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSImageViewerViewController.h; sourceTree = ""; }; 126 | FD89E2CF17CB9A0E00A41D91 /* FSImageViewerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSImageViewerViewController.m; sourceTree = ""; }; 127 | FD89E2D017CB9A0E00A41D91 /* FSPlaceholderImages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSPlaceholderImages.h; sourceTree = ""; }; 128 | FD89E2D117CB9A0E00A41D91 /* FSPlaceholderImages.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSPlaceholderImages.m; sourceTree = ""; }; 129 | FD89E2D817CB9A4400A41D91 /* FSDemoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSDemoViewController.h; sourceTree = ""; }; 130 | FD89E2D917CB9A4400A41D91 /* FSDemoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSDemoViewController.m; sourceTree = ""; }; 131 | FD89E2DA17CB9A4400A41D91 /* FSDemoViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = FSDemoViewController.xib; sourceTree = ""; }; 132 | FD8B868B1BAD6F2400EA0D5D /* FSImageViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSImageViewTests.m; sourceTree = ""; }; 133 | FD8B868D1BAD97DC00EA0D5D /* Testimage.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = Testimage.jpg; sourceTree = ""; }; 134 | FD8B86901BAD986B00EA0D5D /* FSTestImageHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTestImageHelper.h; sourceTree = ""; }; 135 | FD8B86911BAD986B00EA0D5D /* FSTestImageHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSTestImageHelper.m; sourceTree = ""; }; 136 | FD8B86931BAD9C0B00EA0D5D /* FSBasicImageSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSBasicImageSourceTests.m; sourceTree = ""; }; 137 | FDBD662E193B054F006D3BF6 /* FSImageViewerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FSImageViewerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 138 | FDBD662F193B054F006D3BF6 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 139 | FDBD6635193B054F006D3BF6 /* FSImageViewerTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "FSImageViewerTests-Info.plist"; sourceTree = ""; }; 140 | FDBD6637193B054F006D3BF6 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 141 | FDBD6639193B054F006D3BF6 /* FSImageTitleViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSImageTitleViewTests.m; sourceTree = ""; }; 142 | FDBD663B193B054F006D3BF6 /* FSImageViewerTests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FSImageViewerTests-Prefix.pch"; sourceTree = ""; }; 143 | FDCB0B26193B956500F8B8A1 /* FSPlaceholderImagesTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSPlaceholderImagesTests.m; sourceTree = ""; }; 144 | FDD96CE117CB9ADC00B66931 /* FSImageViewer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSImageViewer.h; sourceTree = ""; }; 145 | FDD96CE517CB9B8000B66931 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 146 | FDD96CE717CB9B8500B66931 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 147 | FDD96CE917CB9B8800B66931 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; }; 148 | /* End PBXFileReference section */ 149 | 150 | /* Begin PBXFrameworksBuildPhase section */ 151 | FD89E28D17CB99D900A41D91 /* Frameworks */ = { 152 | isa = PBXFrameworksBuildPhase; 153 | buildActionMask = 2147483647; 154 | files = ( 155 | FD89E29417CB99D900A41D91 /* Foundation.framework in Frameworks */, 156 | 5AB0DA258592480FB4099411 /* libPods.a in Frameworks */, 157 | ); 158 | runOnlyForDeploymentPostprocessing = 0; 159 | }; 160 | FD89E2A217CB99F000A41D91 /* Frameworks */ = { 161 | isa = PBXFrameworksBuildPhase; 162 | buildActionMask = 2147483647; 163 | files = ( 164 | FDD96CE417CB9B7400B66931 /* libFSImageViewer.a in Frameworks */, 165 | FDD96CEB17CB9B9000B66931 /* CFNetwork.framework in Frameworks */, 166 | FDD96CEC17CB9B9100B66931 /* Security.framework in Frameworks */, 167 | FDD96CED17CB9B9300B66931 /* QuartzCore.framework in Frameworks */, 168 | FD89E2A717CB99F000A41D91 /* UIKit.framework in Frameworks */, 169 | FD89E2A817CB99F000A41D91 /* Foundation.framework in Frameworks */, 170 | FD89E2AA17CB99F000A41D91 /* CoreGraphics.framework in Frameworks */, 171 | ); 172 | runOnlyForDeploymentPostprocessing = 0; 173 | }; 174 | FDBD662B193B054F006D3BF6 /* Frameworks */ = { 175 | isa = PBXFrameworksBuildPhase; 176 | buildActionMask = 2147483647; 177 | files = ( 178 | FDBD6630193B054F006D3BF6 /* XCTest.framework in Frameworks */, 179 | FDBD6632193B054F006D3BF6 /* UIKit.framework in Frameworks */, 180 | FDBD6631193B054F006D3BF6 /* Foundation.framework in Frameworks */, 181 | 69855A85F6C241808F6F609F /* libPods-FSImageViewerTests.a in Frameworks */, 182 | ); 183 | runOnlyForDeploymentPostprocessing = 0; 184 | }; 185 | /* End PBXFrameworksBuildPhase section */ 186 | 187 | /* Begin PBXGroup section */ 188 | 09EB87C7BA5D8A556339C8D3 /* Pods */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | 85A9CBC49B63FEB31F1448F5 /* Pods.debug.xcconfig */, 192 | 2926F8C4CE2E738802DA8060 /* Pods.release.xcconfig */, 193 | F438121ACBFC9888194F99FE /* Pods-FSImageViewerTests.debug.xcconfig */, 194 | EAB987CD32B1D53A7F4A3B72 /* Pods-FSImageViewerTests.release.xcconfig */, 195 | ); 196 | name = Pods; 197 | sourceTree = ""; 198 | }; 199 | FD89E28717CB99D900A41D91 = { 200 | isa = PBXGroup; 201 | children = ( 202 | FD89E29517CB99D900A41D91 /* FSImageViewer */, 203 | FD89E2AB17CB99F000A41D91 /* FSImageViewerDemo */, 204 | FDBD6633193B054F006D3BF6 /* FSImageViewerTests */, 205 | FD89E29217CB99D900A41D91 /* Frameworks */, 206 | FD89E29117CB99D900A41D91 /* Products */, 207 | 09EB87C7BA5D8A556339C8D3 /* Pods */, 208 | ); 209 | sourceTree = ""; 210 | }; 211 | FD89E29117CB99D900A41D91 /* Products */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | FD89E29017CB99D900A41D91 /* libFSImageViewer.a */, 215 | FD89E2A517CB99F000A41D91 /* FSImageViewerDemo.app */, 216 | FDBD662E193B054F006D3BF6 /* FSImageViewerTests.xctest */, 217 | ); 218 | name = Products; 219 | sourceTree = ""; 220 | }; 221 | FD89E29217CB99D900A41D91 /* Frameworks */ = { 222 | isa = PBXGroup; 223 | children = ( 224 | FDD96CE917CB9B8800B66931 /* CFNetwork.framework */, 225 | FDD96CE717CB9B8500B66931 /* Security.framework */, 226 | FDD96CE517CB9B8000B66931 /* QuartzCore.framework */, 227 | FD89E29317CB99D900A41D91 /* Foundation.framework */, 228 | FD89E2A617CB99F000A41D91 /* UIKit.framework */, 229 | FD89E2A917CB99F000A41D91 /* CoreGraphics.framework */, 230 | 746D153768E9481F8194ECCE /* libPods.a */, 231 | FDBD662F193B054F006D3BF6 /* XCTest.framework */, 232 | 3E4F4DB52EA04D489651AD23 /* libPods-FSImageViewerTests.a */, 233 | ); 234 | name = Frameworks; 235 | sourceTree = ""; 236 | }; 237 | FD89E29517CB99D900A41D91 /* FSImageViewer */ = { 238 | isa = PBXGroup; 239 | children = ( 240 | FD0FEFBD17D781EF00470082 /* FSBasicImage.h */, 241 | FD0FEFBE17D781EF00470082 /* FSBasicImage.m */, 242 | FD0FEFB917D77F1C00470082 /* FSBasicImageSource.h */, 243 | FD0FEFBA17D77F1C00470082 /* FSBasicImageSource.m */, 244 | FD89E2C017CB9A0700A41D91 /* FSImage.h */, 245 | FD89E2C117CB9A0700A41D91 /* FSImageLoader.h */, 246 | FD89E2C217CB9A0700A41D91 /* FSImageLoader.m */, 247 | FD89E2C317CB9A0700A41D91 /* FSImageScrollView.h */, 248 | FD89E2C417CB9A0700A41D91 /* FSImageScrollView.m */, 249 | FD89E2C517CB9A0700A41D91 /* FSImageSource.h */, 250 | FD89E2C617CB9A0700A41D91 /* FSImageTitleView.h */, 251 | FD89E2C717CB9A0700A41D91 /* FSImageTitleView.m */, 252 | FD89E2C817CB9A0700A41D91 /* FSImageView.h */, 253 | FD89E2C917CB9A0700A41D91 /* FSImageView.m */, 254 | FD37CEBF181EBA5700606D81 /* FSImageViewer.bundle */, 255 | FDD96CE117CB9ADC00B66931 /* FSImageViewer.h */, 256 | FD89E2CE17CB9A0E00A41D91 /* FSImageViewerViewController.h */, 257 | FD89E2CF17CB9A0E00A41D91 /* FSImageViewerViewController.m */, 258 | FD89E2D017CB9A0E00A41D91 /* FSPlaceholderImages.h */, 259 | FD89E2D117CB9A0E00A41D91 /* FSPlaceholderImages.m */, 260 | 589F82D818EDE791007F654A /* FSTitleView.h */, 261 | FD89E29617CB99D900A41D91 /* Supporting Files */, 262 | ); 263 | path = FSImageViewer; 264 | sourceTree = ""; 265 | }; 266 | FD89E29617CB99D900A41D91 /* Supporting Files */ = { 267 | isa = PBXGroup; 268 | children = ( 269 | FD89E29717CB99D900A41D91 /* FSImageViewer-Prefix.pch */, 270 | ); 271 | name = "Supporting Files"; 272 | sourceTree = ""; 273 | }; 274 | FD89E2AB17CB99F000A41D91 /* FSImageViewerDemo */ = { 275 | isa = PBXGroup; 276 | children = ( 277 | FD89E2B417CB99F000A41D91 /* FSAppDelegate.h */, 278 | FD89E2B517CB99F000A41D91 /* FSAppDelegate.m */, 279 | FD89E2D817CB9A4400A41D91 /* FSDemoViewController.h */, 280 | FD89E2D917CB9A4400A41D91 /* FSDemoViewController.m */, 281 | FD89E2DA17CB9A4400A41D91 /* FSDemoViewController.xib */, 282 | 53EF70261A9A38B9000FBFE2 /* Images.xcassets */, 283 | FD89E2AC17CB99F000A41D91 /* Supporting Files */, 284 | ); 285 | path = FSImageViewerDemo; 286 | sourceTree = ""; 287 | }; 288 | FD89E2AC17CB99F000A41D91 /* Supporting Files */ = { 289 | isa = PBXGroup; 290 | children = ( 291 | FD89E2AD17CB99F000A41D91 /* FSImageViewerDemo-Info.plist */, 292 | FD89E2AE17CB99F000A41D91 /* InfoPlist.strings */, 293 | FD89E2B117CB99F000A41D91 /* main.m */, 294 | FD89E2B317CB99F000A41D91 /* FSImageViewerDemo-Prefix.pch */, 295 | ); 296 | name = "Supporting Files"; 297 | sourceTree = ""; 298 | }; 299 | FD8B868F1BAD985600EA0D5D /* Helper */ = { 300 | isa = PBXGroup; 301 | children = ( 302 | FD8B86901BAD986B00EA0D5D /* FSTestImageHelper.h */, 303 | FD8B86911BAD986B00EA0D5D /* FSTestImageHelper.m */, 304 | ); 305 | name = Helper; 306 | sourceTree = ""; 307 | }; 308 | FDBD6633193B054F006D3BF6 /* FSImageViewerTests */ = { 309 | isa = PBXGroup; 310 | children = ( 311 | FD8B868F1BAD985600EA0D5D /* Helper */, 312 | FD8B86931BAD9C0B00EA0D5D /* FSBasicImageSourceTests.m */, 313 | FDBD6639193B054F006D3BF6 /* FSImageTitleViewTests.m */, 314 | FDCB0B26193B956500F8B8A1 /* FSPlaceholderImagesTests.m */, 315 | FD8B868B1BAD6F2400EA0D5D /* FSImageViewTests.m */, 316 | FD8B868D1BAD97DC00EA0D5D /* Testimage.jpg */, 317 | FDBD6634193B054F006D3BF6 /* Supporting Files */, 318 | ); 319 | path = FSImageViewerTests; 320 | sourceTree = ""; 321 | }; 322 | FDBD6634193B054F006D3BF6 /* Supporting Files */ = { 323 | isa = PBXGroup; 324 | children = ( 325 | FDBD6635193B054F006D3BF6 /* FSImageViewerTests-Info.plist */, 326 | FDBD6636193B054F006D3BF6 /* InfoPlist.strings */, 327 | FDBD663B193B054F006D3BF6 /* FSImageViewerTests-Prefix.pch */, 328 | ); 329 | name = "Supporting Files"; 330 | sourceTree = ""; 331 | }; 332 | /* End PBXGroup section */ 333 | 334 | /* Begin PBXNativeTarget section */ 335 | FD89E28F17CB99D900A41D91 /* FSImageViewer */ = { 336 | isa = PBXNativeTarget; 337 | buildConfigurationList = FD89E29E17CB99D900A41D91 /* Build configuration list for PBXNativeTarget "FSImageViewer" */; 338 | buildPhases = ( 339 | 668C822E290F479BBF42B716 /* Check Pods Manifest.lock */, 340 | FD89E28C17CB99D900A41D91 /* Sources */, 341 | FD89E28D17CB99D900A41D91 /* Frameworks */, 342 | FD89E28E17CB99D900A41D91 /* CopyFiles */, 343 | F7A857B1B6AA493B8B48C1BD /* Copy Pods Resources */, 344 | ); 345 | buildRules = ( 346 | ); 347 | dependencies = ( 348 | ); 349 | name = FSImageViewer; 350 | productName = FSImageViewer; 351 | productReference = FD89E29017CB99D900A41D91 /* libFSImageViewer.a */; 352 | productType = "com.apple.product-type.library.static"; 353 | }; 354 | FD89E2A417CB99F000A41D91 /* FSImageViewerDemo */ = { 355 | isa = PBXNativeTarget; 356 | buildConfigurationList = FD89E2BD17CB99F000A41D91 /* Build configuration list for PBXNativeTarget "FSImageViewerDemo" */; 357 | buildPhases = ( 358 | 22672DA7B2433B000CF2AAD2 /* Check Pods Manifest.lock */, 359 | FD89E2A117CB99F000A41D91 /* Sources */, 360 | FD89E2A217CB99F000A41D91 /* Frameworks */, 361 | FD89E2A317CB99F000A41D91 /* Resources */, 362 | 70F9CA2574253E876D355FA3 /* Copy Pods Resources */, 363 | ); 364 | buildRules = ( 365 | ); 366 | dependencies = ( 367 | FDD96CE317CB9B7000B66931 /* PBXTargetDependency */, 368 | ); 369 | name = FSImageViewerDemo; 370 | productName = FSImageViewerDemo; 371 | productReference = FD89E2A517CB99F000A41D91 /* FSImageViewerDemo.app */; 372 | productType = "com.apple.product-type.application"; 373 | }; 374 | FDBD662D193B054F006D3BF6 /* FSImageViewerTests */ = { 375 | isa = PBXNativeTarget; 376 | buildConfigurationList = FDBD6640193B054F006D3BF6 /* Build configuration list for PBXNativeTarget "FSImageViewerTests" */; 377 | buildPhases = ( 378 | E172C5EA369C4663AD247B30 /* Check Pods Manifest.lock */, 379 | FDBD662A193B054F006D3BF6 /* Sources */, 380 | FDBD662B193B054F006D3BF6 /* Frameworks */, 381 | FDBD662C193B054F006D3BF6 /* Resources */, 382 | E9DCDA9D0E3F44EB95291CBC /* Copy Pods Resources */, 383 | ); 384 | buildRules = ( 385 | ); 386 | dependencies = ( 387 | FDBD663D193B054F006D3BF6 /* PBXTargetDependency */, 388 | FDF817091BAC2C75001337B4 /* PBXTargetDependency */, 389 | ); 390 | name = FSImageViewerTests; 391 | productName = FSImageViewerTests; 392 | productReference = FDBD662E193B054F006D3BF6 /* FSImageViewerTests.xctest */; 393 | productType = "com.apple.product-type.bundle.unit-test"; 394 | }; 395 | /* End PBXNativeTarget section */ 396 | 397 | /* Begin PBXProject section */ 398 | FD89E28817CB99D900A41D91 /* Project object */ = { 399 | isa = PBXProject; 400 | attributes = { 401 | LastUpgradeCheck = 0700; 402 | ORGANIZATIONNAME = "Felix Schulze"; 403 | TargetAttributes = { 404 | FDBD662D193B054F006D3BF6 = { 405 | TestTargetID = FD89E2A417CB99F000A41D91; 406 | }; 407 | }; 408 | }; 409 | buildConfigurationList = FD89E28B17CB99D900A41D91 /* Build configuration list for PBXProject "FSImageViewer" */; 410 | compatibilityVersion = "Xcode 3.2"; 411 | developmentRegion = English; 412 | hasScannedForEncodings = 0; 413 | knownRegions = ( 414 | en, 415 | de, 416 | it, 417 | es, 418 | fr, 419 | nl, 420 | ru, 421 | ); 422 | mainGroup = FD89E28717CB99D900A41D91; 423 | productRefGroup = FD89E29117CB99D900A41D91 /* Products */; 424 | projectDirPath = ""; 425 | projectRoot = ""; 426 | targets = ( 427 | FD89E28F17CB99D900A41D91 /* FSImageViewer */, 428 | FD89E2A417CB99F000A41D91 /* FSImageViewerDemo */, 429 | FDBD662D193B054F006D3BF6 /* FSImageViewerTests */, 430 | ); 431 | }; 432 | /* End PBXProject section */ 433 | 434 | /* Begin PBXResourcesBuildPhase section */ 435 | FD89E2A317CB99F000A41D91 /* Resources */ = { 436 | isa = PBXResourcesBuildPhase; 437 | buildActionMask = 2147483647; 438 | files = ( 439 | FD89E2B017CB99F000A41D91 /* InfoPlist.strings in Resources */, 440 | 53EF70271A9A38B9000FBFE2 /* Images.xcassets in Resources */, 441 | FD37CEC0181EBA5700606D81 /* FSImageViewer.bundle in Resources */, 442 | FD89E2DE17CB9A4400A41D91 /* FSDemoViewController.xib in Resources */, 443 | ); 444 | runOnlyForDeploymentPostprocessing = 0; 445 | }; 446 | FDBD662C193B054F006D3BF6 /* Resources */ = { 447 | isa = PBXResourcesBuildPhase; 448 | buildActionMask = 2147483647; 449 | files = ( 450 | FDBD6638193B054F006D3BF6 /* InfoPlist.strings in Resources */, 451 | FD8B868E1BAD97DC00EA0D5D /* Testimage.jpg in Resources */, 452 | ); 453 | runOnlyForDeploymentPostprocessing = 0; 454 | }; 455 | /* End PBXResourcesBuildPhase section */ 456 | 457 | /* Begin PBXShellScriptBuildPhase section */ 458 | 22672DA7B2433B000CF2AAD2 /* Check Pods Manifest.lock */ = { 459 | isa = PBXShellScriptBuildPhase; 460 | buildActionMask = 2147483647; 461 | files = ( 462 | ); 463 | inputPaths = ( 464 | ); 465 | name = "Check Pods Manifest.lock"; 466 | outputPaths = ( 467 | ); 468 | runOnlyForDeploymentPostprocessing = 0; 469 | shellPath = /bin/sh; 470 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 471 | showEnvVarsInLog = 0; 472 | }; 473 | 668C822E290F479BBF42B716 /* Check Pods Manifest.lock */ = { 474 | isa = PBXShellScriptBuildPhase; 475 | buildActionMask = 2147483647; 476 | files = ( 477 | ); 478 | inputPaths = ( 479 | ); 480 | name = "Check Pods Manifest.lock"; 481 | outputPaths = ( 482 | ); 483 | runOnlyForDeploymentPostprocessing = 0; 484 | shellPath = /bin/sh; 485 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 486 | showEnvVarsInLog = 0; 487 | }; 488 | 70F9CA2574253E876D355FA3 /* Copy Pods Resources */ = { 489 | isa = PBXShellScriptBuildPhase; 490 | buildActionMask = 2147483647; 491 | files = ( 492 | ); 493 | inputPaths = ( 494 | ); 495 | name = "Copy Pods Resources"; 496 | outputPaths = ( 497 | ); 498 | runOnlyForDeploymentPostprocessing = 0; 499 | shellPath = /bin/sh; 500 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; 501 | showEnvVarsInLog = 0; 502 | }; 503 | E172C5EA369C4663AD247B30 /* Check Pods Manifest.lock */ = { 504 | isa = PBXShellScriptBuildPhase; 505 | buildActionMask = 2147483647; 506 | files = ( 507 | ); 508 | inputPaths = ( 509 | ); 510 | name = "Check Pods Manifest.lock"; 511 | outputPaths = ( 512 | ); 513 | runOnlyForDeploymentPostprocessing = 0; 514 | shellPath = /bin/sh; 515 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 516 | showEnvVarsInLog = 0; 517 | }; 518 | E9DCDA9D0E3F44EB95291CBC /* Copy Pods Resources */ = { 519 | isa = PBXShellScriptBuildPhase; 520 | buildActionMask = 2147483647; 521 | files = ( 522 | ); 523 | inputPaths = ( 524 | ); 525 | name = "Copy Pods Resources"; 526 | outputPaths = ( 527 | ); 528 | runOnlyForDeploymentPostprocessing = 0; 529 | shellPath = /bin/sh; 530 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FSImageViewerTests/Pods-FSImageViewerTests-resources.sh\"\n"; 531 | showEnvVarsInLog = 0; 532 | }; 533 | F7A857B1B6AA493B8B48C1BD /* Copy Pods Resources */ = { 534 | isa = PBXShellScriptBuildPhase; 535 | buildActionMask = 2147483647; 536 | files = ( 537 | ); 538 | inputPaths = ( 539 | ); 540 | name = "Copy Pods Resources"; 541 | outputPaths = ( 542 | ); 543 | runOnlyForDeploymentPostprocessing = 0; 544 | shellPath = /bin/sh; 545 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; 546 | showEnvVarsInLog = 0; 547 | }; 548 | /* End PBXShellScriptBuildPhase section */ 549 | 550 | /* Begin PBXSourcesBuildPhase section */ 551 | FD89E28C17CB99D900A41D91 /* Sources */ = { 552 | isa = PBXSourcesBuildPhase; 553 | buildActionMask = 2147483647; 554 | files = ( 555 | FD89E2CA17CB9A0700A41D91 /* FSImageLoader.m in Sources */, 556 | FD89E2CB17CB9A0700A41D91 /* FSImageScrollView.m in Sources */, 557 | FD89E2CC17CB9A0700A41D91 /* FSImageTitleView.m in Sources */, 558 | FD89E2CD17CB9A0700A41D91 /* FSImageView.m in Sources */, 559 | FD89E2D217CB9A0E00A41D91 /* FSImageViewerViewController.m in Sources */, 560 | FD89E2D317CB9A0E00A41D91 /* FSPlaceholderImages.m in Sources */, 561 | FD0FEFBB17D77F1C00470082 /* FSBasicImageSource.m in Sources */, 562 | FD0FEFBF17D781EF00470082 /* FSBasicImage.m in Sources */, 563 | ); 564 | runOnlyForDeploymentPostprocessing = 0; 565 | }; 566 | FD89E2A117CB99F000A41D91 /* Sources */ = { 567 | isa = PBXSourcesBuildPhase; 568 | buildActionMask = 2147483647; 569 | files = ( 570 | FD89E2B217CB99F000A41D91 /* main.m in Sources */, 571 | FD89E2B617CB99F000A41D91 /* FSAppDelegate.m in Sources */, 572 | FD89E2DD17CB9A4400A41D91 /* FSDemoViewController.m in Sources */, 573 | ); 574 | runOnlyForDeploymentPostprocessing = 0; 575 | }; 576 | FDBD662A193B054F006D3BF6 /* Sources */ = { 577 | isa = PBXSourcesBuildPhase; 578 | buildActionMask = 2147483647; 579 | files = ( 580 | FDCB0B27193B956500F8B8A1 /* FSPlaceholderImagesTests.m in Sources */, 581 | FDBD663A193B054F006D3BF6 /* FSImageTitleViewTests.m in Sources */, 582 | FD8B86941BAD9C0B00EA0D5D /* FSBasicImageSourceTests.m in Sources */, 583 | FD8B86921BAD986B00EA0D5D /* FSTestImageHelper.m in Sources */, 584 | FD8B868C1BAD6F2400EA0D5D /* FSImageViewTests.m in Sources */, 585 | ); 586 | runOnlyForDeploymentPostprocessing = 0; 587 | }; 588 | /* End PBXSourcesBuildPhase section */ 589 | 590 | /* Begin PBXTargetDependency section */ 591 | FDBD663D193B054F006D3BF6 /* PBXTargetDependency */ = { 592 | isa = PBXTargetDependency; 593 | target = FD89E28F17CB99D900A41D91 /* FSImageViewer */; 594 | targetProxy = FDBD663C193B054F006D3BF6 /* PBXContainerItemProxy */; 595 | }; 596 | FDD96CE317CB9B7000B66931 /* PBXTargetDependency */ = { 597 | isa = PBXTargetDependency; 598 | target = FD89E28F17CB99D900A41D91 /* FSImageViewer */; 599 | targetProxy = FDD96CE217CB9B7000B66931 /* PBXContainerItemProxy */; 600 | }; 601 | FDF817091BAC2C75001337B4 /* PBXTargetDependency */ = { 602 | isa = PBXTargetDependency; 603 | target = FD89E2A417CB99F000A41D91 /* FSImageViewerDemo */; 604 | targetProxy = FDF817081BAC2C75001337B4 /* PBXContainerItemProxy */; 605 | }; 606 | /* End PBXTargetDependency section */ 607 | 608 | /* Begin PBXVariantGroup section */ 609 | FD89E2AE17CB99F000A41D91 /* InfoPlist.strings */ = { 610 | isa = PBXVariantGroup; 611 | children = ( 612 | FD89E2AF17CB99F000A41D91 /* en */, 613 | FD37CEC1181EBCDC00606D81 /* it */, 614 | FD37CEC2181EBCE000606D81 /* es */, 615 | FD37CEC3181EBCEC00606D81 /* fr */, 616 | FD37CEC4181EBCF400606D81 /* nl */, 617 | FD37CEC5181EC01700606D81 /* ru */, 618 | FD37CEC6181EC01E00606D81 /* de */, 619 | ); 620 | name = InfoPlist.strings; 621 | sourceTree = ""; 622 | }; 623 | FDBD6636193B054F006D3BF6 /* InfoPlist.strings */ = { 624 | isa = PBXVariantGroup; 625 | children = ( 626 | FDBD6637193B054F006D3BF6 /* en */, 627 | ); 628 | name = InfoPlist.strings; 629 | sourceTree = ""; 630 | }; 631 | /* End PBXVariantGroup section */ 632 | 633 | /* Begin XCBuildConfiguration section */ 634 | FD89E29C17CB99D900A41D91 /* Debug */ = { 635 | isa = XCBuildConfiguration; 636 | buildSettings = { 637 | CLANG_ENABLE_OBJC_ARC = YES; 638 | CLANG_WARN_CONSTANT_CONVERSION = YES; 639 | CLANG_WARN_EMPTY_BODY = YES; 640 | CLANG_WARN_ENUM_CONVERSION = YES; 641 | CLANG_WARN_INT_CONVERSION = YES; 642 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 643 | COPY_PHASE_STRIP = NO; 644 | ENABLE_TESTABILITY = YES; 645 | GCC_DYNAMIC_NO_PIC = NO; 646 | GCC_OPTIMIZATION_LEVEL = 0; 647 | GCC_PREPROCESSOR_DEFINITIONS = ( 648 | "DEBUG=1", 649 | "$(inherited)", 650 | "FB_REFERENCE_IMAGE_DIR=\"\\\"$(SOURCE_ROOT)/FSImageViewerTests/ReferenceImages\\\"\"", 651 | ); 652 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 653 | GCC_TREAT_WARNINGS_AS_ERRORS = YES; 654 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 655 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 656 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 657 | GCC_WARN_UNUSED_VARIABLE = YES; 658 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 659 | ONLY_ACTIVE_ARCH = YES; 660 | SDKROOT = iphoneos; 661 | }; 662 | name = Debug; 663 | }; 664 | FD89E29D17CB99D900A41D91 /* Release */ = { 665 | isa = XCBuildConfiguration; 666 | buildSettings = { 667 | CLANG_ENABLE_OBJC_ARC = YES; 668 | CLANG_WARN_CONSTANT_CONVERSION = YES; 669 | CLANG_WARN_EMPTY_BODY = YES; 670 | CLANG_WARN_ENUM_CONVERSION = YES; 671 | CLANG_WARN_INT_CONVERSION = YES; 672 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 673 | COPY_PHASE_STRIP = YES; 674 | GCC_TREAT_WARNINGS_AS_ERRORS = YES; 675 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 676 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 677 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 678 | GCC_WARN_UNUSED_VARIABLE = YES; 679 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 680 | SDKROOT = iphoneos; 681 | VALIDATE_PRODUCT = YES; 682 | }; 683 | name = Release; 684 | }; 685 | FD89E29F17CB99D900A41D91 /* Debug */ = { 686 | isa = XCBuildConfiguration; 687 | baseConfigurationReference = 85A9CBC49B63FEB31F1448F5 /* Pods.debug.xcconfig */; 688 | buildSettings = { 689 | DSTROOT = /tmp/FSImageViewer.dst; 690 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 691 | GCC_PREFIX_HEADER = "FSImageViewer/FSImageViewer-Prefix.pch"; 692 | PRODUCT_NAME = "$(TARGET_NAME)"; 693 | SKIP_INSTALL = YES; 694 | }; 695 | name = Debug; 696 | }; 697 | FD89E2A017CB99D900A41D91 /* Release */ = { 698 | isa = XCBuildConfiguration; 699 | baseConfigurationReference = 2926F8C4CE2E738802DA8060 /* Pods.release.xcconfig */; 700 | buildSettings = { 701 | DSTROOT = /tmp/FSImageViewer.dst; 702 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 703 | GCC_PREFIX_HEADER = "FSImageViewer/FSImageViewer-Prefix.pch"; 704 | PRODUCT_NAME = "$(TARGET_NAME)"; 705 | SKIP_INSTALL = YES; 706 | }; 707 | name = Release; 708 | }; 709 | FD89E2BE17CB99F000A41D91 /* Debug */ = { 710 | isa = XCBuildConfiguration; 711 | baseConfigurationReference = 85A9CBC49B63FEB31F1448F5 /* Pods.debug.xcconfig */; 712 | buildSettings = { 713 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 714 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 715 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 716 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 717 | GCC_PREFIX_HEADER = "FSImageViewerDemo/FSImageViewerDemo-Prefix.pch"; 718 | INFOPLIST_FILE = "FSImageViewerDemo/FSImageViewerDemo-Info.plist"; 719 | PRODUCT_BUNDLE_IDENTIFIER = "de.felixschulze.${PRODUCT_NAME:rfc1034identifier}"; 720 | PRODUCT_NAME = "$(TARGET_NAME)"; 721 | TARGETED_DEVICE_FAMILY = "1,2"; 722 | WRAPPER_EXTENSION = app; 723 | }; 724 | name = Debug; 725 | }; 726 | FD89E2BF17CB99F000A41D91 /* Release */ = { 727 | isa = XCBuildConfiguration; 728 | baseConfigurationReference = 2926F8C4CE2E738802DA8060 /* Pods.release.xcconfig */; 729 | buildSettings = { 730 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 731 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 732 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 733 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 734 | GCC_PREFIX_HEADER = "FSImageViewerDemo/FSImageViewerDemo-Prefix.pch"; 735 | INFOPLIST_FILE = "FSImageViewerDemo/FSImageViewerDemo-Info.plist"; 736 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 737 | PRODUCT_BUNDLE_IDENTIFIER = "de.felixschulze.${PRODUCT_NAME:rfc1034identifier}"; 738 | PRODUCT_NAME = "$(TARGET_NAME)"; 739 | TARGETED_DEVICE_FAMILY = "1,2"; 740 | WRAPPER_EXTENSION = app; 741 | }; 742 | name = Release; 743 | }; 744 | FDBD663E193B054F006D3BF6 /* Debug */ = { 745 | isa = XCBuildConfiguration; 746 | baseConfigurationReference = F438121ACBFC9888194F99FE /* Pods-FSImageViewerTests.debug.xcconfig */; 747 | buildSettings = { 748 | BUNDLE_LOADER = "$(TEST_HOST)"; 749 | CLANG_ENABLE_MODULES = YES; 750 | CLANG_WARN_BOOL_CONVERSION = YES; 751 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 752 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 753 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 754 | GCC_PREFIX_HEADER = "FSImageViewerTests/FSImageViewerTests-Prefix.pch"; 755 | GCC_PREPROCESSOR_DEFINITIONS = ( 756 | "DEBUG=1", 757 | "$(inherited)", 758 | ); 759 | GCC_TREAT_WARNINGS_AS_ERRORS = NO; 760 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 761 | GCC_WARN_UNDECLARED_SELECTOR = YES; 762 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 763 | GCC_WARN_UNUSED_FUNCTION = YES; 764 | INFOPLIST_FILE = "FSImageViewerTests/FSImageViewerTests-Info.plist"; 765 | PRODUCT_BUNDLE_IDENTIFIER = "de.felixschulze.${PRODUCT_NAME:rfc1034identifier}"; 766 | PRODUCT_NAME = "$(TARGET_NAME)"; 767 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FSImageViewerDemo.app/FSImageViewerDemo"; 768 | WRAPPER_EXTENSION = xctest; 769 | }; 770 | name = Debug; 771 | }; 772 | FDBD663F193B054F006D3BF6 /* Release */ = { 773 | isa = XCBuildConfiguration; 774 | baseConfigurationReference = EAB987CD32B1D53A7F4A3B72 /* Pods-FSImageViewerTests.release.xcconfig */; 775 | buildSettings = { 776 | BUNDLE_LOADER = "$(TEST_HOST)"; 777 | CLANG_ENABLE_MODULES = YES; 778 | CLANG_WARN_BOOL_CONVERSION = YES; 779 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 780 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 781 | ENABLE_NS_ASSERTIONS = NO; 782 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 783 | GCC_PREFIX_HEADER = "FSImageViewerTests/FSImageViewerTests-Prefix.pch"; 784 | GCC_TREAT_WARNINGS_AS_ERRORS = NO; 785 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 786 | GCC_WARN_UNDECLARED_SELECTOR = YES; 787 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 788 | GCC_WARN_UNUSED_FUNCTION = YES; 789 | INFOPLIST_FILE = "FSImageViewerTests/FSImageViewerTests-Info.plist"; 790 | PRODUCT_BUNDLE_IDENTIFIER = "de.felixschulze.${PRODUCT_NAME:rfc1034identifier}"; 791 | PRODUCT_NAME = "$(TARGET_NAME)"; 792 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FSImageViewerDemo.app/FSImageViewerDemo"; 793 | WRAPPER_EXTENSION = xctest; 794 | }; 795 | name = Release; 796 | }; 797 | /* End XCBuildConfiguration section */ 798 | 799 | /* Begin XCConfigurationList section */ 800 | FD89E28B17CB99D900A41D91 /* Build configuration list for PBXProject "FSImageViewer" */ = { 801 | isa = XCConfigurationList; 802 | buildConfigurations = ( 803 | FD89E29C17CB99D900A41D91 /* Debug */, 804 | FD89E29D17CB99D900A41D91 /* Release */, 805 | ); 806 | defaultConfigurationIsVisible = 0; 807 | defaultConfigurationName = Release; 808 | }; 809 | FD89E29E17CB99D900A41D91 /* Build configuration list for PBXNativeTarget "FSImageViewer" */ = { 810 | isa = XCConfigurationList; 811 | buildConfigurations = ( 812 | FD89E29F17CB99D900A41D91 /* Debug */, 813 | FD89E2A017CB99D900A41D91 /* Release */, 814 | ); 815 | defaultConfigurationIsVisible = 0; 816 | defaultConfigurationName = Release; 817 | }; 818 | FD89E2BD17CB99F000A41D91 /* Build configuration list for PBXNativeTarget "FSImageViewerDemo" */ = { 819 | isa = XCConfigurationList; 820 | buildConfigurations = ( 821 | FD89E2BE17CB99F000A41D91 /* Debug */, 822 | FD89E2BF17CB99F000A41D91 /* Release */, 823 | ); 824 | defaultConfigurationIsVisible = 0; 825 | defaultConfigurationName = Release; 826 | }; 827 | FDBD6640193B054F006D3BF6 /* Build configuration list for PBXNativeTarget "FSImageViewerTests" */ = { 828 | isa = XCConfigurationList; 829 | buildConfigurations = ( 830 | FDBD663E193B054F006D3BF6 /* Debug */, 831 | FDBD663F193B054F006D3BF6 /* Release */, 832 | ); 833 | defaultConfigurationIsVisible = 0; 834 | defaultConfigurationName = Release; 835 | }; 836 | /* End XCConfigurationList section */ 837 | }; 838 | rootObject = FD89E28817CB99D900A41D91 /* Project object */; 839 | } 840 | --------------------------------------------------------------------------------