├── .gitignore ├── JAPanoView.podspec ├── LICENSE ├── PanoTest.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── PanoTest ├── Down_fixed.jpg ├── JAAppDelegate.h ├── JAAppDelegate.m ├── JAPanoView.h ├── JAPanoView.m ├── JAViewController.h ├── JAViewController.m ├── PanoTest-Info.plist ├── PanoTest-Prefix.pch ├── TowerHousepano_b.jpg ├── TowerHousepano_f.jpg ├── TowerHousepano_l.jpg ├── TowerHousepano_r.jpg ├── TowerHousepano_u.jpg ├── en.lproj │ └── InfoPlist.strings └── main.m └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.xcuserdatad 2 | *.xccheckout 3 | -------------------------------------------------------------------------------- /JAPanoView.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "JAPanoView" 3 | s.version = "0.1.2" 4 | s.license = { :type => "MIT", :file => "LICENSE" } 5 | s.author = { "Javier Alonso" => "", "Damien Debin" => "damien.debin@gmail.com" } 6 | s.homepage = "https://github.com/ddebin/JAPanoView" 7 | s.summary = "360° panorama viewer for cubic panoramic images." 8 | s.source = { :git => "https://github.com/ddebin/JAPanoView.git", :tag => s.version.to_s } 9 | s.source_files = "PanoTest/JAPanoView.{h,m}", "PanoTest/JAViewController.{h,m}" 10 | s.platform = :ios, "5.0" 11 | s.frameworks = "QuartzCore" 12 | s.requires_arc = true 13 | end 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2014 Javier Alonso, Damien Debin 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /PanoTest.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3B018C8514ED9FBB00D1F955 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B018C8414ED9FBB00D1F955 /* UIKit.framework */; }; 11 | 3B018C8714ED9FBB00D1F955 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B018C8614ED9FBB00D1F955 /* Foundation.framework */; }; 12 | 3B018C8914ED9FBB00D1F955 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B018C8814ED9FBB00D1F955 /* CoreGraphics.framework */; }; 13 | 3B018C8F14ED9FBB00D1F955 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3B018C8D14ED9FBB00D1F955 /* InfoPlist.strings */; }; 14 | 3B018C9114ED9FBB00D1F955 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B018C9014ED9FBB00D1F955 /* main.m */; }; 15 | 3B018C9514ED9FBB00D1F955 /* JAAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B018C9414ED9FBB00D1F955 /* JAAppDelegate.m */; }; 16 | 3B018C9814ED9FBB00D1F955 /* JAViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B018C9714ED9FBB00D1F955 /* JAViewController.m */; }; 17 | 3B018CA314ED9FEC00D1F955 /* JAPanoView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B018CA214ED9FEC00D1F955 /* JAPanoView.m */; }; 18 | 3B018CA514EDA3C200D1F955 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B018CA414EDA3C200D1F955 /* QuartzCore.framework */; }; 19 | 3B018CAC14EDB03400D1F955 /* Down_fixed.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 3B018CA614EDB03400D1F955 /* Down_fixed.jpg */; }; 20 | 3B018CAD14EDB03400D1F955 /* TowerHousepano_b.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 3B018CA714EDB03400D1F955 /* TowerHousepano_b.jpg */; }; 21 | 3B018CAE14EDB03400D1F955 /* TowerHousepano_f.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 3B018CA814EDB03400D1F955 /* TowerHousepano_f.jpg */; }; 22 | 3B018CAF14EDB03400D1F955 /* TowerHousepano_l.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 3B018CA914EDB03400D1F955 /* TowerHousepano_l.jpg */; }; 23 | 3B018CB014EDB03400D1F955 /* TowerHousepano_r.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 3B018CAA14EDB03400D1F955 /* TowerHousepano_r.jpg */; }; 24 | 3B018CB114EDB03400D1F955 /* TowerHousepano_u.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 3B018CAB14EDB03400D1F955 /* TowerHousepano_u.jpg */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 3B018C8014ED9FBB00D1F955 /* PanoTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PanoTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 3B018C8414ED9FBB00D1F955 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 30 | 3B018C8614ED9FBB00D1F955 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 31 | 3B018C8814ED9FBB00D1F955 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 32 | 3B018C8C14ED9FBB00D1F955 /* PanoTest-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "PanoTest-Info.plist"; sourceTree = ""; }; 33 | 3B018C8E14ED9FBB00D1F955 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 34 | 3B018C9014ED9FBB00D1F955 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 35 | 3B018C9214ED9FBB00D1F955 /* PanoTest-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PanoTest-Prefix.pch"; sourceTree = ""; }; 36 | 3B018C9314ED9FBB00D1F955 /* JAAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JAAppDelegate.h; sourceTree = ""; }; 37 | 3B018C9414ED9FBB00D1F955 /* JAAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JAAppDelegate.m; sourceTree = ""; }; 38 | 3B018C9614ED9FBB00D1F955 /* JAViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JAViewController.h; sourceTree = ""; }; 39 | 3B018C9714ED9FBB00D1F955 /* JAViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JAViewController.m; sourceTree = ""; }; 40 | 3B018CA114ED9FEC00D1F955 /* JAPanoView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JAPanoView.h; sourceTree = ""; }; 41 | 3B018CA214ED9FEC00D1F955 /* JAPanoView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JAPanoView.m; sourceTree = ""; }; 42 | 3B018CA414EDA3C200D1F955 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 43 | 3B018CA614EDB03400D1F955 /* Down_fixed.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = Down_fixed.jpg; sourceTree = ""; }; 44 | 3B018CA714EDB03400D1F955 /* TowerHousepano_b.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = TowerHousepano_b.jpg; sourceTree = ""; }; 45 | 3B018CA814EDB03400D1F955 /* TowerHousepano_f.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = TowerHousepano_f.jpg; sourceTree = ""; }; 46 | 3B018CA914EDB03400D1F955 /* TowerHousepano_l.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = TowerHousepano_l.jpg; sourceTree = ""; }; 47 | 3B018CAA14EDB03400D1F955 /* TowerHousepano_r.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = TowerHousepano_r.jpg; sourceTree = ""; }; 48 | 3B018CAB14EDB03400D1F955 /* TowerHousepano_u.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = TowerHousepano_u.jpg; sourceTree = ""; }; 49 | /* End PBXFileReference section */ 50 | 51 | /* Begin PBXFrameworksBuildPhase section */ 52 | 3B018C7D14ED9FBB00D1F955 /* Frameworks */ = { 53 | isa = PBXFrameworksBuildPhase; 54 | buildActionMask = 2147483647; 55 | files = ( 56 | 3B018CA514EDA3C200D1F955 /* QuartzCore.framework in Frameworks */, 57 | 3B018C8514ED9FBB00D1F955 /* UIKit.framework in Frameworks */, 58 | 3B018C8714ED9FBB00D1F955 /* Foundation.framework in Frameworks */, 59 | 3B018C8914ED9FBB00D1F955 /* CoreGraphics.framework in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | /* End PBXFrameworksBuildPhase section */ 64 | 65 | /* Begin PBXGroup section */ 66 | 3B018C7514ED9FBB00D1F955 = { 67 | isa = PBXGroup; 68 | children = ( 69 | 3B018C8A14ED9FBB00D1F955 /* PanoTest */, 70 | 3B018C8314ED9FBB00D1F955 /* Frameworks */, 71 | 3B018C8114ED9FBB00D1F955 /* Products */, 72 | ); 73 | sourceTree = ""; 74 | }; 75 | 3B018C8114ED9FBB00D1F955 /* Products */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 3B018C8014ED9FBB00D1F955 /* PanoTest.app */, 79 | ); 80 | name = Products; 81 | sourceTree = ""; 82 | }; 83 | 3B018C8314ED9FBB00D1F955 /* Frameworks */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 3B018CA414EDA3C200D1F955 /* QuartzCore.framework */, 87 | 3B018C8414ED9FBB00D1F955 /* UIKit.framework */, 88 | 3B018C8614ED9FBB00D1F955 /* Foundation.framework */, 89 | 3B018C8814ED9FBB00D1F955 /* CoreGraphics.framework */, 90 | ); 91 | name = Frameworks; 92 | sourceTree = ""; 93 | }; 94 | 3B018C8A14ED9FBB00D1F955 /* PanoTest */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 3B018C9314ED9FBB00D1F955 /* JAAppDelegate.h */, 98 | 3B018C9414ED9FBB00D1F955 /* JAAppDelegate.m */, 99 | 3B018C9614ED9FBB00D1F955 /* JAViewController.h */, 100 | 3B018C9714ED9FBB00D1F955 /* JAViewController.m */, 101 | 3B018C8B14ED9FBB00D1F955 /* Supporting Files */, 102 | 3B018CA114ED9FEC00D1F955 /* JAPanoView.h */, 103 | 3B018CA214ED9FEC00D1F955 /* JAPanoView.m */, 104 | ); 105 | path = PanoTest; 106 | sourceTree = ""; 107 | }; 108 | 3B018C8B14ED9FBB00D1F955 /* Supporting Files */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | 3B018CA614EDB03400D1F955 /* Down_fixed.jpg */, 112 | 3B018CA714EDB03400D1F955 /* TowerHousepano_b.jpg */, 113 | 3B018CA814EDB03400D1F955 /* TowerHousepano_f.jpg */, 114 | 3B018CA914EDB03400D1F955 /* TowerHousepano_l.jpg */, 115 | 3B018CAA14EDB03400D1F955 /* TowerHousepano_r.jpg */, 116 | 3B018CAB14EDB03400D1F955 /* TowerHousepano_u.jpg */, 117 | 3B018C8C14ED9FBB00D1F955 /* PanoTest-Info.plist */, 118 | 3B018C8D14ED9FBB00D1F955 /* InfoPlist.strings */, 119 | 3B018C9014ED9FBB00D1F955 /* main.m */, 120 | 3B018C9214ED9FBB00D1F955 /* PanoTest-Prefix.pch */, 121 | ); 122 | name = "Supporting Files"; 123 | sourceTree = ""; 124 | }; 125 | /* End PBXGroup section */ 126 | 127 | /* Begin PBXNativeTarget section */ 128 | 3B018C7F14ED9FBB00D1F955 /* PanoTest */ = { 129 | isa = PBXNativeTarget; 130 | buildConfigurationList = 3B018C9E14ED9FBB00D1F955 /* Build configuration list for PBXNativeTarget "PanoTest" */; 131 | buildPhases = ( 132 | 3B018C7C14ED9FBB00D1F955 /* Sources */, 133 | 3B018C7D14ED9FBB00D1F955 /* Frameworks */, 134 | 3B018C7E14ED9FBB00D1F955 /* Resources */, 135 | ); 136 | buildRules = ( 137 | ); 138 | dependencies = ( 139 | ); 140 | name = PanoTest; 141 | productName = PanoTest; 142 | productReference = 3B018C8014ED9FBB00D1F955 /* PanoTest.app */; 143 | productType = "com.apple.product-type.application"; 144 | }; 145 | /* End PBXNativeTarget section */ 146 | 147 | /* Begin PBXProject section */ 148 | 3B018C7714ED9FBB00D1F955 /* Project object */ = { 149 | isa = PBXProject; 150 | attributes = { 151 | LastUpgradeCheck = 0630; 152 | ORGANIZATIONNAME = "NG Servicios"; 153 | }; 154 | buildConfigurationList = 3B018C7A14ED9FBB00D1F955 /* Build configuration list for PBXProject "PanoTest" */; 155 | compatibilityVersion = "Xcode 3.2"; 156 | developmentRegion = English; 157 | hasScannedForEncodings = 0; 158 | knownRegions = ( 159 | en, 160 | ); 161 | mainGroup = 3B018C7514ED9FBB00D1F955; 162 | productRefGroup = 3B018C8114ED9FBB00D1F955 /* Products */; 163 | projectDirPath = ""; 164 | projectRoot = ""; 165 | targets = ( 166 | 3B018C7F14ED9FBB00D1F955 /* PanoTest */, 167 | ); 168 | }; 169 | /* End PBXProject section */ 170 | 171 | /* Begin PBXResourcesBuildPhase section */ 172 | 3B018C7E14ED9FBB00D1F955 /* Resources */ = { 173 | isa = PBXResourcesBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | 3B018C8F14ED9FBB00D1F955 /* InfoPlist.strings in Resources */, 177 | 3B018CAC14EDB03400D1F955 /* Down_fixed.jpg in Resources */, 178 | 3B018CAD14EDB03400D1F955 /* TowerHousepano_b.jpg in Resources */, 179 | 3B018CAE14EDB03400D1F955 /* TowerHousepano_f.jpg in Resources */, 180 | 3B018CAF14EDB03400D1F955 /* TowerHousepano_l.jpg in Resources */, 181 | 3B018CB014EDB03400D1F955 /* TowerHousepano_r.jpg in Resources */, 182 | 3B018CB114EDB03400D1F955 /* TowerHousepano_u.jpg in Resources */, 183 | ); 184 | runOnlyForDeploymentPostprocessing = 0; 185 | }; 186 | /* End PBXResourcesBuildPhase section */ 187 | 188 | /* Begin PBXSourcesBuildPhase section */ 189 | 3B018C7C14ED9FBB00D1F955 /* Sources */ = { 190 | isa = PBXSourcesBuildPhase; 191 | buildActionMask = 2147483647; 192 | files = ( 193 | 3B018C9114ED9FBB00D1F955 /* main.m in Sources */, 194 | 3B018C9514ED9FBB00D1F955 /* JAAppDelegate.m in Sources */, 195 | 3B018C9814ED9FBB00D1F955 /* JAViewController.m in Sources */, 196 | 3B018CA314ED9FEC00D1F955 /* JAPanoView.m in Sources */, 197 | ); 198 | runOnlyForDeploymentPostprocessing = 0; 199 | }; 200 | /* End PBXSourcesBuildPhase section */ 201 | 202 | /* Begin PBXVariantGroup section */ 203 | 3B018C8D14ED9FBB00D1F955 /* InfoPlist.strings */ = { 204 | isa = PBXVariantGroup; 205 | children = ( 206 | 3B018C8E14ED9FBB00D1F955 /* en */, 207 | ); 208 | name = InfoPlist.strings; 209 | sourceTree = ""; 210 | }; 211 | /* End PBXVariantGroup section */ 212 | 213 | /* Begin XCBuildConfiguration section */ 214 | 3B018C9C14ED9FBB00D1F955 /* Debug */ = { 215 | isa = XCBuildConfiguration; 216 | buildSettings = { 217 | ALWAYS_SEARCH_USER_PATHS = NO; 218 | CLANG_ENABLE_OBJC_ARC = YES; 219 | CLANG_WARN_BOOL_CONVERSION = YES; 220 | CLANG_WARN_CONSTANT_CONVERSION = YES; 221 | CLANG_WARN_EMPTY_BODY = YES; 222 | CLANG_WARN_ENUM_CONVERSION = YES; 223 | CLANG_WARN_INT_CONVERSION = YES; 224 | CLANG_WARN_UNREACHABLE_CODE = YES; 225 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 226 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 227 | COPY_PHASE_STRIP = NO; 228 | ENABLE_STRICT_OBJC_MSGSEND = YES; 229 | GCC_C_LANGUAGE_STANDARD = gnu99; 230 | GCC_NO_COMMON_BLOCKS = YES; 231 | GCC_OPTIMIZATION_LEVEL = 0; 232 | GCC_PREPROCESSOR_DEFINITIONS = ( 233 | "DEBUG=1", 234 | "$(inherited)", 235 | ); 236 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 237 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 238 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 239 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 240 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 241 | GCC_WARN_UNDECLARED_SELECTOR = YES; 242 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 243 | GCC_WARN_UNUSED_FUNCTION = YES; 244 | GCC_WARN_UNUSED_VARIABLE = YES; 245 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 246 | ONLY_ACTIVE_ARCH = YES; 247 | SDKROOT = iphoneos; 248 | TARGETED_DEVICE_FAMILY = 2; 249 | }; 250 | name = Debug; 251 | }; 252 | 3B018C9D14ED9FBB00D1F955 /* Release */ = { 253 | isa = XCBuildConfiguration; 254 | buildSettings = { 255 | ALWAYS_SEARCH_USER_PATHS = NO; 256 | CLANG_ENABLE_OBJC_ARC = YES; 257 | CLANG_WARN_BOOL_CONVERSION = YES; 258 | CLANG_WARN_CONSTANT_CONVERSION = YES; 259 | CLANG_WARN_EMPTY_BODY = YES; 260 | CLANG_WARN_ENUM_CONVERSION = YES; 261 | CLANG_WARN_INT_CONVERSION = YES; 262 | CLANG_WARN_UNREACHABLE_CODE = YES; 263 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 264 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 265 | COPY_PHASE_STRIP = YES; 266 | ENABLE_STRICT_OBJC_MSGSEND = YES; 267 | GCC_C_LANGUAGE_STANDARD = gnu99; 268 | GCC_NO_COMMON_BLOCKS = YES; 269 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 270 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 271 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 272 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 273 | GCC_WARN_UNDECLARED_SELECTOR = YES; 274 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 275 | GCC_WARN_UNUSED_FUNCTION = YES; 276 | GCC_WARN_UNUSED_VARIABLE = YES; 277 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 278 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 279 | SDKROOT = iphoneos; 280 | TARGETED_DEVICE_FAMILY = 2; 281 | VALIDATE_PRODUCT = YES; 282 | }; 283 | name = Release; 284 | }; 285 | 3B018C9F14ED9FBB00D1F955 /* Debug */ = { 286 | isa = XCBuildConfiguration; 287 | buildSettings = { 288 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 289 | GCC_PREFIX_HEADER = "PanoTest/PanoTest-Prefix.pch"; 290 | INFOPLIST_FILE = "PanoTest/PanoTest-Info.plist"; 291 | PRODUCT_NAME = "$(TARGET_NAME)"; 292 | WRAPPER_EXTENSION = app; 293 | }; 294 | name = Debug; 295 | }; 296 | 3B018CA014ED9FBB00D1F955 /* Release */ = { 297 | isa = XCBuildConfiguration; 298 | buildSettings = { 299 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 300 | GCC_PREFIX_HEADER = "PanoTest/PanoTest-Prefix.pch"; 301 | INFOPLIST_FILE = "PanoTest/PanoTest-Info.plist"; 302 | PRODUCT_NAME = "$(TARGET_NAME)"; 303 | WRAPPER_EXTENSION = app; 304 | }; 305 | name = Release; 306 | }; 307 | /* End XCBuildConfiguration section */ 308 | 309 | /* Begin XCConfigurationList section */ 310 | 3B018C7A14ED9FBB00D1F955 /* Build configuration list for PBXProject "PanoTest" */ = { 311 | isa = XCConfigurationList; 312 | buildConfigurations = ( 313 | 3B018C9C14ED9FBB00D1F955 /* Debug */, 314 | 3B018C9D14ED9FBB00D1F955 /* Release */, 315 | ); 316 | defaultConfigurationIsVisible = 0; 317 | defaultConfigurationName = Release; 318 | }; 319 | 3B018C9E14ED9FBB00D1F955 /* Build configuration list for PBXNativeTarget "PanoTest" */ = { 320 | isa = XCConfigurationList; 321 | buildConfigurations = ( 322 | 3B018C9F14ED9FBB00D1F955 /* Debug */, 323 | 3B018CA014ED9FBB00D1F955 /* Release */, 324 | ); 325 | defaultConfigurationIsVisible = 0; 326 | defaultConfigurationName = Release; 327 | }; 328 | /* End XCConfigurationList section */ 329 | }; 330 | rootObject = 3B018C7714ED9FBB00D1F955 /* Project object */; 331 | } 332 | -------------------------------------------------------------------------------- /PanoTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PanoTest/Down_fixed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartapps-fr/JAPanoView/cca75dc4d089484394cf79f5e21d4daa0559b84b/PanoTest/Down_fixed.jpg -------------------------------------------------------------------------------- /PanoTest/JAAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // JAAppDelegate.h 3 | // PanoTest 4 | // 5 | // Created by Javier Alonso Gutiérrez on 16/02/12. 6 | // Copyright (c) 2012 NG Servicios. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class JAViewController; 12 | 13 | @interface JAAppDelegate : UIResponder 14 | 15 | @property (strong, nonatomic) UIWindow *window; 16 | 17 | @property (strong, nonatomic) JAViewController *viewController; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /PanoTest/JAAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // JAAppDelegate.m 3 | // PanoTest 4 | // 5 | // Created by Javier Alonso Gutiérrez on 16/02/12. 6 | // Copyright (c) 2012 NG Servicios. All rights reserved. 7 | // 8 | 9 | #import "JAAppDelegate.h" 10 | 11 | #import "JAViewController.h" 12 | 13 | @implementation JAAppDelegate 14 | 15 | @synthesize window = _window; 16 | @synthesize viewController = _viewController; 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 20 | // Override point for customization after application launch. 21 | self.viewController = [[JAViewController alloc] init]; 22 | self.window.rootViewController = self.viewController; 23 | [self.window makeKeyAndVisible]; 24 | return YES; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /PanoTest/JAPanoView.h: -------------------------------------------------------------------------------- 1 | // 2 | // JAPanoView.h 3 | // PanoTest 4 | // 5 | // Created by Javier Alonso Gutiérrez on 16/02/12. 6 | // Copyright (c) 2012 NG Servicios. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol JAPanoViewDelegate; 12 | 13 | @interface JAPanoView : UIView 14 | 15 | @property (nonatomic) CGFloat zoomFactor; // from 0 to 100; default value: 1 16 | @property (nonatomic) CGFloat hAngle; // azimuth angle in radians; from 0 to 2*PI; default value: 0 17 | @property (nonatomic) CGFloat vAngle; // elavation angle in radians; from -(PI/2) to (PI/2); default value: 0 18 | @property (nonatomic) CGFloat leftLimit, rightLimit, upLimit, downLimit; // angle limits 19 | @property (nonatomic) CGFloat minZoom, maxZoom; // zoom limits; default values: min 0, max 100 20 | @property (nonatomic, getter = isPanEnabled) BOOL panEnabled; 21 | @property (nonatomic, getter = isZoomEnabled) BOOL zoomEnabled; 22 | @property (nonatomic, weak) id delegate; 23 | 24 | -(void)setFrontImage:(UIImage *)i1 rightImage:(UIImage *)i2 backImage:(UIImage *)i3 leftImage:(UIImage *)i4 topImage:(UIImage *)i5 bottomImage:(UIImage *)i6; 25 | -(void)setFrontImageOver:(UIImage *)i1 rightImageOver:(UIImage *)i2 backImageOver:(UIImage *)i3 leftImageOver:(UIImage *)i4 topImageOver:(UIImage *)i5 bottomImageOver:(UIImage *)i6; 26 | 27 | -(void)addHotspot:(UIView*)hotspotView atHAngle:(CGFloat)hAngle vAngle:(CGFloat)vAngle; 28 | 29 | -(void)setImageOverTransparancy:(CGFloat)transparancyValue; 30 | 31 | -(id)initWithFrame:(CGRect)frame enableImageOver:(BOOL)enableImageOver; 32 | 33 | @end 34 | 35 | @protocol JAPanoViewDelegate 36 | 37 | @optional 38 | -(void)panoViewDidPan:(JAPanoView*)panoView; 39 | -(void)panoViewDidZoom:(JAPanoView*)panoView; 40 | 41 | -(void)panoViewWillBeginPanning:(JAPanoView*)panoView; 42 | -(void)panoViewWillBeginZooming:(JAPanoView *)panoView; 43 | 44 | -(void)panoViewDidEndPanning:(JAPanoView *)panoView; 45 | -(void)panoViewDidEndZooming:(JAPanoView *)panoView; 46 | 47 | @end 48 | 49 | 50 | @interface UIView (JAPanoViewHotspot) 51 | 52 | @property (nonatomic, readonly) JAPanoView *panoView; 53 | @property (nonatomic) BOOL shouldApplyPerspective; 54 | 55 | -(void)removeFromPanoView; 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /PanoTest/JAPanoView.m: -------------------------------------------------------------------------------- 1 | // 2 | // JAPanoView.m 3 | // PanoTest 4 | // 5 | // Created by Javier Alonso Gutiérrez on 16/02/12. 6 | // Copyright (c) 2012 NG Servicios. All rights reserved. 7 | // 8 | 9 | #import "JAPanoView.h" 10 | #import 11 | #import 12 | #import 13 | 14 | @interface UIView (JAPanoViewHotspotPrivate) 15 | 16 | @property (nonatomic, assign) JAPanoView *panoView; 17 | @property (nonatomic) CGFloat hAngle; 18 | @property (nonatomic) CGFloat vAngle; 19 | 20 | @end 21 | 22 | @interface JAPanoView() { 23 | UIImageView *_image1,*_image2,*_image3,*_image4,*_image5,*_image6; 24 | UIImageView *_imageOver1,*_imageOver2,*_imageOver3,*_imageOver4,*_imageOver5,*_imageOver6; 25 | CGFloat _referenceSide; 26 | CGFloat _previousZoomFactor; 27 | NSMutableArray *_hotspots; 28 | UIPanGestureRecognizer *_panGestureRecognizer; 29 | UIPinchGestureRecognizer *_pinchGestureRecognizer; 30 | //delegate 31 | BOOL _delegateDidPan; 32 | BOOL _delegateDidZoom; 33 | BOOL _delegateBeginPan; 34 | BOOL _delegateBeginZoom; 35 | BOOL _delegateEndPan; 36 | BOOL _delegateEndZoom; 37 | } 38 | 39 | - (void)defaultValues; 40 | - (void)render; 41 | - (void)removeHotspot:(UIView*)hotspot; 42 | 43 | @end 44 | 45 | @implementation JAPanoView 46 | 47 | @synthesize zoomFactor=_zoomFactor; 48 | @synthesize hAngle=_hAngle; 49 | @synthesize vAngle=_vAngle; 50 | @synthesize leftLimit=_leftLimit; 51 | @synthesize rightLimit=_rightLimit; 52 | @synthesize upLimit=_upLimit; 53 | @synthesize downLimit=_downLimit; 54 | @synthesize minZoom=_minZoom; 55 | @synthesize maxZoom=_maxZoom; 56 | 57 | - (void)setZoomFactor:(CGFloat)zoomFactor { 58 | //a limit of 0 gets a factor of 0,5 59 | //a limit of 100 gets a factor of 4 60 | CGFloat minFactor=(_minZoom*3.5/100.0)+0.5; 61 | CGFloat maxFactor=(_maxZoom*3.5/100.0)+0.5; 62 | if (zoomFactor>maxFactor) { 63 | zoomFactor=maxFactor; 64 | } else if (zoomFactor)delegate { 102 | _delegate=delegate; 103 | _delegateDidPan=[_delegate respondsToSelector:@selector(panoViewDidPan:)]; 104 | _delegateDidZoom=[_delegate respondsToSelector:@selector(panoViewDidZoom:)]; 105 | _delegateBeginPan=[_delegate respondsToSelector:@selector(panoViewWillBeginPanning:)]; 106 | _delegateBeginZoom=[_delegate respondsToSelector:@selector(panoViewWillBeginZooming:)]; 107 | _delegateEndPan=[_delegate respondsToSelector:@selector(panoViewDidEndPanning:)]; 108 | _delegateEndZoom=[_delegate respondsToSelector:@selector(panoViewDidEndZooming:)]; 109 | } 110 | 111 | - (id)initWithFrame:(CGRect)frame { 112 | return [self initWithFrame:frame enableImageOver:NO]; 113 | } 114 | 115 | - (id)initWithFrame:(CGRect)frame enableImageOver:(BOOL)enableImageOver { 116 | self = [super initWithFrame:frame]; 117 | if (self) { 118 | [self defaultValues]; 119 | if (enableImageOver) [self defaultValuesForImageOver]; 120 | } 121 | return self; 122 | } 123 | 124 | - (id)initWithCoder:(NSCoder *)aDecoder { 125 | return [self initWithCoder:aDecoder enableImageOver:NO]; 126 | } 127 | 128 | - (id)initWithCoder:(NSCoder *)aDecoder enableImageOver:(BOOL)enableImageOver { 129 | self=[super initWithCoder:aDecoder]; 130 | if (self) { 131 | [self defaultValues]; 132 | if (enableImageOver) [self defaultValuesForImageOver]; 133 | } 134 | return self; 135 | } 136 | 137 | - (void)defaultValues { 138 | _hotspots=[NSMutableArray array]; 139 | if (self.bounds.size.width>self.bounds.size.height) { 140 | _referenceSide=self.bounds.size.width/2; 141 | } else { 142 | _referenceSide=self.bounds.size.height/2; 143 | } 144 | CGRect rect = CGRectMake(0, 0, _referenceSide*2, _referenceSide*2); 145 | CGPoint centerPoint=CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2); 146 | 147 | // Initialization code. 148 | _image1 = [[UIImageView alloc] initWithFrame:rect]; 149 | _image2 = [[UIImageView alloc] initWithFrame:rect]; 150 | _image3 = [[UIImageView alloc] initWithFrame:rect]; 151 | _image4 = [[UIImageView alloc] initWithFrame:rect]; 152 | _image5 = [[UIImageView alloc] initWithFrame:rect]; 153 | _image6 = [[UIImageView alloc] initWithFrame:rect]; 154 | 155 | _image1.center = centerPoint; 156 | _image2.center = centerPoint; 157 | _image3.center = centerPoint; 158 | _image4.center = centerPoint; 159 | _image5.center = centerPoint; 160 | _image6.center = centerPoint; 161 | 162 | _image1.contentMode = UIViewContentModeScaleToFill; 163 | _image2.contentMode = UIViewContentModeScaleToFill; 164 | _image3.contentMode = UIViewContentModeScaleToFill; 165 | _image4.contentMode = UIViewContentModeScaleToFill; 166 | _image5.contentMode = UIViewContentModeScaleToFill; 167 | _image6.contentMode = UIViewContentModeScaleToFill; 168 | 169 | [self addSubview:_image1]; 170 | [self addSubview:_image2]; 171 | [self addSubview:_image3]; 172 | [self addSubview:_image4]; 173 | [self addSubview:_image5]; 174 | [self addSubview:_image6]; 175 | 176 | _zoomFactor=_referenceSide; 177 | _hAngle=0; 178 | _vAngle=0; 179 | _leftLimit=0; 180 | _rightLimit=0; 181 | _upLimit=M_PI_2; 182 | _downLimit=M_PI_2; 183 | _minZoom=0; 184 | _maxZoom=100; 185 | self.userInteractionEnabled=YES; 186 | UIPanGestureRecognizer *panGR=[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(didPan:)]; 187 | [self addGestureRecognizer:panGR]; 188 | _panGestureRecognizer=panGR; 189 | UIPinchGestureRecognizer *pinchGR=[[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(didPinch:)]; 190 | [self addGestureRecognizer:pinchGR]; 191 | _pinchGestureRecognizer=pinchGR; 192 | } 193 | 194 | - (void)defaultValuesForImageOver { 195 | CGRect rect = _image1.frame; 196 | CGPoint centerPoint = _image1.center; 197 | 198 | _imageOver1 = [[UIImageView alloc] initWithFrame:rect]; 199 | _imageOver2 = [[UIImageView alloc] initWithFrame:rect]; 200 | _imageOver3 = [[UIImageView alloc] initWithFrame:rect]; 201 | _imageOver4 = [[UIImageView alloc] initWithFrame:rect]; 202 | _imageOver5 = [[UIImageView alloc] initWithFrame:rect]; 203 | _imageOver6 = [[UIImageView alloc] initWithFrame:rect]; 204 | 205 | _imageOver1.center = centerPoint; 206 | _imageOver2.center = centerPoint; 207 | _imageOver3.center = centerPoint; 208 | _imageOver4.center = centerPoint; 209 | _imageOver5.center = centerPoint; 210 | _imageOver6.center = centerPoint; 211 | 212 | _imageOver1.contentMode = UIViewContentModeScaleToFill; 213 | _imageOver2.contentMode = UIViewContentModeScaleToFill; 214 | _imageOver3.contentMode = UIViewContentModeScaleToFill; 215 | _imageOver4.contentMode = UIViewContentModeScaleToFill; 216 | _imageOver5.contentMode = UIViewContentModeScaleToFill; 217 | _imageOver6.contentMode = UIViewContentModeScaleToFill; 218 | 219 | [self addSubview:_imageOver1]; 220 | [self addSubview:_imageOver2]; 221 | [self addSubview:_imageOver3]; 222 | [self addSubview:_imageOver4]; 223 | [self addSubview:_imageOver5]; 224 | [self addSubview:_imageOver6]; 225 | 226 | [self setImageOverTransparancy:0]; 227 | } 228 | 229 | - (void)setFrontImage:(UIImage *)i1 rightImage:(UIImage *)i2 backImage:(UIImage *)i3 leftImage:(UIImage *)i4 topImage:(UIImage *)i5 bottomImage:(UIImage *)i6 230 | { 231 | _image1.image = i1; 232 | _image2.image = i2; 233 | _image3.image = i3; 234 | _image4.image = i4; 235 | _image5.image = i5; 236 | _image6.image = i6; 237 | } 238 | 239 | -(void)setFrontImageOver:(UIImage *)i1 rightImageOver:(UIImage *)i2 backImageOver:(UIImage *)i3 leftImageOver:(UIImage *)i4 topImageOver:(UIImage *)i5 bottomImageOver:(UIImage *)i6 240 | { 241 | _imageOver1.image = i1; 242 | _imageOver2.image = i2; 243 | _imageOver3.image = i3; 244 | _imageOver4.image = i4; 245 | _imageOver5.image = i5; 246 | _imageOver6.image = i6; 247 | } 248 | 249 | - (void)render { 250 | 251 | CATransform3D transform3D = CATransform3DIdentity; 252 | 253 | CGFloat tempHAngle=_hAngle; 254 | CGFloat tempVAngle=_vAngle; 255 | transform3D = CATransform3DIdentity; 256 | transform3D.m34 = 1 / -_zoomFactor; 257 | transform3D=CATransform3DTranslate(transform3D, 258 | _referenceSide*sin(-tempHAngle), 259 | -_referenceSide*cos(-tempHAngle)*sin(-tempVAngle), 260 | -(_referenceSide*cos(-tempHAngle)*cos(-tempVAngle)-_zoomFactor) 261 | ); 262 | transform3D=CATransform3DRotate(transform3D, tempHAngle, 0, 1, 0); 263 | if (_imageOver1.image != nil) _imageOver1.layer.transform=CATransform3DRotate(transform3D, tempVAngle, cos(tempHAngle), 0, sin(tempHAngle)); 264 | _image1.layer.transform=CATransform3DRotate(transform3D, tempVAngle, cos(tempHAngle), 0, sin(tempHAngle)); 265 | 266 | tempHAngle=_hAngle-(M_PI/2); 267 | tempVAngle=_vAngle; 268 | transform3D = CATransform3DIdentity; 269 | transform3D.m34 = 1 / -_zoomFactor; 270 | transform3D=CATransform3DTranslate(transform3D, 271 | _referenceSide*sin(-tempHAngle), 272 | -_referenceSide*cos(-tempHAngle)*sin(-tempVAngle), 273 | -(_referenceSide*cos(-tempHAngle)*cos(-tempVAngle)-_zoomFactor) 274 | ); 275 | transform3D=CATransform3DRotate(transform3D, tempHAngle, 0, 1, 0); 276 | if (_imageOver2.image != nil) _imageOver2.layer.transform=CATransform3DRotate(transform3D, tempVAngle, cos(tempHAngle), 0, sin(tempHAngle)); 277 | _image2.layer.transform=CATransform3DRotate(transform3D, tempVAngle, cos(tempHAngle), 0, sin(tempHAngle)); 278 | 279 | tempHAngle=_hAngle-(M_PI); 280 | tempVAngle=_vAngle; 281 | transform3D = CATransform3DIdentity; 282 | transform3D.m34 = 1 / -_zoomFactor; 283 | transform3D=CATransform3DTranslate(transform3D, 284 | _referenceSide*sin(-tempHAngle), 285 | -_referenceSide*cos(-tempHAngle)*sin(-tempVAngle), 286 | -(_referenceSide*cos(-tempHAngle)*cos(-tempVAngle)-_zoomFactor) 287 | ); 288 | transform3D=CATransform3DRotate(transform3D, tempHAngle, 0, 1, 0); 289 | if (_imageOver3.image != nil) _imageOver3.layer.transform=CATransform3DRotate(transform3D, tempVAngle, cos(tempHAngle), 0, sin(tempHAngle)); 290 | _image3.layer.transform=CATransform3DRotate(transform3D, tempVAngle, cos(tempHAngle), 0, sin(tempHAngle)); 291 | 292 | tempHAngle=_hAngle-(3*M_PI/2); 293 | tempVAngle=_vAngle; 294 | transform3D = CATransform3DIdentity; 295 | transform3D.m34 = 1 / -_zoomFactor; 296 | transform3D=CATransform3DTranslate(transform3D, 297 | _referenceSide*sin(-tempHAngle), 298 | -_referenceSide*cos(-tempHAngle)*sin(-tempVAngle), 299 | -(_referenceSide*cos(-tempHAngle)*cos(-tempVAngle)-_zoomFactor) 300 | ); 301 | transform3D=CATransform3DRotate(transform3D, tempHAngle, 0, 1, 0); 302 | if (_imageOver4.image != nil) _imageOver4.layer.transform=CATransform3DRotate(transform3D, tempVAngle, cos(tempHAngle), 0, sin(tempHAngle)); 303 | _image4.layer.transform=CATransform3DRotate(transform3D, tempVAngle, cos(tempHAngle), 0, sin(tempHAngle)); 304 | 305 | tempHAngle=_hAngle; 306 | tempVAngle=_vAngle-(M_PI/2); 307 | transform3D = CATransform3DIdentity; 308 | transform3D.m34 = 1 / -_zoomFactor; 309 | transform3D=CATransform3DTranslate(transform3D, 310 | 0, 311 | -_referenceSide*sin(-tempVAngle), 312 | -(_referenceSide*cos(-tempVAngle)-_zoomFactor) 313 | ); 314 | 315 | transform3D=CATransform3DRotate(transform3D, tempVAngle, 1,0,0); 316 | if (_imageOver5.image != nil) _imageOver5.layer.transform=CATransform3DRotate(transform3D, tempHAngle, 0, 0, 1); 317 | _image5.layer.transform=CATransform3DRotate(transform3D, tempHAngle, 0, 0, 1); 318 | 319 | tempHAngle=_hAngle; 320 | tempVAngle=_vAngle+(M_PI/2); 321 | transform3D = CATransform3DIdentity; 322 | transform3D.m34 = 1 / -_zoomFactor; 323 | transform3D=CATransform3DTranslate(transform3D, 324 | 0, 325 | -_referenceSide*sin(-tempVAngle), 326 | -(_referenceSide*cos(-tempVAngle)-_zoomFactor) 327 | ); 328 | 329 | transform3D=CATransform3DRotate(transform3D, tempVAngle, 1,0,0); 330 | if (_imageOver6.image != nil) _imageOver6.layer.transform=CATransform3DRotate(transform3D, -tempHAngle, 0, 0, 1); 331 | _image6.layer.transform=CATransform3DRotate(transform3D, -tempHAngle, 0, 0, 1); 332 | 333 | CGFloat hotspotReference=_referenceSide; 334 | for (UIView *hotspot in _hotspots) { 335 | tempHAngle=hotspot.hAngle; 336 | tempVAngle=hotspot.vAngle; 337 | 338 | CGFloat x=sin(tempHAngle)*cos(tempVAngle); 339 | CGFloat y=sin(tempVAngle); 340 | CGFloat z=cos(tempVAngle)*cos(tempHAngle); 341 | 342 | CGPoint transformedPoint=CGPointApplyAffineTransform(CGPointMake(x, z), CGAffineTransformMakeRotation(_hAngle)); 343 | x=transformedPoint.x; 344 | z=transformedPoint.y; 345 | transformedPoint=CGPointApplyAffineTransform(CGPointMake(z, y), CGAffineTransformMakeRotation(-_vAngle)); 346 | y=transformedPoint.y; 347 | z=transformedPoint.x; 348 | 349 | transform3D = CATransform3DIdentity; 350 | transform3D.m34 = 1 / -_zoomFactor; 351 | transform3D=CATransform3DTranslate(transform3D, 352 | hotspotReference*x, 353 | -(hotspotReference*y), 354 | -((hotspotReference)*z-_zoomFactor) 355 | ); 356 | if (hotspot.shouldApplyPerspective) { 357 | transform3D=CATransform3DRotate(transform3D, _hAngle, 0, 1, 0); 358 | transform3D=CATransform3DRotate(transform3D, _vAngle, cos(_hAngle), 0, sin(_hAngle)); 359 | transform3D=CATransform3DRotate(transform3D, -hotspot.hAngle, 0, 1, 0); 360 | transform3D=CATransform3DRotate(transform3D, -hotspot.vAngle, 1, 0, 0); 361 | } 362 | 363 | 364 | hotspot.layer.transform=transform3D; 365 | } 366 | } 367 | 368 | - (void)layoutSubviews { 369 | CGFloat tempZoomFactor = self.zoomFactor; 370 | if (self.bounds.size.width > self.bounds.size.height) { 371 | _referenceSide = self.bounds.size.width/2; 372 | }else { 373 | _referenceSide = self.bounds.size.height/2; 374 | } 375 | //recalculate zoomFactor as a function of dim 376 | self.zoomFactor=tempZoomFactor; 377 | CGRect rect = CGRectMake(0, 0, _referenceSide*2, _referenceSide*2); 378 | CGPoint centerPoint=CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2); 379 | 380 | _image1.frame = rect; 381 | _image2.frame = rect; 382 | _image3.frame = rect; 383 | _image4.frame = rect; 384 | _image5.frame = rect; 385 | _image6.frame = rect; 386 | 387 | _imageOver1.frame = rect; 388 | _imageOver2.frame = rect; 389 | _imageOver3.frame = rect; 390 | _imageOver4.frame = rect; 391 | _imageOver5.frame = rect; 392 | _imageOver6.frame = rect; 393 | 394 | _image1.center = centerPoint; 395 | _image2.center = centerPoint; 396 | _image3.center = centerPoint; 397 | _image4.center = centerPoint; 398 | _image5.center = centerPoint; 399 | _image6.center = centerPoint; 400 | 401 | _imageOver1.center = centerPoint; 402 | _imageOver2.center = centerPoint; 403 | _imageOver3.center = centerPoint; 404 | _imageOver4.center = centerPoint; 405 | _imageOver5.center = centerPoint; 406 | _imageOver6.center = centerPoint; 407 | 408 | for (UIView *hotspot in _hotspots) { 409 | hotspot.center = centerPoint; 410 | } 411 | [self render]; 412 | } 413 | 414 | - (void)setImageOverTransparancy:(CGFloat)transparancyValue 415 | { 416 | _imageOver1.alpha = transparancyValue; 417 | _imageOver2.alpha = transparancyValue; 418 | _imageOver3.alpha = transparancyValue; 419 | _imageOver4.alpha = transparancyValue; 420 | _imageOver5.alpha = transparancyValue; 421 | _imageOver6.alpha = transparancyValue; 422 | } 423 | 424 | #pragma mark GestureRecognizers 425 | 426 | - (void)didPan:(UIPanGestureRecognizer *)gestureRecognizer { 427 | if (gestureRecognizer.state==UIGestureRecognizerStateBegan) { 428 | if (_delegate && _delegateBeginPan) { 429 | [_delegate panoViewWillBeginPanning:self]; 430 | } 431 | } 432 | if (gestureRecognizer.state==UIGestureRecognizerStateBegan || 433 | gestureRecognizer.state==UIGestureRecognizerStateChanged) { 434 | CGPoint translation=[gestureRecognizer translationInView:self]; 435 | CGFloat newHAngle = self.hAngle-(translation.x/(_zoomFactor/1.5)); 436 | CGFloat newVAngle = self.vAngle+(translation.y/(_zoomFactor/1.5)); 437 | if (newHAngle>0 && _rightLimit!=0) { 438 | if (newHAngle>_rightLimit) { 439 | newHAngle=_rightLimit; 440 | } 441 | }else if (newHAngle<0 && _leftLimit!=0) { 442 | // negative angle to the left, but limit is always positive (absolute value) 443 | if (newHAngle<(-_leftLimit)) { 444 | newHAngle=-_leftLimit; 445 | } 446 | } 447 | if (newVAngle>0 && _upLimit!=0) { 448 | if (newVAngle>_upLimit) { 449 | newVAngle=_upLimit; 450 | } 451 | }else if (newVAngle<0 && _downLimit!=0) { 452 | // negative angle to the bottom, but limit is always positive (absolute value) 453 | if (newVAngle<(-_downLimit)) { 454 | newVAngle=-_downLimit; 455 | } 456 | } 457 | _hAngle=newHAngle; 458 | _vAngle=newVAngle; 459 | [self render]; 460 | [gestureRecognizer setTranslation:CGPointZero inView:self]; 461 | if (_delegate && _delegateDidPan) { 462 | [_delegate panoViewDidPan:self]; 463 | } 464 | } 465 | if (gestureRecognizer.state==UIGestureRecognizerStateEnded) { 466 | if (_delegate && _delegateEndPan) { 467 | [_delegate panoViewDidEndPanning:self]; 468 | } 469 | } 470 | } 471 | 472 | - (void)didPinch:(UIPinchGestureRecognizer *)gestureRecognizer { 473 | if (gestureRecognizer.state==UIGestureRecognizerStateBegan) { 474 | _previousZoomFactor=self.zoomFactor; 475 | if (_delegate && _delegateBeginZoom) { 476 | [_delegate panoViewWillBeginZooming:self]; 477 | } 478 | } 479 | if (gestureRecognizer.state==UIGestureRecognizerStateBegan || 480 | gestureRecognizer.state==UIGestureRecognizerStateChanged) { 481 | CGFloat newFactor=_previousZoomFactor*gestureRecognizer.scale; 482 | self.zoomFactor=newFactor; 483 | if (_delegate && _delegateDidZoom) { 484 | [_delegate panoViewDidZoom:self]; 485 | } 486 | } 487 | if (gestureRecognizer.state==UIGestureRecognizerStateEnded) { 488 | if (_delegate && _delegateEndZoom) { 489 | [_delegate panoViewDidEndZooming:self]; 490 | } 491 | } 492 | } 493 | 494 | #pragma mark hotspot management 495 | 496 | - (void)addHotspot:(UIView*)hotspotView atHAngle:(CGFloat)hAngle vAngle:(CGFloat)vAngle { 497 | if (hotspotView.panoView!=nil) { 498 | [hotspotView removeFromPanoView]; 499 | } 500 | hotspotView.panoView=self; 501 | hotspotView.hAngle=hAngle; 502 | hotspotView.vAngle=vAngle; 503 | [_hotspots addObject:hotspotView]; 504 | [self addSubview:hotspotView]; 505 | } 506 | 507 | - (void)removeHotspot:(UIView *)hotspot { 508 | if (hotspot.panoView==self) { 509 | [hotspot removeFromSuperview]; 510 | [_hotspots removeObject:hotspot]; 511 | hotspot.panoView=nil; 512 | } 513 | } 514 | 515 | /* 516 | // Only override drawRect: if you perform custom drawing. 517 | // An empty implementation adversely affects performance during animation. 518 | - (void)drawRect:(CGRect)rect 519 | { 520 | // Drawing code 521 | } 522 | */ 523 | 524 | @end 525 | 526 | 527 | static char kUIViewHotSpotPanoViewObjectKey; 528 | static char kUIViewHotSpotShouldApplyPerspectiveObjectKey; 529 | 530 | @implementation UIView (JAPanoViewHotspot) 531 | 532 | @dynamic panoView; 533 | @dynamic shouldApplyPerspective; 534 | 535 | - (void)removeFromPanoView { 536 | if (self.panoView) { 537 | [self.panoView removeHotspot:self]; 538 | } 539 | } 540 | 541 | - (JAPanoView*)panoView { 542 | return (JAPanoView *)objc_getAssociatedObject(self, &kUIViewHotSpotPanoViewObjectKey); 543 | } 544 | 545 | - (void)setShouldApplyPerspective:(BOOL)shouldApplyPerspective { 546 | objc_setAssociatedObject(self, &kUIViewHotSpotShouldApplyPerspectiveObjectKey, @(shouldApplyPerspective), OBJC_ASSOCIATION_COPY_NONATOMIC); 547 | } 548 | 549 | - (BOOL)shouldApplyPerspective { 550 | NSNumber *value=(NSNumber *)objc_getAssociatedObject(self, &kUIViewHotSpotShouldApplyPerspectiveObjectKey); 551 | return value?[value boolValue]:YES; 552 | } 553 | 554 | @end 555 | 556 | 557 | static char kUIViewHotSpotHAngleObjectKey; 558 | static char kUIViewHotSpotVAngleObjectKey; 559 | 560 | @implementation UIView (JAPanoViewHotspotPrivate) 561 | 562 | @dynamic panoView; 563 | @dynamic hAngle; 564 | @dynamic vAngle; 565 | 566 | - (void)setPanoView:(JAPanoView *)panoView { 567 | objc_setAssociatedObject(self, &kUIViewHotSpotPanoViewObjectKey, panoView, OBJC_ASSOCIATION_ASSIGN); 568 | } 569 | 570 | - (void)setHAngle:(CGFloat)hAngle { 571 | objc_setAssociatedObject(self, &kUIViewHotSpotHAngleObjectKey, @(hAngle), OBJC_ASSOCIATION_COPY_NONATOMIC); 572 | } 573 | 574 | - (void)setVAngle:(CGFloat)vAngle { 575 | objc_setAssociatedObject(self, &kUIViewHotSpotVAngleObjectKey, @(vAngle), OBJC_ASSOCIATION_COPY_NONATOMIC); 576 | } 577 | 578 | - (CGFloat)hAngle { 579 | return [(NSNumber *)objc_getAssociatedObject(self, &kUIViewHotSpotHAngleObjectKey) floatValue]; 580 | } 581 | 582 | - (CGFloat)vAngle { 583 | return [(NSNumber *)objc_getAssociatedObject(self, &kUIViewHotSpotVAngleObjectKey) floatValue]; 584 | } 585 | 586 | @end 587 | -------------------------------------------------------------------------------- /PanoTest/JAViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // JAViewController.h 3 | // PanoTest 4 | // 5 | // Created by Javier Alonso Gutiérrez on 16/02/12. 6 | // Copyright (c) 2012 NG Servicios. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "JAPanoView.h" 11 | 12 | @interface JAViewController : UIViewController 13 | 14 | @property (nonatomic, copy) NSString *panoFileName; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /PanoTest/JAViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // JAViewController.m 3 | // PanoTest 4 | // 5 | // Created by Javier Alonso Gutiérrez on 16/02/12. 6 | // Copyright (c) 2012 NG Servicios. All rights reserved. 7 | // 8 | 9 | #import "JAViewController.h" 10 | #import "JAPanoView.h" 11 | #import 12 | 13 | @interface JAViewController () { 14 | UIPopoverController *_testPopover; 15 | } 16 | 17 | @end 18 | 19 | @implementation JAViewController 20 | 21 | - (void)loadView { 22 | JAPanoView *panoView=[[JAPanoView alloc] initWithFrame:CGRectMake(0, 0, 768, 1024)]; 23 | self.view=panoView; 24 | panoView.delegate=self; 25 | [panoView setFrontImage:[UIImage imageNamed:@"TowerHousepano_f.jpg"] rightImage:[UIImage imageNamed:@"TowerHousepano_r.jpg"] backImage:[UIImage imageNamed:@"TowerHousepano_b.jpg"] leftImage:[UIImage imageNamed:@"TowerHousepano_l.jpg"] topImage:[UIImage imageNamed:@"TowerHousepano_u.jpg"] bottomImage:[UIImage imageNamed:@"Down_fixed.jpg"]]; 26 | 27 | UILabel *hotspot1=[[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 25)]; 28 | hotspot1.backgroundColor=[UIColor clearColor]; 29 | hotspot1.textColor=[UIColor redColor]; 30 | hotspot1.text=@"DOOR"; 31 | hotspot1.textAlignment=NSTextAlignmentCenter; 32 | [panoView addHotspot:hotspot1 atHAngle:0 vAngle:0]; 33 | UIView *hotspot2=[[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)]; 34 | hotspot2.backgroundColor=[UIColor yellowColor]; 35 | hotspot2.alpha=0.5; 36 | hotspot2.layer.cornerRadius=25; 37 | [panoView addHotspot:hotspot2 atHAngle:M_PI_4 vAngle:M_PI_4]; 38 | 39 | UIButton *hotspot3=[UIButton buttonWithType:UIButtonTypeRoundedRect]; 40 | [hotspot3 setTitle:@"clouds" forState:UIControlStateNormal]; 41 | [hotspot3 setFrame:CGRectMake(0, 0, 100, 30)]; 42 | hotspot3.shouldApplyPerspective=NO; 43 | [hotspot3 addTarget:self action:@selector(centerClouds:) forControlEvents:UIControlEventTouchUpInside]; 44 | [panoView addHotspot:hotspot3 atHAngle:-M_PI_2 vAngle:M_PI_4]; 45 | 46 | 47 | UITapGestureRecognizer *tapgr=[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)]; 48 | [hotspot2 addGestureRecognizer:tapgr]; 49 | } 50 | 51 | - (void)tapped:(UITapGestureRecognizer *)tapGR { 52 | _testPopover=[[UIPopoverController alloc] initWithContentViewController:[[UIViewController alloc] init]]; 53 | [_testPopover presentPopoverFromRect:tapGR.view.frame inView:tapGR.view.superview permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; 54 | } 55 | 56 | - (void)centerClouds:(id)sender { 57 | [(JAPanoView*)self.view setHAngle:-M_PI_2]; 58 | [(JAPanoView*)self.view setVAngle:M_PI_4]; 59 | } 60 | 61 | #pragma mark - View lifecycle 62 | 63 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { 64 | // Return YES for supported orientations 65 | return YES; 66 | } 67 | 68 | #pragma mark JAPanoViewDelegate 69 | 70 | - (void)panoViewDidPan:(JAPanoView *)panoView { 71 | NSLog(@"didPan"); 72 | } 73 | 74 | - (void)panoViewDidEndPanning:(JAPanoView *)panoView { 75 | NSLog(@"didEndPanning"); 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /PanoTest/PanoTest-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFiles 12 | 13 | CFBundleIdentifier 14 | com.javieralonso.${PRODUCT_NAME:rfc1034identifier} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | ${PRODUCT_NAME} 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleSignature 24 | ???? 25 | CFBundleVersion 26 | 1.0 27 | LSRequiresIPhoneOS 28 | 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations~ipad 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationPortraitUpsideDown 37 | UIInterfaceOrientationLandscapeLeft 38 | UIInterfaceOrientationLandscapeRight 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /PanoTest/PanoTest-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'PanoTest' target in the 'PanoTest' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_4_0 8 | #warning "This project uses features only available in iOS SDK 4.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /PanoTest/TowerHousepano_b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartapps-fr/JAPanoView/cca75dc4d089484394cf79f5e21d4daa0559b84b/PanoTest/TowerHousepano_b.jpg -------------------------------------------------------------------------------- /PanoTest/TowerHousepano_f.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartapps-fr/JAPanoView/cca75dc4d089484394cf79f5e21d4daa0559b84b/PanoTest/TowerHousepano_f.jpg -------------------------------------------------------------------------------- /PanoTest/TowerHousepano_l.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartapps-fr/JAPanoView/cca75dc4d089484394cf79f5e21d4daa0559b84b/PanoTest/TowerHousepano_l.jpg -------------------------------------------------------------------------------- /PanoTest/TowerHousepano_r.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartapps-fr/JAPanoView/cca75dc4d089484394cf79f5e21d4daa0559b84b/PanoTest/TowerHousepano_r.jpg -------------------------------------------------------------------------------- /PanoTest/TowerHousepano_u.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartapps-fr/JAPanoView/cca75dc4d089484394cf79f5e21d4daa0559b84b/PanoTest/TowerHousepano_u.jpg -------------------------------------------------------------------------------- /PanoTest/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /PanoTest/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // PanoTest 4 | // 5 | // Created by Javier Alonso Gutiérrez on 16/02/12. 6 | // Copyright (c) 2012 NG Servicios. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "JAAppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([JAAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a fork of [JAPanoView](https://bitbucket.org/javieralonso/japanoview)... 2 | - compatible with the latest iOS SDK (8.0). 3 | - adds Podspec 4 | 5 | - - - 6 | 7 | 8 | # JAPanoView: open source panorama viewer 9 | 10 | JAPanoView is a UIView subclass that renders 360-180 degree panoramic views created from cubic panoramic images with interactive panning and zooming. You can add any UIView as a hotspot into JAPanoView. 11 | Watch a video: http://www.youtube.com/watch?v=8g0LcuNzzNM 12 | 13 | JAPanoView source code is distributed under Apache license. More info at http://www.apache.org/licenses/LICENSE-2.0.html 14 | Sample images are from http://www.remedypanoramic.com/ and © Remedy Panoramic Design 2010. Included with the permission of the copyright holder. 15 | 16 | JAPanoView has been created by Javier Alonso. 17 | http://javieralog.blogspot.com 18 | @javieralog 19 | 20 | You can get the latest version from https://github.com/ddebin/JAPanoView 21 | 22 | 23 | # How to use it 24 | 25 | Copy JAPanoView.h and JAPanoView.m to your project. 26 | Make sure your project includes the following frameworks: 27 | - QuartzCore.framework 28 | - UIKit.framework 29 | 30 | JAPanoView code uses ARC and iOS deployment target version is iOS 5.0 or later 31 | Latest version was tested using iOS SDK 8.0 32 | 33 | Creating a panoramic view: 34 | 35 | ```objc 36 | JAPanoView *panoView = [[JAPanoView alloc] initWithFrame:self.view.bounds]; 37 | [panoView setFrontImage:[UIImage imageNamed:@"TowerHousepano_f.jpg"] 38 | rightImage:[UIImage imageNamed:@"TowerHousepano_r.jpg"] 39 | backImage:[UIImage imageNamed:@"TowerHousepano_b.jpg"] 40 | leftImage:[UIImage imageNamed:@"TowerHousepano_l.jpg"] 41 | topImage:[UIImage imageNamed:@"TowerHousepano_u.jpg"] 42 | bottomImage:[UIImage imageNamed:@"Down_fixed.jpg"]]; 43 | [self.view addSubview:panoView];` 44 | ``` 45 | 46 | # Hotspots 47 | 48 | Instantiate any UIView subclass and add it as a hotspot with the method `addHotspot:atHAngle:vAngle:` indicating the horizontal angle (azimuth) and vertical angle (elevation). To remove a hotspot form a JAPanoView just call `removeFromPanoView` on the hotspot instance (like `addSubview:` and `removeFromSuperview`). 49 | 50 | Since any UIView can be a hotspot, you can add any UIControl or add any UIGestureRecognizer to your hotspot and make it fully user interactive. You can also take advantage of the convertPoint/Rect:from/toView: UIView methods and show popovers from a hotspot as seen in the demo project: 51 | 52 | ```objc 53 | [popover presentPopoverFromRect:hotspot.frame 54 | inView:hotspot.superview 55 | permittedArrowDirections:UIPopoverArrowDirectionAny 56 | animated:YES]; 57 | ``` 58 | 59 | There's also a property for UIView: shouldApplyPerspective. It indicates if the hotspot should be rendered always parallel to the screen (`shouldApplyPerspective==NO`) or perpendicular to the point-of-view/hotspot axis (`shouldApplyPerspective==YES`; default value). --------------------------------------------------------------------------------