├── .gitignore ├── DraggableFloatingViewController.podspec ├── LICENSE.md ├── README.md ├── Screenshot.gif ├── Screenshot2.gif ├── YouTubeDraggableVideo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── YouTubeDraggableVideo.xccheckout │ └── xcuserdata │ │ ├── Takuya.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ │ └── sandeepmukherjee.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── Takuya.xcuserdatad │ └── xcschemes │ │ ├── YouTubeDraggableVideo.xcscheme │ │ └── xcschememanagement.plist │ └── sandeepmukherjee.xcuserdatad │ └── xcschemes │ ├── YouTubeDraggableVideo.xcscheme │ └── xcschememanagement.plist ├── YouTubeDraggableVideo ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ └── LaunchScreen.xib ├── Demo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── AppDelegate.swift │ ├── FirstViewController.swift │ ├── NSTimerHelper.swift │ ├── SecondViewController.swift │ ├── VideoDetailViewController.swift │ └── YouTubeDraggableVideo-Bridging-Header.h ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── DownArrow.imageset │ │ ├── Contents.json │ │ ├── DownArrow~iPhone-667h@2x.png │ │ ├── DownArrow~iPhone-736h@3x.png │ │ └── DownArrow~iPhone.png │ └── test.imageset │ │ ├── Contents.json │ │ └── test.jpg ├── Info.plist ├── Source │ ├── DraggableFloatingViewController.h │ └── DraggableFloatingViewController.m ├── main.m ├── profilePhoto.png └── sampleMovies │ ├── sample.mp4 │ └── test.mp4 └── YouTubeDraggableVideoTests ├── Info.plist └── YouTubeDraggableVideoTests.m /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.xcbkptlist 3 | -------------------------------------------------------------------------------- /DraggableFloatingViewController.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint DraggableFloatingViewController.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 = "DraggableFloatingViewController" 19 | # s.version = "v1.0" 20 | s.summary = "A short description of DraggableFloatingViewController." 21 | 22 | s.description = <<-DESC 23 | A longer description of DraggableFloatingViewController in Markdown format. 24 | 25 | * Think: Why did you write this? What is the focus? What does it do? 26 | * CocoaPods will be using this to generate tags, and improve search results. 27 | * Try to keep it short, snappy and to the point. 28 | * Finally, don't worry about the indent, CocoaPods strips it! 29 | DESC 30 | 31 | s.homepage = "http://github.com/entotsu/DraggableFloatingViewController" 32 | # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 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 = "MIT (example)" 43 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" } 44 | 45 | 46 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 47 | # 48 | # Specify the authors of the library, with email addresses. Email addresses 49 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 50 | # accepts just a name if you'd rather not provide an email address. 51 | # 52 | # Specify a social_media_url where others can refer to, for example a twitter 53 | # profile URL. 54 | # 55 | 56 | s.author = { "Takuya.Okamoto" => "blackn.red42@gmail.com" } 57 | # Or just: s.author = "Takuya.Okamoto" 58 | # s.authors = { "Takuya.Okamoto" => "blackn.red42@gmail.com" } 59 | # s.social_media_url = "http://twitter.com/Takuya.Okamoto" 60 | 61 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 62 | # 63 | # If this Pod runs only on iOS or OS X, then specify the platform and 64 | # the deployment target. You can optionally include the target after the platform. 65 | # 66 | 67 | # s.platform = :ios 68 | s.platform = :ios, "6.0" 69 | 70 | # When using multiple platforms 71 | # s.ios.deployment_target = "5.0" 72 | # s.osx.deployment_target = "10.7" 73 | 74 | 75 | s.source = { :git => "http://github.com/entotsu/DraggableFloatingViewController.git", :tag => "v1.0" } 76 | s.source_files = "Classes", "YouTubeDraggableVideo/Source/*.{h,m}" 77 | # s.exclude_files = "Classes/Exclude" 78 | 79 | # s.public_header_files = "Classes/**/*.h" 80 | 81 | 82 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 83 | # 84 | # A list of resources included with the Pod. These are copied into the 85 | # target bundle with a build phase script. Anything else will be cleaned. 86 | # You can preserve files from being cleaned, please don't preserve 87 | # non-essential files like tests, examples and documentation. 88 | # 89 | 90 | # s.resource = "icon.png" 91 | # s.resources = "Resources/*.png" 92 | 93 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 94 | 95 | 96 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 97 | # 98 | # Link your library with frameworks, or libraries. Libraries do not include 99 | # the lib prefix of their name. 100 | # 101 | 102 | # s.framework = "SomeFramework" 103 | # s.frameworks = "SomeFramework", "AnotherFramework" 104 | 105 | # s.library = "iconv" 106 | # s.libraries = "iconv", "xml2" 107 | 108 | 109 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 110 | # 111 | # If your library depends on compiler flags you can set them in the xcconfig hash 112 | # where they will only apply to your library. If you depend on other Podspecs 113 | # you can include multiple dependencies to ensure it works. 114 | 115 | # s.requires_arc = true 116 | 117 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 118 | # s.dependency "JSONKit", "~> 1.4" 119 | 120 | end 121 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Sandeep Mukherjee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # DraggableFloatingViewController 5 | 6 | ### Like YouTube app 7 | 8 | DraggableFloatingViewController allows you to play videos on a floating mini window at the bottom of your screen from sites like YouTube, Vimeo & Facebook or custom video , yes you have to prepare your video view for that. 9 | 10 | 11 | How it works 12 | ------------ 13 | The view will animate the view just like Youtube mobile app, while tapping on video a UIView pops up from right corner of the screen and the view can be dragged to right corner through Pan Gesture and more features are there as Youtube iOS app 14 | 15 | 16 | Screenshot 17 | ------------ 18 | ![Output sample](https://github.com/entotsu/DraggableFloatingViewController/raw/master/Screenshot2.gif) 19 | 20 | 21 | 22 | # Usage 23 | 24 | 25 | ## extend this class 26 | 27 | #### set your video view in "viewDidLoad" of subclass 28 | 29 | ```swift 30 | override func viewDidLoad() { 31 | 32 | self.setupViewsWithVideoView(yourMoivePlayer.view, //UIView 33 | videoViewHeight: yourPlayerHeight, //CGFloat 34 | minimizeButton: yourButton //UIButton 35 | ) 36 | 37 | // add your view to bodyView 38 | self.bodyView.addSubview(yourView) 39 | } 40 | ``` 41 | 42 | ## in parent view controller 43 | 44 | ### show 45 | 46 | ```swift 47 | func showSecondController() { 48 | removeDraggableFloatingViewController() 49 | self.videoViewController = VideoDetailViewController() 50 | self.videoViewController.delegate = self 51 | self.videoViewController.showVideoViewControllerOnParentVC(self) 52 | } 53 | ``` 54 | 55 | 56 | ### dismiss 57 | 58 | ```swift 59 | func removeDraggableFloatingViewController() { 60 | if self.videoViewController != nil { 61 | self.videoViewController.removeAllViews() 62 | self.videoViewController = nil 63 | } 64 | } 65 | ``` 66 | 67 | 68 | 69 | 70 | -------------------------------------------------- 71 | 72 | 73 | # Please edit "info.plist" 74 | To disable swipe down gesture of Notification Center, you need to edit "info.plist" to hide status bar. 75 | http://stackoverflow.com/questions/18059703/cannot-hide-status-bar-in-ios7 76 | ![editInfoPlist](http://i.stack.imgur.com/dM32P.png "editInfoPlist") 77 | 78 | 79 | -------------------------------------------------- 80 | 81 | -------------------------------------------------- 82 | 83 | -------------------------------------------------- 84 | # please override if you want 85 | ```swift 86 | override func didExpand() { 87 | showVideoControl() 88 | } 89 | override func didMinimize() { 90 | hideVideoControl() 91 | } 92 | ``` 93 | 94 | ------------------------------------------------------- 95 | 96 | ## Please see demo app 97 | If you want to use this, you have to check this demo app. 98 | 99 | 100 | -------------------------------------------------------------------------------- /Screenshot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takuoka/DraggableFloatingViewController/f588f100aaf033054386b1f8c0748e64e65b59e6/Screenshot.gif -------------------------------------------------------------------------------- /Screenshot2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takuoka/DraggableFloatingViewController/f588f100aaf033054386b1f8c0748e64e65b59e6/Screenshot2.gif -------------------------------------------------------------------------------- /YouTubeDraggableVideo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 253F2E2D1A7F5907007FD89E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 253F2E2C1A7F5907007FD89E /* main.m */; }; 11 | 253F2E381A7F5907007FD89E /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 253F2E371A7F5907007FD89E /* Images.xcassets */; }; 12 | 253F2E3B1A7F5907007FD89E /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 253F2E391A7F5907007FD89E /* LaunchScreen.xib */; }; 13 | 253F2E471A7F5907007FD89E /* YouTubeDraggableVideoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 253F2E461A7F5907007FD89E /* YouTubeDraggableVideoTests.m */; }; 14 | 253F2E5C1A7F6027007FD89E /* profilePhoto.png in Resources */ = {isa = PBXBuildFile; fileRef = 253F2E5B1A7F6027007FD89E /* profilePhoto.png */; }; 15 | B01147D81B33B30300D4CEB6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01147D71B33B30300D4CEB6 /* AppDelegate.swift */; }; 16 | B01147DA1B33D56900D4CEB6 /* SecondViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01147D91B33D56900D4CEB6 /* SecondViewController.swift */; }; 17 | B01147DC1B33DAC700D4CEB6 /* NSTimerHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01147DB1B33DAC700D4CEB6 /* NSTimerHelper.swift */; }; 18 | B0125D7B1B1BD88B005FF053 /* FirstViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0125D781B1BD88B005FF053 /* FirstViewController.swift */; }; 19 | B0125D7C1B1BD88B005FF053 /* VideoDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0125D791B1BD88B005FF053 /* VideoDetailViewController.swift */; }; 20 | B0125D801B1BD897005FF053 /* DraggableFloatingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0125D7F1B1BD897005FF053 /* DraggableFloatingViewController.m */; }; 21 | B04E93C31B168CEB004384E8 /* test.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = B04E93C21B168CEB004384E8 /* test.mp4 */; }; 22 | B0B076F01B33F36A004A027A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B0B076EF1B33F36A004A027A /* AppDelegate.m */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXContainerItemProxy section */ 26 | 253F2E411A7F5907007FD89E /* PBXContainerItemProxy */ = { 27 | isa = PBXContainerItemProxy; 28 | containerPortal = 253F2E1F1A7F5907007FD89E /* Project object */; 29 | proxyType = 1; 30 | remoteGlobalIDString = 253F2E261A7F5907007FD89E; 31 | remoteInfo = YouTubeDraggableVideo; 32 | }; 33 | /* End PBXContainerItemProxy section */ 34 | 35 | /* Begin PBXFileReference section */ 36 | 253F2E271A7F5907007FD89E /* YouTubeDraggableVideo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = YouTubeDraggableVideo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 253F2E2B1A7F5907007FD89E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | 253F2E2C1A7F5907007FD89E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 39 | 253F2E371A7F5907007FD89E /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 40 | 253F2E3A1A7F5907007FD89E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 41 | 253F2E401A7F5907007FD89E /* YouTubeDraggableVideoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = YouTubeDraggableVideoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 253F2E451A7F5907007FD89E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 43 | 253F2E461A7F5907007FD89E /* YouTubeDraggableVideoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = YouTubeDraggableVideoTests.m; sourceTree = ""; }; 44 | 253F2E5B1A7F6027007FD89E /* profilePhoto.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = profilePhoto.png; sourceTree = ""; }; 45 | B01147D71B33B30300D4CEB6 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 46 | B01147D91B33D56900D4CEB6 /* SecondViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecondViewController.swift; sourceTree = ""; }; 47 | B01147DB1B33DAC700D4CEB6 /* NSTimerHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTimerHelper.swift; sourceTree = ""; }; 48 | B0125D781B1BD88B005FF053 /* FirstViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FirstViewController.swift; sourceTree = ""; }; 49 | B0125D791B1BD88B005FF053 /* VideoDetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoDetailViewController.swift; sourceTree = ""; }; 50 | B0125D7A1B1BD88B005FF053 /* YouTubeDraggableVideo-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "YouTubeDraggableVideo-Bridging-Header.h"; sourceTree = ""; }; 51 | B0125D7E1B1BD897005FF053 /* DraggableFloatingViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DraggableFloatingViewController.h; sourceTree = ""; }; 52 | B0125D7F1B1BD897005FF053 /* DraggableFloatingViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DraggableFloatingViewController.m; sourceTree = ""; }; 53 | B04E93C21B168CEB004384E8 /* test.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = test.mp4; sourceTree = ""; }; 54 | B04E93C41B168D16004384E8 /* sample.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = sample.mp4; sourceTree = ""; }; 55 | B0B076EE1B33F36A004A027A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 56 | B0B076EF1B33F36A004A027A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 57 | /* End PBXFileReference section */ 58 | 59 | /* Begin PBXFrameworksBuildPhase section */ 60 | 253F2E241A7F5907007FD89E /* Frameworks */ = { 61 | isa = PBXFrameworksBuildPhase; 62 | buildActionMask = 2147483647; 63 | files = ( 64 | ); 65 | runOnlyForDeploymentPostprocessing = 0; 66 | }; 67 | 253F2E3D1A7F5907007FD89E /* Frameworks */ = { 68 | isa = PBXFrameworksBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | ); 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | /* End PBXFrameworksBuildPhase section */ 75 | 76 | /* Begin PBXGroup section */ 77 | 253F2E1E1A7F5907007FD89E = { 78 | isa = PBXGroup; 79 | children = ( 80 | 253F2E291A7F5907007FD89E /* YouTubeDraggableVideo */, 81 | 253F2E431A7F5907007FD89E /* YouTubeDraggableVideoTests */, 82 | 253F2E281A7F5907007FD89E /* Products */, 83 | ); 84 | sourceTree = ""; 85 | }; 86 | 253F2E281A7F5907007FD89E /* Products */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 253F2E271A7F5907007FD89E /* YouTubeDraggableVideo.app */, 90 | 253F2E401A7F5907007FD89E /* YouTubeDraggableVideoTests.xctest */, 91 | ); 92 | name = Products; 93 | sourceTree = ""; 94 | }; 95 | 253F2E291A7F5907007FD89E /* YouTubeDraggableVideo */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | B0125D7D1B1BD897005FF053 /* Source */, 99 | B0125D771B1BD88B005FF053 /* Demo */, 100 | 253F2E5A1A7F5A68007FD89E /* AppDelegate */, 101 | 253F2E371A7F5907007FD89E /* Images.xcassets */, 102 | 253F2E391A7F5907007FD89E /* LaunchScreen.xib */, 103 | B04E93C11B168CEB004384E8 /* sampleMovies */, 104 | 253F2E2A1A7F5907007FD89E /* Supporting Files */, 105 | ); 106 | path = YouTubeDraggableVideo; 107 | sourceTree = ""; 108 | }; 109 | 253F2E2A1A7F5907007FD89E /* Supporting Files */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 253F2E5B1A7F6027007FD89E /* profilePhoto.png */, 113 | 253F2E2B1A7F5907007FD89E /* Info.plist */, 114 | 253F2E2C1A7F5907007FD89E /* main.m */, 115 | ); 116 | name = "Supporting Files"; 117 | sourceTree = ""; 118 | }; 119 | 253F2E431A7F5907007FD89E /* YouTubeDraggableVideoTests */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 253F2E461A7F5907007FD89E /* YouTubeDraggableVideoTests.m */, 123 | 253F2E441A7F5907007FD89E /* Supporting Files */, 124 | ); 125 | path = YouTubeDraggableVideoTests; 126 | sourceTree = ""; 127 | }; 128 | 253F2E441A7F5907007FD89E /* Supporting Files */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 253F2E451A7F5907007FD89E /* Info.plist */, 132 | ); 133 | name = "Supporting Files"; 134 | sourceTree = ""; 135 | }; 136 | 253F2E5A1A7F5A68007FD89E /* AppDelegate */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | ); 140 | path = AppDelegate; 141 | sourceTree = ""; 142 | }; 143 | B0125D771B1BD88B005FF053 /* Demo */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | B0125D791B1BD88B005FF053 /* VideoDetailViewController.swift */, 147 | B0125D781B1BD88B005FF053 /* FirstViewController.swift */, 148 | B01147D91B33D56900D4CEB6 /* SecondViewController.swift */, 149 | B01147DB1B33DAC700D4CEB6 /* NSTimerHelper.swift */, 150 | B0B076EE1B33F36A004A027A /* AppDelegate.h */, 151 | B0B076EF1B33F36A004A027A /* AppDelegate.m */, 152 | B01147D71B33B30300D4CEB6 /* AppDelegate.swift */, 153 | B0125D7A1B1BD88B005FF053 /* YouTubeDraggableVideo-Bridging-Header.h */, 154 | ); 155 | path = Demo; 156 | sourceTree = ""; 157 | }; 158 | B0125D7D1B1BD897005FF053 /* Source */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | B0125D7E1B1BD897005FF053 /* DraggableFloatingViewController.h */, 162 | B0125D7F1B1BD897005FF053 /* DraggableFloatingViewController.m */, 163 | ); 164 | path = Source; 165 | sourceTree = ""; 166 | }; 167 | B04E93C11B168CEB004384E8 /* sampleMovies */ = { 168 | isa = PBXGroup; 169 | children = ( 170 | B04E93C41B168D16004384E8 /* sample.mp4 */, 171 | B04E93C21B168CEB004384E8 /* test.mp4 */, 172 | ); 173 | path = sampleMovies; 174 | sourceTree = ""; 175 | }; 176 | /* End PBXGroup section */ 177 | 178 | /* Begin PBXNativeTarget section */ 179 | 253F2E261A7F5907007FD89E /* YouTubeDraggableVideo */ = { 180 | isa = PBXNativeTarget; 181 | buildConfigurationList = 253F2E4A1A7F5907007FD89E /* Build configuration list for PBXNativeTarget "YouTubeDraggableVideo" */; 182 | buildPhases = ( 183 | 253F2E231A7F5907007FD89E /* Sources */, 184 | 253F2E241A7F5907007FD89E /* Frameworks */, 185 | 253F2E251A7F5907007FD89E /* Resources */, 186 | ); 187 | buildRules = ( 188 | ); 189 | dependencies = ( 190 | ); 191 | name = YouTubeDraggableVideo; 192 | productName = YouTubeDraggableVideo; 193 | productReference = 253F2E271A7F5907007FD89E /* YouTubeDraggableVideo.app */; 194 | productType = "com.apple.product-type.application"; 195 | }; 196 | 253F2E3F1A7F5907007FD89E /* YouTubeDraggableVideoTests */ = { 197 | isa = PBXNativeTarget; 198 | buildConfigurationList = 253F2E4D1A7F5907007FD89E /* Build configuration list for PBXNativeTarget "YouTubeDraggableVideoTests" */; 199 | buildPhases = ( 200 | 253F2E3C1A7F5907007FD89E /* Sources */, 201 | 253F2E3D1A7F5907007FD89E /* Frameworks */, 202 | 253F2E3E1A7F5907007FD89E /* Resources */, 203 | ); 204 | buildRules = ( 205 | ); 206 | dependencies = ( 207 | 253F2E421A7F5907007FD89E /* PBXTargetDependency */, 208 | ); 209 | name = YouTubeDraggableVideoTests; 210 | productName = YouTubeDraggableVideoTests; 211 | productReference = 253F2E401A7F5907007FD89E /* YouTubeDraggableVideoTests.xctest */; 212 | productType = "com.apple.product-type.bundle.unit-test"; 213 | }; 214 | /* End PBXNativeTarget section */ 215 | 216 | /* Begin PBXProject section */ 217 | 253F2E1F1A7F5907007FD89E /* Project object */ = { 218 | isa = PBXProject; 219 | attributes = { 220 | LastUpgradeCheck = 0610; 221 | ORGANIZATIONNAME = "Sandeep Mukherjee"; 222 | TargetAttributes = { 223 | 253F2E261A7F5907007FD89E = { 224 | CreatedOnToolsVersion = 6.1; 225 | DevelopmentTeam = 6N9U78BNZR; 226 | }; 227 | 253F2E3F1A7F5907007FD89E = { 228 | CreatedOnToolsVersion = 6.1; 229 | TestTargetID = 253F2E261A7F5907007FD89E; 230 | }; 231 | }; 232 | }; 233 | buildConfigurationList = 253F2E221A7F5907007FD89E /* Build configuration list for PBXProject "YouTubeDraggableVideo" */; 234 | compatibilityVersion = "Xcode 3.2"; 235 | developmentRegion = English; 236 | hasScannedForEncodings = 0; 237 | knownRegions = ( 238 | en, 239 | Base, 240 | ); 241 | mainGroup = 253F2E1E1A7F5907007FD89E; 242 | productRefGroup = 253F2E281A7F5907007FD89E /* Products */; 243 | projectDirPath = ""; 244 | projectRoot = ""; 245 | targets = ( 246 | 253F2E261A7F5907007FD89E /* YouTubeDraggableVideo */, 247 | 253F2E3F1A7F5907007FD89E /* YouTubeDraggableVideoTests */, 248 | ); 249 | }; 250 | /* End PBXProject section */ 251 | 252 | /* Begin PBXResourcesBuildPhase section */ 253 | 253F2E251A7F5907007FD89E /* Resources */ = { 254 | isa = PBXResourcesBuildPhase; 255 | buildActionMask = 2147483647; 256 | files = ( 257 | 253F2E5C1A7F6027007FD89E /* profilePhoto.png in Resources */, 258 | B04E93C31B168CEB004384E8 /* test.mp4 in Resources */, 259 | 253F2E3B1A7F5907007FD89E /* LaunchScreen.xib in Resources */, 260 | 253F2E381A7F5907007FD89E /* Images.xcassets in Resources */, 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | }; 264 | 253F2E3E1A7F5907007FD89E /* Resources */ = { 265 | isa = PBXResourcesBuildPhase; 266 | buildActionMask = 2147483647; 267 | files = ( 268 | ); 269 | runOnlyForDeploymentPostprocessing = 0; 270 | }; 271 | /* End PBXResourcesBuildPhase section */ 272 | 273 | /* Begin PBXSourcesBuildPhase section */ 274 | 253F2E231A7F5907007FD89E /* Sources */ = { 275 | isa = PBXSourcesBuildPhase; 276 | buildActionMask = 2147483647; 277 | files = ( 278 | B01147DA1B33D56900D4CEB6 /* SecondViewController.swift in Sources */, 279 | B01147D81B33B30300D4CEB6 /* AppDelegate.swift in Sources */, 280 | 253F2E2D1A7F5907007FD89E /* main.m in Sources */, 281 | B0125D7C1B1BD88B005FF053 /* VideoDetailViewController.swift in Sources */, 282 | B0B076F01B33F36A004A027A /* AppDelegate.m in Sources */, 283 | B0125D801B1BD897005FF053 /* DraggableFloatingViewController.m in Sources */, 284 | B0125D7B1B1BD88B005FF053 /* FirstViewController.swift in Sources */, 285 | B01147DC1B33DAC700D4CEB6 /* NSTimerHelper.swift in Sources */, 286 | ); 287 | runOnlyForDeploymentPostprocessing = 0; 288 | }; 289 | 253F2E3C1A7F5907007FD89E /* Sources */ = { 290 | isa = PBXSourcesBuildPhase; 291 | buildActionMask = 2147483647; 292 | files = ( 293 | 253F2E471A7F5907007FD89E /* YouTubeDraggableVideoTests.m in Sources */, 294 | ); 295 | runOnlyForDeploymentPostprocessing = 0; 296 | }; 297 | /* End PBXSourcesBuildPhase section */ 298 | 299 | /* Begin PBXTargetDependency section */ 300 | 253F2E421A7F5907007FD89E /* PBXTargetDependency */ = { 301 | isa = PBXTargetDependency; 302 | target = 253F2E261A7F5907007FD89E /* YouTubeDraggableVideo */; 303 | targetProxy = 253F2E411A7F5907007FD89E /* PBXContainerItemProxy */; 304 | }; 305 | /* End PBXTargetDependency section */ 306 | 307 | /* Begin PBXVariantGroup section */ 308 | 253F2E391A7F5907007FD89E /* LaunchScreen.xib */ = { 309 | isa = PBXVariantGroup; 310 | children = ( 311 | 253F2E3A1A7F5907007FD89E /* Base */, 312 | ); 313 | name = LaunchScreen.xib; 314 | sourceTree = ""; 315 | }; 316 | /* End PBXVariantGroup section */ 317 | 318 | /* Begin XCBuildConfiguration section */ 319 | 253F2E481A7F5907007FD89E /* Debug */ = { 320 | isa = XCBuildConfiguration; 321 | buildSettings = { 322 | ALWAYS_SEARCH_USER_PATHS = NO; 323 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 324 | CLANG_CXX_LIBRARY = "libc++"; 325 | CLANG_ENABLE_MODULES = YES; 326 | CLANG_ENABLE_OBJC_ARC = YES; 327 | CLANG_WARN_BOOL_CONVERSION = YES; 328 | CLANG_WARN_CONSTANT_CONVERSION = YES; 329 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 330 | CLANG_WARN_EMPTY_BODY = YES; 331 | CLANG_WARN_ENUM_CONVERSION = YES; 332 | CLANG_WARN_INT_CONVERSION = YES; 333 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 334 | CLANG_WARN_UNREACHABLE_CODE = YES; 335 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 336 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 337 | COPY_PHASE_STRIP = NO; 338 | ENABLE_STRICT_OBJC_MSGSEND = YES; 339 | GCC_C_LANGUAGE_STANDARD = gnu99; 340 | GCC_DYNAMIC_NO_PIC = NO; 341 | GCC_OPTIMIZATION_LEVEL = 0; 342 | GCC_PREPROCESSOR_DEFINITIONS = ( 343 | "DEBUG=1", 344 | "$(inherited)", 345 | ); 346 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 347 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 348 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 349 | GCC_WARN_UNDECLARED_SELECTOR = YES; 350 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 351 | GCC_WARN_UNUSED_FUNCTION = YES; 352 | GCC_WARN_UNUSED_VARIABLE = YES; 353 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 354 | MTL_ENABLE_DEBUG_INFO = YES; 355 | ONLY_ACTIVE_ARCH = YES; 356 | SDKROOT = iphoneos; 357 | }; 358 | name = Debug; 359 | }; 360 | 253F2E491A7F5907007FD89E /* Release */ = { 361 | isa = XCBuildConfiguration; 362 | buildSettings = { 363 | ALWAYS_SEARCH_USER_PATHS = NO; 364 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 365 | CLANG_CXX_LIBRARY = "libc++"; 366 | CLANG_ENABLE_MODULES = YES; 367 | CLANG_ENABLE_OBJC_ARC = YES; 368 | CLANG_WARN_BOOL_CONVERSION = YES; 369 | CLANG_WARN_CONSTANT_CONVERSION = YES; 370 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 371 | CLANG_WARN_EMPTY_BODY = YES; 372 | CLANG_WARN_ENUM_CONVERSION = YES; 373 | CLANG_WARN_INT_CONVERSION = YES; 374 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 375 | CLANG_WARN_UNREACHABLE_CODE = YES; 376 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 377 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 378 | COPY_PHASE_STRIP = YES; 379 | ENABLE_NS_ASSERTIONS = NO; 380 | ENABLE_STRICT_OBJC_MSGSEND = YES; 381 | GCC_C_LANGUAGE_STANDARD = gnu99; 382 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 383 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 384 | GCC_WARN_UNDECLARED_SELECTOR = YES; 385 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 386 | GCC_WARN_UNUSED_FUNCTION = YES; 387 | GCC_WARN_UNUSED_VARIABLE = YES; 388 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 389 | MTL_ENABLE_DEBUG_INFO = NO; 390 | SDKROOT = iphoneos; 391 | VALIDATE_PRODUCT = YES; 392 | }; 393 | name = Release; 394 | }; 395 | 253F2E4B1A7F5907007FD89E /* Debug */ = { 396 | isa = XCBuildConfiguration; 397 | buildSettings = { 398 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 399 | CLANG_ENABLE_MODULES = YES; 400 | CODE_SIGN_IDENTITY = "iPhone Developer"; 401 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 402 | INFOPLIST_FILE = YouTubeDraggableVideo/Info.plist; 403 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 404 | PRODUCT_NAME = "$(TARGET_NAME)"; 405 | PROVISIONING_PROFILE = ""; 406 | SWIFT_OBJC_BRIDGING_HEADER = "YouTubeDraggableVideo/Demo/YouTubeDraggableVideo-Bridging-Header.h"; 407 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 408 | SWIFT_VERSION = 4.0; 409 | }; 410 | name = Debug; 411 | }; 412 | 253F2E4C1A7F5907007FD89E /* Release */ = { 413 | isa = XCBuildConfiguration; 414 | buildSettings = { 415 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 416 | CLANG_ENABLE_MODULES = YES; 417 | CODE_SIGN_IDENTITY = "iPhone Developer"; 418 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 419 | INFOPLIST_FILE = YouTubeDraggableVideo/Info.plist; 420 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 421 | PRODUCT_NAME = "$(TARGET_NAME)"; 422 | PROVISIONING_PROFILE = ""; 423 | SWIFT_OBJC_BRIDGING_HEADER = "YouTubeDraggableVideo/Demo/YouTubeDraggableVideo-Bridging-Header.h"; 424 | SWIFT_VERSION = 4.0; 425 | }; 426 | name = Release; 427 | }; 428 | 253F2E4E1A7F5907007FD89E /* Debug */ = { 429 | isa = XCBuildConfiguration; 430 | buildSettings = { 431 | BUNDLE_LOADER = "$(TEST_HOST)"; 432 | FRAMEWORK_SEARCH_PATHS = ( 433 | "$(SDKROOT)/Developer/Library/Frameworks", 434 | "$(inherited)", 435 | ); 436 | GCC_PREPROCESSOR_DEFINITIONS = ( 437 | "DEBUG=1", 438 | "$(inherited)", 439 | ); 440 | INFOPLIST_FILE = YouTubeDraggableVideoTests/Info.plist; 441 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 442 | PRODUCT_NAME = "$(TARGET_NAME)"; 443 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/YouTubeDraggableVideo.app/YouTubeDraggableVideo"; 444 | }; 445 | name = Debug; 446 | }; 447 | 253F2E4F1A7F5907007FD89E /* Release */ = { 448 | isa = XCBuildConfiguration; 449 | buildSettings = { 450 | BUNDLE_LOADER = "$(TEST_HOST)"; 451 | FRAMEWORK_SEARCH_PATHS = ( 452 | "$(SDKROOT)/Developer/Library/Frameworks", 453 | "$(inherited)", 454 | ); 455 | INFOPLIST_FILE = YouTubeDraggableVideoTests/Info.plist; 456 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 457 | PRODUCT_NAME = "$(TARGET_NAME)"; 458 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/YouTubeDraggableVideo.app/YouTubeDraggableVideo"; 459 | }; 460 | name = Release; 461 | }; 462 | /* End XCBuildConfiguration section */ 463 | 464 | /* Begin XCConfigurationList section */ 465 | 253F2E221A7F5907007FD89E /* Build configuration list for PBXProject "YouTubeDraggableVideo" */ = { 466 | isa = XCConfigurationList; 467 | buildConfigurations = ( 468 | 253F2E481A7F5907007FD89E /* Debug */, 469 | 253F2E491A7F5907007FD89E /* Release */, 470 | ); 471 | defaultConfigurationIsVisible = 0; 472 | defaultConfigurationName = Release; 473 | }; 474 | 253F2E4A1A7F5907007FD89E /* Build configuration list for PBXNativeTarget "YouTubeDraggableVideo" */ = { 475 | isa = XCConfigurationList; 476 | buildConfigurations = ( 477 | 253F2E4B1A7F5907007FD89E /* Debug */, 478 | 253F2E4C1A7F5907007FD89E /* Release */, 479 | ); 480 | defaultConfigurationIsVisible = 0; 481 | defaultConfigurationName = Release; 482 | }; 483 | 253F2E4D1A7F5907007FD89E /* Build configuration list for PBXNativeTarget "YouTubeDraggableVideoTests" */ = { 484 | isa = XCConfigurationList; 485 | buildConfigurations = ( 486 | 253F2E4E1A7F5907007FD89E /* Debug */, 487 | 253F2E4F1A7F5907007FD89E /* Release */, 488 | ); 489 | defaultConfigurationIsVisible = 0; 490 | defaultConfigurationName = Release; 491 | }; 492 | /* End XCConfigurationList section */ 493 | }; 494 | rootObject = 253F2E1F1A7F5907007FD89E /* Project object */; 495 | } 496 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo.xcodeproj/project.xcworkspace/xcshareddata/YouTubeDraggableVideo.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | B696D8E4-3947-4F0E-AE1A-BBE1F3C64F66 9 | IDESourceControlProjectName 10 | YouTubeDraggableVideo 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | C0796BA5C0F25FE444F34B630D9B7AA6F9BEA0B4 14 | https://github.com/entotsu/DraggableYoutubeFloatingVideo.git 15 | 16 | IDESourceControlProjectPath 17 | YouTubeDraggableVideo.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | C0796BA5C0F25FE444F34B630D9B7AA6F9BEA0B4 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/entotsu/DraggableYoutubeFloatingVideo.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | C0796BA5C0F25FE444F34B630D9B7AA6F9BEA0B4 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | C0796BA5C0F25FE444F34B630D9B7AA6F9BEA0B4 36 | IDESourceControlWCCName 37 | DraggableYoutubeFloatingVideo 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo.xcodeproj/project.xcworkspace/xcuserdata/Takuya.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takuoka/DraggableFloatingViewController/f588f100aaf033054386b1f8c0748e64e65b59e6/YouTubeDraggableVideo.xcodeproj/project.xcworkspace/xcuserdata/Takuya.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /YouTubeDraggableVideo.xcodeproj/project.xcworkspace/xcuserdata/sandeepmukherjee.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takuoka/DraggableFloatingViewController/f588f100aaf033054386b1f8c0748e64e65b59e6/YouTubeDraggableVideo.xcodeproj/project.xcworkspace/xcuserdata/sandeepmukherjee.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /YouTubeDraggableVideo.xcodeproj/xcuserdata/Takuya.xcuserdatad/xcschemes/YouTubeDraggableVideo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 94 | 96 | 102 | 103 | 104 | 105 | 107 | 108 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo.xcodeproj/xcuserdata/Takuya.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | YouTubeDraggableVideo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 253F2E261A7F5907007FD89E 16 | 17 | primary 18 | 19 | 20 | 253F2E3F1A7F5907007FD89E 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo.xcodeproj/xcuserdata/sandeepmukherjee.xcuserdatad/xcschemes/YouTubeDraggableVideo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 94 | 100 | 101 | 102 | 103 | 105 | 106 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo.xcodeproj/xcuserdata/sandeepmukherjee.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | YouTubeDraggableVideo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 253F2E261A7F5907007FD89E 16 | 17 | primary 18 | 19 | 20 | 253F2E3F1A7F5907007FD89E 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // test 4 | // 5 | // Created by Takuya Okamoto on 2015/06/19. 6 | // Copyright (c) 2015年 Uniface. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // test 4 | // 5 | // Created by Takuya Okamoto on 2015/06/19. 6 | // Copyright (c) 2015年 Uniface. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // 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. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // 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. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // 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. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo/Demo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // test 4 | // 5 | // Created by Takuya Okamoto on 2015/06/19. 6 | // Copyright (c) 2015年 Uniface. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @class VideoDetailViewController; 13 | 14 | @interface AppDelegate : UIResponder 15 | 16 | @property (strong, nonatomic) UIWindow *window; 17 | @property (strong, nonatomic) VideoDetailViewController *videoViewController; 18 | 19 | @end 20 | 21 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo/Demo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // test 4 | // 5 | // Created by Takuya Okamoto on 2015/06/19. 6 | // Copyright (c) 2015年 Uniface. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "YouTubeDraggableVideo-Swift.h" 11 | 12 | 13 | @interface AppDelegate () 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | //+ (VideoDetailViewController) video 19 | 20 | // static func appDelegate() -> AppDelegate? { 21 | // return UIApplication.sharedApplication().delegate as? AppDelegate 22 | // } 23 | 24 | + (AppDelegate *) appDelegate { 25 | return [[UIApplication sharedApplication] delegate]; 26 | } 27 | 28 | //+ (VideoDetailViewController *) videoController { 29 | // return [[[UIApplication sharedApplication] delegate] videoViewController]; 30 | //} 31 | 32 | 33 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 34 | 35 | self.videoViewController = [[VideoDetailViewController alloc] init]; 36 | 37 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 38 | self.window.rootViewController = [[FirstViewController alloc] init]; 39 | [self.window makeKeyAndVisible]; 40 | return YES; 41 | } 42 | 43 | - (void)applicationWillResignActive:(UIApplication *)application { 44 | // 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. 45 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 46 | } 47 | 48 | - (void)applicationDidEnterBackground:(UIApplication *)application { 49 | // 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. 50 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 51 | } 52 | 53 | - (void)applicationWillEnterForeground:(UIApplication *)application { 54 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 55 | } 56 | 57 | - (void)applicationDidBecomeActive:(UIApplication *)application { 58 | // 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. 59 | } 60 | 61 | - (void)applicationWillTerminate:(UIApplication *)application { 62 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo/Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | //// 2 | //// AppDelegate.swift 3 | //// APlayerViewController 4 | //// 5 | //// Created by Takuya Okamoto on 2015/05/28. 6 | //// Copyright (c) 2015年 Uniface. All rights reserved. 7 | //// 8 | // 9 | //import UIKit 10 | // 11 | //@UIApplicationMain 12 | //class AppDelegate: UIResponder, UIApplicationDelegate { 13 | // 14 | // var window: UIWindow? 15 | // var videoViewController = VideoDetailViewController() 16 | // 17 | // 18 | // static func appDelegate() -> AppDelegate? { 19 | // return UIApplication.sharedApplication().delegate as? AppDelegate 20 | // } 21 | // 22 | // static func videoController() -> VideoDetailViewController { 23 | // return appDelegate()!.videoViewController 24 | // } 25 | // 26 | // static func getWindow() -> UIWindow { 27 | // return appDelegate()!.window! 28 | // } 29 | // 30 | // 31 | // func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 32 | // // Override point for customization after application launch. 33 | // self.window = UIWindow(frame: UIScreen.mainScreen().bounds) 34 | // self.window!.rootViewController = FirstViewController() 35 | // self.window!.makeKeyAndVisible() 36 | // return true 37 | // } 38 | // 39 | // func applicationWillResignActive(application: UIApplication) { 40 | // // 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. 41 | // // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 42 | // } 43 | // 44 | // func applicationDidEnterBackground(application: UIApplication) { 45 | // // 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. 46 | // // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 47 | // } 48 | // 49 | // func applicationWillEnterForeground(application: UIApplication) { 50 | // // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 51 | // } 52 | // 53 | // func applicationDidBecomeActive(application: UIApplication) { 54 | // // 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. 55 | // } 56 | // 57 | // func applicationWillTerminate(application: UIApplication) { 58 | // // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 59 | // } 60 | // 61 | // 62 | //} 63 | // 64 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo/Demo/FirstViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FirstViewController.swift 3 | // YouTubeDraggableVideo 4 | // 5 | // Created by Takuya Okamoto on 2015/05/28. 6 | // Copyright (c) 2015年 Sandeep Mukherjee. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class FirstViewController: UIViewController { 12 | 13 | 14 | override func viewDidLoad() { 15 | self.view.backgroundColor = .white 16 | 17 | let btn = UIButton() 18 | btn.frame = CGRect(x: 10, y: 10, width: 100, height: 100) 19 | btn.backgroundColor = .red 20 | btn.addTarget(self, action: #selector(onTapShowButton), for: .touchUpInside) 21 | self.view.addSubview(btn) 22 | 23 | let dismissBtn = UIButton() 24 | dismissBtn.frame = CGRect(x: 150, y: 150, width: 100, height: 100) 25 | dismissBtn.backgroundColor = .green 26 | dismissBtn.addTarget(self, action: #selector(onTapShowSecondVCButton), for: .touchUpInside) 27 | self.view.addSubview(dismissBtn) 28 | 29 | } 30 | 31 | @objc func onTapShowButton() { 32 | (UIApplication.shared.delegate as! AppDelegate).videoViewController.show() //👈 33 | } 34 | 35 | @objc func onTapShowSecondVCButton() { 36 | (UIApplication.shared.delegate as! AppDelegate).videoViewController.bringToFront()//👈 37 | let secondVC = SecondViewController() 38 | self.present(secondVC, animated: true, completion: nil) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo/Demo/NSTimerHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSTimerHelper.swift 3 | // YouTubeDraggableVideo 4 | // 5 | // Created by Takuya Okamoto on 2015/06/19. 6 | // Copyright (c) 2015年 Sandeep Mukherjee. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Timer { 12 | static func schedule(delay: TimeInterval, handler: @escaping(Timer!) -> Void) -> Timer? { 13 | let fireDate = delay + CFAbsoluteTimeGetCurrent() 14 | let timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, fireDate, 0, 0, 0, handler) 15 | CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, CFRunLoopMode.commonModes) 16 | return timer 17 | } 18 | 19 | static func schedule(repeatInterval interval: TimeInterval, handler: @escaping(Timer!) -> Void) -> Timer? { 20 | let fireDate = interval + CFAbsoluteTimeGetCurrent() 21 | let timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, fireDate, interval, 0, 0, handler) 22 | CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, CFRunLoopMode.commonModes) 23 | return timer 24 | } 25 | } 26 | 27 | 28 | // Usage: 29 | 30 | //var count = 0 31 | //NSTimer.schedule(repeatInterval: 1) { timer in 32 | // println(++count) 33 | // if count >= 10 { 34 | // timer.invalidate() 35 | // } 36 | //} 37 | // 38 | //NSTimer.schedule(delay: 5) { timer in 39 | // println("5 seconds") 40 | //} 41 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo/Demo/SecondViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SecondViewController.swift 3 | // YouTubeDraggableVideo 4 | // 5 | // Created by Takuya Okamoto on 2015/06/19. 6 | // Copyright (c) 2015年 Sandeep Mukherjee. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class SecondViewController: UIViewController { 12 | 13 | 14 | @objc func onTapButton() { 15 | (UIApplication.shared.delegate as! AppDelegate).videoViewController.show()//👈 16 | } 17 | 18 | @objc func onTapDismissButton() { 19 | let parentVC = self.presentingViewController 20 | self.dismiss(animated: true, completion: nil) 21 | // NSTimer.schedule(delay: 0.2) { timer in 22 | // AppDelegate.videoController().changeParentVC(parentVC)//👈 23 | // } 24 | } 25 | 26 | override func viewDidLoad() { 27 | self.view.backgroundColor = .white 28 | 29 | let btn = UIButton() 30 | btn.frame = CGRect(x: 10, y: 10, width: 100, height: 100) 31 | btn.backgroundColor = .blue 32 | btn.addTarget(self, action: #selector(onTapButton), for: .touchUpInside) 33 | self.view.addSubview(btn) 34 | 35 | let dismissBtn = UIButton() 36 | dismissBtn.frame = CGRect(x: 150, y: 150, width: 100, height: 100) 37 | dismissBtn.backgroundColor = .orange 38 | dismissBtn.addTarget(self, action: #selector(onTapDismissButton), for: .touchUpInside) 39 | self.view.addSubview(dismissBtn) 40 | 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo/Demo/VideoDetailViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VideoViewController.swift 3 | // YouTubeDraggableVideo 4 | // 5 | // Created by Takuya Okamoto on 2015/05/28. 6 | // Copyright (c) 2015年 Sandeep Mukherjee. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | @objc class VideoDetailViewController: DraggableFloatingViewController { 13 | 14 | var moviePlayer: MPMoviePlayerController! 15 | private let loadingSpinner = UIActivityIndicatorView() 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | moviePlayer = MPMoviePlayerController() 21 | 22 | self.setupViews(withVideoView: moviePlayer.view, videoViewHeight: 160)//, minimizeButton: minimizeButton) 23 | 24 | setupMoviePlayer() 25 | 26 | addObserver(selector: #selector(onOrientationChanged), name: NSNotification.Name.UIDeviceOrientationDidChange.rawValue) 27 | 28 | // design controller view 29 | let minimizeButton = UIButton() 30 | minimizeButton.frame = CGRect(x: 0, y: 0, width: 44, height: 44) 31 | minimizeButton.setImage(UIImage(named: "DownArrow"), for: .normal) 32 | minimizeButton.addTarget(self, action: #selector(onTapMinimizeButton), for: .touchUpInside) 33 | self.controllerView.addSubview(minimizeButton) 34 | let testControl = UILabel() 35 | testControl.frame = CGRect(x: 100, y: 5, width: 150, height: 40) 36 | testControl.text = "controller view" 37 | testControl.textColor = .white 38 | self.controllerView.addSubview(testControl) 39 | 40 | // design body view 41 | self.bodyView.backgroundColor = .white 42 | self.bodyView.layer.borderColor = UIColor.red.cgColor 43 | self.bodyView.layer.borderWidth = 10.0 44 | let testView = UILabel() 45 | testView.frame = CGRect(x: 20, y: 10, width: 100, height: 40) 46 | testView.text = "body view" 47 | testView.textColor = .red 48 | self.bodyView.addSubview(testView) 49 | 50 | // design message view 51 | self.messageView.backgroundColor = UIColor.black.withAlphaComponent(0.8) 52 | loadingSpinner.frame = CGRect(x: 0, y: 0, width: 50, height: 50) 53 | loadingSpinner.center = self.messageView.center 54 | loadingSpinner.hidesWhenStopped = false 55 | loadingSpinner.activityIndicatorViewStyle = .white 56 | self.messageView.addSubview(loadingSpinner) 57 | } 58 | 59 | override func didDisappear() { 60 | moviePlayer.pause() 61 | } 62 | 63 | override func didReAppear() { 64 | setupMoviePlayer() 65 | } 66 | 67 | 68 | 69 | func onTapButton() { 70 | print("onTapButton") 71 | } 72 | 73 | override func showMessageView() { 74 | loadingSpinner.startAnimating() 75 | super.showMessageView() 76 | } 77 | override func hideMessageView() { 78 | super.hideMessageView() 79 | loadingSpinner.stopAnimating() 80 | } 81 | 82 | override func didFullExpandByGesture() { 83 | UIApplication.shared.setStatusBarHidden(true, with: .none) 84 | showVideoControl() 85 | } 86 | override func didExpand() { 87 | print("didExpand") 88 | // [[UIApplication sharedApplication] setStatusBarHidden:YES]; 89 | UIApplication.shared.setStatusBarHidden(true, with: .none) 90 | showVideoControl() 91 | } 92 | override func didMinimize() { 93 | print("didMinimized") 94 | hideVideoControl() 95 | } 96 | 97 | override func didStartMinimizeGesture() { 98 | print("didStartMinimizeGesture") 99 | UIApplication.shared.setStatusBarHidden(false, with: .none) 100 | } 101 | 102 | 103 | @objc func onTapMinimizeButton() { 104 | UIApplication.shared.setStatusBarHidden(false, with: .none) 105 | self.minimizeView() 106 | } 107 | 108 | 109 | 110 | // -------------------------------------------------------------------------------------------------- 111 | 112 | func setupMoviePlayer() { 113 | // setupMovie 114 | let url = NSURL.fileURL(withPath: Bundle.main.path(forResource: "test", ofType: "mp4")!) 115 | moviePlayer.contentURL = url 116 | moviePlayer.isFullscreen = false 117 | moviePlayer.controlStyle = .none 118 | moviePlayer.repeatMode = .none 119 | moviePlayer.prepareToPlay() 120 | 121 | // play 122 | let seconds = 1.0 123 | let delay = seconds * Double(NSEC_PER_SEC)// nanoseconds per seconds 124 | DispatchQueue.main.asyncAfter(deadline: .now() + delay) { 125 | self.moviePlayer.play() 126 | } 127 | 128 | // for movie loop 129 | 130 | NotificationCenter.default.addObserver(self, selector: #selector(moviePlayBackDidFinish), 131 | name: NSNotification.Name.MPMoviePlayerPlaybackDidFinish, 132 | object: moviePlayer) 133 | } 134 | 135 | // movie loop 136 | @objc func moviePlayBackDidFinish(notification: NSNotification) { 137 | print("moviePlayBackDidFinish:") 138 | moviePlayer.play() 139 | removeObserver(aName: NSNotification.Name.MPMoviePlayerPlaybackDidFinish.rawValue) 140 | } 141 | 142 | 143 | 144 | 145 | // ----------------------------- events ----------------------------------------------- 146 | 147 | // MARK: Orientation 148 | @objc func onOrientationChanged() { 149 | let orientation: UIInterfaceOrientation = getOrientation() 150 | 151 | switch orientation { 152 | 153 | case .portrait, .portraitUpsideDown: 154 | print("portrait") 155 | exitFullScreen() 156 | 157 | case .landscapeLeft, .landscapeRight: 158 | print("landscape") 159 | goFullScreen() 160 | 161 | default: 162 | print("no action for this orientation:" + orientation.rawValue.description) 163 | } 164 | 165 | } 166 | 167 | override func didReceiveMemoryWarning() { 168 | super.didReceiveMemoryWarning() 169 | } 170 | 171 | 172 | 173 | 174 | 175 | // --------------------------------- util ------------------------------------------ 176 | 177 | // MARK: FullScreen Method 178 | func isFullScreen() -> Bool { 179 | // println("isFullScreen: " + String(stringInterpolationSegment: moviePlayer.fullscreen)) 180 | return moviePlayer.isFullscreen 181 | } 182 | func goFullScreen() { 183 | if !isFullScreen() { 184 | // println("goFullScreen") 185 | moviePlayer.controlStyle = MPMovieControlStyle.fullscreen 186 | moviePlayer.isFullscreen = true 187 | addObserver(selector: #selector(willExitFullScreen), name: NSNotification.Name.MPMoviePlayerWillExitFullscreen.rawValue) 188 | } 189 | } 190 | func exitFullScreen() { 191 | if isFullScreen() { 192 | // println("exit fullscreen"); 193 | moviePlayer.isFullscreen = false 194 | } 195 | } 196 | @objc func willExitFullScreen() { 197 | // println("willExitFullScreen") 198 | if isLandscape() 199 | { 200 | setOrientation(orientation: .portrait) 201 | } 202 | 203 | 204 | removeObserver(aName: NSNotification.Name.MPMoviePlayerWillExitFullscreen.rawValue) 205 | } 206 | 207 | 208 | // FIXIT: Don't work 209 | func showVideoControl() { 210 | // println("showVideoControl"); 211 | moviePlayer.controlStyle = .none 212 | } 213 | 214 | // FIXIT: Don't work 215 | func hideVideoControl() { 216 | // println("hideVideoControl") 217 | moviePlayer.controlStyle = .none 218 | } 219 | 220 | 221 | 222 | 223 | //----------------------------------------------------------------------------------- 224 | 225 | func getOrientation() -> UIInterfaceOrientation { 226 | return UIApplication.shared.statusBarOrientation 227 | } 228 | 229 | func setOrientation(orientation: UIInterfaceOrientation) { 230 | let orientationNum: NSNumber = NSNumber(value: orientation.rawValue) 231 | UIDevice.current.setValue(orientationNum, forKey: "orientation") 232 | } 233 | 234 | func addObserver(selector aSelector: Selector, name aName: String) { 235 | NotificationCenter.default.addObserver(self, selector: aSelector, name:NSNotification.Name(aName), object: nil) 236 | } 237 | 238 | func removeObserver(aName: String) { 239 | NotificationCenter.default.removeObserver(self, name: NSNotification.Name(aName), object: nil) 240 | } 241 | 242 | func isLandscape() -> Bool { 243 | if (UIInterfaceOrientationIsLandscape(UIApplication.shared.statusBarOrientation)) { 244 | return true 245 | } 246 | else { 247 | return false 248 | } 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo/Demo/YouTubeDraggableVideo-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import "DraggableFloatingViewController.h" 6 | #import "AppDelegate.h" -------------------------------------------------------------------------------- /YouTubeDraggableVideo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /YouTubeDraggableVideo/Images.xcassets/DownArrow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "DownArrow~iPhone.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "DownArrow~iPhone-667h@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "DownArrow~iPhone-736h@3x.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /YouTubeDraggableVideo/Images.xcassets/DownArrow.imageset/DownArrow~iPhone-667h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takuoka/DraggableFloatingViewController/f588f100aaf033054386b1f8c0748e64e65b59e6/YouTubeDraggableVideo/Images.xcassets/DownArrow.imageset/DownArrow~iPhone-667h@2x.png -------------------------------------------------------------------------------- /YouTubeDraggableVideo/Images.xcassets/DownArrow.imageset/DownArrow~iPhone-736h@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takuoka/DraggableFloatingViewController/f588f100aaf033054386b1f8c0748e64e65b59e6/YouTubeDraggableVideo/Images.xcassets/DownArrow.imageset/DownArrow~iPhone-736h@3x.png -------------------------------------------------------------------------------- /YouTubeDraggableVideo/Images.xcassets/DownArrow.imageset/DownArrow~iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takuoka/DraggableFloatingViewController/f588f100aaf033054386b1f8c0748e64e65b59e6/YouTubeDraggableVideo/Images.xcassets/DownArrow.imageset/DownArrow~iPhone.png -------------------------------------------------------------------------------- /YouTubeDraggableVideo/Images.xcassets/test.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "test.jpg" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /YouTubeDraggableVideo/Images.xcassets/test.imageset/test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takuoka/DraggableFloatingViewController/f588f100aaf033054386b1f8c0748e64e65b59e6/YouTubeDraggableVideo/Images.xcassets/test.imageset/test.jpg -------------------------------------------------------------------------------- /YouTubeDraggableVideo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.vizllx.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UIViewControllerBasedStatusBarAppearance 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo/Source/DraggableFloatingViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BSVideoDetailController.h 3 | // YouTubeDraggableVideo 4 | // 5 | // Created by Sandeep Mukherjee on 02/02/15. 6 | // Copyright (c) 2015 Sandeep Mukherjee. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | 13 | 14 | @protocol DraggableFloatingViewControllerDelegate 15 | @required 16 | - (void)removeDraggableFloatingViewController; 17 | @end 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | // please extend 27 | @interface DraggableFloatingViewController : UIViewController 28 | 29 | 30 | 31 | // ---------- use from SubClass ------------------------ 32 | 33 | // please add subview on this 34 | @property(nonatomic, strong) UIView *bodyView; 35 | //please add controller on this 36 | @property(nonatomic, strong) UIView *controllerView; 37 | //please add loading spiner on this 38 | @property(nonatomic, strong) UIView *messageView; 39 | 40 | // please call from "viewDidLoad" from sub class 41 | - (void) setupViewsWithVideoView: (UIView *)vView 42 | videoViewHeight: (CGFloat) videoHeight; 43 | 44 | // please override if you want 45 | - (void) didExpand; 46 | - (void) didMinimize; 47 | - (void) didStartMinimizeGesture; 48 | - (void) didFullExpandByGesture;//stil dev 49 | - (void) didDisappear; 50 | - (void) didReAppear; 51 | 52 | 53 | // please call if you want 54 | - (void) minimizeView; 55 | - (void) expandView; 56 | - (void) hideControllerView; 57 | - (void) showControllerView; 58 | - (void) showMessageView; 59 | - (void) hideMessageView; 60 | 61 | 62 | 63 | // ---------- use from other class ------------------------ 64 | // please call from parent view controller 65 | - (void) show; 66 | - (void) bringToFront; 67 | 68 | 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo/Source/DraggableFloatingViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BSVideoDetailController.m 3 | // YouTubeDraggableVideo 4 | // 5 | // Created by Sandeep Mukherjee on 02/02/15. 6 | // Copyright (c) 2015 Sandeep Mukherjee. All rights reserved. 7 | // 8 | 9 | 10 | #import "DraggableFloatingViewController.h" 11 | #import "QuartzCore/CALayer.h" 12 | 13 | typedef NS_ENUM(NSUInteger, UIPanGestureRecognizerDirection) { 14 | UIPanGestureRecognizerDirectionUndefined, 15 | UIPanGestureRecognizerDirectionUp, 16 | UIPanGestureRecognizerDirectionDown, 17 | UIPanGestureRecognizerDirectionLeft, 18 | UIPanGestureRecognizerDirectionRight 19 | }; 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | @interface DraggableFloatingViewController () 29 | @end 30 | 31 | 32 | @implementation DraggableFloatingViewController 33 | { 34 | 35 | //local Frame storee 36 | CGRect videoWrapperFrame; 37 | CGRect minimizedVideoFrame; 38 | CGRect pageWrapperFrame; 39 | 40 | // animation Frame 41 | CGRect wFrame; 42 | CGRect vFrame; 43 | 44 | //local touch location 45 | CGFloat _touchPositionInHeaderY; 46 | CGFloat _touchPositionInHeaderX; 47 | 48 | //detecting Pan gesture Direction 49 | UIPanGestureRecognizerDirection direction; 50 | 51 | UITapGestureRecognizer *tapRecognizer; 52 | 53 | //Creating a transparent Black layer view 54 | UIView *transparentBlackSheet; 55 | 56 | //Just to Check wether view is expanded or not 57 | BOOL isExpandedMode; 58 | 59 | 60 | UIView *pageWrapper; 61 | UIView *videoWrapper; 62 | // UIButton *foldButton; 63 | 64 | UIView *videoView; 65 | // border of mini vieo view 66 | UIView *borderView; 67 | 68 | CGFloat maxH; 69 | CGFloat maxW; 70 | CGFloat videoHeightRatio; 71 | CGFloat finalViewOffsetY; 72 | CGFloat minimamVideoHeight; 73 | 74 | UIView *parentView; 75 | 76 | BOOL isDisplayController; 77 | NSTimer *hideControllerTimer; 78 | 79 | BOOL isMinimizingByGesture; 80 | 81 | BOOL isAppear; 82 | BOOL isSetuped; 83 | 84 | CGRect windowFrame; 85 | } 86 | 87 | const CGFloat finalMargin = 3.0; 88 | const CGFloat minimamVideoWidth = 140; 89 | const CGFloat flickVelocity = 1000; 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | // please override if you want 102 | - (void) didExpand {} 103 | - (void) didMinimize {} 104 | - (void) didStartMinimizeGesture { 105 | [[UIApplication sharedApplication] setStatusBarHidden:NO]; 106 | } 107 | - (void) didFullExpandByGesture {}// TODO: meke this stable 108 | - (void) didDisappear{} 109 | - (void) didReAppear{} 110 | 111 | - (id)init 112 | { 113 | self = [super init]; 114 | if (self) { 115 | self.bodyView = [[UIView alloc] init]; 116 | self.controllerView = [[UIView alloc] init]; 117 | self.messageView = [[UIView alloc] init]; 118 | } 119 | return self; 120 | } 121 | - (id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { 122 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 123 | if (self) { 124 | self.bodyView = [[UIView alloc] init]; 125 | self.controllerView = [[UIView alloc] init]; 126 | self.messageView = [[UIView alloc] init]; 127 | } 128 | return self; 129 | } 130 | 131 | 132 | 133 | # pragma mark - init 134 | 135 | - (void) show { 136 | if (!isSetuped) { 137 | [self setup]; 138 | } 139 | else { 140 | if (!isAppear) { 141 | [self reAppearWithAnimation]; 142 | } 143 | } 144 | } 145 | 146 | 147 | - (void) setup { 148 | 149 | isSetuped = true; 150 | 151 | NSLog(@"showVideoViewControllerOnParentVC"); 152 | 153 | // if( ![parentVC conformsToProtocol:@protocol(DraggableFloatingViewControllerDelegate)] ) { 154 | // NSAssert(NO, @"❌❌Parent view controller must confirm to protocol .❌❌"); 155 | // } 156 | // self.delegate = parentVC; 157 | 158 | // set portrait 159 | if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) { 160 | [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger: UIInterfaceOrientationPortrait] forKey:@"orientation"]; 161 | } 162 | 163 | [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide]; 164 | 165 | // parentView = parentVC.view; 166 | // [parentView addSubview:self.view];// then, "viewDidLoad" called 167 | [[self getWindow] addSubview:self.view]; 168 | 169 | // wait to run "viewDidLoad" before "showThisView" 170 | [self performSelector:@selector(showThisView) withObject:nil afterDelay:0.0]; 171 | 172 | isAppear = true; 173 | } 174 | // ↓ 175 | // VIEW DID LOAD 176 | - (void) setupViewsWithVideoView: (UIView *)vView 177 | videoViewHeight: (CGFloat) videoHeight 178 | // minimizeButton: (UIButton *)foldBtn 179 | { 180 | NSLog(@"setupViewsWithVideoView"); 181 | 182 | videoView = vView; 183 | // foldButton = foldBtn;//control show and hide 184 | 185 | windowFrame = [[UIScreen mainScreen] bounds]; 186 | maxH = windowFrame.size.height; 187 | maxW = windowFrame.size.width; 188 | CGFloat videoWidth = maxW; 189 | videoHeightRatio = videoHeight / videoWidth; 190 | minimamVideoHeight = minimamVideoWidth * videoHeightRatio; 191 | finalViewOffsetY = maxH - minimamVideoHeight - finalMargin; 192 | 193 | videoWrapper = [[UIView alloc] init]; 194 | videoWrapper.frame = CGRectMake(0, 0, videoWidth, videoHeight); 195 | 196 | videoView.frame = videoWrapper.frame; 197 | self.controllerView.frame = videoWrapper.frame; 198 | self.messageView.frame = videoWrapper.frame; 199 | 200 | pageWrapper = [[UIView alloc] init]; 201 | pageWrapper.frame = CGRectMake(0, 0, maxW, maxH); 202 | 203 | videoWrapperFrame = videoWrapper.frame; 204 | pageWrapperFrame = pageWrapper.frame; 205 | 206 | 207 | borderView = [[UIView alloc] init]; 208 | borderView.clipsToBounds = YES; 209 | borderView.layer.masksToBounds = NO; 210 | borderView.layer.borderColor = [[UIColor whiteColor] CGColor]; 211 | borderView.layer.borderWidth = 0.5f; 212 | // borderView.layer.shadowOffset = CGSizeMake(0.0f, 0.0f); 213 | // borderView.layer.shadowColor = [UIColor blackColor].CGColor; 214 | // borderView.layer.shadowRadius = 1.0; 215 | // borderView.layer.shadowOpacity = 1.0; 216 | borderView.alpha = 0; 217 | borderView.frame = CGRectMake(videoView.frame.origin.y - 1, 218 | videoView.frame.origin.x - 1, 219 | videoView.frame.size.width + 1, 220 | videoView.frame.size.height + 1); 221 | 222 | self.bodyView.frame = CGRectMake(0, videoHeight, maxW, maxH - videoHeight); 223 | } 224 | // ↓ 225 | - (void) showThisView { 226 | // only first time, SubViews add to "self.view". 227 | // After animation, they move to "parentView" 228 | videoView.backgroundColor = [UIColor blackColor]; 229 | videoWrapper.backgroundColor = [UIColor blackColor]; 230 | [pageWrapper addSubview:self.bodyView]; 231 | [videoWrapper addSubview:videoView]; 232 | // move subviews from "self.view" to "parentView" after animation 233 | [self.view addSubview:pageWrapper]; 234 | [self.view addSubview:videoWrapper]; 235 | 236 | transparentBlackSheet = [[UIView alloc] initWithFrame:windowFrame]; 237 | transparentBlackSheet.backgroundColor = [UIColor blackColor]; 238 | transparentBlackSheet.alpha = 1; 239 | 240 | [self appearAnimation]; 241 | } 242 | // ↓ 243 | - (void) appearAnimation { 244 | 245 | self.view.frame = CGRectMake(windowFrame.size.width - 50, 246 | windowFrame.size.height - 50, 247 | windowFrame.size.width, 248 | windowFrame.size.height); 249 | self.view.transform = CGAffineTransformMakeScale(0.2, 0.2); 250 | self.view.alpha = 0; 251 | [UIView animateWithDuration:0.2 252 | delay:0.0 253 | options:UIViewAnimationOptionCurveEaseInOut 254 | animations:^ { 255 | self.view.transform = CGAffineTransformMakeScale(1.0, 1.0); 256 | self.view.alpha = 1; 257 | self.view.frame = CGRectMake(windowFrame.origin.x, 258 | windowFrame.origin.y, 259 | windowFrame.size.width, 260 | windowFrame.size.height); 261 | } 262 | completion:^(BOOL finished) { 263 | [self afterAppearAnimation]; 264 | }]; 265 | } 266 | // ↓ 267 | -(void) afterAppearAnimation { 268 | videoView.backgroundColor = videoWrapper.backgroundColor = [UIColor clearColor]; 269 | 270 | 271 | // [parentView addSubview:transparentBlackSheet]; 272 | // move from self.view 273 | // [parentView addSubview:pageWrapper]; 274 | // [parentView addSubview:videoWrapper]; 275 | 276 | [[self getWindow] addSubview:transparentBlackSheet]; 277 | [[self getWindow] addSubview:pageWrapper]; 278 | [[self getWindow] addSubview:videoWrapper]; 279 | 280 | 281 | self.view.hidden = TRUE; 282 | 283 | [videoView addSubview:borderView]; 284 | 285 | [videoWrapper addSubview:self.controllerView]; 286 | 287 | self.messageView.hidden = TRUE; 288 | [videoWrapper addSubview:self.messageView]; 289 | 290 | [self showControllerView]; 291 | 292 | UITapGestureRecognizer* expandedTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapExpandedVideoView)]; 293 | expandedTap.numberOfTapsRequired = 1; 294 | expandedTap.delegate = self; 295 | [videoWrapper addGestureRecognizer:expandedTap]; 296 | 297 | vFrame = videoWrapperFrame; 298 | wFrame = pageWrapperFrame; 299 | 300 | // adding Pan Gesture 301 | UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)]; 302 | pan.delegate = self; 303 | [videoWrapper addGestureRecognizer:pan]; 304 | 305 | isExpandedMode = TRUE; 306 | } 307 | 308 | 309 | 310 | 311 | 312 | 313 | - (void) disappear { 314 | isAppear = false; 315 | // [self.delegate removeDraggableFloatingViewController]; 316 | } 317 | 318 | 319 | - (void) reAppearWithAnimation { 320 | borderView.alpha = 0; 321 | transparentBlackSheet.alpha = 0; 322 | 323 | videoWrapper.alpha = 0; 324 | pageWrapper.alpha = 0; 325 | 326 | pageWrapper.frame = pageWrapperFrame; 327 | videoWrapper.frame = videoWrapperFrame; 328 | videoView.frame = videoWrapperFrame; 329 | self.controllerView.frame = videoView.frame; 330 | self.bodyView.frame = CGRectMake(0, 331 | videoView.frame.size.height,// keep stay on bottom of videoView 332 | self.bodyView.frame.size.width, 333 | self.bodyView.frame.size.height); 334 | borderView.frame = CGRectMake(videoView.frame.origin.y - 1, 335 | videoView.frame.origin.x - 1, 336 | videoView.frame.size.width + 1, 337 | videoView.frame.size.height + 1); 338 | 339 | // parentViewにのってるViewは pageWrapper と videoWrapper と transparentView 340 | // pageWrapper と videoWrapper をself.viewと同様のアニメーションをさせた後に、parentViewに戻す 341 | // transparentView は あとで1にすればいい 342 | pageWrapper.frame = CGRectMake(windowFrame.size.width - 50, 343 | windowFrame.size.height - 150, 344 | pageWrapper.frame.size.width, 345 | pageWrapper.frame.size.height); 346 | // pageWrapper.transform = CGAffineTransformMakeScale(0.2, 0.2); 347 | 348 | videoWrapper.frame = CGRectMake(windowFrame.size.width - 50, 349 | windowFrame.size.height - 150, 350 | videoWrapper.frame.size.width, 351 | videoWrapper.frame.size.height); 352 | // videoWrapper.transform = CGAffineTransformMakeScale(0.2, 0.2); 353 | 354 | [UIView animateWithDuration:0.5 355 | delay:0.0 356 | options:UIViewAnimationOptionCurveEaseInOut 357 | animations:^ { 358 | // pageWrapper.transform = CGAffineTransformMakeScale(1.0, 1.0); 359 | pageWrapper.alpha = 1; 360 | pageWrapper.frame = CGRectMake(windowFrame.origin.x, 361 | windowFrame.origin.y, 362 | pageWrapper.frame.size.width, 363 | pageWrapper.frame.size.height); 364 | 365 | // videoWrapper.transform = CGAffineTransformMakeScale(1.0, 1.0); 366 | videoWrapper.alpha = 1; 367 | videoWrapper.frame = CGRectMake(windowFrame.origin.x, 368 | windowFrame.origin.y, 369 | videoWrapper.frame.size.width, 370 | videoWrapper.frame.size.height); 371 | 372 | } 373 | completion:^(BOOL finished) { 374 | 375 | transparentBlackSheet.alpha = 1.0; 376 | 377 | for (UIGestureRecognizer *recognizer in videoWrapper.gestureRecognizers) { 378 | if([recognizer isKindOfClass:[UITapGestureRecognizer class]]) { 379 | [videoWrapper removeGestureRecognizer:recognizer]; 380 | } 381 | } 382 | UITapGestureRecognizer* expandedTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapExpandedVideoView)]; 383 | expandedTap.numberOfTapsRequired = 1; 384 | expandedTap.delegate = self; 385 | [videoWrapper addGestureRecognizer:expandedTap]; 386 | 387 | isExpandedMode = TRUE; 388 | [self didExpand]; 389 | [self didReAppear]; 390 | }]; 391 | } 392 | 393 | 394 | 395 | - (void) bringToFront { 396 | // [parentView addSubview:self.view];// then, "viewDidLoad" called 397 | // [parentView addSubview:transparentBlackSheet]; 398 | // [parentView addSubview:pageWrapper]; 399 | // [parentView addSubview:videoWrapper]; 400 | if (isSetuped) { 401 | [[self getWindow] bringSubviewToFront:self.view]; 402 | [[self getWindow] bringSubviewToFront:transparentBlackSheet]; 403 | [[self getWindow] bringSubviewToFront:pageWrapper]; 404 | [[self getWindow] bringSubviewToFront:videoWrapper]; 405 | } 406 | } 407 | // 408 | //- (void) changeParentVC: (UIViewController*) parentVC { 409 | //// if (isSetuped) { 410 | //// parentView = parentVC.view; 411 | //// [parentView addSubview:self.view];// then, "viewDidLoad" called 412 | //// [parentView addSubview:transparentBlackSheet]; 413 | //// [parentView addSubview:pageWrapper]; 414 | //// [parentView addSubview:videoWrapper]; 415 | //// } 416 | //} 417 | // 418 | 419 | - (UIWindow *) getWindow { 420 | return [[[UIApplication sharedApplication] delegate] window]; 421 | } 422 | 423 | 424 | -(void)removeAllViews 425 | { 426 | [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide]; 427 | [videoWrapper removeFromSuperview]; 428 | [pageWrapper removeFromSuperview]; 429 | [transparentBlackSheet removeFromSuperview]; 430 | [self.view removeFromSuperview]; 431 | } 432 | 433 | - (void)dealloc 434 | { 435 | // NSLog(@"dealloc DraggableFloatingViewController"); 436 | } 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | -(void) showMessageView { 447 | self.messageView.hidden = FALSE; 448 | } 449 | -(void) hideMessageView { 450 | self.messageView.hidden = TRUE; 451 | } 452 | 453 | 454 | 455 | -(void) setHideControllerTimer { 456 | if ([hideControllerTimer isValid]) { 457 | [hideControllerTimer invalidate]; 458 | } 459 | hideControllerTimer = [NSTimer scheduledTimerWithTimeInterval:3.0f 460 | target:self 461 | selector:@selector(hideControllerView) 462 | userInfo:nil 463 | repeats:NO]; 464 | } 465 | -(void) showControllerView { 466 | NSLog(@"showControllerView"); 467 | isDisplayController = true; 468 | [self setHideControllerTimer]; 469 | [UIView animateWithDuration:0.5 470 | delay:0.0 471 | options:UIViewAnimationOptionCurveEaseInOut 472 | animations:^ { 473 | self.controllerView.alpha = 1.0; 474 | } 475 | completion:^(BOOL finished) { 476 | }]; 477 | } 478 | -(void) hideControllerView { 479 | NSLog(@"hideControllerView"); 480 | isDisplayController = false; 481 | if ([hideControllerTimer isValid]) { 482 | [hideControllerTimer invalidate]; 483 | } 484 | [UIView animateWithDuration:0.5 485 | delay:0.0 486 | options:UIViewAnimationOptionCurveEaseInOut 487 | animations:^ { 488 | self.controllerView.alpha = 0.0; 489 | } 490 | completion:^(BOOL finished) { 491 | }]; 492 | } 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | - (void) showControllerAfterExpanded { 506 | [NSTimer scheduledTimerWithTimeInterval:0.5f 507 | target:self 508 | selector:@selector(showControllerView) 509 | userInfo:nil 510 | repeats:NO]; 511 | } 512 | 513 | 514 | 515 | 516 | # pragma mark - tap action 517 | //- (void) onTapDownButton { 518 | // [self minimizeView]; 519 | //} 520 | 521 | 522 | - (void) onTapExpandedVideoView { 523 | NSLog(@"onTapExpandedVideoView"); 524 | if (self.controllerView.alpha == 0.0) { 525 | [self showControllerView]; 526 | } 527 | else if (self.controllerView.alpha == 1.0){ 528 | [self hideControllerView]; 529 | } 530 | } 531 | 532 | - (void)expandViewOnTap:(UITapGestureRecognizer*)sender { 533 | [self expandView]; 534 | [self showControllerAfterExpanded]; 535 | 536 | } 537 | 538 | 539 | 540 | #pragma mark- Pan Gesture Selector Action 541 | 542 | -(void)panAction:(UIPanGestureRecognizer *)recognizer 543 | { 544 | CGFloat touchPosInViewY = [recognizer locationInView:self.view].y; 545 | 546 | 547 | if(recognizer.state == UIGestureRecognizerStateBegan) { 548 | 549 | direction = UIPanGestureRecognizerDirectionUndefined; 550 | //storing direction 551 | CGPoint velocity = [recognizer velocityInView:recognizer.view]; 552 | [self detectPanDirection:velocity]; 553 | 554 | isMinimizingByGesture = false; 555 | //Snag the Y position of the touch when panning begins 556 | _touchPositionInHeaderY = [recognizer locationInView:videoWrapper].y; 557 | _touchPositionInHeaderX = [recognizer locationInView:videoWrapper].x; 558 | if(direction == UIPanGestureRecognizerDirectionDown) { 559 | if(videoView.frame.size.height > minimamVideoHeight) { 560 | // player.controlStyle = MPMovieControlStyleNone; 561 | NSLog(@"minimize gesture start"); 562 | isMinimizingByGesture = true; 563 | [self didStartMinimizeGesture]; 564 | } 565 | } 566 | } 567 | 568 | 569 | else if(recognizer.state == UIGestureRecognizerStateChanged) { 570 | if(direction == UIPanGestureRecognizerDirectionDown || direction == UIPanGestureRecognizerDirectionUp) { 571 | 572 | // CGFloat appendY = 20; 573 | // if (direction == UIPanGestureRecognizerDirectionUp) appendY = -appendY; 574 | 575 | CGFloat newOffsetY = touchPosInViewY - _touchPositionInHeaderY;// + appendY; 576 | 577 | // CGFloat newOffsetX = newOffsetY * 0.35; 578 | [self adjustViewOnVerticalPan:newOffsetY recognizer:recognizer]; 579 | } 580 | else if (direction==UIPanGestureRecognizerDirectionRight || direction==UIPanGestureRecognizerDirectionLeft) { 581 | [self adjustViewOnHorizontalPan:recognizer]; 582 | } 583 | } 584 | 585 | 586 | 587 | else if(recognizer.state == UIGestureRecognizerStateEnded) { 588 | 589 | CGPoint velocity = [recognizer velocityInView:recognizer.view]; 590 | 591 | if(direction == UIPanGestureRecognizerDirectionDown || direction == UIPanGestureRecognizerDirectionUp) 592 | { 593 | if(velocity.y < -flickVelocity) 594 | { 595 | // NSLog(@"flick up"); 596 | [self expandView]; 597 | if (isMinimizingByGesture == false) { 598 | [self showControllerAfterExpanded]; 599 | } 600 | [recognizer setTranslation:CGPointZero inView:recognizer.view]; 601 | return; 602 | } 603 | else if(velocity.y > flickVelocity) 604 | { 605 | // NSLog(@"flick down"); 606 | [self minimizeView]; 607 | [recognizer setTranslation:CGPointZero inView:recognizer.view]; 608 | return; 609 | } 610 | else if(recognizer.view.frame.origin.y>(windowFrame.size.width/2)) 611 | { 612 | [self minimizeView]; 613 | [recognizer setTranslation:CGPointZero inView:recognizer.view]; 614 | return; 615 | } 616 | else if(recognizer.view.frame.origin.y < (windowFrame.size.width/2) || recognizer.view.frame.origin.y < 0) 617 | { 618 | [self expandView]; 619 | if (isMinimizingByGesture == false) { 620 | [self showControllerAfterExpanded]; 621 | } 622 | [recognizer setTranslation:CGPointZero inView:recognizer.view]; 623 | return; 624 | } 625 | } 626 | 627 | else if (direction==UIPanGestureRecognizerDirectionLeft) 628 | { 629 | if(pageWrapper.alpha <= 0) 630 | { 631 | if(velocity.x < -flickVelocity || pageWrapper.alpha < 0.3) 632 | { 633 | [self fadeOutViewToLeft:recognizer completion: ^{ 634 | [self disappear]; 635 | }]; 636 | return; 637 | } 638 | else if(recognizer.view.frame.origin.x < 0) 639 | { 640 | [self disappear]; 641 | } 642 | else 643 | { 644 | [self animateMiniViewToNormalPosition:recognizer completion:nil]; 645 | 646 | } 647 | } 648 | } 649 | 650 | else if (direction==UIPanGestureRecognizerDirectionRight) 651 | { 652 | if(pageWrapper.alpha <= 0) 653 | { 654 | if(velocity.x > flickVelocity) 655 | { 656 | [self fadeOutViewToRight:recognizer completion: ^{ 657 | [self disappear]; 658 | }]; 659 | return; 660 | } 661 | if(recognizer.view.frame.origin.x > windowFrame.size.width - 50) 662 | { 663 | [self disappear]; 664 | } 665 | else 666 | { 667 | [self animateMiniViewToNormalPosition:recognizer completion:nil]; 668 | } 669 | } 670 | } 671 | 672 | isMinimizingByGesture = false; 673 | } 674 | } 675 | 676 | 677 | -(void)detectPanDirection:(CGPoint )velocity 678 | { 679 | // foldButton.hidden=TRUE; 680 | BOOL isVerticalGesture = fabs(velocity.y) > fabs(velocity.x); 681 | 682 | if (isVerticalGesture) 683 | { 684 | if (velocity.y > 0) { 685 | direction = UIPanGestureRecognizerDirectionDown; 686 | 687 | } else { 688 | direction = UIPanGestureRecognizerDirectionUp; 689 | } 690 | } 691 | else 692 | { 693 | if(velocity.x > 0) 694 | { 695 | direction = UIPanGestureRecognizerDirectionRight; 696 | } 697 | else 698 | { 699 | direction = UIPanGestureRecognizerDirectionLeft; 700 | } 701 | } 702 | } 703 | 704 | 705 | 706 | -(void)adjustViewOnVerticalPan:(CGFloat)newOffsetY recognizer:(UIPanGestureRecognizer *)recognizer 707 | { 708 | CGFloat touchPosInViewY = [recognizer locationInView:self.view].y; 709 | 710 | CGFloat progressRate = newOffsetY / finalViewOffsetY; 711 | 712 | if(progressRate >= 0.99) { 713 | progressRate = 1; 714 | newOffsetY = finalViewOffsetY; 715 | } 716 | 717 | [self calcNewFrameWithParsentage:progressRate newOffsetY:newOffsetY]; 718 | 719 | if (progressRate <= 1 && pageWrapper.frame.origin.y >= 0) { 720 | pageWrapper.frame = wFrame; 721 | videoWrapper.frame = vFrame; 722 | videoView.frame = CGRectMake( 723 | videoView.frame.origin.x, videoView.frame.origin.x, 724 | vFrame.size.width, vFrame.size.height 725 | ); 726 | self.bodyView.frame = CGRectMake( 727 | 0, 728 | videoView.frame.size.height,// keep stay on bottom of videoView 729 | self.bodyView.frame.size.width, 730 | self.bodyView.frame.size.height 731 | ); 732 | 733 | borderView.frame = CGRectMake(videoView.frame.origin.y - 1, 734 | videoView.frame.origin.x - 1, 735 | videoView.frame.size.width + 1, 736 | videoView.frame.size.height + 1); 737 | 738 | self.controllerView.frame = videoView.frame; 739 | 740 | CGFloat percentage = touchPosInViewY / windowFrame.size.height; 741 | 742 | pageWrapper.alpha = transparentBlackSheet.alpha = 1.0 - (percentage * 1.5); 743 | if (percentage > 0.2) borderView.alpha = percentage; 744 | else borderView.alpha = 0; 745 | 746 | if (isDisplayController) { 747 | self.controllerView.alpha = 1.0 - (percentage * 2); 748 | // if (percentage > 0.2) borderView.alpha = percentage; 749 | // else borderView.alpha = 0; 750 | } 751 | 752 | if(direction==UIPanGestureRecognizerDirectionDown) 753 | { 754 | // [parentView bringSubviewToFront:self.view]; 755 | [self bringToFront]; 756 | } 757 | 758 | 759 | if(direction==UIPanGestureRecognizerDirectionUp && videoView.frame.origin.y <= 10) 760 | { 761 | [self didFullExpandByGesture]; 762 | } 763 | } 764 | // what is this case...? 765 | else if (wFrame.origin.y < finalViewOffsetY && wFrame.origin.y > 0) 766 | { 767 | pageWrapper.frame = wFrame; 768 | videoWrapper.frame = vFrame; 769 | videoView.frame=CGRectMake( videoView.frame.origin.x, videoView.frame.origin.x, vFrame.size.width, vFrame.size.height); 770 | 771 | self.bodyView.frame = CGRectMake( 772 | 0, 773 | videoView.frame.size.height,// keep stay on bottom of videoView 774 | self.bodyView.frame.size.width, 775 | self.bodyView.frame.size.height 776 | ); 777 | borderView.frame = CGRectMake(videoView.frame.origin.y - 1, 778 | videoView.frame.origin.x - 1, 779 | videoView.frame.size.width + 1, 780 | videoView.frame.size.height + 1); 781 | 782 | borderView.alpha = progressRate; 783 | 784 | self.controllerView.frame = videoView.frame; 785 | } 786 | 787 | 788 | [recognizer setTranslation:CGPointZero inView:recognizer.view]; 789 | 790 | } 791 | 792 | 793 | 794 | 795 | -(void)adjustViewOnHorizontalPan:(UIPanGestureRecognizer *)recognizer { 796 | // [self.txtViewGrowing resignFirstResponder]; 797 | if(pageWrapper.alpha<=0) { 798 | 799 | CGFloat x = [recognizer locationInView:self.view].x; 800 | 801 | if (direction==UIPanGestureRecognizerDirectionLeft) 802 | { 803 | // NSLog(@"recognizer x=%f",recognizer.view.frame.origin.x); 804 | CGPoint velocity = [recognizer velocityInView:recognizer.view]; 805 | 806 | BOOL isVerticalGesture = fabs(velocity.y) > fabs(velocity.x); 807 | 808 | 809 | CGPoint translation = [recognizer translationInView:recognizer.view]; 810 | 811 | recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x, 812 | recognizer.view.center.y ); 813 | 814 | 815 | if (!isVerticalGesture) { 816 | 817 | CGFloat percentage = (x/windowFrame.size.width); 818 | 819 | recognizer.view.alpha = percentage; 820 | 821 | } 822 | 823 | [recognizer setTranslation:CGPointZero inView:recognizer.view]; 824 | } 825 | else if (direction==UIPanGestureRecognizerDirectionRight) 826 | { 827 | // NSLog(@"recognizer x=%f",recognizer.view.frame.origin.x); 828 | CGPoint velocity = [recognizer velocityInView:recognizer.view]; 829 | 830 | BOOL isVerticalGesture = fabs(velocity.y) > fabs(velocity.x); 831 | 832 | CGPoint translation = [recognizer translationInView:recognizer.view]; 833 | 834 | recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x, 835 | recognizer.view.center.y ); 836 | 837 | if (!isVerticalGesture) { 838 | 839 | if(velocity.x > 0) 840 | { 841 | 842 | CGFloat percentage = (x/windowFrame.size.width); 843 | recognizer.view.alpha =1.0- percentage; } 844 | else 845 | { 846 | CGFloat percentage = (x/windowFrame.size.width); 847 | recognizer.view.alpha =percentage; 848 | 849 | 850 | } 851 | 852 | } 853 | 854 | [recognizer setTranslation:CGPointZero inView:recognizer.view]; 855 | } 856 | } 857 | } 858 | 859 | 860 | - (void) calcNewFrameWithParsentage:(CGFloat) persentage newOffsetY:(CGFloat) newOffsetY{ 861 | CGFloat newWidth = minimamVideoWidth + ((maxW - minimamVideoWidth) * (1 - persentage)); 862 | CGFloat newHeight = newWidth * videoHeightRatio; 863 | 864 | CGFloat newOffsetX = maxW - newWidth - (finalMargin * persentage); 865 | 866 | vFrame.size.width = newWidth;//self.view.bounds.size.width - xOffset; 867 | vFrame.size.height = newHeight;//(200 - xOffset * 0.5); 868 | 869 | vFrame.origin.y = newOffsetY;//trueOffset - finalMargin * 2; 870 | wFrame.origin.y = newOffsetY; 871 | 872 | vFrame.origin.x = newOffsetX;//maxW - vFrame.size.width - finalMargin; 873 | wFrame.origin.x = newOffsetX; 874 | // vFrame.origin.y = realNewOffsetY;//trueOffset - finalMargin * 2; 875 | // wFrame.origin.y = realNewOffsetY; 876 | 877 | } 878 | 879 | -(void) setFinalFrame { 880 | vFrame.size.width = minimamVideoWidth;//self.view.bounds.size.width - xOffset; 881 | // ↓ 882 | vFrame.size.height = vFrame.size.width * videoHeightRatio;//(200 - xOffset * 0.5); 883 | vFrame.origin.y = maxH - vFrame.size.height - finalMargin;//trueOffset - finalMargin * 2; 884 | vFrame.origin.x = maxW - vFrame.size.width - finalMargin; 885 | wFrame.origin.y = vFrame.origin.y; 886 | wFrame.origin.x = vFrame.origin.x; 887 | } 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | # pragma mark - animations 898 | 899 | -(void)expandView 900 | { 901 | // [self.txtViewGrowing resignFirstResponder]; 902 | [UIView animateWithDuration:0.5 903 | delay:0.0 904 | options:UIViewAnimationOptionCurveEaseInOut 905 | animations:^ { 906 | pageWrapper.frame = pageWrapperFrame; 907 | videoWrapper.frame = videoWrapperFrame; 908 | videoWrapper.alpha = 1; 909 | videoView.frame = videoWrapperFrame; 910 | pageWrapper.alpha = 1.0; 911 | transparentBlackSheet.alpha = 1.0; 912 | borderView.alpha = 0.0; 913 | 914 | self.bodyView.frame = CGRectMake(0, 915 | videoView.frame.size.height,// keep stay on bottom of videoView 916 | self.bodyView.frame.size.width, 917 | self.bodyView.frame.size.height); 918 | 919 | borderView.frame = CGRectMake(videoView.frame.origin.y - 1, 920 | videoView.frame.origin.x - 1, 921 | videoView.frame.size.width + 1, 922 | videoView.frame.size.height + 1); 923 | 924 | self.controllerView.frame = videoView.frame; 925 | } 926 | completion:^(BOOL finished) { 927 | 928 | for (UIGestureRecognizer *recognizer in videoWrapper.gestureRecognizers) { 929 | if([recognizer isKindOfClass:[UITapGestureRecognizer class]]) { 930 | [videoWrapper removeGestureRecognizer:recognizer]; 931 | } 932 | } 933 | 934 | UITapGestureRecognizer* expandedTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapExpandedVideoView)]; 935 | expandedTap.numberOfTapsRequired = 1; 936 | expandedTap.delegate = self; 937 | [videoWrapper addGestureRecognizer:expandedTap]; 938 | 939 | 940 | // player.controlStyle = MPMovieControlStyleDefault; 941 | // [self showVideoControl]; 942 | isExpandedMode = TRUE; 943 | // self.controllerView.hidden = FALSE; 944 | [self didExpand]; 945 | }]; 946 | } 947 | 948 | 949 | 950 | -(void)minimizeView 951 | { 952 | // self.controllerView.hidden = TRUE; 953 | 954 | [self setFinalFrame]; 955 | [self hideControllerView]; 956 | 957 | [UIView animateWithDuration:0.5 958 | delay:0.0 959 | options:UIViewAnimationOptionCurveEaseInOut 960 | animations:^ { 961 | pageWrapper.frame = wFrame; 962 | videoWrapper.frame = vFrame; 963 | videoView.frame=CGRectMake( videoView.frame.origin.x, videoView.frame.origin.x, vFrame.size.width, vFrame.size.height); 964 | pageWrapper.alpha=0; 965 | transparentBlackSheet.alpha=0.0; 966 | borderView.alpha = 1.0; 967 | 968 | borderView.frame = CGRectMake(videoView.frame.origin.y - 1, 969 | videoView.frame.origin.x - 1, 970 | videoView.frame.size.width + 1, 971 | videoView.frame.size.height + 1); 972 | 973 | self.controllerView.frame = videoView.frame; 974 | } 975 | completion:^(BOOL finished) { 976 | // [self hideVideoControl]; 977 | [self didMinimize]; 978 | //add tap gesture 979 | tapRecognizer = nil; 980 | if(tapRecognizer == nil) 981 | { 982 | 983 | for (UIGestureRecognizer *recognizer in videoWrapper.gestureRecognizers) { 984 | if([recognizer isKindOfClass:[UITapGestureRecognizer class]]) { 985 | [videoWrapper removeGestureRecognizer:recognizer]; 986 | } 987 | } 988 | 989 | tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(expandViewOnTap:)]; 990 | tapRecognizer.numberOfTapsRequired = 1; 991 | tapRecognizer.delegate = self; 992 | [videoWrapper addGestureRecognizer:tapRecognizer]; 993 | } 994 | 995 | isExpandedMode=FALSE; 996 | minimizedVideoFrame = videoWrapper.frame; 997 | 998 | if(direction==UIPanGestureRecognizerDirectionDown) 999 | { 1000 | // [parentView bringSubviewToFront:self.view]; 1001 | [self bringToFront]; 1002 | } 1003 | }]; 1004 | } 1005 | 1006 | -(void)animateMiniViewToNormalPosition:(UIPanGestureRecognizer *)recognizer completion:(void (^)())completion { 1007 | 1008 | [self setFinalFrame]; 1009 | 1010 | [UIView animateWithDuration:0.25 1011 | delay:0.0 1012 | options:UIViewAnimationOptionCurveEaseInOut 1013 | animations:^ { 1014 | pageWrapper.frame = wFrame; 1015 | videoWrapper.frame = vFrame; 1016 | videoView.frame=CGRectMake( 1017 | videoView.frame.origin.x, 1018 | videoView.frame.origin.x, 1019 | vFrame.size.width, 1020 | vFrame.size.height 1021 | ); 1022 | pageWrapper.alpha = 0; 1023 | videoWrapper.alpha = 1; 1024 | borderView.alpha = 1; 1025 | 1026 | self.controllerView.frame = videoView.frame; 1027 | } 1028 | completion:^(BOOL finished) { 1029 | if (completion) completion(); 1030 | }]; 1031 | [recognizer setTranslation:CGPointZero inView:recognizer.view]; 1032 | } 1033 | 1034 | -(void)fadeOutViewToRight:(UIPanGestureRecognizer *)recognizer completion:(void (^)())completion { 1035 | // [self.txtViewGrowing resignFirstResponder]; 1036 | 1037 | vFrame.origin.x = maxW + minimamVideoWidth; 1038 | wFrame.origin.x = maxW + minimamVideoWidth; 1039 | 1040 | [UIView animateWithDuration:0.1 1041 | delay:0.0 1042 | options:UIViewAnimationOptionCurveEaseInOut 1043 | animations:^ { 1044 | pageWrapper.frame = wFrame; 1045 | videoWrapper.frame = vFrame; 1046 | videoView.frame=CGRectMake( videoView.frame.origin.x, videoView.frame.origin.x, vFrame.size.width, vFrame.size.height); 1047 | pageWrapper.alpha = 0; 1048 | videoWrapper.alpha = 0; 1049 | borderView.alpha = 0; 1050 | 1051 | self.controllerView.frame = videoView.frame; 1052 | } 1053 | completion:^(BOOL finished) { 1054 | if (completion) completion(); 1055 | [self didDisappear]; 1056 | }]; 1057 | [recognizer setTranslation:CGPointZero inView:recognizer.view]; 1058 | } 1059 | 1060 | -(void)fadeOutViewToLeft:(UIPanGestureRecognizer *)recognizer completion:(void (^)())completion { 1061 | // [self.txtViewGrowing resignFirstResponder]; 1062 | 1063 | vFrame.origin.x = -maxW; 1064 | wFrame.origin.x = -maxW; 1065 | 1066 | [UIView animateWithDuration:0.1 1067 | delay:0.0 1068 | options:UIViewAnimationOptionCurveEaseInOut 1069 | animations:^ { 1070 | pageWrapper.frame = wFrame; 1071 | videoWrapper.frame = vFrame; 1072 | videoView.frame=CGRectMake( videoView.frame.origin.x, videoView.frame.origin.x, vFrame.size.width, vFrame.size.height); 1073 | pageWrapper.alpha = 0; 1074 | videoWrapper.alpha = 0; 1075 | borderView.alpha = 0; 1076 | 1077 | self.controllerView.frame = videoView.frame; 1078 | } 1079 | completion:^(BOOL finished) { 1080 | if (completion) completion(); 1081 | [self didDisappear]; 1082 | }]; 1083 | 1084 | [recognizer setTranslation:CGPointZero inView:recognizer.view]; 1085 | } 1086 | 1087 | 1088 | #pragma mark- Pan Gesture Delagate 1089 | 1090 | - (BOOL)gestureRecognizerShould:(UIGestureRecognizer *)gestureRecognizer { 1091 | if(gestureRecognizer.view.frame.origin.y < 0) 1092 | { 1093 | return NO; 1094 | } 1095 | else { 1096 | return YES; 1097 | } 1098 | } 1099 | 1100 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer 1101 | { 1102 | return YES; 1103 | } 1104 | 1105 | 1106 | 1107 | #pragma mark- Status Bar Hidden function 1108 | 1109 | - (BOOL)prefersStatusBarHidden { 1110 | return YES; 1111 | } 1112 | - (NSUInteger) supportedInterfaceOrientations { 1113 | return UIInterfaceOrientationMaskPortrait; 1114 | } 1115 | - (BOOL)shouldAutorotate 1116 | { 1117 | return NO; 1118 | } 1119 | 1120 | @end 1121 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // YouTubeDraggableVideo 4 | // 5 | // Created by Sandeep Mukherjee on 02/02/15. 6 | // Copyright (c) 2015 Sandeep Mukherjee. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /YouTubeDraggableVideo/profilePhoto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takuoka/DraggableFloatingViewController/f588f100aaf033054386b1f8c0748e64e65b59e6/YouTubeDraggableVideo/profilePhoto.png -------------------------------------------------------------------------------- /YouTubeDraggableVideo/sampleMovies/sample.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takuoka/DraggableFloatingViewController/f588f100aaf033054386b1f8c0748e64e65b59e6/YouTubeDraggableVideo/sampleMovies/sample.mp4 -------------------------------------------------------------------------------- /YouTubeDraggableVideo/sampleMovies/test.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takuoka/DraggableFloatingViewController/f588f100aaf033054386b1f8c0748e64e65b59e6/YouTubeDraggableVideo/sampleMovies/test.mp4 -------------------------------------------------------------------------------- /YouTubeDraggableVideoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.vizllx.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /YouTubeDraggableVideoTests/YouTubeDraggableVideoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // YouTubeDraggableVideoTests.m 3 | // YouTubeDraggableVideoTests 4 | // 5 | // Created by Sandeep Mukherjee on 02/02/15. 6 | // Copyright (c) 2015 Sandeep Mukherjee. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface YouTubeDraggableVideoTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation YouTubeDraggableVideoTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | --------------------------------------------------------------------------------