├── .swift-version ├── Images ├── about.png ├── logo.png ├── pixel.png ├── xcode.png ├── portrait.png └── landscape.png ├── .gitignore ├── Browser ├── Browser │ ├── Explorer.gif │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── AppIcon.appiconset │ │ │ ├── icon-20.png │ │ │ ├── icon-29.png │ │ │ ├── icon-40.png │ │ │ ├── icon-76.png │ │ │ ├── Icon-40@2x.png │ │ │ ├── Icon-60@2x.png │ │ │ ├── Icon-60@3x.png │ │ │ ├── Icon-76@2x.png │ │ │ ├── icon-20@2x.png │ │ │ ├── icon-20@3x.png │ │ │ ├── icon-29@2x.png │ │ │ ├── icon-29@3x.png │ │ │ ├── icon-40@3x.png │ │ │ ├── icon-20@2x-1.png │ │ │ ├── icon-29@2x-1.png │ │ │ ├── icon-40@2x-1.png │ │ │ ├── icon-83.5@2x.png │ │ │ ├── iTunesArtwork@2x.png │ │ │ └── Contents.json │ │ ├── help.imageset │ │ │ ├── cbpg4-jzqiv-0.png │ │ │ ├── cbpg4-jzqiv-1.png │ │ │ └── Contents.json │ │ ├── homepage.imageset │ │ │ ├── homepage_alt.png │ │ │ ├── homepage_alt-1.png │ │ │ └── Contents.json │ │ ├── search.imageset │ │ │ ├── cb6n7-gufu6-0.png │ │ │ ├── cb6n7-gufu6-1.png │ │ │ └── Contents.json │ │ └── SplashIcon.imageset │ │ │ ├── browser ico-1.png │ │ │ ├── browser ico.png │ │ │ └── Contents.json │ ├── Windows Navigation Start.wav │ ├── Info.plist │ ├── AppDelegate.swift │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ └── ViewController.swift ├── Podfile ├── Pods │ ├── Target Support Files │ │ ├── YYImage │ │ │ ├── YYImage.modulemap │ │ │ ├── YYImage-dummy.m │ │ │ ├── YYImage-prefix.pch │ │ │ ├── YYImage-umbrella.h │ │ │ ├── YYImage.xcconfig │ │ │ └── Info.plist │ │ └── Pods-Browser │ │ │ ├── Pods-Browser.modulemap │ │ │ ├── Pods-Browser-dummy.m │ │ │ ├── Pods-Browser-umbrella.h │ │ │ ├── Pods-Browser.debug.xcconfig │ │ │ ├── Pods-Browser.release.xcconfig │ │ │ ├── Info.plist │ │ │ ├── Pods-Browser-acknowledgements.markdown │ │ │ ├── Pods-Browser-acknowledgements.plist │ │ │ ├── Pods-Browser-frameworks.sh │ │ │ └── Pods-Browser-resources.sh │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ └── xcuserdata │ │ │ └── blaketsuzaki.xcuserdatad │ │ │ └── xcschemes │ │ │ ├── xcschememanagement.plist │ │ │ ├── YYImage.xcscheme │ │ │ └── Pods-Browser.xcscheme │ └── YYImage │ │ ├── LICENSE │ │ ├── YYImage │ │ ├── YYSpriteSheetImage.m │ │ ├── YYImage.h │ │ ├── YYSpriteSheetImage.h │ │ ├── YYFrameImage.h │ │ ├── YYAnimatedImageView.h │ │ ├── YYFrameImage.m │ │ ├── YYImage.m │ │ └── YYImageCoder.h │ │ └── README.md ├── Browser.xcodeproj │ ├── xcuserdata │ │ └── blaketsuzaki.xcuserdatad │ │ │ ├── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ │ │ └── xcschemes │ │ │ ├── xcschememanagement.plist │ │ │ └── Browser.xcscheme │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcuserdata │ │ └── blaketsuzaki.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Browser.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── Podfile.lock ├── Components ├── Fonts │ ├── fixedsys.ttf │ └── MS Sans Serif.ttf ├── Assets.xcassets │ ├── Contents.json │ ├── scrollbar-up.imageset │ │ ├── up.png │ │ └── Contents.json │ ├── scrollbar-down.imageset │ │ ├── down.png │ │ └── Contents.json │ ├── scrollbar-left.imageset │ │ ├── left.png │ │ └── Contents.json │ ├── back arrow.imageset │ │ ├── arrow_left.png │ │ ├── arrow_left-1.png │ │ └── Contents.json │ ├── folder.imageset │ │ ├── cbws2-kmlpn-0.png │ │ ├── cbws2-kmlpn-1.png │ │ └── Contents.json │ ├── refresh.imageset │ │ ├── cbdpg-4uhvl-0.png │ │ ├── cbdpg-4uhvl-1.png │ │ └── Contents.json │ ├── scrollbar-right.imageset │ │ ├── right.png │ │ └── Contents.json │ ├── media file.imageset │ │ ├── cb5nb-46845-0.png │ │ ├── cb5nb-46845-1.png │ │ └── Contents.json │ ├── forward arrow.imageset │ │ ├── arrow_right.png │ │ ├── arrow_right-1.png │ │ └── Contents.json │ ├── stop.imageset │ │ ├── media_player_stream_no-1.png │ │ ├── media_player_stream_no.png │ │ └── Contents.json │ ├── teal.colorset │ │ └── Contents.json │ ├── magnesium.colorset │ │ └── Contents.json │ ├── midnight.colorset │ │ └── Contents.json │ └── silver.colorset │ │ └── Contents.json ├── CKLabel.swift ├── CKImageButton.swift ├── CKNavigationBar.swift ├── CKTextButton.swift ├── CKStatusView.swift ├── CKGradientView.swift ├── CKImageView.swift ├── CKFillableView.swift ├── CKProgressView.swift ├── CKDialogViewController.swift ├── CKView.swift ├── CKDefaults.swift ├── CKTextField.swift ├── CKBlueScreenViewController.swift ├── CKVerticalScrollBar.swift ├── CKHorizontalScrollBar.swift ├── CKButton.swift └── CKContentWrapperView.swift ├── README.md └── ClassicKit.podspec /.swift-version: -------------------------------------------------------------------------------- 1 | 4.0 2 | -------------------------------------------------------------------------------- /Images/about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Images/about.png -------------------------------------------------------------------------------- /Images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Images/logo.png -------------------------------------------------------------------------------- /Images/pixel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Images/pixel.png -------------------------------------------------------------------------------- /Images/xcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Images/xcode.png -------------------------------------------------------------------------------- /Images/portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Images/portrait.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Build/ 2 | *.xcuserdatad 3 | *.DS_Store 4 | 5 | Spotify/ 6 | SpotifyConfiguration.plist -------------------------------------------------------------------------------- /Images/landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Images/landscape.png -------------------------------------------------------------------------------- /Browser/Browser/Explorer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Explorer.gif -------------------------------------------------------------------------------- /Components/Fonts/fixedsys.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Components/Fonts/fixedsys.ttf -------------------------------------------------------------------------------- /Browser/Podfile: -------------------------------------------------------------------------------- 1 | target 'Browser' do 2 | use_frameworks! 3 | inhibit_all_warnings! 4 | 5 | pod 'YYImage' 6 | end 7 | -------------------------------------------------------------------------------- /Components/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Components/Fonts/MS Sans Serif.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Components/Fonts/MS Sans Serif.ttf -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Browser/Browser/Windows Navigation Start.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Windows Navigation Start.wav -------------------------------------------------------------------------------- /Components/Assets.xcassets/scrollbar-up.imageset/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Components/Assets.xcassets/scrollbar-up.imageset/up.png -------------------------------------------------------------------------------- /Components/Assets.xcassets/scrollbar-down.imageset/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Components/Assets.xcassets/scrollbar-down.imageset/down.png -------------------------------------------------------------------------------- /Components/Assets.xcassets/scrollbar-left.imageset/left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Components/Assets.xcassets/scrollbar-left.imageset/left.png -------------------------------------------------------------------------------- /Components/Assets.xcassets/back arrow.imageset/arrow_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Components/Assets.xcassets/back arrow.imageset/arrow_left.png -------------------------------------------------------------------------------- /Components/Assets.xcassets/folder.imageset/cbws2-kmlpn-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Components/Assets.xcassets/folder.imageset/cbws2-kmlpn-0.png -------------------------------------------------------------------------------- /Components/Assets.xcassets/folder.imageset/cbws2-kmlpn-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Components/Assets.xcassets/folder.imageset/cbws2-kmlpn-1.png -------------------------------------------------------------------------------- /Components/Assets.xcassets/refresh.imageset/cbdpg-4uhvl-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Components/Assets.xcassets/refresh.imageset/cbdpg-4uhvl-0.png -------------------------------------------------------------------------------- /Components/Assets.xcassets/refresh.imageset/cbdpg-4uhvl-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Components/Assets.xcassets/refresh.imageset/cbdpg-4uhvl-1.png -------------------------------------------------------------------------------- /Components/Assets.xcassets/scrollbar-right.imageset/right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Components/Assets.xcassets/scrollbar-right.imageset/right.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-20.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-29.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-40.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-76.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/help.imageset/cbpg4-jzqiv-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/help.imageset/cbpg4-jzqiv-0.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/help.imageset/cbpg4-jzqiv-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/help.imageset/cbpg4-jzqiv-1.png -------------------------------------------------------------------------------- /Components/Assets.xcassets/back arrow.imageset/arrow_left-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Components/Assets.xcassets/back arrow.imageset/arrow_left-1.png -------------------------------------------------------------------------------- /Components/Assets.xcassets/media file.imageset/cb5nb-46845-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Components/Assets.xcassets/media file.imageset/cb5nb-46845-0.png -------------------------------------------------------------------------------- /Components/Assets.xcassets/media file.imageset/cb5nb-46845-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Components/Assets.xcassets/media file.imageset/cb5nb-46845-1.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/homepage.imageset/homepage_alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/homepage.imageset/homepage_alt.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/search.imageset/cb6n7-gufu6-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/search.imageset/cb6n7-gufu6-0.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/search.imageset/cb6n7-gufu6-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/search.imageset/cb6n7-gufu6-1.png -------------------------------------------------------------------------------- /Browser/Pods/Target Support Files/YYImage/YYImage.modulemap: -------------------------------------------------------------------------------- 1 | framework module YYImage { 2 | umbrella header "YYImage-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Components/Assets.xcassets/forward arrow.imageset/arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Components/Assets.xcassets/forward arrow.imageset/arrow_right.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-20@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-20@2x-1.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-29@2x-1.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-40@2x-1.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/SplashIcon.imageset/browser ico-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/SplashIcon.imageset/browser ico-1.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/SplashIcon.imageset/browser ico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/SplashIcon.imageset/browser ico.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/homepage.imageset/homepage_alt-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/homepage.imageset/homepage_alt-1.png -------------------------------------------------------------------------------- /Components/Assets.xcassets/forward arrow.imageset/arrow_right-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Components/Assets.xcassets/forward arrow.imageset/arrow_right-1.png -------------------------------------------------------------------------------- /Components/Assets.xcassets/stop.imageset/media_player_stream_no-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Components/Assets.xcassets/stop.imageset/media_player_stream_no-1.png -------------------------------------------------------------------------------- /Components/Assets.xcassets/stop.imageset/media_player_stream_no.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Components/Assets.xcassets/stop.imageset/media_player_stream_no.png -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/AppIcon.appiconset/iTunesArtwork@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser/Assets.xcassets/AppIcon.appiconset/iTunesArtwork@2x.png -------------------------------------------------------------------------------- /Browser/Pods/Target Support Files/YYImage/YYImage-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_YYImage : NSObject 3 | @end 4 | @implementation PodsDummy_YYImage 5 | @end 6 | -------------------------------------------------------------------------------- /Browser/Pods/Target Support Files/Pods-Browser/Pods-Browser.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_Browser { 2 | umbrella header "Pods-Browser-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Browser/Browser.xcodeproj/xcuserdata/blaketsuzaki.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Browser/Pods/Target Support Files/Pods-Browser/Pods-Browser-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_Browser : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_Browser 5 | @end 6 | -------------------------------------------------------------------------------- /Browser/Browser.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Browser/Browser.xcodeproj/project.xcworkspace/xcuserdata/blaketsuzaki.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baddaboo/ClassicKit/HEAD/Browser/Browser.xcodeproj/project.xcworkspace/xcuserdata/blaketsuzaki.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Browser/Pods/Target Support Files/YYImage/YYImage-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Browser/Browser.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Browser/Browser.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Browser/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - YYImage (1.0.4): 3 | - YYImage/Core (= 1.0.4) 4 | - YYImage/Core (1.0.4) 5 | 6 | DEPENDENCIES: 7 | - YYImage 8 | 9 | SPEC CHECKSUMS: 10 | YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54 11 | 12 | PODFILE CHECKSUM: 4ab686edef8e7a883b5d87456ac66854434e3a05 13 | 14 | COCOAPODS: 1.2.1 15 | -------------------------------------------------------------------------------- /Browser/Browser.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Browser/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - YYImage (1.0.4): 3 | - YYImage/Core (= 1.0.4) 4 | - YYImage/Core (1.0.4) 5 | 6 | DEPENDENCIES: 7 | - YYImage 8 | 9 | SPEC CHECKSUMS: 10 | YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54 11 | 12 | PODFILE CHECKSUM: 4ab686edef8e7a883b5d87456ac66854434e3a05 13 | 14 | COCOAPODS: 1.2.1 15 | -------------------------------------------------------------------------------- /Components/Assets.xcassets/scrollbar-up.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "up.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Browser/Pods/Target Support Files/Pods-Browser/Pods-Browser-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_BrowserVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_BrowserVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Components/Assets.xcassets/scrollbar-down.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "down.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Components/Assets.xcassets/scrollbar-left.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "left.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Components/Assets.xcassets/scrollbar-right.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "right.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Components/Assets.xcassets/teal.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0.043", 13 | "alpha" : "1.000", 14 | "blue" : "0.498", 15 | "green" : "0.498" 16 | } 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /Components/Assets.xcassets/magnesium.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0.753", 13 | "alpha" : "1.000", 14 | "blue" : "0.753", 15 | "green" : "0.753" 16 | } 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /Components/Assets.xcassets/midnight.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0.008", 13 | "alpha" : "1.000", 14 | "blue" : "0.494", 15 | "green" : "0.000" 16 | } 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /Components/Assets.xcassets/silver.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0.875", 13 | "alpha" : "1.000", 14 | "blue" : "0.878", 15 | "green" : "0.875" 16 | } 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /Components/Assets.xcassets/back arrow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "arrow_left-1.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "arrow_left.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Components/Assets.xcassets/folder.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "cbws2-kmlpn-0.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "cbws2-kmlpn-1.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Components/Assets.xcassets/refresh.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "cbdpg-4uhvl-0.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "cbdpg-4uhvl-1.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/help.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "cbpg4-jzqiv-0.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "cbpg4-jzqiv-1.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/search.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "cb6n7-gufu6-0.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "cb6n7-gufu6-1.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Components/Assets.xcassets/forward arrow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "arrow_right-1.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "arrow_right.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Components/Assets.xcassets/media file.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "cb5nb-46845-0.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "cb5nb-46845-1.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/SplashIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "browser ico.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "browser ico-1.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/homepage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "homepage_alt-1.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "homepage_alt.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Components/Assets.xcassets/stop.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "media_player_stream_no-1.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "media_player_stream_no.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Browser/Pods/Target Support Files/YYImage/YYImage-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "YYAnimatedImageView.h" 14 | #import "YYFrameImage.h" 15 | #import "YYImage.h" 16 | #import "YYImageCoder.h" 17 | #import "YYSpriteSheetImage.h" 18 | 19 | FOUNDATION_EXPORT double YYImageVersionNumber; 20 | FOUNDATION_EXPORT const unsigned char YYImageVersionString[]; 21 | 22 | -------------------------------------------------------------------------------- /Browser/Browser.xcodeproj/xcuserdata/blaketsuzaki.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Browser.xcscheme 8 | 9 | orderHint 10 | 2 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 126F7C2220A5732C00CD1C0F 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Browser/Pods/Target Support Files/Pods-Browser/Pods-Browser.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/YYImage" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/YYImage/YYImage.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "YYImage" 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | -------------------------------------------------------------------------------- /Browser/Pods/Target Support Files/Pods-Browser/Pods-Browser.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/YYImage" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/YYImage/YYImage.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "YYImage" 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | -------------------------------------------------------------------------------- /Browser/Pods/Pods.xcodeproj/xcuserdata/blaketsuzaki.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Pods-Browser.xcscheme 8 | 9 | isShown 10 | 11 | orderHint 12 | 0 13 | 14 | YYImage.xcscheme 15 | 16 | isShown 17 | 18 | orderHint 19 | 1 20 | 21 | 22 | SuppressBuildableAutocreation 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Browser/Pods/Target Support Files/YYImage/YYImage.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/YYImage 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_LDFLAGS = -l"z" -framework "Accelerate" -framework "AssetsLibrary" -framework "CoreFoundation" -framework "ImageIO" -framework "MobileCoreServices" -framework "QuartzCore" -framework "UIKit" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/YYImage 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /Browser/Pods/Target Support Files/YYImage/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 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.4 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Browser/Pods/Target Support Files/Pods-Browser/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 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Components/CKLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CKLabel.swift 3 | // Browser 4 | // 5 | // Created by Blake Tsuzaki on 5/11/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @IBDesignable class CKLabel: UILabel { 12 | @IBInspectable var textSize: CGFloat = 30 { 13 | didSet { 14 | font = UIFont(name: CKDefaults.fontName, size: textSize) 15 | } 16 | } 17 | 18 | @IBInspectable var leftInset: CGFloat = 0 { 19 | didSet { setNeedsDisplay() } 20 | } 21 | 22 | 23 | override init(frame: CGRect) { 24 | super.init(frame: frame) 25 | 26 | configure() 27 | } 28 | 29 | required init?(coder aDecoder: NSCoder) { 30 | super.init(coder: aDecoder) 31 | 32 | configure() 33 | } 34 | 35 | private func configure() { 36 | font = UIFont(name: CKDefaults.fontName, size: 30) 37 | } 38 | 39 | override func drawText(in rect: CGRect) { 40 | let insets = UIEdgeInsets(top: 0, left: leftInset, bottom: 0, right: 0) 41 | super.drawText(in: rect.inset(by: insets)) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Browser/Pods/YYImage/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 ibireme 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 all 13 | 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 THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Components/CKImageButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CKImageButton.swift 3 | // Browser 4 | // 5 | // Created by Blake Tsuzaki on 5/11/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @IBDesignable class CKImageButton: CKButton { 12 | @IBInspectable var image: UIImage? { 13 | didSet { imageView.image = image } 14 | } 15 | 16 | @IBInspectable var imageInset: CGFloat = 0 17 | 18 | let imageView = UIImageView() 19 | 20 | override init(frame: CGRect) { 21 | super.init(frame: frame) 22 | 23 | configure() 24 | } 25 | 26 | required init?(coder aDecoder: NSCoder) { 27 | super.init(coder: aDecoder) 28 | 29 | configure() 30 | } 31 | 32 | private func configure() { 33 | isOpaque = false 34 | 35 | imageView.contentMode = .scaleAspectFit 36 | imageView.layer.magnificationFilter = CALayerContentsFilter.nearest 37 | contentView.addSubview(imageView) 38 | } 39 | 40 | override func draw(_ rect: CGRect) { 41 | super.draw(rect) 42 | 43 | imageView.frame = contentView.bounds.insetBy(dx: imageInset, dy: imageInset) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Components/CKNavigationBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CKNavigationBar.swift 3 | // Browser 4 | // 5 | // Created by Blake Tsuzaki on 5/11/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @IBDesignable class CKNavigationBar: UIView { 12 | override func layoutSubviews() { 13 | super.layoutSubviews() 14 | setNeedsDisplay() 15 | } 16 | 17 | override func draw(_ rect: CGRect) { 18 | super.draw(rect) 19 | 20 | let outerBottom = UIBezierPath() 21 | let innerBottom = UIBezierPath() 22 | let lineWidth: CGFloat = CKDefaults.bevelWidth 23 | 24 | outerBottom.lineWidth = lineWidth 25 | outerBottom.move(to: CGPoint(x: 0, y: bounds.height - lineWidth/2)) 26 | outerBottom.addLine(to: CGPoint(x: bounds.width, y: bounds.height - lineWidth/2)) 27 | UIColor.black.set() 28 | outerBottom.stroke() 29 | 30 | innerBottom.lineWidth = lineWidth 31 | innerBottom.move(to: CGPoint(x: 0, y: bounds.height - lineWidth * 1.5)) 32 | innerBottom.addLine(to: CGPoint(x: bounds.width, y: bounds.height - lineWidth * 1.5)) 33 | UIColor(white: 0, alpha: 0.3).set() 34 | innerBottom.stroke() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Components/CKTextButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CKTextButton.swift 3 | // Browser 4 | // 5 | // Created by Blake Tsuzaki on 5/11/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @IBDesignable class CKTextButton: CKButton { 12 | @IBInspectable var textColor: UIColor = .black { 13 | didSet { label.textColor = textColor } 14 | } 15 | 16 | @IBInspectable var text: String = "" { 17 | didSet { label.text = text } 18 | } 19 | 20 | @IBInspectable var textSize: CGFloat = 30 { 21 | didSet { label.font = UIFont(name: CKDefaults.fontName, size: textSize) } 22 | } 23 | 24 | private let label = UILabel() 25 | 26 | override init(frame: CGRect) { 27 | super.init(frame: frame) 28 | 29 | configure() 30 | } 31 | 32 | required init?(coder aDecoder: NSCoder) { 33 | super.init(coder: aDecoder) 34 | 35 | configure() 36 | } 37 | 38 | private func configure() { 39 | isOpaque = false 40 | 41 | label.font = UIFont(name: CKDefaults.fontName, size: 30) 42 | label.textColor = textColor 43 | label.textAlignment = .center 44 | 45 | contentView = label 46 | } 47 | 48 | override func draw(_ rect: CGRect) { 49 | super.draw(rect) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Browser/Pods/Target Support Files/Pods-Browser/Pods-Browser-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## YYImage 5 | 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2015 ibireme 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | 29 | Generated by CocoaPods - https://cocoapods.org 30 | -------------------------------------------------------------------------------- /Components/CKStatusView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CKStatusView.swift 3 | // Browser 4 | // 5 | // Created by Blake Tsuzaki on 5/11/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @IBDesignable class CKStatusView: UIView { 12 | @IBInspectable var message: String = "" { 13 | didSet { 14 | label.text = message 15 | } 16 | } 17 | 18 | @IBInspectable var textSize: CGFloat = 16 { 19 | didSet { 20 | label.font = UIFont(name: CKDefaults.fontName, size: textSize) 21 | } 22 | } 23 | 24 | var label = CKLabel() 25 | 26 | override init(frame: CGRect) { 27 | super.init(frame: frame) 28 | 29 | configure() 30 | } 31 | 32 | required init?(coder aDecoder: NSCoder) { 33 | super.init(coder: aDecoder) 34 | 35 | configure() 36 | } 37 | 38 | private func configure() { 39 | label.leftInset = 4 40 | label.font = UIFont(name: CKDefaults.fontName, size: textSize) 41 | label.textColor = .black 42 | 43 | addSubview(label) 44 | } 45 | 46 | override func layoutSubviews() { 47 | super.layoutSubviews() 48 | setNeedsDisplay() 49 | } 50 | 51 | override func draw(_ rect: CGRect) { 52 | super.draw(rect) 53 | 54 | CKDefaults.drawInsetBevel(with: rect) 55 | 56 | let lineWidth = CKDefaults.bevelWidth 57 | 58 | var contentFrame = CGRect() 59 | contentFrame.origin.x = lineWidth * 2 60 | contentFrame.origin.y = lineWidth * 2 61 | contentFrame.size.height = rect.size.height - lineWidth * 4 62 | contentFrame.size.width = rect.size.width - lineWidth * 4 63 | 64 | label.frame = contentFrame 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Components/CKGradientView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CKGradientView.swift 3 | // Spotify 4 | // 5 | // Created by Blake Tsuzaki on 5/17/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @IBDesignable 12 | class CKGradientView: UIView { 13 | 14 | @IBInspectable 15 | var firstColor: UIColor = .white 16 | 17 | @IBInspectable 18 | var secondColor: UIColor = .black 19 | 20 | var additionalColors = [UIColor]() 21 | 22 | @IBInspectable 23 | var verticalGradient: Bool = false 24 | 25 | private var gradientLayer = CAGradientLayer() 26 | 27 | override init(frame: CGRect) { 28 | super.init(frame: frame) 29 | layer.insertSublayer(gradientLayer, at: 0) 30 | } 31 | 32 | required init?(coder aDecoder: NSCoder) { 33 | super.init(coder: aDecoder) 34 | layer.insertSublayer(gradientLayer, at: 0) 35 | } 36 | 37 | override func layoutSubviews() { 38 | super.layoutSubviews() 39 | setNeedsDisplay() 40 | } 41 | 42 | override func draw(_ rect: CGRect) { 43 | super.draw(rect) 44 | 45 | var additionalColors = [CGColor]() 46 | for color in self.additionalColors { additionalColors.append(color.cgColor) } 47 | 48 | let gradientLayer = CAGradientLayer() 49 | gradientLayer.frame = bounds 50 | gradientLayer.colors = [firstColor.cgColor, secondColor.cgColor] + additionalColors 51 | gradientLayer.startPoint = CGPoint() 52 | 53 | if verticalGradient { 54 | gradientLayer.endPoint = CGPoint(x: 0, y: 1) 55 | } else { 56 | gradientLayer.endPoint = CGPoint(x: 1, y: 0) 57 | } 58 | 59 | layer.replaceSublayer(self.gradientLayer, with: gradientLayer) 60 | 61 | self.gradientLayer = gradientLayer 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Components/CKImageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CKImageView.swift 3 | // Browser 4 | // 5 | // Created by Blake Tsuzaki on 5/14/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import YYImage 11 | 12 | @IBDesignable class CKImageView: UIView { 13 | @IBInspectable var shouldAnimate: Bool { 14 | get { return imageView.isAnimating } 15 | set { 16 | if newValue { imageView.startAnimating() } 17 | else { 18 | imageView.stopAnimating() 19 | imageView.currentAnimatedImageIndex = 0 20 | } 21 | } 22 | } 23 | 24 | @IBInspectable var image: UIImage? { 25 | get { return imageView.image } 26 | set { imageView.image = newValue } 27 | } 28 | 29 | private let imageView = YYAnimatedImageView() 30 | 31 | override init(frame: CGRect) { 32 | super.init(frame: frame) 33 | 34 | configure() 35 | } 36 | 37 | required init?(coder aDecoder: NSCoder) { 38 | super.init(coder: aDecoder) 39 | 40 | configure() 41 | } 42 | 43 | private func configure() { 44 | isOpaque = false 45 | 46 | imageView.contentMode = .scaleAspectFit 47 | addSubview(imageView) 48 | } 49 | 50 | override func layoutSubviews() { 51 | super.layoutSubviews() 52 | setNeedsDisplay() 53 | } 54 | 55 | override func draw(_ rect: CGRect) { 56 | super.draw(rect) 57 | 58 | CKDefaults.drawInsetBevel(with: rect) 59 | 60 | let lineWidth: CGFloat = CKDefaults.bevelWidth 61 | 62 | var contentFrame = CGRect() 63 | contentFrame.origin.x = lineWidth * 2 64 | contentFrame.origin.y = lineWidth * 2 65 | contentFrame.size.height = rect.size.height - lineWidth * 4 66 | contentFrame.size.width = rect.size.width - lineWidth * 4 67 | 68 | imageView.frame = contentFrame 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Components/CKFillableView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CKFillableView.swift 3 | // Browser 4 | // 5 | // Created by Blake Tsuzaki on 5/11/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @IBDesignable class CKFillableView: UIView { 12 | override func layoutSubviews() { 13 | super.layoutSubviews() 14 | setNeedsDisplay() 15 | } 16 | 17 | override func draw(_ rect: CGRect) { 18 | super.draw(rect) 19 | 20 | let outerBottom = UIBezierPath() 21 | let innerBottom = UIBezierPath() 22 | let innerTop = UIBezierPath() 23 | let outerTop = UIBezierPath() 24 | let lineWidth: CGFloat = CKDefaults.bevelWidth 25 | 26 | outerBottom.lineWidth = lineWidth 27 | outerBottom.move(to: CGPoint(x: 0, y: bounds.height - safeAreaInsets.bottom - lineWidth * 1.5)) 28 | outerBottom.addLine(to: CGPoint(x: bounds.width, y: bounds.height - safeAreaInsets.bottom - lineWidth * 1.5)) 29 | UIColor(white: 0, alpha: 0.3).set() 30 | outerBottom.stroke() 31 | 32 | innerBottom.lineWidth = lineWidth 33 | innerBottom.move(to: CGPoint(x: 0, y: bounds.height - safeAreaInsets.bottom - lineWidth * 0.5)) 34 | innerBottom.addLine(to: CGPoint(x: bounds.width, y: bounds.height - safeAreaInsets.bottom - lineWidth * 0.5)) 35 | UIColor(white: 1, alpha: 0.5).set() 36 | innerBottom.stroke() 37 | 38 | outerTop.lineWidth = lineWidth 39 | outerTop.move(to: CGPoint(x: 0, y: lineWidth * 0.5 + safeAreaInsets.top)) 40 | outerTop.addLine(to: CGPoint(x: bounds.width, y: lineWidth * 0.5 + safeAreaInsets.top)) 41 | UIColor(white: 0, alpha: 0.3).set() 42 | outerTop.stroke() 43 | 44 | innerTop.lineWidth = lineWidth 45 | innerTop.move(to: CGPoint(x: 0, y: lineWidth * 1.5 + safeAreaInsets.top)) 46 | innerTop.addLine(to: CGPoint(x: bounds.width, y: lineWidth * 1.5 + safeAreaInsets.top)) 47 | UIColor(white: 1, alpha: 0.5).set() 48 | innerTop.stroke() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Browser/Browser/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UIViewControllerBasedStatusBarAppearance 6 | 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleDisplayName 10 | Browser 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | $(PRODUCT_NAME) 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | NSAllowsArbitraryLoadsInWebContent 32 | 33 | 34 | UIAppFonts 35 | 36 | fixedsys.ttf 37 | MS Sans Serif.ttf 38 | 39 | UILaunchStoryboardName 40 | LaunchScreen 41 | UIMainStoryboardFile 42 | Main 43 | UIRequiredDeviceCapabilities 44 | 45 | armv7 46 | 47 | UISupportedInterfaceOrientations 48 | 49 | UIInterfaceOrientationPortrait 50 | UIInterfaceOrientationLandscapeLeft 51 | UIInterfaceOrientationLandscapeRight 52 | 53 | UISupportedInterfaceOrientations~ipad 54 | 55 | UIInterfaceOrientationPortrait 56 | UIInterfaceOrientationPortraitUpsideDown 57 | UIInterfaceOrientationLandscapeLeft 58 | UIInterfaceOrientationLandscapeRight 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Components/CKProgressView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CKProgressView.swift 3 | // Browser 4 | // 5 | // Created by Blake Tsuzaki on 5/11/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @IBDesignable class CKProgressView: UIControl { 12 | @IBInspectable var value: CGFloat = 50 { 13 | didSet { 14 | if value != oldValue { setNeedsDisplay() } 15 | } 16 | } 17 | 18 | @IBInspectable var range: CGFloat = 100 { 19 | didSet { 20 | if range != oldValue { setNeedsDisplay() } 21 | } 22 | } 23 | 24 | @IBInspectable var spacing: CGFloat = 4 { 25 | didSet { 26 | if spacing != oldValue { setNeedsDisplay() } 27 | } 28 | } 29 | 30 | @IBInspectable var progressColor: UIColor = CKDefaults.highlightColor ?? UIColor.black { 31 | didSet { 32 | if progressColor != oldValue { setNeedsDisplay() } 33 | } 34 | } 35 | 36 | override func layoutSubviews() { 37 | super.layoutSubviews() 38 | setNeedsDisplay() 39 | } 40 | 41 | override func draw(_ rect: CGRect) { 42 | super.draw(rect) 43 | 44 | CKDefaults.drawInsetBevel(with: rect) 45 | 46 | if state == .disabled { return } 47 | 48 | let totalBars = Double(floor(rect.width / (rect.height * 0.6))) 49 | let barWidth = (rect.width - CGFloat(totalBars + 1) * spacing)/CGFloat(totalBars) 50 | 51 | for idx in 0...Int(CGFloat(totalBars) * (value/range)) { 52 | let barPath = UIBezierPath() 53 | barPath.move(to: CGPoint(x: spacing * (CGFloat(idx) + 1) + CGFloat(idx) * barWidth, y: spacing)) 54 | barPath.addLine(to: CGPoint(x: spacing * (CGFloat(idx) + 1) + CGFloat(idx) * barWidth, y: rect.height - spacing)) 55 | barPath.addLine(to: CGPoint(x: spacing * (CGFloat(idx) + 1) + (CGFloat(idx) + 1) * barWidth, y: rect.height - spacing)) 56 | barPath.addLine(to: CGPoint(x: spacing * (CGFloat(idx) + 1) + (CGFloat(idx) + 1) * barWidth, y: spacing)) 57 | barPath.close() 58 | progressColor.set() 59 | barPath.fill() 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Browser/Browser/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Browser 4 | // 5 | // Created by Blake Tsuzaki on 5/10/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // 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. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // 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. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // 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. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Components/CKDialogViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CKDialogViewController.swift 3 | // Browser 4 | // 5 | // Created by Blake Tsuzaki on 5/14/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @IBDesignable class CKDialogViewController: UIViewController { 12 | var contentView: UIView = UIView() { 13 | didSet { 14 | oldValue.removeFromSuperview() 15 | dialogView.addSubview(contentView) 16 | } 17 | } 18 | 19 | private var dialogView = CKView() 20 | 21 | private let shadowView = UIView() 22 | private var tapGestureRecognizer: UITapGestureRecognizer? 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | 27 | dialogView.frame = CGRect(x: 0, y: 0, width: 100, height: 100) 28 | dialogView.backgroundColor = CKDefaults.backgroundColor 29 | dialogView.addSubview(contentView) 30 | 31 | let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(CKDialogViewController.shadowViewWasTapped)) 32 | shadowView.backgroundColor = UIColor(white: 0, alpha: 0.6) 33 | shadowView.addGestureRecognizer(tapGestureRecognizer) 34 | 35 | self.tapGestureRecognizer = tapGestureRecognizer 36 | 37 | view.addSubview(shadowView) 38 | view.addSubview(dialogView) 39 | view.backgroundColor = .clear 40 | view.isOpaque = false 41 | } 42 | 43 | override func viewDidAppear(_ animated: Bool) { 44 | super.viewDidAppear(animated) 45 | 46 | shadowView.frame.origin = CGPoint() 47 | shadowView.frame.size = CGSize(width: view.frame.width, height: 0) 48 | 49 | UIView.animate(withDuration: 1, delay: 0, options: .curveLinear, animations: { 50 | self.shadowView.frame = self.view.bounds 51 | }, completion: nil) 52 | } 53 | 54 | @objc func shadowViewWasTapped() { 55 | dismiss(animated: false, completion: nil) 56 | } 57 | 58 | override func viewWillLayoutSubviews() { 59 | super.viewWillLayoutSubviews() 60 | 61 | dialogView.frame.size = CGSize(width: 250, height: 200) 62 | dialogView.center = view.center 63 | 64 | contentView.frame = dialogView.bounds 65 | shadowView.frame = view.bounds 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Browser/Pods/Target Support Files/Pods-Browser/Pods-Browser-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | The MIT License (MIT) 18 | 19 | Copyright (c) 2015 ibireme <ibireme@gmail.com> 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | 40 | License 41 | MIT 42 | Title 43 | YYImage 44 | Type 45 | PSGroupSpecifier 46 | 47 | 48 | FooterText 49 | Generated by CocoaPods - https://cocoapods.org 50 | Title 51 | 52 | Type 53 | PSGroupSpecifier 54 | 55 | 56 | StringsTable 57 | Acknowledgements 58 | Title 59 | Acknowledgements 60 | 61 | 62 | -------------------------------------------------------------------------------- /Components/CKView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CKView.swift 3 | // Browser 4 | // 5 | // Created by Blake Tsuzaki on 5/10/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @IBDesignable class CKView: UIView { 12 | enum Style { 13 | case window, control 14 | } 15 | 16 | var style: Style = .window { 17 | didSet { setNeedsDisplay() } 18 | } 19 | 20 | override func layoutSubviews() { 21 | super.layoutSubviews() 22 | setNeedsDisplay() 23 | } 24 | 25 | override func draw(_ rect: CGRect) { 26 | super.draw(rect) 27 | 28 | let outerBottom = UIBezierPath() 29 | let innerBottom = UIBezierPath() 30 | let innerTop = UIBezierPath() 31 | let outerTop = UIBezierPath() 32 | let lineWidth: CGFloat = CKDefaults.bevelWidth 33 | 34 | switch style { 35 | case .control: fallthrough 36 | case .window: 37 | outerBottom.lineWidth = lineWidth 38 | outerBottom.move(to: CGPoint(x: 0, y: bounds.height - lineWidth/2)) 39 | outerBottom.addLine(to: CGPoint(x: bounds.width - lineWidth/2, y: bounds.height - lineWidth/2)) 40 | outerBottom.addLine(to: CGPoint(x: bounds.width - lineWidth/2, y: 0)) 41 | UIColor.black.set() 42 | outerBottom.stroke() 43 | 44 | innerBottom.lineWidth = lineWidth 45 | innerBottom.move(to: CGPoint(x: lineWidth, y: bounds.height - lineWidth * 1.5)) 46 | innerBottom.addLine(to: CGPoint(x: bounds.width - lineWidth * 1.5, y: bounds.height - lineWidth * 1.5)) 47 | innerBottom.addLine(to: CGPoint(x: bounds.width - lineWidth * 1.5, y: lineWidth)) 48 | UIColor(white: 0, alpha: 0.3).set() 49 | innerBottom.stroke() 50 | 51 | outerTop.lineWidth = lineWidth 52 | outerTop.move(to: CGPoint(x: lineWidth/2, y: bounds.height - lineWidth)) 53 | outerTop.addLine(to: CGPoint(x: lineWidth/2, y: lineWidth/2)) 54 | outerTop.addLine(to: CGPoint(x: bounds.width - lineWidth, y: lineWidth/2)) 55 | style == .window ? UIColor(white: 1, alpha: 0.5).set() : UIColor.white.set() 56 | outerTop.stroke() 57 | 58 | innerTop.lineWidth = lineWidth 59 | innerTop.move(to: CGPoint(x: lineWidth * 1.5, y: bounds.height - lineWidth * 2)) 60 | innerTop.addLine(to: CGPoint(x: lineWidth * 1.5, y: lineWidth * 1.5)) 61 | innerTop.addLine(to: CGPoint(x: bounds.width - lineWidth * 2, y: lineWidth * 1.5)) 62 | style == .window ? UIColor.white.set() : UIColor(white: 1, alpha: 0.5).set() 63 | innerTop.stroke() 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![ClassicKit](Images/logo.png) 2 | A collection of classic-style UI components for UIKit, influenced by Windows 95 3 | 4 | ![about](Images/about.png) ![portrait](Images/portrait.png) ![landscape](Images/landscape.png) 5 | 6 | ## Introduction 7 | This is a little exploration into applying '90s-era design & principles into a modern platform with some primitive components. The assets and design metrics were (for the most part) taken from an actual installation of Windows 95. These are pixel-accurate renditions of the original design: 8 | 9 | ![](Images/pixel.png) 10 | 11 | **Update:** 12 | - (3/17) Added a simple Blue Screen of Death! Simply shake the device vigorously. 13 | 14 | ## Usage 15 | - The `Browser` example can be run out-of-the-box. 16 | - You _should_ be able to include some or all of the files under `/Components` in your project. 17 | - Each component is intended to be used like their UIKit counterparts. For example, `CKButton` should respond to gesture events just as `UIButton` would. 18 | - These are `@IBDesignable` components! That means you can lay out your entire app with these components in Interface Builder and Xcode will render them for you: 19 | ![xcode](Images/xcode.png) 20 | 21 | ## Notes & FAQ 22 | - This project is very much a work-in-progress. Although it was designed with modularity and robustness in mind, there are no guarantees on reliability. 23 | - **Q: Why did you do this?** 24 | - This project was born out of some wholesome sarcasm and Millennial jokes with [Ben Galassi.](http://bengalassi.com) Check out his work! 25 | - **Q: If this project is made for Apple devices, why based it on Microsoft's design?** 26 | - Most people were using Windows during this age and far more people recognize the Windows Standard design pattern than Platinum. If you need any proof, [PCs sold close to 100 million units in 1998, compared to just 2.7 million for Macintosh.](https://arstechnica.com/features/2005/12/total-share/8/) 27 | - I may revisit this point in the future. 28 | - **Q: Why isn't the UI rendering in my Storyboard?** 29 | - Interface Builder is very finicky. Rendering typically fails because the underlying code has an error or a constraint cannot be satisfied. Even if you fix these issues, you may have to clear the cache and restart Xcode. 30 | - **Q: Why do I need YYImage?** 31 | - You don't. `YYImage` is used in `CKImageView` to animate the throbber animation in the `Browser` example, but you can remove the reference in `CKImageView.swift`. Eventually this dependency will be removed. 32 | - Please let me know if you have any questions or comments! I'd also love to chat about design or tech nostalgia 🙂 33 | 34 | ## Disclaimer 35 | I do not claim ownership of the assets or logos used in this project. Windows and Internet Explorer are registered trademarks of Microsoft Inc. 36 | 37 | ## License 38 | MIT 39 | -------------------------------------------------------------------------------- /Browser/Pods/Pods.xcodeproj/xcuserdata/blaketsuzaki.xcuserdatad/xcschemes/YYImage.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 66 | 67 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Browser/Pods/Pods.xcodeproj/xcuserdata/blaketsuzaki.xcuserdatad/xcschemes/Pods-Browser.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 66 | 67 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Browser/Pods/YYImage/YYImage/YYSpriteSheetImage.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYSpriteImage.m 3 | // YYImage 4 | // 5 | // Created by ibireme on 15/4/21. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYSpriteSheetImage.h" 13 | 14 | @implementation YYSpriteSheetImage 15 | 16 | - (instancetype)initWithSpriteSheetImage:(UIImage *)image 17 | contentRects:(NSArray *)contentRects 18 | frameDurations:(NSArray *)frameDurations 19 | loopCount:(NSUInteger)loopCount { 20 | if (!image.CGImage) return nil; 21 | if (contentRects.count < 1 || frameDurations.count < 1) return nil; 22 | if (contentRects.count != frameDurations.count) return nil; 23 | 24 | self = [super initWithCGImage:image.CGImage scale:image.scale orientation:image.imageOrientation]; 25 | if (!self) return nil; 26 | 27 | _contentRects = contentRects.copy; 28 | _frameDurations = frameDurations.copy; 29 | _loopCount = loopCount; 30 | return self; 31 | } 32 | 33 | - (CGRect)contentsRectForCALayerAtIndex:(NSUInteger)index { 34 | CGRect layerRect = CGRectMake(0, 0, 1, 1); 35 | if (index >= _contentRects.count) return layerRect; 36 | 37 | CGSize imageSize = self.size; 38 | CGRect rect = [self animatedImageContentsRectAtIndex:index]; 39 | if (imageSize.width > 0.01 && imageSize.height > 0.01) { 40 | layerRect.origin.x = rect.origin.x / imageSize.width; 41 | layerRect.origin.y = rect.origin.y / imageSize.height; 42 | layerRect.size.width = rect.size.width / imageSize.width; 43 | layerRect.size.height = rect.size.height / imageSize.height; 44 | layerRect = CGRectIntersection(layerRect, CGRectMake(0, 0, 1, 1)); 45 | if (CGRectIsNull(layerRect) || CGRectIsEmpty(layerRect)) { 46 | layerRect = CGRectMake(0, 0, 1, 1); 47 | } 48 | } 49 | return layerRect; 50 | } 51 | 52 | #pragma mark @protocol YYAnimatedImage 53 | 54 | - (NSUInteger)animatedImageFrameCount { 55 | return _contentRects.count; 56 | } 57 | 58 | - (NSUInteger)animatedImageLoopCount { 59 | return _loopCount; 60 | } 61 | 62 | - (NSUInteger)animatedImageBytesPerFrame { 63 | return 0; 64 | } 65 | 66 | - (UIImage *)animatedImageFrameAtIndex:(NSUInteger)index { 67 | return self; 68 | } 69 | 70 | - (NSTimeInterval)animatedImageDurationAtIndex:(NSUInteger)index { 71 | if (index >= _frameDurations.count) return 0; 72 | return ((NSNumber *)_frameDurations[index]).doubleValue; 73 | } 74 | 75 | - (CGRect)animatedImageContentsRectAtIndex:(NSUInteger)index { 76 | if (index >= _contentRects.count) return CGRectZero; 77 | return ((NSValue *)_contentRects[index]).CGRectValue; 78 | } 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /Browser/Browser/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "icon-20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "icon-20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "icon-29@2x.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "icon-29@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "icon-40@2x-1.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "icon-40@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-60@2x.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-60@3x.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "20x20", 53 | "idiom" : "ipad", 54 | "filename" : "icon-20.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "icon-20@2x-1.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "29x29", 65 | "idiom" : "ipad", 66 | "filename" : "icon-29.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "icon-29@2x-1.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "40x40", 77 | "idiom" : "ipad", 78 | "filename" : "icon-40.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-40@2x.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "76x76", 89 | "idiom" : "ipad", 90 | "filename" : "icon-76.png", 91 | "scale" : "1x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-76@2x.png", 97 | "scale" : "2x" 98 | }, 99 | { 100 | "size" : "83.5x83.5", 101 | "idiom" : "ipad", 102 | "filename" : "icon-83.5@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "1024x1024", 107 | "idiom" : "ios-marketing", 108 | "filename" : "iTunesArtwork@2x.png", 109 | "scale" : "1x" 110 | } 111 | ], 112 | "info" : { 113 | "version" : 1, 114 | "author" : "xcode" 115 | } 116 | } -------------------------------------------------------------------------------- /Browser/Browser/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Components/CKDefaults.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CKDefaults.swift 3 | // Browser 4 | // 5 | // Created by Blake Tsuzaki on 5/10/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class CKDefaults { 12 | static let bevelWidth: CGFloat = 1 13 | static let backgroundColor = UIColor(named: "magnesium") 14 | static let desktopColor = UIColor(named: "teal") 15 | static let highlightColor = UIColor(named: "midnight") 16 | static let textureLightColor = UIColor(named: "silver") 17 | static let fontName = "MS Sans Serif" 18 | static let blueScreenFontName = "FixedsysTTF" 19 | 20 | class func drawInsetBevel(with bounds: CGRect) { 21 | let outerBottom = UIBezierPath() 22 | let innerBottom = UIBezierPath() 23 | let innerTop = UIBezierPath() 24 | let outerTop = UIBezierPath() 25 | let lineWidth: CGFloat = CKDefaults.bevelWidth 26 | 27 | innerBottom.lineWidth = lineWidth 28 | innerBottom.move(to: CGPoint(x: 0, y: bounds.height - lineWidth/2)) 29 | innerBottom.addLine(to: CGPoint(x: bounds.width - lineWidth/2, y: bounds.height - lineWidth/2)) 30 | innerBottom.addLine(to: CGPoint(x: bounds.width - lineWidth/2, y: 0)) 31 | UIColor.white.set() 32 | innerBottom.stroke() 33 | 34 | outerBottom.lineWidth = lineWidth 35 | outerBottom.move(to: CGPoint(x: lineWidth, y: bounds.height - lineWidth * 1.5)) 36 | outerBottom.addLine(to: CGPoint(x: bounds.width - lineWidth * 1.5, y: bounds.height - lineWidth * 1.5)) 37 | outerBottom.addLine(to: CGPoint(x: bounds.width - lineWidth * 1.5, y: lineWidth)) 38 | UIColor(white: 1, alpha: 0.5).set() 39 | outerBottom.stroke() 40 | 41 | outerTop.lineWidth = lineWidth 42 | outerTop.move(to: CGPoint(x: lineWidth/2, y: bounds.height - lineWidth)) 43 | outerTop.addLine(to: CGPoint(x: lineWidth/2, y: lineWidth/2)) 44 | outerTop.addLine(to: CGPoint(x: bounds.width - lineWidth, y: lineWidth/2)) 45 | UIColor(white: 0, alpha: 0.3).set() 46 | outerTop.stroke() 47 | 48 | innerTop.lineWidth = lineWidth 49 | innerTop.move(to: CGPoint(x: lineWidth * 1.5, y: bounds.height - lineWidth * 2)) 50 | innerTop.addLine(to: CGPoint(x: lineWidth * 1.5, y: lineWidth * 1.5)) 51 | innerTop.addLine(to: CGPoint(x: bounds.width - lineWidth * 2, y: lineWidth * 1.5)) 52 | UIColor.black.set() 53 | innerTop.stroke() 54 | } 55 | } 56 | 57 | extension UIColor { 58 | func lighter(by percentage: CGFloat = 30) -> UIColor { 59 | return self.adjust(by: abs(percentage)) 60 | } 61 | func darker(by percentage: CGFloat = 30) -> UIColor { 62 | return self.adjust(by: -1 * abs(percentage) ) 63 | } 64 | 65 | func adjust(by percentage: CGFloat = 30) -> UIColor { 66 | var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0 67 | 68 | if(self.getRed(&r, green: &g, blue: &b, alpha: &a)){ 69 | return UIColor(red: min(r + percentage/100, 1.0), 70 | green: min(g + percentage/100, 1.0), 71 | blue: min(b + percentage/100, 1.0), 72 | alpha: a) 73 | }else{ 74 | return UIColor() 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Browser/Pods/YYImage/YYImage/YYImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYImage.h 3 | // YYImage 4 | // 5 | // Created by ibireme on 14/10/20. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | #if __has_include() 15 | FOUNDATION_EXPORT double YYImageVersionNumber; 16 | FOUNDATION_EXPORT const unsigned char YYImageVersionString[]; 17 | #import 18 | #import 19 | #import 20 | #import 21 | #elif __has_include() 22 | #import 23 | #import 24 | #import 25 | #import 26 | #else 27 | #import "YYFrameImage.h" 28 | #import "YYSpriteSheetImage.h" 29 | #import "YYImageCoder.h" 30 | #import "YYAnimatedImageView.h" 31 | #endif 32 | 33 | NS_ASSUME_NONNULL_BEGIN 34 | 35 | 36 | /** 37 | A YYImage object is a high-level way to display animated image data. 38 | 39 | @discussion It is a fully compatible `UIImage` subclass. It extends the UIImage 40 | to support animated WebP, APNG and GIF format image data decoding. It also 41 | support NSCoding protocol to archive and unarchive multi-frame image data. 42 | 43 | If the image is created from multi-frame image data, and you want to play the 44 | animation, try replace UIImageView with `YYAnimatedImageView`. 45 | 46 | Sample Code: 47 | 48 | // animation@3x.webp 49 | YYImage *image = [YYImage imageNamed:@"animation.webp"]; 50 | YYAnimatedImageView *imageView = [YYAnimatedImageView alloc] initWithImage:image]; 51 | [view addSubView:imageView]; 52 | 53 | */ 54 | @interface YYImage : UIImage 55 | 56 | + (nullable YYImage *)imageNamed:(NSString *)name; // no cache! 57 | + (nullable YYImage *)imageWithContentsOfFile:(NSString *)path; 58 | + (nullable YYImage *)imageWithData:(NSData *)data; 59 | + (nullable YYImage *)imageWithData:(NSData *)data scale:(CGFloat)scale; 60 | 61 | /** 62 | If the image is created from data or file, then the value indicates the data type. 63 | */ 64 | @property (nonatomic, readonly) YYImageType animatedImageType; 65 | 66 | /** 67 | If the image is created from animated image data (multi-frame GIF/APNG/WebP), 68 | this property stores the original image data. 69 | */ 70 | @property (nullable, nonatomic, readonly) NSData *animatedImageData; 71 | 72 | /** 73 | The total memory usage (in bytes) if all frame images was loaded into memory. 74 | The value is 0 if the image is not created from a multi-frame image data. 75 | */ 76 | @property (nonatomic, readonly) NSUInteger animatedImageMemorySize; 77 | 78 | /** 79 | Preload all frame image to memory. 80 | 81 | @discussion Set this property to `YES` will block the calling thread to decode 82 | all animation frame image to memory, set to `NO` will release the preloaded frames. 83 | If the image is shared by lots of image views (such as emoticon), preload all 84 | frames will reduce the CPU cost. 85 | 86 | See `animatedImageMemorySize` for memory cost. 87 | */ 88 | @property (nonatomic) BOOL preloadAllAnimatedImageFrames; 89 | 90 | @end 91 | 92 | NS_ASSUME_NONNULL_END 93 | -------------------------------------------------------------------------------- /Components/CKTextField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CKTextField.swift 3 | // Browser 4 | // 5 | // Created by Blake Tsuzaki on 5/11/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @IBDesignable class CKTextField: UIView { 12 | @IBInspectable var textSize: CGFloat = 30 { 13 | didSet { textfield.font = UIFont(name: CKDefaults.fontName, size: textSize) } 14 | } 15 | 16 | @IBInspectable var placeholderText: String = "" { 17 | didSet { textfield.placeholder = placeholderText } 18 | } 19 | 20 | @IBInspectable var text: String? { 21 | get { return textfield.text } 22 | set { textfield.text = newValue } 23 | } 24 | 25 | var returnKeyType: UIReturnKeyType { 26 | get { return textfield.returnKeyType } 27 | set { textfield.returnKeyType = newValue } 28 | } 29 | 30 | var keyboardType: UIKeyboardType { 31 | get { return textfield.keyboardType } 32 | set { textfield.keyboardType = newValue } 33 | } 34 | 35 | var delegate: UITextFieldDelegate? { 36 | didSet { 37 | textfield.delegate = delegate 38 | } 39 | } 40 | 41 | private class _TextField: UITextField { 42 | override func editingRect(forBounds bounds: CGRect) -> CGRect { 43 | var frame = bounds 44 | frame.origin.x = 5 45 | frame.size.width -= 5 46 | return frame 47 | } 48 | 49 | override func textRect(forBounds bounds: CGRect) -> CGRect { 50 | var frame = bounds 51 | frame.origin.x = 5 52 | frame.size.width -= 5 53 | return frame 54 | } 55 | } 56 | 57 | private let textfield = _TextField() 58 | 59 | override init(frame: CGRect) { 60 | super.init(frame: frame) 61 | 62 | configure() 63 | } 64 | 65 | required init?(coder aDecoder: NSCoder) { 66 | super.init(coder: aDecoder) 67 | 68 | configure() 69 | } 70 | 71 | private func configure() { 72 | isOpaque = false 73 | textfield.autocapitalizationType = .none 74 | textfield.autocorrectionType = .no 75 | textfield.font = UIFont(name: CKDefaults.fontName, size: 30) 76 | 77 | textfield.backgroundColor = .white 78 | addSubview(textfield) 79 | } 80 | 81 | func addTarget(_ target: Any?, action: Selector, for controlEvents: UIControl.Event) { 82 | textfield.addTarget(target, action: action, for: controlEvents) 83 | } 84 | 85 | func hideKeyboard() { 86 | textfield.resignFirstResponder() 87 | } 88 | 89 | override func layoutSubviews() { 90 | super.layoutSubviews() 91 | setNeedsDisplay() 92 | } 93 | 94 | override func draw(_ rect: CGRect) { 95 | super.draw(rect) 96 | 97 | CKDefaults.drawInsetBevel(with: rect) 98 | 99 | let lineWidth: CGFloat = CKDefaults.bevelWidth 100 | 101 | var contentFrame = CGRect() 102 | contentFrame.origin.x = lineWidth * 2 103 | contentFrame.origin.y = lineWidth * 2 104 | contentFrame.size.height = rect.size.height - lineWidth * 4 105 | contentFrame.size.width = rect.size.width - lineWidth * 4 106 | 107 | textfield.frame = contentFrame 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Browser/Browser.xcodeproj/xcuserdata/blaketsuzaki.xcuserdatad/xcschemes/Browser.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Browser/Pods/YYImage/YYImage/YYSpriteSheetImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYSpriteImage.h 3 | // YYImage 4 | // 5 | // Created by ibireme on 15/4/21. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | #if __has_include() 15 | #import 16 | #elif __has_include() 17 | #import 18 | #else 19 | #import "YYAnimatedImageView.h" 20 | #endif 21 | 22 | NS_ASSUME_NONNULL_BEGIN 23 | 24 | /** 25 | An image to display sprite sheet animation. 26 | 27 | @discussion It is a fully compatible `UIImage` subclass. 28 | The animation can be played by YYAnimatedImageView. 29 | 30 | Sample Code: 31 | 32 | // 8 * 12 sprites in a single sheet image 33 | UIImage *spriteSheet = [UIImage imageNamed:@"sprite-sheet"]; 34 | NSMutableArray *contentRects = [NSMutableArray new]; 35 | NSMutableArray *durations = [NSMutableArray new]; 36 | for (int j = 0; j < 12; j++) { 37 | for (int i = 0; i < 8; i++) { 38 | CGRect rect; 39 | rect.size = CGSizeMake(img.size.width / 8, img.size.height / 12); 40 | rect.origin.x = img.size.width / 8 * i; 41 | rect.origin.y = img.size.height / 12 * j; 42 | [contentRects addObject:[NSValue valueWithCGRect:rect]]; 43 | [durations addObject:@(1 / 60.0)]; 44 | } 45 | } 46 | YYSpriteSheetImage *sprite; 47 | sprite = [[YYSpriteSheetImage alloc] initWithSpriteSheetImage:img 48 | contentRects:contentRects 49 | frameDurations:durations 50 | loopCount:0]; 51 | YYAnimatedImageView *imgView = [YYAnimatedImageView new]; 52 | imgView.size = CGSizeMake(img.size.width / 8, img.size.height / 12); 53 | imgView.image = sprite; 54 | 55 | 56 | 57 | @discussion It can also be used to display single frame in sprite sheet image. 58 | Sample Code: 59 | 60 | YYSpriteSheetImage *sheet = ...; 61 | UIImageView *imageView = ...; 62 | imageView.image = sheet; 63 | imageView.layer.contentsRect = [sheet contentsRectForCALayerAtIndex:6]; 64 | 65 | */ 66 | @interface YYSpriteSheetImage : UIImage 67 | 68 | /** 69 | Creates and returns an image object. 70 | 71 | @param image The sprite sheet image (contains all frames). 72 | 73 | @param contentRects The sprite sheet image frame rects in the image coordinates. 74 | The rectangle should not outside the image's bounds. The objects in this array 75 | should be created with [NSValue valueWithCGRect:]. 76 | 77 | @param frameDurations The sprite sheet image frame's durations in seconds. 78 | The objects in this array should be NSNumber. 79 | 80 | @param loopCount Animation loop count, 0 means infinite looping. 81 | 82 | @return An image object, or nil if an error occurs. 83 | */ 84 | - (nullable instancetype)initWithSpriteSheetImage:(UIImage *)image 85 | contentRects:(NSArray *)contentRects 86 | frameDurations:(NSArray *)frameDurations 87 | loopCount:(NSUInteger)loopCount; 88 | 89 | @property (nonatomic, readonly) NSArray *contentRects; 90 | @property (nonatomic, readonly) NSArray *frameDurations; 91 | @property (nonatomic, readonly) NSUInteger loopCount; 92 | 93 | /** 94 | Get the contents rect for CALayer. 95 | See "contentsRect" property in CALayer for more information. 96 | 97 | @param index Index of frame. 98 | @return Contents Rect. 99 | */ 100 | - (CGRect)contentsRectForCALayerAtIndex:(NSUInteger)index; 101 | 102 | @end 103 | 104 | NS_ASSUME_NONNULL_END 105 | -------------------------------------------------------------------------------- /Browser/Pods/Target Support Files/Pods-Browser/Pods-Browser-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 63 | 64 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 65 | code_sign_cmd="$code_sign_cmd &" 66 | fi 67 | echo "$code_sign_cmd" 68 | eval "$code_sign_cmd" 69 | fi 70 | } 71 | 72 | # Strip invalid architectures 73 | strip_invalid_archs() { 74 | binary="$1" 75 | # Get architectures for current file 76 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 77 | stripped="" 78 | for arch in $archs; do 79 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 80 | # Strip non-valid architectures in-place 81 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 82 | stripped="$stripped $arch" 83 | fi 84 | done 85 | if [[ "$stripped" ]]; then 86 | echo "Stripped $binary of architectures:$stripped" 87 | fi 88 | } 89 | 90 | 91 | if [[ "$CONFIGURATION" == "Debug" ]]; then 92 | install_framework "$BUILT_PRODUCTS_DIR/YYImage/YYImage.framework" 93 | fi 94 | if [[ "$CONFIGURATION" == "Release" ]]; then 95 | install_framework "$BUILT_PRODUCTS_DIR/YYImage/YYImage.framework" 96 | fi 97 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 98 | wait 99 | fi 100 | -------------------------------------------------------------------------------- /Components/CKBlueScreenViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CKBlueScreenViewController.swift 3 | // Browser 4 | // 5 | // Created by Blake Tsuzaki on 5/17/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class CKBlueScreenViewController: UIViewController { 12 | let productLabelHorizontalMargin: CGFloat = 10 13 | let textDelay: TimeInterval = 0.5 14 | let textIncrement: TimeInterval = 0.05 15 | 16 | var textSize: CGFloat = 12 17 | var errorReason: String = "" 18 | 19 | override var prefersStatusBarHidden: Bool { return true } 20 | 21 | private var gestureRecognizer: UITapGestureRecognizer? 22 | private var productLabel = CKLabel() 23 | private var endLabel = CKLabel() 24 | private var textView = UITextView() 25 | 26 | override func viewDidLoad() { 27 | super.viewDidLoad() 28 | 29 | let gestureRecognzier = UITapGestureRecognizer(target: self, action: #selector(CKBlueScreenViewController.viewWasTapped)) 30 | 31 | productLabel.text = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String ?? "iOS" 32 | productLabel.textColor = .blue 33 | productLabel.textAlignment = .center 34 | productLabel.font = UIFont(name: CKDefaults.blueScreenFontName, size: textSize) 35 | productLabel.backgroundColor = CKDefaults.textureLightColor 36 | 37 | endLabel.text = "Tap anywhere to continue _" 38 | endLabel.textColor = .white 39 | endLabel.textAlignment = .center 40 | endLabel.font = UIFont(name: CKDefaults.blueScreenFontName, size: textSize) 41 | 42 | textView.font = UIFont(name: CKDefaults.blueScreenFontName, size: textSize) 43 | textView.textColor = .white 44 | textView.backgroundColor = .clear 45 | 46 | view.addGestureRecognizer(gestureRecognzier) 47 | view.backgroundColor = .blue 48 | view.addSubview(productLabel) 49 | view.addSubview(textView) 50 | view.addSubview(endLabel) 51 | 52 | self.gestureRecognizer = gestureRecognzier 53 | } 54 | 55 | override func viewWillAppear(_ animated: Bool) { 56 | super.viewWillAppear(animated) 57 | 58 | textView.text = "A fatal exception has occurred: \(errorReason). The current application will be terminated.\n\n* Tap anywhere to terminate the current application.\n* You will lose any unsaved information in this application." 59 | 60 | setNeedsStatusBarAppearanceUpdate() 61 | } 62 | 63 | override func viewDidAppear(_ animated: Bool) { 64 | super.viewDidAppear(animated) 65 | 66 | productLabel.isHidden = true 67 | textView.isHidden = true 68 | endLabel.isHidden = true 69 | 70 | DispatchQueue.main.asyncAfter(deadline: .now() + textDelay) { 71 | self.productLabel.isHidden = false 72 | } 73 | DispatchQueue.main.asyncAfter(deadline: .now() + textDelay + textIncrement) { 74 | self.textView.isHidden = false 75 | } 76 | DispatchQueue.main.asyncAfter(deadline: .now() + textDelay + 2 * textIncrement) { 77 | self.endLabel.isHidden = false 78 | } 79 | } 80 | 81 | override func viewDidLayoutSubviews() { 82 | super.viewDidLayoutSubviews() 83 | 84 | textView.frame.size = CGSize(width: 280, height: 120) 85 | textView.center = view.center 86 | 87 | if let productText = productLabel.text { 88 | productLabel.frame.size = productText.size(withAttributes: [NSAttributedString.Key.font: UIFont(name: CKDefaults.blueScreenFontName, size: textSize) as Any]) 89 | productLabel.frame.size.width += productLabelHorizontalMargin 90 | productLabel.center = CGPoint(x: view.center.x, y: textView.frame.origin.y - productLabel.frame.size.height) 91 | } 92 | 93 | endLabel.frame.size = CGSize(width: textView.frame.size.width, height: 20) 94 | endLabel.center = CGPoint(x: view.center.x, y: textView.frame.origin.y + textView.frame.size.height + endLabel.frame.size.height) 95 | } 96 | 97 | @objc func viewWasTapped() { 98 | fatalError() 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Browser/Pods/YYImage/YYImage/YYFrameImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYFrameImage.h 3 | // YYImage 4 | // 5 | // Created by ibireme on 14/12/9. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | #if __has_include() 15 | #import 16 | #elif __has_include() 17 | #import 18 | #else 19 | #import "YYAnimatedImageView.h" 20 | #endif 21 | 22 | NS_ASSUME_NONNULL_BEGIN 23 | 24 | /** 25 | An image to display frame-based animation. 26 | 27 | @discussion It is a fully compatible `UIImage` subclass. 28 | It only support system image format such as png and jpeg. 29 | The animation can be played by YYAnimatedImageView. 30 | 31 | Sample Code: 32 | 33 | NSArray *paths = @[@"/ani/frame1.png", @"/ani/frame2.png", @"/ani/frame3.png"]; 34 | NSArray *times = @[@0.1, @0.2, @0.1]; 35 | YYFrameImage *image = [YYFrameImage alloc] initWithImagePaths:paths frameDurations:times repeats:YES]; 36 | YYAnimatedImageView *imageView = [YYAnimatedImageView alloc] initWithImage:image]; 37 | [view addSubView:imageView]; 38 | */ 39 | @interface YYFrameImage : UIImage 40 | 41 | /** 42 | Create a frame animated image from files. 43 | 44 | @param paths An array of NSString objects, contains the full or 45 | partial path to each image file. 46 | e.g. @[@"/ani/1.png",@"/ani/2.png",@"/ani/3.png"] 47 | 48 | @param oneFrameDuration The duration (in seconds) per frame. 49 | 50 | @param loopCount The animation loop count, 0 means infinite. 51 | 52 | @return An initialized YYFrameImage object, or nil when an error occurs. 53 | */ 54 | - (nullable instancetype)initWithImagePaths:(NSArray *)paths 55 | oneFrameDuration:(NSTimeInterval)oneFrameDuration 56 | loopCount:(NSUInteger)loopCount; 57 | 58 | /** 59 | Create a frame animated image from files. 60 | 61 | @param paths An array of NSString objects, contains the full or 62 | partial path to each image file. 63 | e.g. @[@"/ani/frame1.png",@"/ani/frame2.png",@"/ani/frame3.png"] 64 | 65 | @param frameDurations An array of NSNumber objects, contains the duration (in seconds) per frame. 66 | e.g. @[@0.1, @0.2, @0.3]; 67 | 68 | @param loopCount The animation loop count, 0 means infinite. 69 | 70 | @return An initialized YYFrameImage object, or nil when an error occurs. 71 | */ 72 | - (nullable instancetype)initWithImagePaths:(NSArray *)paths 73 | frameDurations:(NSArray *)frameDurations 74 | loopCount:(NSUInteger)loopCount; 75 | 76 | /** 77 | Create a frame animated image from an array of data. 78 | 79 | @param dataArray An array of NSData objects. 80 | 81 | @param oneFrameDuration The duration (in seconds) per frame. 82 | 83 | @param loopCount The animation loop count, 0 means infinite. 84 | 85 | @return An initialized YYFrameImage object, or nil when an error occurs. 86 | */ 87 | - (nullable instancetype)initWithImageDataArray:(NSArray *)dataArray 88 | oneFrameDuration:(NSTimeInterval)oneFrameDuration 89 | loopCount:(NSUInteger)loopCount; 90 | 91 | /** 92 | Create a frame animated image from an array of data. 93 | 94 | @param dataArray An array of NSData objects. 95 | 96 | @param frameDurations An array of NSNumber objects, contains the duration (in seconds) per frame. 97 | e.g. @[@0.1, @0.2, @0.3]; 98 | 99 | @param loopCount The animation loop count, 0 means infinite. 100 | 101 | @return An initialized YYFrameImage object, or nil when an error occurs. 102 | */ 103 | - (nullable instancetype)initWithImageDataArray:(NSArray *)dataArray 104 | frameDurations:(NSArray *)frameDurations 105 | loopCount:(NSUInteger)loopCount; 106 | 107 | @end 108 | 109 | NS_ASSUME_NONNULL_END 110 | -------------------------------------------------------------------------------- /Browser/Pods/YYImage/YYImage/YYAnimatedImageView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYAnimatedImageView.h 3 | // YYImage 4 | // 5 | // Created by ibireme on 14/10/19. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | An image view for displaying animated image. 18 | 19 | @discussion It is a fully compatible `UIImageView` subclass. 20 | If the `image` or `highlightedImage` property adopt to the `YYAnimatedImage` protocol, 21 | then it can be used to play the multi-frame animation. The animation can also be 22 | controlled with the UIImageView methods `-startAnimating`, `-stopAnimating` and `-isAnimating`. 23 | 24 | This view request the frame data just in time. When the device has enough free memory, 25 | this view may cache some or all future frames in an inner buffer for lower CPU cost. 26 | Buffer size is dynamically adjusted based on the current state of the device memory. 27 | 28 | Sample Code: 29 | 30 | // ani@3x.gif 31 | YYImage *image = [YYImage imageNamed:@"ani"]; 32 | YYAnimatedImageView *imageView = [YYAnimatedImageView alloc] initWithImage:image]; 33 | [view addSubView:imageView]; 34 | */ 35 | @interface YYAnimatedImageView : UIImageView 36 | 37 | /** 38 | If the image has more than one frame, set this value to `YES` will automatically 39 | play/stop the animation when the view become visible/invisible. 40 | 41 | The default value is `YES`. 42 | */ 43 | @property (nonatomic) BOOL autoPlayAnimatedImage; 44 | 45 | /** 46 | Index of the currently displayed frame (index from 0). 47 | 48 | Set a new value to this property will cause to display the new frame immediately. 49 | If the new value is invalid, this method has no effect. 50 | 51 | You can add an observer to this property to observe the playing status. 52 | */ 53 | @property (nonatomic) NSUInteger currentAnimatedImageIndex; 54 | 55 | /** 56 | Whether the image view is playing animation currently. 57 | 58 | You can add an observer to this property to observe the playing status. 59 | */ 60 | @property (nonatomic, readonly) BOOL currentIsPlayingAnimation; 61 | 62 | /** 63 | The animation timer's runloop mode, default is `NSRunLoopCommonModes`. 64 | 65 | Set this property to `NSDefaultRunLoopMode` will make the animation pause during 66 | UIScrollView scrolling. 67 | */ 68 | @property (nonatomic, copy) NSString *runloopMode; 69 | 70 | /** 71 | The max size (in bytes) for inner frame buffer size, default is 0 (dynamically). 72 | 73 | When the device has enough free memory, this view will request and decode some or 74 | all future frame image into an inner buffer. If this property's value is 0, then 75 | the max buffer size will be dynamically adjusted based on the current state of 76 | the device free memory. Otherwise, the buffer size will be limited by this value. 77 | 78 | When receive memory warning or app enter background, the buffer will be released 79 | immediately, and may grow back at the right time. 80 | */ 81 | @property (nonatomic) NSUInteger maxBufferSize; 82 | 83 | @end 84 | 85 | 86 | 87 | /** 88 | The YYAnimatedImage protocol declares the required methods for animated image 89 | display with YYAnimatedImageView. 90 | 91 | Subclass a UIImage and implement this protocol, so that instances of that class 92 | can be set to YYAnimatedImageView.image or YYAnimatedImageView.highlightedImage 93 | to display animation. 94 | 95 | See `YYImage` and `YYFrameImage` for example. 96 | */ 97 | @protocol YYAnimatedImage 98 | @required 99 | /// Total animated frame count. 100 | /// It the frame count is less than 1, then the methods below will be ignored. 101 | - (NSUInteger)animatedImageFrameCount; 102 | 103 | /// Animation loop count, 0 means infinite looping. 104 | - (NSUInteger)animatedImageLoopCount; 105 | 106 | /// Bytes per frame (in memory). It may used to optimize memory buffer size. 107 | - (NSUInteger)animatedImageBytesPerFrame; 108 | 109 | /// Returns the frame image from a specified index. 110 | /// This method may be called on background thread. 111 | /// @param index Frame index (zero based). 112 | - (nullable UIImage *)animatedImageFrameAtIndex:(NSUInteger)index; 113 | 114 | /// Returns the frames's duration from a specified index. 115 | /// @param index Frame index (zero based). 116 | - (NSTimeInterval)animatedImageDurationAtIndex:(NSUInteger)index; 117 | 118 | @optional 119 | /// A rectangle in image coordinates defining the subrectangle of the image that 120 | /// will be displayed. The rectangle should not outside the image's bounds. 121 | /// It may used to display sprite animation with a single image (sprite sheet). 122 | - (CGRect)animatedImageContentsRectAtIndex:(NSUInteger)index; 123 | @end 124 | 125 | NS_ASSUME_NONNULL_END 126 | -------------------------------------------------------------------------------- /Components/CKVerticalScrollBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CKVerticalScrollBar.swift 3 | // Browser 4 | // 5 | // Created by Blake Tsuzaki on 5/11/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @IBDesignable class CKVerticalScrollBar: UIControl { 12 | @IBInspectable var buttonColor: UIColor? { 13 | didSet { 14 | upButton.buttonColor = buttonColor 15 | downButton.buttonColor = buttonColor 16 | slider.backgroundColor = buttonColor 17 | } 18 | } 19 | 20 | @IBInspectable var value: CGFloat = 0 { 21 | didSet { 22 | if value > range { value = range } 23 | else if value < 0 { value = 0 } 24 | if value != oldValue { 25 | setNeedsLayout() 26 | if isScrolling { 27 | sendActions(for: .valueChanged) 28 | } 29 | } 30 | } 31 | } 32 | 33 | @IBInspectable var range: CGFloat = 100 { 34 | didSet { 35 | if range <= 0 { range = 1 } 36 | else if range != oldValue { setNeedsLayout() } 37 | } 38 | } 39 | 40 | @IBInspectable var thumbSize: CGFloat = 100 { 41 | didSet { 42 | if thumbSize < frame.size.width { thumbSize = frame.size.width } 43 | else if thumbSize != oldValue { setNeedsLayout() } 44 | } 45 | } 46 | 47 | var isScrolling = false 48 | var scrollSize: CGFloat { return frame.size.height - 2 * frame.size.width } 49 | var isGrayedOut: Bool { return state == .disabled || thumbSize >= scrollSize } 50 | 51 | private let upButton = CKImageButton() 52 | private let downButton = CKImageButton() 53 | private let slider = CKView() 54 | 55 | private var basePoint = CGPoint() 56 | private var baseValue: CGFloat = 0 57 | 58 | override init(frame: CGRect) { 59 | super.init(frame: frame) 60 | 61 | configure() 62 | } 63 | 64 | required init?(coder aDecoder: NSCoder) { 65 | super.init(coder: aDecoder) 66 | 67 | configure() 68 | } 69 | 70 | private func configure() { 71 | upButton.image = UIImage(named: "scrollbar-up") 72 | upButton.addTarget(self, action: #selector(CKVerticalScrollBar.upButtonPressed), for: .touchUpInside) 73 | downButton.image = UIImage(named: "scrollbar-down") 74 | downButton.addTarget(self, action: #selector(CKVerticalScrollBar.downButtonPressed), for: .touchUpInside) 75 | slider.isUserInteractionEnabled = false 76 | addSubview(upButton) 77 | addSubview(downButton) 78 | addSubview(slider) 79 | } 80 | 81 | @objc func upButtonPressed() { 82 | isScrolling = true 83 | value -= range/10 84 | isScrolling = false 85 | } 86 | 87 | @objc func downButtonPressed() { 88 | isScrolling = true 89 | value += range/10 90 | isScrolling = false 91 | } 92 | 93 | override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { 94 | let touchPoint = touch.location(in: self) 95 | 96 | isScrolling = true 97 | 98 | if slider.frame.contains(touchPoint) { 99 | basePoint = touchPoint 100 | baseValue = value 101 | return true 102 | } 103 | return false 104 | } 105 | 106 | override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { 107 | let touchPoint = touch.location(in: self) 108 | value = baseValue + range * (touchPoint.y - basePoint.y)/(frame.size.height - 2 * frame.size.width - slider.frame.size.height) 109 | 110 | return true 111 | } 112 | 113 | override func endTracking(_ touch: UITouch?, with event: UIEvent?) { 114 | isScrolling = false 115 | } 116 | 117 | override func layoutSubviews() { 118 | super.layoutSubviews() 119 | 120 | var upFrame = CGRect() 121 | upFrame.size.height = frame.size.width 122 | upFrame.size.width = frame.size.width 123 | upFrame.origin = CGPoint() 124 | upButton.frame = upFrame 125 | 126 | var downFrame = CGRect() 127 | downFrame.size.height = frame.size.width 128 | downFrame.size.width = frame.size.width 129 | downFrame.origin.y = frame.size.height - frame.size.width 130 | downFrame.origin.x = 0 131 | downButton.frame = downFrame 132 | 133 | var thumbFrame = CGRect() 134 | thumbFrame.size.height = min(thumbSize, frame.size.height - 2 * frame.size.width) 135 | thumbFrame.size.width = frame.size.width 136 | thumbFrame.origin.y = (value / range) * (frame.size.height - 2 * frame.size.width - thumbFrame.size.height) + frame.size.width 137 | thumbFrame.origin.x = 0 138 | slider.frame = thumbFrame 139 | 140 | if isGrayedOut { 141 | isUserInteractionEnabled = false 142 | slider.isHidden = true 143 | upButton.isEnabled = false 144 | downButton.isEnabled = false 145 | } else { 146 | isUserInteractionEnabled = true 147 | slider.isHidden = false 148 | upButton.isEnabled = true 149 | downButton.isEnabled = true 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /Components/CKHorizontalScrollBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CKHorizontalScrollBar.swift 3 | // Browser 4 | // 5 | // Created by Blake Tsuzaki on 5/11/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @IBDesignable class CKHorizontalScrollBar: UIControl { 12 | @IBInspectable var buttonColor: UIColor? { 13 | didSet { 14 | leftButton.buttonColor = buttonColor 15 | rightButton.buttonColor = buttonColor 16 | slider.backgroundColor = buttonColor 17 | } 18 | } 19 | 20 | @IBInspectable var value: CGFloat = 0 { 21 | didSet { 22 | if value > range { value = range } 23 | else if value < 0 { value = 0 } 24 | if value != oldValue { 25 | setNeedsLayout() 26 | if isScrolling { 27 | sendActions(for: .valueChanged) 28 | } 29 | } 30 | } 31 | } 32 | 33 | @IBInspectable var range: CGFloat = 100 { 34 | didSet { 35 | if range <= 0 { range = 1 } 36 | else if range != oldValue { setNeedsLayout() } 37 | } 38 | } 39 | 40 | @IBInspectable var thumbSize: CGFloat = 100 { 41 | didSet { 42 | if thumbSize < frame.size.height { thumbSize = frame.size.height } 43 | else if thumbSize != oldValue { setNeedsLayout() } 44 | } 45 | } 46 | 47 | var isScrolling = false 48 | var scrollSize: CGFloat { return frame.size.width - 2 * frame.size.height } 49 | var isGrayedOut: Bool { return state == .disabled || thumbSize >= scrollSize } 50 | 51 | let leftButton = CKImageButton() 52 | let rightButton = CKImageButton() 53 | let slider = CKView() 54 | 55 | private var basePoint = CGPoint() 56 | private var baseValue: CGFloat = 0 57 | 58 | override init(frame: CGRect) { 59 | super.init(frame: frame) 60 | 61 | configure() 62 | } 63 | 64 | required init?(coder aDecoder: NSCoder) { 65 | super.init(coder: aDecoder) 66 | 67 | configure() 68 | } 69 | 70 | private func configure() { 71 | leftButton.image = UIImage(named: "scrollbar-left") 72 | leftButton.addTarget(self, action: #selector(CKHorizontalScrollBar.leftButtonPressed), for: .touchUpInside) 73 | rightButton.image = UIImage(named: "scrollbar-right") 74 | rightButton.addTarget(self, action: #selector(CKHorizontalScrollBar.rightButtonPressed), for: .touchUpInside) 75 | slider.isUserInteractionEnabled = false 76 | addSubview(leftButton) 77 | addSubview(rightButton) 78 | addSubview(slider) 79 | } 80 | 81 | @objc func leftButtonPressed() { 82 | isScrolling = true 83 | value -= range/10 84 | isScrolling = false 85 | } 86 | 87 | @objc func rightButtonPressed() { 88 | isScrolling = true 89 | value += range/10 90 | isScrolling = false 91 | } 92 | 93 | override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { 94 | let touchPoint = touch.location(in: self) 95 | 96 | isScrolling = true 97 | 98 | if slider.frame.contains(touchPoint) { 99 | basePoint = touchPoint 100 | baseValue = value 101 | return true 102 | } 103 | return false 104 | } 105 | 106 | override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { 107 | let touchPoint = touch.location(in: self) 108 | value = baseValue + range * (touchPoint.x - basePoint.x)/(frame.size.width - 2 * frame.size.height - slider.frame.size.width) 109 | 110 | return true 111 | } 112 | 113 | override func endTracking(_ touch: UITouch?, with event: UIEvent?) { 114 | isScrolling = false 115 | } 116 | 117 | override func layoutSubviews() { 118 | super.layoutSubviews() 119 | 120 | var leftFrame = CGRect() 121 | leftFrame.size.height = frame.size.height 122 | leftFrame.size.width = frame.size.height 123 | leftFrame.origin = CGPoint() 124 | leftButton.frame = leftFrame 125 | 126 | var rightFrame = CGRect() 127 | rightFrame.size.height = frame.size.height 128 | rightFrame.size.width = frame.size.height 129 | rightFrame.origin.y = 0 130 | rightFrame.origin.x = frame.size.width - frame.size.height 131 | rightButton.frame = rightFrame 132 | 133 | var thumbFrame = CGRect() 134 | thumbFrame.size.height = frame.size.height 135 | thumbFrame.size.width = min(thumbSize, frame.size.width - 2 * frame.size.height) 136 | thumbFrame.origin.y = 0 137 | thumbFrame.origin.x = (value / range) * (frame.size.width - 2 * frame.size.height - thumbFrame.size.width) + frame.size.height 138 | slider.frame = thumbFrame 139 | 140 | if isGrayedOut { 141 | isUserInteractionEnabled = false 142 | slider.isHidden = true 143 | leftButton.isEnabled = false 144 | rightButton.isEnabled = false 145 | } else { 146 | isUserInteractionEnabled = true 147 | slider.isHidden = false 148 | leftButton.isEnabled = true 149 | rightButton.isEnabled = true 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /Browser/Pods/Target Support Files/Pods-Browser/Pods-Browser-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | 3) 22 | TARGET_DEVICE_ARGS="--target-device tv" 23 | ;; 24 | 4) 25 | TARGET_DEVICE_ARGS="--target-device watch" 26 | ;; 27 | *) 28 | TARGET_DEVICE_ARGS="--target-device mac" 29 | ;; 30 | esac 31 | 32 | install_resource() 33 | { 34 | if [[ "$1" = /* ]] ; then 35 | RESOURCE_PATH="$1" 36 | else 37 | RESOURCE_PATH="${PODS_ROOT}/$1" 38 | fi 39 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 40 | cat << EOM 41 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 42 | EOM 43 | exit 1 44 | fi 45 | case $RESOURCE_PATH in 46 | *.storyboard) 47 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 48 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 49 | ;; 50 | *.xib) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.framework) 55 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 56 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 57 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 58 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 59 | ;; 60 | *.xcdatamodel) 61 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 62 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 63 | ;; 64 | *.xcdatamodeld) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 67 | ;; 68 | *.xcmappingmodel) 69 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 70 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 71 | ;; 72 | *.xcassets) 73 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 74 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 75 | ;; 76 | *) 77 | echo "$RESOURCE_PATH" 78 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 79 | ;; 80 | esac 81 | } 82 | 83 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 85 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 86 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 87 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | fi 89 | rm -f "$RESOURCES_TO_COPY" 90 | 91 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 92 | then 93 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 94 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 95 | while read line; do 96 | if [[ $line != "${PODS_ROOT}*" ]]; then 97 | XCASSET_FILES+=("$line") 98 | fi 99 | done <<<"$OTHER_XCASSETS" 100 | 101 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 102 | fi 103 | -------------------------------------------------------------------------------- /Browser/Pods/YYImage/YYImage/YYFrameImage.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYFrameImage.m 3 | // YYImage 4 | // 5 | // Created by ibireme on 14/12/9. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYFrameImage.h" 13 | #import "YYImageCoder.h" 14 | 15 | 16 | /** 17 | Return the path scale. 18 | 19 | e.g. 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
Path Scale
"icon.png" 1
"icon@2x.png" 2
"icon@2.5x.png" 2.5
"icon@2x" 1
"icon@2x..png" 1
"icon@2x.png/" 1
29 | */ 30 | static CGFloat _NSStringPathScale(NSString *string) { 31 | if (string.length == 0 || [string hasSuffix:@"/"]) return 1; 32 | NSString *name = string.stringByDeletingPathExtension; 33 | __block CGFloat scale = 1; 34 | 35 | NSRegularExpression *pattern = [NSRegularExpression regularExpressionWithPattern:@"@[0-9]+\\.?[0-9]*x$" options:NSRegularExpressionAnchorsMatchLines error:nil]; 36 | [pattern enumerateMatchesInString:name options:kNilOptions range:NSMakeRange(0, name.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { 37 | if (result.range.location >= 3) { 38 | scale = [string substringWithRange:NSMakeRange(result.range.location + 1, result.range.length - 2)].doubleValue; 39 | } 40 | }]; 41 | 42 | return scale; 43 | } 44 | 45 | 46 | 47 | @implementation YYFrameImage { 48 | NSUInteger _loopCount; 49 | NSUInteger _oneFrameBytes; 50 | NSArray *_imagePaths; 51 | NSArray *_imageDatas; 52 | NSArray *_frameDurations; 53 | } 54 | 55 | - (instancetype)initWithImagePaths:(NSArray *)paths oneFrameDuration:(NSTimeInterval)oneFrameDuration loopCount:(NSUInteger)loopCount { 56 | NSMutableArray *durations = [NSMutableArray new]; 57 | for (int i = 0, max = (int)paths.count; i < max; i++) { 58 | [durations addObject:@(oneFrameDuration)]; 59 | } 60 | return [self initWithImagePaths:paths frameDurations:durations loopCount:loopCount]; 61 | } 62 | 63 | - (instancetype)initWithImagePaths:(NSArray *)paths frameDurations:(NSArray *)frameDurations loopCount:(NSUInteger)loopCount { 64 | if (paths.count == 0) return nil; 65 | if (paths.count != frameDurations.count) return nil; 66 | 67 | NSString *firstPath = paths[0]; 68 | NSData *firstData = [NSData dataWithContentsOfFile:firstPath]; 69 | CGFloat scale = _NSStringPathScale(firstPath); 70 | UIImage *firstCG = [[[UIImage alloc] initWithData:firstData] yy_imageByDecoded]; 71 | self = [self initWithCGImage:firstCG.CGImage scale:scale orientation:UIImageOrientationUp]; 72 | if (!self) return nil; 73 | long frameByte = CGImageGetBytesPerRow(firstCG.CGImage) * CGImageGetHeight(firstCG.CGImage); 74 | _oneFrameBytes = (NSUInteger)frameByte; 75 | _imagePaths = paths.copy; 76 | _frameDurations = frameDurations.copy; 77 | _loopCount = loopCount; 78 | 79 | return self; 80 | } 81 | 82 | - (instancetype)initWithImageDataArray:(NSArray *)dataArray oneFrameDuration:(NSTimeInterval)oneFrameDuration loopCount:(NSUInteger)loopCount { 83 | NSMutableArray *durations = [NSMutableArray new]; 84 | for (int i = 0, max = (int)dataArray.count; i < max; i++) { 85 | [durations addObject:@(oneFrameDuration)]; 86 | } 87 | return [self initWithImageDataArray:dataArray frameDurations:durations loopCount:loopCount]; 88 | } 89 | 90 | - (instancetype)initWithImageDataArray:(NSArray *)dataArray frameDurations:(NSArray *)frameDurations loopCount:(NSUInteger)loopCount { 91 | if (dataArray.count == 0) return nil; 92 | if (dataArray.count != frameDurations.count) return nil; 93 | 94 | NSData *firstData = dataArray[0]; 95 | CGFloat scale = [UIScreen mainScreen].scale; 96 | UIImage *firstCG = [[[UIImage alloc] initWithData:firstData] yy_imageByDecoded]; 97 | self = [self initWithCGImage:firstCG.CGImage scale:scale orientation:UIImageOrientationUp]; 98 | if (!self) return nil; 99 | long frameByte = CGImageGetBytesPerRow(firstCG.CGImage) * CGImageGetHeight(firstCG.CGImage); 100 | _oneFrameBytes = (NSUInteger)frameByte; 101 | _imageDatas = dataArray.copy; 102 | _frameDurations = frameDurations.copy; 103 | _loopCount = loopCount; 104 | 105 | return self; 106 | } 107 | 108 | #pragma mark - YYAnimtedImage 109 | 110 | - (NSUInteger)animatedImageFrameCount { 111 | if (_imagePaths) { 112 | return _imagePaths.count; 113 | } else if (_imageDatas) { 114 | return _imageDatas.count; 115 | } else { 116 | return 1; 117 | } 118 | } 119 | 120 | - (NSUInteger)animatedImageLoopCount { 121 | return _loopCount; 122 | } 123 | 124 | - (NSUInteger)animatedImageBytesPerFrame { 125 | return _oneFrameBytes; 126 | } 127 | 128 | - (UIImage *)animatedImageFrameAtIndex:(NSUInteger)index { 129 | if (_imagePaths) { 130 | if (index >= _imagePaths.count) return nil; 131 | NSString *path = _imagePaths[index]; 132 | CGFloat scale = _NSStringPathScale(path); 133 | NSData *data = [NSData dataWithContentsOfFile:path]; 134 | return [[UIImage imageWithData:data scale:scale] yy_imageByDecoded]; 135 | } else if (_imageDatas) { 136 | if (index >= _imageDatas.count) return nil; 137 | NSData *data = _imageDatas[index]; 138 | return [[UIImage imageWithData:data scale:[UIScreen mainScreen].scale] yy_imageByDecoded]; 139 | } else { 140 | return index == 0 ? self : nil; 141 | } 142 | } 143 | 144 | - (NSTimeInterval)animatedImageDurationAtIndex:(NSUInteger)index { 145 | if (index >= _frameDurations.count) return 0; 146 | NSNumber *num = _frameDurations[index]; 147 | return [num doubleValue]; 148 | } 149 | 150 | @end 151 | -------------------------------------------------------------------------------- /Browser/Browser/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Browser 4 | // 5 | // Created by Blake Tsuzaki on 5/10/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import AudioToolbox 10 | import AVFoundation 11 | import UIKit 12 | import WebKit 13 | import YYImage 14 | 15 | class ViewController: UIViewController { 16 | @IBOutlet weak var backButton: CKImageButton! 17 | @IBOutlet weak var forwardButton: CKImageButton! 18 | @IBOutlet weak var homeButton: CKImageButton! 19 | @IBOutlet weak var stopButton: CKImageButton! 20 | @IBOutlet weak var refreshButton: CKImageButton! 21 | @IBOutlet weak var addressBar: CKTextField! 22 | @IBOutlet weak var goButton: CKTextButton! 23 | @IBOutlet weak var helpButton: CKImageButton! 24 | @IBOutlet weak var statusBar: CKStatusView! 25 | @IBOutlet weak var progressView: CKProgressView! 26 | @IBOutlet weak var imageView: CKImageView! 27 | @IBOutlet weak var wrapperView: CKContentWrapperView! 28 | 29 | private var webView: WKWebView = WKWebView() 30 | private var player: AVPlayer! 31 | 32 | override func viewDidLoad() { 33 | super.viewDidLoad() 34 | 35 | webView.navigationDelegate = self 36 | webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil) 37 | wrapperView.contentView = webView 38 | 39 | statusBar.message = "Ready" 40 | 41 | progressView.range = 1 42 | progressView.isEnabled = false 43 | backButton.isEnabled = false 44 | forwardButton.isEnabled = false 45 | stopButton.isEnabled = false 46 | 47 | if let url = Bundle.main.url(forResource: "Explorer", withExtension: "gif") { 48 | do { 49 | imageView.image = YYImage(data: try Data(contentsOf: url)) 50 | } catch {} 51 | imageView.backgroundColor = .black 52 | } 53 | 54 | goButton.addTarget(self, action: #selector(ViewController.goButtonTapped), for: .touchUpInside) 55 | backButton.addTarget(self, action: #selector(ViewController.backButtonTapped), for: .touchUpInside) 56 | forwardButton.addTarget(self, action: #selector(ViewController.forwardButtonTapped), for: .touchUpInside) 57 | homeButton.addTarget(self, action: #selector(ViewController.homeButtonTapped), for: .touchUpInside) 58 | stopButton.addTarget(self, action: #selector(ViewController.stopButtonTapped), for: .touchUpInside) 59 | refreshButton.addTarget(self, action: #selector(ViewController.refreshButtonTapped), for: .touchUpInside) 60 | helpButton.addTarget(self, action: #selector(ViewController.helpButtonTapped), for: .touchUpInside) 61 | 62 | addressBar.returnKeyType = .go 63 | addressBar.addTarget(self, action: #selector(ViewController.goButtonTapped), for: .primaryActionTriggered) 64 | } 65 | 66 | override func viewDidAppear(_ animated: Bool) { 67 | super.viewDidAppear(animated) 68 | imageView.shouldAnimate = false 69 | } 70 | 71 | @objc func goButtonTapped() { 72 | if let text = addressBar.text, 73 | let url = URL(string: text) { 74 | let request = URLRequest(url: url) 75 | webView.load(request) 76 | } 77 | addressBar.hideKeyboard() 78 | } 79 | 80 | @objc func backButtonTapped() { webView.goBack() } 81 | @objc func forwardButtonTapped() { webView.goForward() } 82 | 83 | @objc func stopButtonTapped() { webView.stopLoading() } 84 | @objc func refreshButtonTapped() { webView.reload() } 85 | 86 | @objc func homeButtonTapped() { 87 | addressBar.text = "https://google.com/" 88 | goButtonTapped() 89 | } 90 | 91 | @objc func helpButtonTapped() { 92 | let viewController = CKDialogViewController() 93 | viewController.modalPresentationStyle = .overCurrentContext 94 | viewController.contentView = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "aboutView").view 95 | present(viewController, animated: false, completion: nil) 96 | } 97 | 98 | override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 99 | if keyPath == "estimatedProgress" { 100 | progressView.value = CGFloat(webView.estimatedProgress) 101 | } 102 | } 103 | 104 | override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) { 105 | super.motionEnded(motion, with: event) 106 | 107 | if motion == .motionShake { 108 | let viewController = CKBlueScreenViewController() 109 | viewController.errorReason = "TOO_MUCH_SHAKING" 110 | present(viewController, animated: false, completion: nil) 111 | } 112 | } 113 | 114 | func playNavigationSound() { 115 | do { 116 | try AVAudioSession.sharedInstance().setCategory(convertFromAVAudioSessionCategory(AVAudioSession.Category.playback)) 117 | try AVAudioSession.sharedInstance().setActive(true) 118 | if let url = Bundle.main.url(forResource: "Windows Navigation Start", withExtension: "wav") { 119 | player = AVPlayer(url: url) 120 | player.play() 121 | } 122 | } catch { print(error.localizedDescription) } 123 | } 124 | } 125 | 126 | extension ViewController: WKNavigationDelegate { 127 | func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { 128 | statusBar.message = "Opening page..." 129 | progressView.isEnabled = true 130 | stopButton.isEnabled = true 131 | 132 | playNavigationSound() 133 | addressBar.text = webView.url?.absoluteString 134 | imageView.shouldAnimate = true 135 | } 136 | 137 | func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { 138 | statusBar.message = "Ready" 139 | progressView.isEnabled = false 140 | stopButton.isEnabled = false 141 | backButton.isEnabled = webView.canGoBack 142 | forwardButton.isEnabled = webView.canGoForward 143 | 144 | addressBar.text = webView.url?.absoluteString 145 | imageView.shouldAnimate = false 146 | } 147 | } 148 | 149 | // Helper function inserted by Swift 4.2 migrator. 150 | fileprivate func convertFromAVAudioSessionCategory(_ input: AVAudioSession.Category) -> String { 151 | return input.rawValue 152 | } 153 | -------------------------------------------------------------------------------- /ClassicKit.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint ClassicKit.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | s.name = "ClassicKit" 19 | s.version = "1.0.0" 20 | s.summary = "Classic UI components for iOS" 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | s.description = <<-DESC 28 | A collection of classic-style UI components for UIKit, influenced by Windows 95. 29 | DESC 30 | 31 | s.homepage = "https://github.com/Baddaboo/ClassicKit" 32 | s.screenshots = "https://github.com/Baddaboo/ClassicKit/raw/master/Images/about.png", "https://github.com/Baddaboo/ClassicKit/raw/master/Images/portrait.png", "https://github.com/Baddaboo/ClassicKit/raw/master/Images/landscape.png" 33 | 34 | 35 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 36 | # 37 | # Licensing your code is important. See http://choosealicense.com for more info. 38 | # CocoaPods will detect a license file if there is a named LICENSE* 39 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 40 | # 41 | 42 | s.license = { :type => 'MIT', :text => <<-LICENSE 43 | Copyright 2018 Blake Tsuzaki and ClassicKit contributors 44 | 45 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 46 | 47 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 48 | 49 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 50 | LICENSE 51 | } 52 | 53 | 54 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 55 | # 56 | # Specify the authors of the library, with email addresses. Email addresses 57 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 58 | # accepts just a name if you'd rather not provide an email address. 59 | # 60 | # Specify a social_media_url where others can refer to, for example a twitter 61 | # profile URL. 62 | # 63 | 64 | s.author = { "Blake Tsuzaki" => "blake589t@gmail.com" } 65 | # Or just: s.author = "Blake Tsuzaki" 66 | # s.authors = { "Blake Tsuzaki" => "blake589t@gmail.com" } 67 | s.social_media_url = "https://baddaboo.github.io" 68 | 69 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 70 | # 71 | # If this Pod runs only on iOS or OS X, then specify the platform and 72 | # the deployment target. You can optionally include the target after the platform. 73 | # 74 | 75 | s.platform = :ios 76 | # s.platform = :ios, "5.0" 77 | 78 | # When using multiple platforms 79 | s.ios.deployment_target = "11.0" 80 | # s.osx.deployment_target = "10.7" 81 | # s.watchos.deployment_target = "2.0" 82 | # s.tvos.deployment_target = "9.0" 83 | 84 | 85 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 86 | # 87 | # Specify the location from where the source should be retrieved. 88 | # Supports git, hg, bzr, svn and HTTP. 89 | # 90 | 91 | s.source = { :git => "https://github.com/Baddaboo/ClassicKit.git", :tag => "#{s.version}" } 92 | 93 | 94 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 95 | # 96 | # CocoaPods is smart about how it includes source code. For source files 97 | # giving a folder will include any swift, h, m, mm, c & cpp files. 98 | # For header files it will include any header in the folder. 99 | # Not including the public_header_files will make all headers public. 100 | # 101 | 102 | s.source_files = "Components/*" 103 | 104 | # s.public_header_files = "Classes/**/*.h" 105 | 106 | 107 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 108 | # 109 | # A list of resources included with the Pod. These are copied into the 110 | # target bundle with a build phase script. Anything else will be cleaned. 111 | # You can preserve files from being cleaned, please don't preserve 112 | # non-essential files like tests, examples and documentation. 113 | # 114 | 115 | # s.resource = "icon.png" 116 | # s.resources = "Resources/*.png" 117 | 118 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 119 | 120 | 121 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 122 | # 123 | # Link your library with frameworks, or libraries. Libraries do not include 124 | # the lib prefix of their name. 125 | # 126 | 127 | # s.framework = "SomeFramework" 128 | # s.frameworks = "SomeFramework", "AnotherFramework" 129 | 130 | # s.library = "iconv" 131 | # s.libraries = "iconv", "xml2" 132 | 133 | 134 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 135 | # 136 | # If your library depends on compiler flags you can set them in the xcconfig hash 137 | # where they will only apply to your library. If you depend on other Podspecs 138 | # you can include multiple dependencies to ensure it works. 139 | 140 | # s.requires_arc = true 141 | 142 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 143 | s.dependency "YYImage" 144 | 145 | end 146 | -------------------------------------------------------------------------------- /Components/CKButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CKButton.swift 3 | // Browser 4 | // 5 | // Created by Blake Tsuzaki on 5/11/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @IBDesignable class CKButton: UIControl { 12 | @IBInspectable var buttonColor: UIColor? { 13 | didSet { contentView.backgroundColor = buttonColor } 14 | } 15 | 16 | var contentView: UIView = UIView() { 17 | didSet { 18 | oldValue.removeFromSuperview() 19 | contentView.isUserInteractionEnabled = false 20 | insertSubview(contentView, belowSubview: shadeView) 21 | setNeedsDisplay() 22 | } 23 | } 24 | 25 | private var shadeView = UIView() 26 | 27 | override init(frame: CGRect) { 28 | super.init(frame: frame) 29 | 30 | configure() 31 | } 32 | 33 | required init?(coder aDecoder: NSCoder) { 34 | super.init(coder: aDecoder) 35 | 36 | configure() 37 | } 38 | 39 | private func configure() { 40 | isOpaque = false 41 | 42 | contentView.isUserInteractionEnabled = false 43 | 44 | addSubview(contentView) 45 | 46 | shadeView.backgroundColor = UIColor(white: 0, alpha: 0.1) 47 | shadeView.isHidden = true 48 | 49 | addSubview(shadeView) 50 | } 51 | 52 | override func layoutSubviews() { 53 | super.layoutSubviews() 54 | setNeedsDisplay() 55 | } 56 | 57 | override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { 58 | setNeedsDisplay() 59 | shadeView.isHidden = false 60 | sendActions(for: .touchDown) 61 | return true 62 | } 63 | 64 | override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { 65 | setNeedsDisplay() 66 | if frame.contains(touch.location(in: self)) { 67 | sendActions(for: .touchDragInside) 68 | } else { 69 | sendActions(for: .touchDragOutside) 70 | } 71 | return true 72 | } 73 | 74 | override func endTracking(_ touch: UITouch?, with event: UIEvent?) { 75 | setNeedsDisplay() 76 | shadeView.isHidden = true 77 | if let touch = touch, frame.contains(touch.location(in: self)) { 78 | sendActions(for: .touchUpInside) 79 | } else { 80 | sendActions(for: .touchUpOutside) 81 | } 82 | } 83 | 84 | override func draw(_ rect: CGRect) { 85 | super.draw(rect) 86 | 87 | let outerBottom = UIBezierPath() 88 | let innerBottom = UIBezierPath() 89 | let innerTop = UIBezierPath() 90 | let outerTop = UIBezierPath() 91 | let lineWidth: CGFloat = CKDefaults.bevelWidth 92 | 93 | contentView.alpha = 1 94 | 95 | switch state { 96 | case .disabled: 97 | contentView.alpha = 0.5 98 | fallthrough 99 | case .normal: 100 | outerBottom.lineWidth = lineWidth 101 | outerBottom.move(to: CGPoint(x: 0, y: bounds.height - lineWidth/2)) 102 | outerBottom.addLine(to: CGPoint(x: bounds.width - lineWidth/2, y: bounds.height - lineWidth/2)) 103 | outerBottom.addLine(to: CGPoint(x: bounds.width - lineWidth/2, y: 0)) 104 | UIColor.black.set() 105 | outerBottom.stroke() 106 | 107 | innerBottom.lineWidth = lineWidth 108 | innerBottom.move(to: CGPoint(x: lineWidth, y: bounds.height - lineWidth * 1.5)) 109 | innerBottom.addLine(to: CGPoint(x: bounds.width - lineWidth * 1.5, y: bounds.height - lineWidth * 1.5)) 110 | innerBottom.addLine(to: CGPoint(x: bounds.width - lineWidth * 1.5, y: lineWidth)) 111 | buttonColor?.darker(by: 30).set() 112 | innerBottom.stroke() 113 | 114 | outerTop.lineWidth = lineWidth 115 | outerTop.move(to: CGPoint(x: lineWidth/2, y: bounds.height - lineWidth)) 116 | outerTop.addLine(to: CGPoint(x: lineWidth/2, y: lineWidth/2)) 117 | outerTop.addLine(to: CGPoint(x: bounds.width - lineWidth, y: lineWidth/2)) 118 | UIColor.white.set() 119 | outerTop.stroke() 120 | 121 | innerTop.lineWidth = lineWidth 122 | innerTop.move(to: CGPoint(x: lineWidth * 1.5, y: bounds.height - lineWidth * 2)) 123 | innerTop.addLine(to: CGPoint(x: lineWidth * 1.5, y: lineWidth * 1.5)) 124 | innerTop.addLine(to: CGPoint(x: bounds.width - lineWidth * 2, y: lineWidth * 1.5)) 125 | UIColor.white.set() 126 | innerTop.stroke() 127 | 128 | var contentFrame = CGRect() 129 | contentFrame.origin.x = lineWidth * 2 130 | contentFrame.origin.y = lineWidth * 2 131 | contentFrame.size.height = rect.size.height - lineWidth * 4 132 | contentFrame.size.width = rect.size.width - lineWidth * 4 133 | 134 | contentView.frame = contentFrame 135 | shadeView.frame = contentFrame 136 | case .highlighted: 137 | innerBottom.lineWidth = lineWidth 138 | innerBottom.move(to: CGPoint(x: 0, y: bounds.height - lineWidth/2)) 139 | innerBottom.addLine(to: CGPoint(x: bounds.width - lineWidth/2, y: bounds.height - lineWidth/2)) 140 | innerBottom.addLine(to: CGPoint(x: bounds.width - lineWidth/2, y: 0)) 141 | UIColor.white.set() 142 | innerBottom.stroke() 143 | 144 | outerBottom.lineWidth = lineWidth 145 | outerBottom.move(to: CGPoint(x: lineWidth, y: bounds.height - lineWidth * 1.5)) 146 | outerBottom.addLine(to: CGPoint(x: bounds.width - lineWidth * 1.5, y: bounds.height - lineWidth * 1.5)) 147 | outerBottom.addLine(to: CGPoint(x: bounds.width - lineWidth * 1.5, y: lineWidth)) 148 | UIColor(white: 1, alpha: 0.5).set() 149 | outerBottom.stroke() 150 | 151 | outerTop.lineWidth = lineWidth 152 | outerTop.move(to: CGPoint(x: lineWidth/2, y: bounds.height - lineWidth)) 153 | outerTop.addLine(to: CGPoint(x: lineWidth/2, y: lineWidth/2)) 154 | outerTop.addLine(to: CGPoint(x: bounds.width - lineWidth, y: lineWidth/2)) 155 | UIColor(white: 0, alpha: 0.3).set() 156 | outerTop.stroke() 157 | 158 | innerTop.lineWidth = lineWidth 159 | innerTop.move(to: CGPoint(x: lineWidth * 1.5, y: bounds.height - lineWidth * 2)) 160 | innerTop.addLine(to: CGPoint(x: lineWidth * 1.5, y: lineWidth * 1.5)) 161 | innerTop.addLine(to: CGPoint(x: bounds.width - lineWidth * 2, y: lineWidth * 1.5)) 162 | UIColor.black.set() 163 | innerTop.stroke() 164 | 165 | let innerInnerTop = UIBezierPath() 166 | innerInnerTop.lineWidth = lineWidth * 2 167 | innerInnerTop.move(to: CGPoint(x: lineWidth * 3, y: bounds.height - lineWidth * 2)) 168 | innerInnerTop.addLine(to: CGPoint(x: lineWidth * 3, y: lineWidth * 3)) 169 | innerInnerTop.addLine(to: CGPoint(x: bounds.width - lineWidth * 2, y: lineWidth * 3)) 170 | contentView.backgroundColor?.set() 171 | innerInnerTop.stroke() 172 | 173 | var contentFrame = CGRect() 174 | contentFrame.origin.x = lineWidth * 4 175 | contentFrame.origin.y = lineWidth * 4 176 | contentFrame.size.height = rect.size.height - lineWidth * 6 177 | contentFrame.size.width = rect.size.width - lineWidth * 6 178 | 179 | contentView.frame = contentFrame 180 | 181 | contentFrame.origin.x = lineWidth * 2 182 | contentFrame.origin.y = lineWidth * 2 183 | contentFrame.size.height = rect.size.height - lineWidth * 4 184 | contentFrame.size.width = rect.size.width - lineWidth * 4 185 | 186 | shadeView.frame = contentFrame 187 | default: break 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /Components/CKContentWrapperView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CKContentWrapperView.swift 3 | // Browser 4 | // 5 | // Created by Blake Tsuzaki on 5/11/18. 6 | // Copyright © 2018 Modoki. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | 12 | @IBDesignable class CKContentWrapperView: UIView { 13 | @IBInspectable var scrollBarWidth: CGFloat = 20 { 14 | didSet { setNeedsLayout() } 15 | } 16 | 17 | @IBInspectable var barColor: UIColor? = CKDefaults.backgroundColor { 18 | didSet { 19 | verticalScrollBar.buttonColor = barColor 20 | horizontalScrollBar.buttonColor = barColor 21 | } 22 | } 23 | 24 | @IBInspectable var showVerticalScrollBarAlways: Bool = true 25 | @IBInspectable var showHorizontalScrollBarAlways: Bool = true 26 | 27 | @IBOutlet weak var contentView: UIView? { 28 | didSet { 29 | if let oldValue = oldValue { 30 | oldValue.removeFromSuperview() 31 | } 32 | if let contentView = contentView { 33 | addSubview(contentView) 34 | 35 | contentView.clipsToBounds = true 36 | 37 | if let contentView = contentView as? UIScrollView { scrollView = contentView } 38 | else if let contentView = contentView as? WKWebView { scrollView = contentView.scrollView } 39 | else { scrollView = nil } 40 | } 41 | setNeedsDisplay() 42 | } 43 | } 44 | 45 | private var verticalScrollBar = CKVerticalScrollBar() 46 | private var horizontalScrollBar = CKHorizontalScrollBar() 47 | private var scrollView: UIScrollView? { 48 | didSet { 49 | if let scrollView = scrollView { 50 | scrollView.showsVerticalScrollIndicator = false 51 | scrollView.showsHorizontalScrollIndicator = false 52 | scrollView.addObserver(self, forKeyPath: #keyPath(UIScrollView.contentSize), options: .new, context: nil) 53 | scrollView.delegate = self 54 | 55 | updateScrollBars() 56 | } else { 57 | verticalScrollBar.range = 1 58 | verticalScrollBar.value = 1 59 | horizontalScrollBar.range = 1 60 | horizontalScrollBar.value = 1 61 | } 62 | } 63 | } 64 | private var isVerticalScrollBarGrayed: Bool = false { 65 | didSet { 66 | if isVerticalScrollBarGrayed != oldValue { setNeedsLayout() } 67 | } 68 | } 69 | private var isHorizontalScrollBarGrayed: Bool = false { 70 | didSet { 71 | if isHorizontalScrollBarGrayed != oldValue { setNeedsLayout() } 72 | } 73 | } 74 | 75 | override init(frame: CGRect) { 76 | super.init(frame: frame) 77 | 78 | configure() 79 | } 80 | 81 | required init?(coder aDecoder: NSCoder) { 82 | super.init(coder: aDecoder) 83 | 84 | configure() 85 | } 86 | 87 | private func configure() { 88 | if let contentView = contentView { 89 | addSubview(contentView) 90 | } 91 | addSubview(verticalScrollBar) 92 | addSubview(horizontalScrollBar) 93 | 94 | verticalScrollBar.range = 1 95 | verticalScrollBar.value = 1 96 | horizontalScrollBar.range = 1 97 | horizontalScrollBar.value = 1 98 | 99 | verticalScrollBar.buttonColor = barColor 100 | horizontalScrollBar.buttonColor = barColor 101 | 102 | verticalScrollBar.addTarget(self, action: #selector(CKContentWrapperView.verticalSliderDidMove), for: .valueChanged) 103 | horizontalScrollBar.addTarget(self, action: #selector(CKContentWrapperView.horizontalSliderDidMove), for: .valueChanged) 104 | } 105 | 106 | override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 107 | if let object = object as? UIScrollView, object == scrollView && keyPath == "contentSize" { 108 | updateScrollBars() 109 | } 110 | } 111 | 112 | @objc func verticalSliderDidMove() { 113 | if verticalScrollBar.isScrolling { 114 | scrollView?.contentOffset.y = verticalScrollBar.value 115 | } 116 | } 117 | 118 | @objc func horizontalSliderDidMove() { 119 | if horizontalScrollBar.isScrolling { 120 | scrollView?.contentOffset.x = horizontalScrollBar.value 121 | } 122 | } 123 | 124 | override func layoutSubviews() { 125 | super.layoutSubviews() 126 | let lineWidth = CKDefaults.bevelWidth 127 | let showVerticalScrollBar = showVerticalScrollBarAlways || !isVerticalScrollBarGrayed 128 | let showHorizontalScrollBar = showHorizontalScrollBarAlways || !isHorizontalScrollBarGrayed 129 | 130 | if let contentView = contentView { 131 | var contentFrame = CGRect() 132 | contentFrame.origin.x = lineWidth * 2 133 | contentFrame.origin.y = lineWidth * 2 134 | contentFrame.size.height = frame.size.height - lineWidth * 4 - (showHorizontalScrollBar ? scrollBarWidth : 0) 135 | contentFrame.size.width = frame.size.width - lineWidth * 4 - (showVerticalScrollBar ? scrollBarWidth : 0) 136 | 137 | contentView.frame = contentFrame 138 | } 139 | 140 | var verticalScrollFrame = CGRect() 141 | verticalScrollFrame.origin.x = frame.size.width - lineWidth * 2 - scrollBarWidth 142 | verticalScrollFrame.origin.y = lineWidth * 2 143 | verticalScrollFrame.size.height = frame.size.height - lineWidth * 4 - (showHorizontalScrollBar ? scrollBarWidth : 0) 144 | verticalScrollFrame.size.width = scrollBarWidth 145 | 146 | verticalScrollBar.frame = verticalScrollFrame 147 | verticalScrollBar.isHidden = !showVerticalScrollBar 148 | 149 | var horizontalScrollFrame = CGRect() 150 | horizontalScrollFrame.origin.x = lineWidth * 2 151 | horizontalScrollFrame.origin.y = frame.size.height - lineWidth * 2 - scrollBarWidth 152 | horizontalScrollFrame.size.height = scrollBarWidth 153 | horizontalScrollFrame.size.width = frame.size.width - lineWidth * 4 - (showVerticalScrollBar ? scrollBarWidth : 0) 154 | 155 | horizontalScrollBar.frame = horizontalScrollFrame 156 | horizontalScrollBar.isHidden = !showHorizontalScrollBar 157 | 158 | setNeedsDisplay() 159 | } 160 | 161 | override func draw(_ rect: CGRect) { 162 | super.draw(rect) 163 | 164 | CKDefaults.drawInsetBevel(with: rect) 165 | } 166 | 167 | func updateScrollBars() { 168 | guard let scrollView = scrollView else { return } 169 | 170 | if !verticalScrollBar.isScrolling { 171 | verticalScrollBar.value = scrollView.contentOffset.y 172 | } 173 | if !horizontalScrollBar.isScrolling { 174 | horizontalScrollBar.value = scrollView.contentOffset.x 175 | } 176 | 177 | let contentHeight = max(1, scrollView.contentSize.height) 178 | let frameHeight = scrollView.frame.height 179 | verticalScrollBar.range = contentHeight - frameHeight 180 | verticalScrollBar.thumbSize = (frameHeight / contentHeight) * verticalScrollBar.scrollSize 181 | 182 | let contentWidth = max(scrollView.contentSize.width, 1) 183 | let frameWidth = scrollView.frame.width 184 | horizontalScrollBar.range = contentWidth - frameWidth 185 | horizontalScrollBar.thumbSize = (frameWidth / contentWidth) * horizontalScrollBar.scrollSize 186 | 187 | isVerticalScrollBarGrayed = verticalScrollBar.isGrayedOut 188 | isHorizontalScrollBarGrayed = horizontalScrollBar.isGrayedOut 189 | } 190 | } 191 | 192 | extension CKContentWrapperView: UIScrollViewDelegate { 193 | func scrollViewDidScroll(_ scrollView: UIScrollView) { 194 | if scrollView == self.scrollView { updateScrollBars() } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /Browser/Pods/YYImage/YYImage/YYImage.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYImage.m 3 | // YYImage 4 | // 5 | // Created by ibireme on 14/10/20. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYImage.h" 13 | 14 | /** 15 | An array of NSNumber objects, shows the best order for path scale search. 16 | e.g. iPhone3GS:@[@1,@2,@3] iPhone5:@[@2,@3,@1] iPhone6 Plus:@[@3,@2,@1] 17 | */ 18 | static NSArray *_NSBundlePreferredScales() { 19 | static NSArray *scales; 20 | static dispatch_once_t onceToken; 21 | dispatch_once(&onceToken, ^{ 22 | CGFloat screenScale = [UIScreen mainScreen].scale; 23 | if (screenScale <= 1) { 24 | scales = @[@1,@2,@3]; 25 | } else if (screenScale <= 2) { 26 | scales = @[@2,@3,@1]; 27 | } else { 28 | scales = @[@3,@2,@1]; 29 | } 30 | }); 31 | return scales; 32 | } 33 | 34 | /** 35 | Add scale modifier to the file name (without path extension), 36 | From @"name" to @"name@2x". 37 | 38 | e.g. 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
Before After(scale:2)
"icon" "icon@2x"
"icon " "icon @2x"
"icon.top" "icon.top@2x"
"/p/name" "/p/name@2x"
"/path/" "/path/"
47 | 48 | @param scale Resource scale. 49 | @return String by add scale modifier, or just return if it's not end with file name. 50 | */ 51 | static NSString *_NSStringByAppendingNameScale(NSString *string, CGFloat scale) { 52 | if (!string) return nil; 53 | if (fabs(scale - 1) <= __FLT_EPSILON__ || string.length == 0 || [string hasSuffix:@"/"]) return string.copy; 54 | return [string stringByAppendingFormat:@"@%@x", @(scale)]; 55 | } 56 | 57 | /** 58 | Return the path scale. 59 | 60 | e.g. 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 |
Path Scale
"icon.png" 1
"icon@2x.png" 2
"icon@2.5x.png" 2.5
"icon@2x" 1
"icon@2x..png" 1
"icon@2x.png/" 1
70 | */ 71 | static CGFloat _NSStringPathScale(NSString *string) { 72 | if (string.length == 0 || [string hasSuffix:@"/"]) return 1; 73 | NSString *name = string.stringByDeletingPathExtension; 74 | __block CGFloat scale = 1; 75 | 76 | NSRegularExpression *pattern = [NSRegularExpression regularExpressionWithPattern:@"@[0-9]+\\.?[0-9]*x$" options:NSRegularExpressionAnchorsMatchLines error:nil]; 77 | [pattern enumerateMatchesInString:name options:kNilOptions range:NSMakeRange(0, name.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { 78 | if (result.range.location >= 3) { 79 | scale = [string substringWithRange:NSMakeRange(result.range.location + 1, result.range.length - 2)].doubleValue; 80 | } 81 | }]; 82 | 83 | return scale; 84 | } 85 | 86 | 87 | @implementation YYImage { 88 | YYImageDecoder *_decoder; 89 | NSArray *_preloadedFrames; 90 | dispatch_semaphore_t _preloadedLock; 91 | NSUInteger _bytesPerFrame; 92 | } 93 | 94 | + (YYImage *)imageNamed:(NSString *)name { 95 | if (name.length == 0) return nil; 96 | if ([name hasSuffix:@"/"]) return nil; 97 | 98 | NSString *res = name.stringByDeletingPathExtension; 99 | NSString *ext = name.pathExtension; 100 | NSString *path = nil; 101 | CGFloat scale = 1; 102 | 103 | // If no extension, guess by system supported (same as UIImage). 104 | NSArray *exts = ext.length > 0 ? @[ext] : @[@"", @"png", @"jpeg", @"jpg", @"gif", @"webp", @"apng"]; 105 | NSArray *scales = _NSBundlePreferredScales(); 106 | for (int s = 0; s < scales.count; s++) { 107 | scale = ((NSNumber *)scales[s]).floatValue; 108 | NSString *scaledName = _NSStringByAppendingNameScale(res, scale); 109 | for (NSString *e in exts) { 110 | path = [[NSBundle mainBundle] pathForResource:scaledName ofType:e]; 111 | if (path) break; 112 | } 113 | if (path) break; 114 | } 115 | if (path.length == 0) return nil; 116 | 117 | NSData *data = [NSData dataWithContentsOfFile:path]; 118 | if (data.length == 0) return nil; 119 | 120 | return [[self alloc] initWithData:data scale:scale]; 121 | } 122 | 123 | + (YYImage *)imageWithContentsOfFile:(NSString *)path { 124 | return [[self alloc] initWithContentsOfFile:path]; 125 | } 126 | 127 | + (YYImage *)imageWithData:(NSData *)data { 128 | return [[self alloc] initWithData:data]; 129 | } 130 | 131 | + (YYImage *)imageWithData:(NSData *)data scale:(CGFloat)scale { 132 | return [[self alloc] initWithData:data scale:scale]; 133 | } 134 | 135 | - (instancetype)initWithContentsOfFile:(NSString *)path { 136 | NSData *data = [NSData dataWithContentsOfFile:path]; 137 | return [self initWithData:data scale:_NSStringPathScale(path)]; 138 | } 139 | 140 | - (instancetype)initWithData:(NSData *)data { 141 | return [self initWithData:data scale:1]; 142 | } 143 | 144 | - (instancetype)initWithData:(NSData *)data scale:(CGFloat)scale { 145 | if (data.length == 0) return nil; 146 | if (scale <= 0) scale = [UIScreen mainScreen].scale; 147 | _preloadedLock = dispatch_semaphore_create(1); 148 | @autoreleasepool { 149 | YYImageDecoder *decoder = [YYImageDecoder decoderWithData:data scale:scale]; 150 | YYImageFrame *frame = [decoder frameAtIndex:0 decodeForDisplay:YES]; 151 | UIImage *image = frame.image; 152 | if (!image) return nil; 153 | self = [self initWithCGImage:image.CGImage scale:decoder.scale orientation:image.imageOrientation]; 154 | if (!self) return nil; 155 | _animatedImageType = decoder.type; 156 | if (decoder.frameCount > 1) { 157 | _decoder = decoder; 158 | _bytesPerFrame = CGImageGetBytesPerRow(image.CGImage) * CGImageGetHeight(image.CGImage); 159 | _animatedImageMemorySize = _bytesPerFrame * decoder.frameCount; 160 | } 161 | self.yy_isDecodedForDisplay = YES; 162 | } 163 | return self; 164 | } 165 | 166 | - (NSData *)animatedImageData { 167 | return _decoder.data; 168 | } 169 | 170 | - (void)setPreloadAllAnimatedImageFrames:(BOOL)preloadAllAnimatedImageFrames { 171 | if (_preloadAllAnimatedImageFrames != preloadAllAnimatedImageFrames) { 172 | if (preloadAllAnimatedImageFrames && _decoder.frameCount > 0) { 173 | NSMutableArray *frames = [NSMutableArray new]; 174 | for (NSUInteger i = 0, max = _decoder.frameCount; i < max; i++) { 175 | UIImage *img = [self animatedImageFrameAtIndex:i]; 176 | if (img) { 177 | [frames addObject:img]; 178 | } else { 179 | [frames addObject:[NSNull null]]; 180 | } 181 | } 182 | dispatch_semaphore_wait(_preloadedLock, DISPATCH_TIME_FOREVER); 183 | _preloadedFrames = frames; 184 | dispatch_semaphore_signal(_preloadedLock); 185 | } else { 186 | dispatch_semaphore_wait(_preloadedLock, DISPATCH_TIME_FOREVER); 187 | _preloadedFrames = nil; 188 | dispatch_semaphore_signal(_preloadedLock); 189 | } 190 | } 191 | } 192 | 193 | #pragma mark - protocol NSCoding 194 | 195 | - (instancetype)initWithCoder:(NSCoder *)aDecoder { 196 | NSNumber *scale = [aDecoder decodeObjectForKey:@"YYImageScale"]; 197 | NSData *data = [aDecoder decodeObjectForKey:@"YYImageData"]; 198 | if (data.length) { 199 | self = [self initWithData:data scale:scale.doubleValue]; 200 | } else { 201 | self = [super initWithCoder:aDecoder]; 202 | } 203 | return self; 204 | } 205 | 206 | - (void)encodeWithCoder:(NSCoder *)aCoder { 207 | if (_decoder.data.length) { 208 | [aCoder encodeObject:@(self.scale) forKey:@"YYImageScale"]; 209 | [aCoder encodeObject:_decoder.data forKey:@"YYImageData"]; 210 | } else { 211 | [super encodeWithCoder:aCoder]; // Apple use UIImagePNGRepresentation() to encode UIImage. 212 | } 213 | } 214 | 215 | #pragma mark - protocol YYAnimatedImage 216 | 217 | - (NSUInteger)animatedImageFrameCount { 218 | return _decoder.frameCount; 219 | } 220 | 221 | - (NSUInteger)animatedImageLoopCount { 222 | return _decoder.loopCount; 223 | } 224 | 225 | - (NSUInteger)animatedImageBytesPerFrame { 226 | return _bytesPerFrame; 227 | } 228 | 229 | - (UIImage *)animatedImageFrameAtIndex:(NSUInteger)index { 230 | if (index >= _decoder.frameCount) return nil; 231 | dispatch_semaphore_wait(_preloadedLock, DISPATCH_TIME_FOREVER); 232 | UIImage *image = _preloadedFrames[index]; 233 | dispatch_semaphore_signal(_preloadedLock); 234 | if (image) return image == (id)[NSNull null] ? nil : image; 235 | return [_decoder frameAtIndex:index decodeForDisplay:YES].image; 236 | } 237 | 238 | - (NSTimeInterval)animatedImageDurationAtIndex:(NSUInteger)index { 239 | NSTimeInterval duration = [_decoder frameDurationAtIndex:index]; 240 | 241 | /* 242 | http://opensource.apple.com/source/WebCore/WebCore-7600.1.25/platform/graphics/cg/ImageSourceCG.cpp 243 | Many annoying ads specify a 0 duration to make an image flash as quickly as 244 | possible. We follow Safari and Firefox's behavior and use a duration of 100 ms 245 | for any frames that specify a duration of <= 10 ms. 246 | See and for more information. 247 | 248 | See also: http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser. 249 | */ 250 | if (duration < 0.011f) return 0.100f; 251 | return duration; 252 | } 253 | 254 | @end 255 | -------------------------------------------------------------------------------- /Browser/Pods/YYImage/README.md: -------------------------------------------------------------------------------- 1 | YYImage 2 | ============== 3 | [![License MIT](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://raw.githubusercontent.com/ibireme/YYImage/master/LICENSE)  4 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)  5 | [![CocoaPods](http://img.shields.io/cocoapods/v/YYImage.svg?style=flat)](http://cocoapods.org/?q= YYImage)  6 | [![CocoaPods](http://img.shields.io/cocoapods/p/YYImage.svg?style=flat)](http://cocoapods.org/?q= YYImage)  7 | [![Support](https://img.shields.io/badge/support-iOS%206%2B%20-blue.svg?style=flat)](https://www.apple.com/nl/ios/)  8 | [![Build Status](https://travis-ci.org/ibireme/YYImage.svg?branch=master)](https://travis-ci.org/ibireme/YYImage) 9 | 10 | Image framework for iOS to display/encode/decode animated WebP, APNG, GIF, and more.
11 | (It's a component of [YYKit](https://github.com/ibireme/YYKit)) 12 | 13 | ![niconiconi~](https://raw.github.com/ibireme/YYImage/master/Demo/YYImageDemo/niconiconi@2x.gif 14 | ) 15 | 16 | Features 17 | ============== 18 | - Display/encode/decode animated image with these types:
    WebP, APNG, GIF. 19 | - Display/encode/decode still image with these types:
    WebP, PNG, GIF, JPEG, JP2, TIFF, BMP, ICO, ICNS. 20 | - Baseline/progressive/interlaced image decode with these types:
    PNG, GIF, JPEG, BMP. 21 | - Display frame based image animation and sprite sheet animation. 22 | - Dynamic memory buffer for lower memory usage. 23 | - Fully compatible with UIImage and UIImageView class. 24 | - Extendable protocol for custom image animation. 25 | - Fully documented. 26 | 27 | Usage 28 | ============== 29 | 30 | ###Display animated image 31 | 32 | // File: ani@3x.gif 33 | UIImage *image = [YYImage imageNamed:@"ani.gif"]; 34 | UIImageView *imageView = [[YYAnimatedImageView alloc] initWithImage:image]; 35 | [self.view addSubView:imageView]; 36 | 37 | 38 | ###Display frame animation 39 | 40 | // Files: frame1.png, frame2.png, frame3.png 41 | NSArray *paths = @[@"/ani/frame1.png", @"/ani/frame2.png", @"/ani/frame3.png"]; 42 | NSArray *times = @[@0.1, @0.2, @0.1]; 43 | UIImage *image = [YYFrameImage alloc] initWithImagePaths:paths frameDurations:times repeats:YES]; 44 | UIImageView *imageView = [YYAnimatedImageView alloc] initWithImage:image]; 45 | [self.view addSubView:imageView]; 46 | 47 | ###Display sprite sheet animation 48 | 49 | // 8 * 12 sprites in a single sheet image 50 | UIImage *spriteSheet = [UIImage imageNamed:@"sprite-sheet"]; 51 | NSMutableArray *contentRects = [NSMutableArray new]; 52 | NSMutableArray *durations = [NSMutableArray new]; 53 | for (int j = 0; j < 12; j++) { 54 | for (int i = 0; i < 8; i++) { 55 | CGRect rect; 56 | rect.size = CGSizeMake(img.size.width / 8, img.size.height / 12); 57 | rect.origin.x = img.size.width / 8 * i; 58 | rect.origin.y = img.size.height / 12 * j; 59 | [contentRects addObject:[NSValue valueWithCGRect:rect]]; 60 | [durations addObject:@(1 / 60.0)]; 61 | } 62 | } 63 | YYSpriteSheetImage *sprite; 64 | sprite = [[YYSpriteSheetImage alloc] initWithSpriteSheetImage:img 65 | contentRects:contentRects 66 | frameDurations:durations 67 | loopCount:0]; 68 | YYAnimatedImageView *imageView = [YYAnimatedImageView new]; 69 | imageView.size = CGSizeMake(img.size.width / 8, img.size.height / 12); 70 | imageView.image = sprite; 71 | [self.view addSubView:imageView]; 72 | 73 | ###Animation control 74 | 75 | YYAnimatedImageView *imageView = ...; 76 | // pause: 77 | [imageView stopAnimating]; 78 | // play: 79 | [imageView startAnimating]; 80 | // set frame index: 81 | imageView.currentAnimatedImageIndex = 12; 82 | // get current status 83 | image.currentIsPlayingAnimation; 84 | 85 | ###Image decoder 86 | 87 | // Decode single frame: 88 | NSData *data = [NSData dataWithContentsOfFile:@"/tmp/image.webp"]; 89 | YYImageDecoder *decoder = [YYImageDecoder decoderWithData:data scale:2.0]; 90 | UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image; 91 | 92 | // Progressive: 93 | NSMutableData *data = [NSMutableData new]; 94 | YYImageDecoder *decoder = [[YYImageDecoder alloc] initWithScale:2.0]; 95 | while(newDataArrived) { 96 | [data appendData:newData]; 97 | [decoder updateData:data final:NO]; 98 | if (decoder.frameCount > 0) { 99 | UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image; 100 | // progressive display... 101 | } 102 | } 103 | [decoder updateData:data final:YES]; 104 | UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image; 105 | // final display... 106 | 107 | ###Image encoder 108 | 109 | // Encode still image: 110 | YYImageEncoder *jpegEncoder = [[YYImageEncoder alloc] initWithType:YYImageTypeJPEG]; 111 | jpegEncoder.quality = 0.9; 112 | [jpegEncoder addImage:image duration:0]; 113 | NSData jpegData = [jpegEncoder encode]; 114 | 115 | // Encode animated image: 116 | YYImageEncoder *webpEncoder = [[YYImageEncoder alloc] initWithType:YYImageTypeWebP]; 117 | webpEncoder.loopCount = 5; 118 | [webpEncoder addImage:image0 duration:0.1]; 119 | [webpEncoder addImage:image1 duration:0.15]; 120 | [webpEncoder addImage:image2 duration:0.2]; 121 | NSData webpData = [webpEncoder encode]; 122 | 123 | ###Image type detection 124 | 125 | // Get image type from image data 126 | YYImageType type = YYImageDetectType(data); 127 | if (type == YYImageTypePNG) ... 128 | 129 | 130 | Installation 131 | ============== 132 | 133 | ### CocoaPods 134 | 135 | 1. Update cocoapods to the latest version. 136 | 2. Add `pod 'YYImage'` to your Podfile. 137 | 3. Run `pod install` or `pod update`. 138 | 4. Import \. 139 | 5. Notice: it doesn't include WebP subspec by default, if you want to support WebP format, you may add `pod 'YYImage/WebP'` to your Podfile. 140 | 141 | ### Carthage 142 | 143 | 1. Add `github "ibireme/YYImage"` to your Cartfile. 144 | 2. Run `carthage update --platform ios` and add the framework to your project. 145 | 3. Import \. 146 | 4. Notice: carthage framework doesn't include WebP component, if you want to support WebP format, use CocoaPods or install manually. 147 | 148 | ### Manually 149 | 150 | 1. Download all the files in the YYImage subdirectory. 151 | 2. Add the source files to your Xcode project. 152 | 3. Link with required frameworks: 153 | * UIKit 154 | * CoreFoundation 155 | * QuartzCore 156 | * AssetsLibrary 157 | * ImageIO 158 | * Accelerate 159 | * MobileCoreServices 160 | * libz 161 | 4. Import `YYImage.h`. 162 | 5. Notice: if you want to support WebP format, you may add `Vendor/WebP.framework`(static library) to your Xcode project. 163 | 164 | FAQ 165 | ============== 166 | _Q: Why I can't display WebP image?_ 167 | 168 | A: Make sure you added the `WebP.framework` in your project. You may call `YYImageWebPAvailable()` to check whether the WebP subspec is installed correctly. 169 | 170 | _Q: Why I can't play APNG animation?_ 171 | 172 | A: You should disable the `Compress PNG Files` and `Remove Text Metadata From PNG Files` in your project's build settings. Or you can rename your APNG file's extension name with `apng`. 173 | 174 | Documentation 175 | ============== 176 | Full API documentation is available on [CocoaDocs](http://cocoadocs.org/docsets/YYImage/).
177 | You can also install documentation locally using [appledoc](https://github.com/tomaz/appledoc). 178 | 179 | 180 | 181 | Requirements 182 | ============== 183 | This library requires `iOS 6.0+` and `Xcode 7.0+`. 184 | 185 | 186 | License 187 | ============== 188 | YYImage is provided under the MIT license. See LICENSE file for details. 189 | 190 | 191 |

192 | --- 193 | 中文介绍 194 | ============== 195 | YYImage: 功能强大的 iOS 图像框架。
196 | (该项目是 [YYKit](https://github.com/ibireme/YYKit) 组件之一) 197 | 198 | ![niconiconi~](https://raw.github.com/ibireme/YYImage/master/Demo/YYImageDemo/niconiconi@2x.gif 199 | ) 200 | 201 | 特性 202 | ============== 203 | - 支持以下类型动画图像的播放/编码/解码:
204 |     WebP, APNG, GIF。 205 | - 支持以下类型静态图像的显示/编码/解码:
206 |     WebP, PNG, GIF, JPEG, JP2, TIFF, BMP, ICO, ICNS。 207 | - 支持以下类型图片的渐进式/逐行扫描/隔行扫描解码:
208 |     PNG, GIF, JPEG, BMP。 209 | - 支持多张图片构成的帧动画播放,支持单张图片的 sprite sheet 动画。 210 | - 高效的动态内存缓存管理,以保证高性能低内存的动画播放。 211 | - 完全兼容 UIImage 和 UIImageView,使用方便。 212 | - 保留可扩展的接口,以支持自定义动画。 213 | - 每个类和方法都有完善的文档注释。 214 | 215 | 216 | 用法 217 | ============== 218 | 219 | ###显示动画类型的图片 220 | 221 | // 文件: ani@3x.gif 222 | UIImage *image = [YYImage imageNamed:@"ani.gif"]; 223 | UIImageView *imageView = [[YYAnimatedImageView alloc] initWithImage:image]; 224 | [self.view addSubView:imageView]; 225 | 226 | 227 | ###播放帧动画 228 | 229 | // 文件: frame1.png, frame2.png, frame3.png 230 | NSArray *paths = @[@"/ani/frame1.png", @"/ani/frame2.png", @"/ani/frame3.png"]; 231 | NSArray *times = @[@0.1, @0.2, @0.1]; 232 | UIImage *image = [YYFrameImage alloc] initWithImagePaths:paths frameDurations:times repeats:YES]; 233 | UIImageView *imageView = [YYAnimatedImageView alloc] initWithImage:image]; 234 | [self.view addSubView:imageView]; 235 | 236 | ###播放 sprite sheet 动画 237 | 238 | // 8 * 12 sprites in a single sheet image 239 | UIImage *spriteSheet = [UIImage imageNamed:@"sprite-sheet"]; 240 | NSMutableArray *contentRects = [NSMutableArray new]; 241 | NSMutableArray *durations = [NSMutableArray new]; 242 | for (int j = 0; j < 12; j++) { 243 | for (int i = 0; i < 8; i++) { 244 | CGRect rect; 245 | rect.size = CGSizeMake(img.size.width / 8, img.size.height / 12); 246 | rect.origin.x = img.size.width / 8 * i; 247 | rect.origin.y = img.size.height / 12 * j; 248 | [contentRects addObject:[NSValue valueWithCGRect:rect]]; 249 | [durations addObject:@(1 / 60.0)]; 250 | } 251 | } 252 | YYSpriteSheetImage *sprite; 253 | sprite = [[YYSpriteSheetImage alloc] initWithSpriteSheetImage:img 254 | contentRects:contentRects 255 | frameDurations:durations 256 | loopCount:0]; 257 | YYAnimatedImageView *imageView = [YYAnimatedImageView new]; 258 | imageView.size = CGSizeMake(img.size.width / 8, img.size.height / 12); 259 | imageView.image = sprite; 260 | [self.view addSubView:imageView]; 261 | 262 | ###动画播放控制 263 | 264 | YYAnimatedImageView *imageView = ...; 265 | // 暂停: 266 | [imageView stopAnimating]; 267 | // 播放: 268 | [imageView startAnimating]; 269 | // 设置播放进度: 270 | imageView.currentAnimatedImageIndex = 12; 271 | // 获取播放状态: 272 | image.currentIsPlayingAnimation; 273 | //上面两个属性都支持 KVO。 274 | 275 | ###图片解码 276 | 277 | // 解码单帧图片: 278 | NSData *data = [NSData dataWithContentsOfFile:@"/tmp/image.webp"]; 279 | YYImageDecoder *decoder = [YYImageDecoder decoderWithData:data scale:2.0]; 280 | UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image; 281 | 282 | // 渐进式图片解码 (可用于图片下载显示): 283 | NSMutableData *data = [NSMutableData new]; 284 | YYImageDecoder *decoder = [[YYImageDecoder alloc] initWithScale:2.0]; 285 | while(newDataArrived) { 286 | [data appendData:newData]; 287 | [decoder updateData:data final:NO]; 288 | if (decoder.frameCount > 0) { 289 | UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image; 290 | // progressive display... 291 | } 292 | } 293 | [decoder updateData:data final:YES]; 294 | UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image; 295 | // final display... 296 | 297 | ###图片编码 298 | 299 | // 编码静态图 (支持各种常见图片格式): 300 | YYImageEncoder *jpegEncoder = [[YYImageEncoder alloc] initWithType:YYImageTypeJPEG]; 301 | jpegEncoder.quality = 0.9; 302 | [jpegEncoder addImage:image duration:0]; 303 | NSData jpegData = [jpegEncoder encode]; 304 | 305 | // 编码动态图 (支持 GIF/APNG/WebP): 306 | YYImageEncoder *webpEncoder = [[YYImageEncoder alloc] initWithType:YYImageTypeWebP]; 307 | webpEncoder.loopCount = 5; 308 | [webpEncoder addImage:image0 duration:0.1]; 309 | [webpEncoder addImage:image1 duration:0.15]; 310 | [webpEncoder addImage:image2 duration:0.2]; 311 | NSData webpData = [webpEncoder encode]; 312 | 313 | ###图片类型探测 314 | 315 | // 获取图片类型 316 | YYImageType type = YYImageDetectType(data); 317 | if (type == YYImageTypePNG) ... 318 | 319 | 320 | 安装 321 | ============== 322 | 323 | ### CocoaPods 324 | 325 | 1. 将 cocoapods 更新至最新版本. 326 | 2. 在 Podfile 中添加 `pod 'YYImage'`。 327 | 3. 执行 `pod install` 或 `pod update`。 328 | 4. 导入 \。 329 | 5. 注意:pod 配置并没有包含 WebP 组件, 如果你需要支持 WebP,可以在 Podfile 中添加 `pod 'YYImage/WebP'`。 330 | 331 | ### Carthage 332 | 333 | 1. 在 Cartfile 中添加 `github "ibireme/YYImage"`。 334 | 2. 执行 `carthage update --platform ios` 并将生成的 framework 添加到你的工程。 335 | 3. 导入 \。 336 | 4. 注意:carthage framework 并没有包含 WebP 组件。如果你需要支持 WebP,可以用 CocoaPods 安装,或者手动安装。 337 | 338 | ### 手动安装 339 | 340 | 1. 下载 YYImage 文件夹内的所有内容。 341 | 2. 将 YYImage 内的源文件添加(拖放)到你的工程。 342 | 3. 链接以下 frameworks: 343 | * UIKit 344 | * CoreFoundation 345 | * QuartzCore 346 | * AssetsLibrary 347 | * ImageIO 348 | * Accelerate 349 | * MobileCoreServices 350 | * libz 351 | 4. 导入 `YYImage.h`。 352 | 5. 注意:如果你需要支持 WebP,可以将 `Vendor/WebP.framework`(静态库) 加入你的工程。 353 | 354 | 常见问题 355 | ============== 356 | _Q: 为什么我不能显示 WebP 图片?_ 357 | 358 | A: 确保 `WebP.framework` 已经被添加到你的工程内了。你可以调用 `YYImageWebPAvailable()` 来检查一下 WebP 组件是否被正确安装。 359 | 360 | _Q: 为什么我不能播放 APNG 动画?_ 361 | 362 | A: 你应该禁用 Build Settings 中的 `Compress PNG Files` 和 `Remove Text Metadata From PNG Files`. 或者你也可以把 APNG 文件的扩展名改为`apng`. 363 | 364 | 文档 365 | ============== 366 | 你可以在 [CocoaDocs](http://cocoadocs.org/docsets/YYImage/) 查看在线 API 文档,也可以用 [appledoc](https://github.com/tomaz/appledoc) 本地生成文档。 367 | 368 | 369 | 系统要求 370 | ============== 371 | 该项目最低支持 `iOS 6.0` 和 `Xcode 7.0`。 372 | 373 | 374 | 许可证 375 | ============== 376 | YYImage 使用 MIT 许可证,详情见 LICENSE 文件。 377 | 378 | 379 | 相关链接 380 | ============== 381 | [移动端图片格式调研](http://blog.ibireme.com/2015/11/02/mobile_image_benchmark/)
382 | 383 | [iOS 处理图片的一些小 Tip](http://blog.ibireme.com/2015/11/02/ios_image_tips/) 384 | 385 | -------------------------------------------------------------------------------- /Browser/Pods/YYImage/YYImage/YYImageCoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYImageCoder.h 3 | // YYImage 4 | // 5 | // Created by ibireme on 15/5/13. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Image file type. 18 | */ 19 | typedef NS_ENUM(NSUInteger, YYImageType) { 20 | YYImageTypeUnknown = 0, ///< unknown 21 | YYImageTypeJPEG, ///< jpeg, jpg 22 | YYImageTypeJPEG2000, ///< jp2 23 | YYImageTypeTIFF, ///< tiff, tif 24 | YYImageTypeBMP, ///< bmp 25 | YYImageTypeICO, ///< ico 26 | YYImageTypeICNS, ///< icns 27 | YYImageTypeGIF, ///< gif 28 | YYImageTypePNG, ///< png 29 | YYImageTypeWebP, ///< webp 30 | YYImageTypeOther, ///< other image format 31 | }; 32 | 33 | 34 | /** 35 | Dispose method specifies how the area used by the current frame is to be treated 36 | before rendering the next frame on the canvas. 37 | */ 38 | typedef NS_ENUM(NSUInteger, YYImageDisposeMethod) { 39 | 40 | /** 41 | No disposal is done on this frame before rendering the next; the contents 42 | of the canvas are left as is. 43 | */ 44 | YYImageDisposeNone = 0, 45 | 46 | /** 47 | The frame's region of the canvas is to be cleared to fully transparent black 48 | before rendering the next frame. 49 | */ 50 | YYImageDisposeBackground, 51 | 52 | /** 53 | The frame's region of the canvas is to be reverted to the previous contents 54 | before rendering the next frame. 55 | */ 56 | YYImageDisposePrevious, 57 | }; 58 | 59 | /** 60 | Blend operation specifies how transparent pixels of the current frame are 61 | blended with those of the previous canvas. 62 | */ 63 | typedef NS_ENUM(NSUInteger, YYImageBlendOperation) { 64 | 65 | /** 66 | All color components of the frame, including alpha, overwrite the current 67 | contents of the frame's canvas region. 68 | */ 69 | YYImageBlendNone = 0, 70 | 71 | /** 72 | The frame should be composited onto the output buffer based on its alpha. 73 | */ 74 | YYImageBlendOver, 75 | }; 76 | 77 | /** 78 | An image frame object. 79 | */ 80 | @interface YYImageFrame : NSObject 81 | @property (nonatomic) NSUInteger index; ///< Frame index (zero based) 82 | @property (nonatomic) NSUInteger width; ///< Frame width 83 | @property (nonatomic) NSUInteger height; ///< Frame height 84 | @property (nonatomic) NSUInteger offsetX; ///< Frame origin.x in canvas (left-bottom based) 85 | @property (nonatomic) NSUInteger offsetY; ///< Frame origin.y in canvas (left-bottom based) 86 | @property (nonatomic) NSTimeInterval duration; ///< Frame duration in seconds 87 | @property (nonatomic) YYImageDisposeMethod dispose; ///< Frame dispose method. 88 | @property (nonatomic) YYImageBlendOperation blend; ///< Frame blend operation. 89 | @property (nullable, nonatomic, strong) UIImage *image; ///< The image. 90 | + (instancetype)frameWithImage:(UIImage *)image; 91 | @end 92 | 93 | 94 | #pragma mark - Decoder 95 | 96 | /** 97 | An image decoder to decode image data. 98 | 99 | @discussion This class supports decoding animated WebP, APNG, GIF and system 100 | image format such as PNG, JPG, JP2, BMP, TIFF, PIC, ICNS and ICO. It can be used 101 | to decode complete image data, or to decode incremental image data during image 102 | download. This class is thread-safe. 103 | 104 | Example: 105 | 106 | // Decode single image: 107 | NSData *data = [NSData dataWithContentOfFile:@"/tmp/image.webp"]; 108 | YYImageDecoder *decoder = [YYImageDecoder decoderWithData:data scale:2.0]; 109 | UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image; 110 | 111 | // Decode image during download: 112 | NSMutableData *data = [NSMutableData new]; 113 | YYImageDecoder *decoder = [[YYImageDecoder alloc] initWithScale:2.0]; 114 | while(newDataArrived) { 115 | [data appendData:newData]; 116 | [decoder updateData:data final:NO]; 117 | if (decoder.frameCount > 0) { 118 | UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image; 119 | // progressive display... 120 | } 121 | } 122 | [decoder updateData:data final:YES]; 123 | UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image; 124 | // final display... 125 | 126 | */ 127 | @interface YYImageDecoder : NSObject 128 | 129 | @property (nullable, nonatomic, readonly) NSData *data; ///< Image data. 130 | @property (nonatomic, readonly) YYImageType type; ///< Image data type. 131 | @property (nonatomic, readonly) CGFloat scale; ///< Image scale. 132 | @property (nonatomic, readonly) NSUInteger frameCount; ///< Image frame count. 133 | @property (nonatomic, readonly) NSUInteger loopCount; ///< Image loop count, 0 means infinite. 134 | @property (nonatomic, readonly) NSUInteger width; ///< Image canvas width. 135 | @property (nonatomic, readonly) NSUInteger height; ///< Image canvas height. 136 | @property (nonatomic, readonly, getter=isFinalized) BOOL finalized; 137 | 138 | /** 139 | Creates an image decoder. 140 | 141 | @param scale Image's scale. 142 | @return An image decoder. 143 | */ 144 | - (instancetype)initWithScale:(CGFloat)scale NS_DESIGNATED_INITIALIZER; 145 | 146 | /** 147 | Updates the incremental image with new data. 148 | 149 | @discussion You can use this method to decode progressive/interlaced/baseline 150 | image when you do not have the complete image data. The `data` was retained by 151 | decoder, you should not modify the data in other thread during decoding. 152 | 153 | @param data The data to add to the image decoder. Each time you call this 154 | function, the 'data' parameter must contain all of the image file data 155 | accumulated so far. 156 | 157 | @param final A value that specifies whether the data is the final set. 158 | Pass YES if it is, NO otherwise. When the data is already finalized, you can 159 | not update the data anymore. 160 | 161 | @return Whether succeed. 162 | */ 163 | - (BOOL)updateData:(nullable NSData *)data final:(BOOL)final; 164 | 165 | /** 166 | Convenience method to create a decoder with specified data. 167 | @param data Image data. 168 | @param scale Image's scale. 169 | @return A new decoder, or nil if an error occurs. 170 | */ 171 | + (nullable instancetype)decoderWithData:(NSData *)data scale:(CGFloat)scale; 172 | 173 | /** 174 | Decodes and returns a frame from a specified index. 175 | @param index Frame image index (zero-based). 176 | @param decodeForDisplay Whether decode the image to memory bitmap for display. 177 | If NO, it will try to returns the original frame data without blend. 178 | @return A new frame with image, or nil if an error occurs. 179 | */ 180 | - (nullable YYImageFrame *)frameAtIndex:(NSUInteger)index decodeForDisplay:(BOOL)decodeForDisplay; 181 | 182 | /** 183 | Returns the frame duration from a specified index. 184 | @param index Frame image (zero-based). 185 | @return Duration in seconds. 186 | */ 187 | - (NSTimeInterval)frameDurationAtIndex:(NSUInteger)index; 188 | 189 | /** 190 | Returns the frame's properties. See "CGImageProperties.h" in ImageIO.framework 191 | for more information. 192 | 193 | @param index Frame image index (zero-based). 194 | @return The ImageIO frame property. 195 | */ 196 | - (nullable NSDictionary *)framePropertiesAtIndex:(NSUInteger)index; 197 | 198 | /** 199 | Returns the image's properties. See "CGImageProperties.h" in ImageIO.framework 200 | for more information. 201 | */ 202 | - (nullable NSDictionary *)imageProperties; 203 | 204 | @end 205 | 206 | 207 | 208 | #pragma mark - Encoder 209 | 210 | /** 211 | An image encoder to encode image to data. 212 | 213 | @discussion It supports encoding single frame image with the type defined in YYImageType. 214 | It also supports encoding multi-frame image with GIF, APNG and WebP. 215 | 216 | Example: 217 | 218 | YYImageEncoder *jpegEncoder = [[YYImageEncoder alloc] initWithType:YYImageTypeJPEG]; 219 | jpegEncoder.quality = 0.9; 220 | [jpegEncoder addImage:image duration:0]; 221 | NSData jpegData = [jpegEncoder encode]; 222 | 223 | YYImageEncoder *gifEncoder = [[YYImageEncoder alloc] initWithType:YYImageTypeGIF]; 224 | gifEncoder.loopCount = 5; 225 | [gifEncoder addImage:image0 duration:0.1]; 226 | [gifEncoder addImage:image1 duration:0.15]; 227 | [gifEncoder addImage:image2 duration:0.2]; 228 | NSData gifData = [gifEncoder encode]; 229 | 230 | @warning It just pack the images together when encoding multi-frame image. If you 231 | want to reduce the image file size, try imagemagick/ffmpeg for GIF and WebP, 232 | and apngasm for APNG. 233 | */ 234 | @interface YYImageEncoder : NSObject 235 | 236 | @property (nonatomic, readonly) YYImageType type; ///< Image type. 237 | @property (nonatomic) NSUInteger loopCount; ///< Loop count, 0 means infinit, only available for GIF/APNG/WebP. 238 | @property (nonatomic) BOOL lossless; ///< Lossless, only available for WebP. 239 | @property (nonatomic) CGFloat quality; ///< Compress quality, 0.0~1.0, only available for JPG/JP2/WebP. 240 | 241 | - (instancetype)init UNAVAILABLE_ATTRIBUTE; 242 | + (instancetype)new UNAVAILABLE_ATTRIBUTE; 243 | 244 | /** 245 | Create an image encoder with a specified type. 246 | @param type Image type. 247 | @return A new encoder, or nil if an error occurs. 248 | */ 249 | - (nullable instancetype)initWithType:(YYImageType)type NS_DESIGNATED_INITIALIZER; 250 | 251 | /** 252 | Add an image to encoder. 253 | @param image Image. 254 | @param duration Image duration for animation. Pass 0 to ignore this parameter. 255 | */ 256 | - (void)addImage:(UIImage *)image duration:(NSTimeInterval)duration; 257 | 258 | /** 259 | Add an image with image data to encoder. 260 | @param data Image data. 261 | @param duration Image duration for animation. Pass 0 to ignore this parameter. 262 | */ 263 | - (void)addImageWithData:(NSData *)data duration:(NSTimeInterval)duration; 264 | 265 | /** 266 | Add an image from a file path to encoder. 267 | @param image Image file path. 268 | @param duration Image duration for animation. Pass 0 to ignore this parameter. 269 | */ 270 | - (void)addImageWithFile:(NSString *)path duration:(NSTimeInterval)duration; 271 | 272 | /** 273 | Encodes the image and returns the image data. 274 | @return The image data, or nil if an error occurs. 275 | */ 276 | - (nullable NSData *)encode; 277 | 278 | /** 279 | Encodes the image to a file. 280 | @param path The file path (overwrite if exist). 281 | @return Whether succeed. 282 | */ 283 | - (BOOL)encodeToFile:(NSString *)path; 284 | 285 | /** 286 | Convenience method to encode single frame image. 287 | @param image The image. 288 | @param type The destination image type. 289 | @param quality Image quality, 0.0~1.0. 290 | @return The image data, or nil if an error occurs. 291 | */ 292 | + (nullable NSData *)encodeImage:(UIImage *)image type:(YYImageType)type quality:(CGFloat)quality; 293 | 294 | /** 295 | Convenience method to encode image from a decoder. 296 | @param decoder The image decoder. 297 | @param type The destination image type; 298 | @param quality Image quality, 0.0~1.0. 299 | @return The image data, or nil if an error occurs. 300 | */ 301 | + (nullable NSData *)encodeImageWithDecoder:(YYImageDecoder *)decoder type:(YYImageType)type quality:(CGFloat)quality; 302 | 303 | @end 304 | 305 | 306 | #pragma mark - UIImage 307 | 308 | @interface UIImage (YYImageCoder) 309 | 310 | /** 311 | Decompress this image to bitmap, so when the image is displayed on screen, 312 | the main thread won't be blocked by additional decode. If the image has already 313 | been decoded or unable to decode, it just returns itself. 314 | 315 | @return an image decoded, or just return itself if no needed. 316 | @see yy_isDecodedForDisplay 317 | */ 318 | - (instancetype)yy_imageByDecoded; 319 | 320 | /** 321 | Wherher the image can be display on screen without additional decoding. 322 | @warning It just a hint for your code, change it has no other effect. 323 | */ 324 | @property (nonatomic) BOOL yy_isDecodedForDisplay; 325 | 326 | /** 327 | Saves this image to iOS Photos Album. 328 | 329 | @discussion This method attempts to save the original data to album if the 330 | image is created from an animated GIF/APNG, otherwise, it will save the image 331 | as JPEG or PNG (based on the alpha information). 332 | 333 | @param completionBlock The block invoked (in main thread) after the save operation completes. 334 | assetURL: An URL that identifies the saved image file. If the image is not saved, assetURL is nil. 335 | error: If the image is not saved, an error object that describes the reason for failure, otherwise nil. 336 | */ 337 | - (void)yy_saveToAlbumWithCompletionBlock:(nullable void(^)(NSURL * _Nullable assetURL, NSError * _Nullable error))completionBlock; 338 | 339 | /** 340 | Return a 'best' data representation for this image. 341 | 342 | @discussion The convertion based on these rule: 343 | 1. If the image is created from an animated GIF/APNG/WebP, it returns the original data. 344 | 2. It returns PNG or JPEG(0.9) representation based on the alpha information. 345 | 346 | @return Image data, or nil if an error occurs. 347 | */ 348 | - (nullable NSData *)yy_imageDataRepresentation; 349 | 350 | @end 351 | 352 | 353 | 354 | #pragma mark - Helper 355 | 356 | /// Detect a data's image type by reading the data's header 16 bytes (very fast). 357 | CG_EXTERN YYImageType YYImageDetectType(CFDataRef data); 358 | 359 | /// Convert YYImageType to UTI (such as kUTTypeJPEG). 360 | CG_EXTERN CFStringRef _Nullable YYImageTypeToUTType(YYImageType type); 361 | 362 | /// Convert UTI (such as kUTTypeJPEG) to YYImageType. 363 | CG_EXTERN YYImageType YYImageTypeFromUTType(CFStringRef uti); 364 | 365 | /// Get image type's file extension (such as @"jpg"). 366 | CG_EXTERN NSString *_Nullable YYImageTypeGetExtension(YYImageType type); 367 | 368 | 369 | 370 | /// Returns the shared DeviceRGB color space. 371 | CG_EXTERN CGColorSpaceRef YYCGColorSpaceGetDeviceRGB(); 372 | 373 | /// Returns the shared DeviceGray color space. 374 | CG_EXTERN CGColorSpaceRef YYCGColorSpaceGetDeviceGray(); 375 | 376 | /// Returns whether a color space is DeviceRGB. 377 | CG_EXTERN BOOL YYCGColorSpaceIsDeviceRGB(CGColorSpaceRef space); 378 | 379 | /// Returns whether a color space is DeviceGray. 380 | CG_EXTERN BOOL YYCGColorSpaceIsDeviceGray(CGColorSpaceRef space); 381 | 382 | 383 | 384 | /// Convert EXIF orientation value to UIImageOrientation. 385 | CG_EXTERN UIImageOrientation YYUIImageOrientationFromEXIFValue(NSInteger value); 386 | 387 | /// Convert UIImageOrientation to EXIF orientation value. 388 | CG_EXTERN NSInteger YYUIImageOrientationToEXIFValue(UIImageOrientation orientation); 389 | 390 | 391 | 392 | /** 393 | Create a decoded image. 394 | 395 | @discussion If the source image is created from a compressed image data (such as 396 | PNG or JPEG), you can use this method to decode the image. After decoded, you can 397 | access the decoded bytes with CGImageGetDataProvider() and CGDataProviderCopyData() 398 | without additional decode process. If the image has already decoded, this method 399 | just copy the decoded bytes to the new image. 400 | 401 | @param imageRef The source image. 402 | @param decodeForDisplay If YES, this method will decode the image and convert 403 | it to BGRA8888 (premultiplied) or BGRX8888 format for CALayer display. 404 | 405 | @return A decoded image, or NULL if an error occurs. 406 | */ 407 | CG_EXTERN CGImageRef _Nullable YYCGImageCreateDecodedCopy(CGImageRef imageRef, BOOL decodeForDisplay); 408 | 409 | /** 410 | Create an image copy with an orientation. 411 | 412 | @param imageRef Source image 413 | @param orientation Image orientation which will applied to the image. 414 | @param destBitmapInfo Destimation image bitmap, only support 32bit format (such as ARGB8888). 415 | @return A new image, or NULL if an error occurs. 416 | */ 417 | CG_EXTERN CGImageRef _Nullable YYCGImageCreateCopyWithOrientation(CGImageRef imageRef, 418 | UIImageOrientation orientation, 419 | CGBitmapInfo destBitmapInfo); 420 | 421 | /** 422 | Create an image copy with CGAffineTransform. 423 | 424 | @param imageRef Source image. 425 | @param transform Transform applied to image (left-bottom based coordinate system). 426 | @param destSize Destination image size 427 | @param destBitmapInfo Destimation image bitmap, only support 32bit format (such as ARGB8888). 428 | @return A new image, or NULL if an error occurs. 429 | */ 430 | CG_EXTERN CGImageRef _Nullable YYCGImageCreateAffineTransformCopy(CGImageRef imageRef, 431 | CGAffineTransform transform, 432 | CGSize destSize, 433 | CGBitmapInfo destBitmapInfo); 434 | 435 | /** 436 | Encode an image to data with CGImageDestination. 437 | 438 | @param imageRef The image. 439 | @param type The image destination data type. 440 | @param quality The quality (0.0~1.0) 441 | @return A new image data, or nil if an error occurs. 442 | */ 443 | CG_EXTERN CFDataRef _Nullable YYCGImageCreateEncodedData(CGImageRef imageRef, YYImageType type, CGFloat quality); 444 | 445 | 446 | /** 447 | Whether WebP is available in YYImage. 448 | */ 449 | CG_EXTERN BOOL YYImageWebPAvailable(); 450 | 451 | /** 452 | Get a webp image frame count; 453 | 454 | @param webpData WebP data. 455 | @return Image frame count, or 0 if an error occurs. 456 | */ 457 | CG_EXTERN NSUInteger YYImageGetWebPFrameCount(CFDataRef webpData); 458 | 459 | /** 460 | Decode an image from WebP data, returns NULL if an error occurs. 461 | 462 | @param webpData The WebP data. 463 | @param decodeForDisplay If YES, this method will decode the image and convert it 464 | to BGRA8888 (premultiplied) format for CALayer display. 465 | @param useThreads YES to enable multi-thread decode. 466 | (speed up, but cost more CPU) 467 | @param bypassFiltering YES to skip the in-loop filtering. 468 | (speed up, but may lose some smooth) 469 | @param noFancyUpsampling YES to use faster pointwise upsampler. 470 | (speed down, and may lose some details). 471 | @return The decoded image, or NULL if an error occurs. 472 | */ 473 | CG_EXTERN CGImageRef _Nullable YYCGImageCreateWithWebPData(CFDataRef webpData, 474 | BOOL decodeForDisplay, 475 | BOOL useThreads, 476 | BOOL bypassFiltering, 477 | BOOL noFancyUpsampling); 478 | 479 | typedef NS_ENUM(NSUInteger, YYImagePreset) { 480 | YYImagePresetDefault = 0, ///< default preset. 481 | YYImagePresetPicture, ///< digital picture, like portrait, inner shot 482 | YYImagePresetPhoto, ///< outdoor photograph, with natural lighting 483 | YYImagePresetDrawing, ///< hand or line drawing, with high-contrast details 484 | YYImagePresetIcon, ///< small-sized colorful images 485 | YYImagePresetText ///< text-like 486 | }; 487 | 488 | /** 489 | Encode a CGImage to WebP data 490 | 491 | @param imageRef image 492 | @param lossless YES=lossless (similar to PNG), NO=lossy (similar to JPEG) 493 | @param quality 0.0~1.0 (0=smallest file, 1.0=biggest file) 494 | For lossless image, try the value near 1.0; for lossy, try the value near 0.8. 495 | @param compressLevel 0~6 (0=fast, 6=slower-better). Default is 4. 496 | @param preset Preset for different image type, default is YYImagePresetDefault. 497 | @return WebP data, or nil if an error occurs. 498 | */ 499 | CG_EXTERN CFDataRef _Nullable YYCGImageCreateEncodedWebPData(CGImageRef imageRef, 500 | BOOL lossless, 501 | CGFloat quality, 502 | int compressLevel, 503 | YYImagePreset preset); 504 | 505 | NS_ASSUME_NONNULL_END 506 | --------------------------------------------------------------------------------