├── .gitignore ├── LICENSE ├── README.md ├── TWSReleaseNotesView Docs ├── html │ ├── Classes │ │ ├── TWSReleaseNotesDownloadOperation.html │ │ └── TWSReleaseNotesView.html │ ├── css │ │ ├── styles.css │ │ └── stylesPrint.css │ ├── hierarchy.html │ ├── img │ │ ├── button_bar_background.png │ │ ├── disclosure.png │ │ ├── disclosure_open.png │ │ ├── library_background.png │ │ └── title_background.png │ └── index.html └── it.matteolallone.TWSReleaseNotesView.docset │ └── Contents │ ├── Info.plist │ └── Resources │ ├── Documents │ ├── Classes │ │ ├── TWSReleaseNotesDownloadOperation.html │ │ └── TWSReleaseNotesView.html │ ├── css │ │ ├── styles.css │ │ └── stylesPrint.css │ ├── hierarchy.html │ ├── img │ │ ├── button_bar_background.png │ │ ├── disclosure.png │ │ ├── disclosure_open.png │ │ ├── library_background.png │ │ └── title_background.png │ └── index.html │ ├── docSet.dsidx │ ├── docSet.mom │ ├── docSet.skidx │ └── docSet.toc ├── TWSReleaseNotesView ├── TWSReleaseNotesDownloadOperation.h ├── TWSReleaseNotesDownloadOperation.m ├── TWSReleaseNotesView.h ├── TWSReleaseNotesView.m ├── UIImage+ImageEffects.h └── UIImage+ImageEffects.m ├── TWSReleaseNotesViewSample ├── TWSReleaseNotesViewSample.xcodeproj │ └── project.pbxproj └── TWSReleaseNotesViewSample │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Default-568h@2x.png │ ├── Default.png │ ├── Default@2x.png │ ├── RootViewController.h │ ├── RootViewController.m │ ├── RootViewController.xib │ ├── TWSReleaseNotesViewSample-Info.plist │ ├── TWSReleaseNotesViewSample-Prefix.pch │ ├── background.png │ ├── background@2x.png │ ├── btn_bg.png │ ├── btn_bg@2x.png │ ├── btn_bg_hl.png │ ├── btn_bg_hl@2x.png │ ├── en.lproj │ └── InfoPlist.strings │ ├── icon.png │ ├── icon@2x.png │ ├── icon_7@2x.png │ └── main.m └── TutorialImages ├── sampleProject01.png └── sampleProject02.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Matteo Lallone 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TWSReleaseNotesView 2 | =================== 3 | 4 | Among other crazy features, iOS 7 enables users to have automatic updates for their apps, wiping away the infamous App Store badge. This is really convenient both for users and developers, but it comes with a couple of downsides: 5 | 6 | * users are not aware about the changes introduced in the latest update, unless they explicitly open the App Store page to check the release notes; 7 | * developers who spend their time working on well-written release notes lose their chance to inform and communicate with their users. 8 | 9 | ## So what? 10 | TWSReleaseNotesView is a simple way to address those issues. It comes with a straightforward API which enables developers to show in-app release notes with a fully customizable popup view. 11 | 12 | 13 | ## How to get started 14 | ### CocoaPods 15 | 1. Just add the following line to your Podfile: `pod 'TWSReleaseNotesView', '~> 1.2.0'` 16 | 2. You're good to go! 17 | 18 | ### Manual installation 19 | 1. Download the TWSReleaseNotesView folder and add it to your project. 20 | 2. In addition to the default `UIKit`, `CoreGraphics` and `Foundation`, there's a dependency from the `Accelerate` and the `Quartzcore` frameworks. If any of them is missing in your Frameworks list, follow these steps in order to add them: 21 | * Go to the "Build Phases" tab for your project target. 22 | * Click the `+` button in the collapsible "Link Binary With Libraries" section. 23 | * Add the missing frameworks. 24 | 3. That's it! 25 | 26 | # Example usage 27 | 28 | ## Version check and local release notes view setup 29 | ```objective-c 30 | // Check for first app launch and app update 31 | if (![TWSReleaseNotesView isAppOnFirstLaunch] && [TWSReleaseNotesView isAppVersionUpdated]) 32 | { 33 | // Create the release notes view 34 | NSString *currentAppVersion = [[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"]; 35 | TWSReleaseNotesView *releaseNotesView = [TWSReleaseNotesView viewWithReleaseNotesTitle:[NSString stringWithFormat:@"What's new in version %@:", currentAppVersion] text:@"• Great new feature\n• Annoying bug wiped away\n• Optimizations and other great stuff!\n• Additional performance and stability improvements" closeButtonTitle:@"Close"]; 36 | 37 | // Show the release notes view 38 | [releaseNotesView showInView:self.view]; 39 | } 40 | ``` 41 | 42 | ## Version check and remote release notes view setup 43 | ```objective-c 44 | // Check for app update 45 | if ([TWSReleaseNotesView isAppVersionUpdated]) 46 | { 47 | // Setup a remote release notes view 48 | NSString *currentAppVersion = [[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"]; 49 | [TWSReleaseNotesView setupViewWithAppIdentifier:@"XXXXXXXXX" releaseNotesTitle:[NSString stringWithFormat:@"What's new in version %@:", currentAppVersion] closeButtonTitle:@"Close" completionBlock:^(TWSReleaseNotesView *releaseNotesView, NSString *releaseNotesText, NSError *error){ 50 | if (error) 51 | { 52 | // Handle errors 53 | NSLog(@"An error occurred: %@", [error localizedDescription]); 54 | } 55 | else 56 | { 57 | // Create and show release notes view 58 | [releaseNotesView showInView:self.view]; 59 | } 60 | }]; 61 | } 62 | ``` 63 | 64 | ## Sample project 65 | The **TWSReleaseNotesViewSample** sample project shows how to deal with the two use cases described above. 66 | 67 | ![Sample project menu](TutorialImages/sampleProject01.png) ![Sample project view](TutorialImages/sampleProject02.png) 68 | 69 | ## Features 70 | * First app launch check. 71 | * Version check in order to choose whether showing the release notes view or not. 72 | * Local release notes view with custom appearance and text information. 73 | * Remote release notes view using the application's Apple ID, in order to retrieve the last release notes directly from App Store, using the iTunes Search API. 74 | 75 | ## Documentation 76 | The code is fully commented using a [Javadoc](http://en.wikipedia.org/wiki/Javadoc)-like syntax, in order to use [appledoc](https://github.com/tomaz/appledoc) for the documentation generation. Check the **TWReleaseNotesView Docs** folder for the complete documentation, in both html and .docset format. The .docset file can be copied in the `~/Library/Developer/Shared/Documentation/DocSets` folder, in order to be able to read the documentation in Xcode. If you decide to do so, please, remember to close Xcode before copying the .docset file in the proper folder, then re-open it. 77 | 78 | ## Credits 79 | TWSReleaseNotesView was developed by **Tapwings**: Matteo Lallone (iOS developer - [@iGriever](https://twitter.com/iGriever)) and Gianluca Divisi (UI/UX designer - [@gianlucadivisi](https://twitter.com/gianlucadivisi)). Follow us on Twitter ([@tapwings](https://twitter.com/tapwings)) to receive updates on our work. 80 | 81 | ## License 82 | The MIT License (MIT) 83 | 84 | Copyright (c) 2013 Matteo Lallone 85 | 86 | Permission is hereby granted, free of charge, to any person obtaining a copy of 87 | this software and associated documentation files (the "Software"), to deal in 88 | the Software without restriction, including without limitation the rights to 89 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 90 | the Software, and to permit persons to whom the Software is furnished to do so, 91 | subject to the following conditions: 92 | 93 | The above copyright notice and this permission notice shall be included in all 94 | copies or substantial portions of the Software. 95 | 96 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 97 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 98 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 99 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 100 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 101 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 102 | -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/html/Classes/TWSReleaseNotesDownloadOperation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TWSReleaseNotesDownloadOperation Class Reference 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |

TWSReleaseNotesView

16 | Matteo Lallone 17 |
18 | 19 | 22 | 61 |
62 | 103 |
104 |
105 | 106 | 112 | 117 |
118 | 119 |
120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 |
Inherits fromNSOperation
Declared inTWSReleaseNotesDownloadOperation.h
TWSReleaseNotesDownloadOperation.m
128 | 129 | 130 | 131 | 132 |
133 | 134 |

Overview

135 |

Use the TWSReleaseNotesDownloadOperation class to create an operation with the purpose of downloading the release notes text for a specified app, using the iTunes Search API.

136 | 137 |

The result of the operation is accessible in the completionBlock, using the releaseNotesText and the error properties.

138 |
139 | 140 | 141 | 142 | 143 | 144 |
145 | 146 |

Tasks

147 | 148 | 149 | 150 |

Getting main properties

151 | 152 |
    153 |
  • 154 | 155 |   releaseNotesText 156 |

    The downloaded release notes text.

    157 |
    158 | property 159 | 160 |
  • 161 | 162 |   error 163 |

    An error object associated to the failed operation.

    164 |
    165 | property 166 | 167 |
  • 168 |
169 | 170 | 171 | 172 |

Creating the operation

173 | 174 | 184 | 185 |
186 | 187 | 188 | 189 | 190 | 191 |
192 | 193 |

Properties

194 | 195 |
196 | 197 |

error

198 | 199 | 200 | 201 |
202 |

An error object associated to the failed operation.

203 |
204 | 205 | 206 | 207 |
@property (readonly, strong, nonatomic) NSError *error
208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 |
224 |

Declared In

225 | TWSReleaseNotesDownloadOperation.h
226 |
227 | 228 | 229 |
230 | 231 |
232 | 233 |

releaseNotesText

234 | 235 | 236 | 237 |
238 |

The downloaded release notes text.

239 |
240 | 241 | 242 | 243 |
@property (readonly, copy, nonatomic) NSString *releaseNotesText
244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 |
260 |

Declared In

261 | TWSReleaseNotesDownloadOperation.h
262 |
263 | 264 | 265 |
266 | 267 |
268 | 269 | 270 | 271 | 272 | 273 |
274 | 275 |

Instance Methods

276 | 277 |
278 | 279 |

initWithAppIdentifier:

280 | 281 | 282 | 283 |
284 |

Creates and operation with custom parameters.

285 |
286 | 287 | 288 | 289 |
- (id)initWithAppIdentifier:(NSString *)appIdentifier
290 | 291 | 292 | 293 |
294 |

Parameters

295 | 296 |
297 |
appIdentifier
298 |

The App Store app identifier for remote release notes retrieval.

299 |
300 | 301 |
302 | 303 | 304 | 305 |
306 |

Return Value

307 |

The initialized operation.

308 |
309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 |
321 |

Declared In

322 | TWSReleaseNotesDownloadOperation.h
323 |
324 | 325 | 326 |
327 | 328 |
329 | 330 | 331 |
332 | 338 | 347 |
348 |
349 | 440 | 441 | -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/html/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 3 | font-size: 13px; 4 | } 5 | 6 | code { 7 | font-family: Courier, Consolas, monospace; 8 | font-size: 13px; 9 | color: #666; 10 | } 11 | 12 | pre { 13 | font-family: Courier, Consolas, monospace; 14 | font-size: 13px; 15 | line-height: 18px; 16 | tab-interval: 0.5em; 17 | border: 1px solid #C7CFD5; 18 | background-color: #F1F5F9; 19 | color: #666; 20 | padding: 0.3em 1em; 21 | } 22 | 23 | ul { 24 | list-style-type: square; 25 | } 26 | 27 | li { 28 | margin-bottom: 10px; 29 | } 30 | 31 | a, a code { 32 | text-decoration: none; 33 | color: #36C; 34 | } 35 | 36 | a:hover, a:hover code { 37 | text-decoration: underline; 38 | color: #36C; 39 | } 40 | 41 | h2 { 42 | border-bottom: 1px solid #8391A8; 43 | color: #3C4C6C; 44 | font-size: 187%; 45 | font-weight: normal; 46 | margin-top: 1.75em; 47 | padding-bottom: 2px; 48 | } 49 | 50 | table { 51 | margin-bottom: 4em; 52 | border-collapse:collapse; 53 | vertical-align: middle; 54 | } 55 | 56 | td { 57 | border: 1px solid #9BB3CD; 58 | padding: .667em; 59 | font-size: 100%; 60 | } 61 | 62 | th { 63 | border: 1px solid #9BB3CD; 64 | padding: .3em .667em .3em .667em; 65 | background: #93A5BB; 66 | font-size: 103%; 67 | font-weight: bold; 68 | color: white; 69 | text-align: left; 70 | } 71 | 72 | /* @group Common page elements */ 73 | 74 | #top_header { 75 | height: 91px; 76 | left: 0; 77 | min-width: 598px; 78 | position: absolute; 79 | right: 0; 80 | top: 0; 81 | z-index: 900; 82 | } 83 | 84 | #footer { 85 | clear: both; 86 | padding-top: 20px; 87 | text-align: center; 88 | } 89 | 90 | #contents, #overview_contents { 91 | -webkit-overflow-scrolling: touch; 92 | border-top: 1px solid #2B334F; 93 | position: absolute; 94 | top: 91px; 95 | left: 0; 96 | right: 0; 97 | bottom: 0; 98 | overflow-x: hidden; 99 | overflow-y: auto; 100 | padding-left: 2em; 101 | padding-right: 2em; 102 | padding-top: 1em; 103 | min-width: 550px; 104 | } 105 | 106 | #contents.isShowingTOC { 107 | left: 230px; 108 | min-width: 320px; 109 | } 110 | 111 | .copyright { 112 | font-size: 12px; 113 | } 114 | 115 | .generator { 116 | font-size: 11px; 117 | } 118 | 119 | .main-navigation ul li { 120 | display: inline; 121 | margin-left: 15px; 122 | list-style: none; 123 | } 124 | 125 | .navigation-top { 126 | clear: both; 127 | float: right; 128 | } 129 | 130 | .navigation-bottom { 131 | clear: both; 132 | float: right; 133 | margin-top: 20px; 134 | margin-bottom: -10px; 135 | } 136 | 137 | .open > .disclosure { 138 | background-image: url("../img/disclosure_open.png"); 139 | } 140 | 141 | .disclosure { 142 | background: url("../img/disclosure.png") no-repeat scroll 0 0; 143 | } 144 | 145 | .disclosure, .nodisclosure { 146 | display: inline-block; 147 | height: 8px; 148 | margin-right: 5px; 149 | position: relative; 150 | width: 9px; 151 | } 152 | 153 | /* @end */ 154 | 155 | /* @group Header */ 156 | 157 | #top_header #library { 158 | background: url("../img/library_background.png") repeat-x 0 0 #485E78; 159 | background-color: #ccc; 160 | height: 35px; 161 | font-size: 115%; 162 | } 163 | 164 | #top_header #library #libraryTitle { 165 | color: #FFFFFF; 166 | margin-left: 15px; 167 | text-shadow: 0 -1px 0 #485E78; 168 | top: 8px; 169 | position: absolute; 170 | } 171 | 172 | #top_header #library #developerHome { 173 | color: #92979E; 174 | right: 15px; 175 | top: 8px; 176 | position: absolute; 177 | } 178 | 179 | #top_header #library a:hover { 180 | text-decoration: none; 181 | } 182 | 183 | #top_header #title { 184 | background: url("../img/title_background.png") repeat-x 0 0 #8A98A9; 185 | border-bottom: 1px solid #B6B6B6; 186 | height: 25px; 187 | overflow: hidden; 188 | } 189 | 190 | #top_header h1 { 191 | font-size: 115%; 192 | font-weight: normal; 193 | margin: 0; 194 | padding: 3px 0 2px; 195 | text-align: center; 196 | text-shadow: 0 1px 0 #D5D5D5; 197 | white-space: nowrap; 198 | } 199 | 200 | #headerButtons { 201 | background-color: #D8D8D8; 202 | background-image: url("../img/button_bar_background.png"); 203 | border-bottom: 1px solid #EDEDED; 204 | border-top: 1px solid #2B334F; 205 | font-size: 8pt; 206 | height: 28px; 207 | left: 0; 208 | list-style: none outside none; 209 | margin: 0; 210 | overflow: hidden; 211 | padding: 0; 212 | position: absolute; 213 | right: 0; 214 | top: 61px; 215 | } 216 | 217 | #headerButtons li { 218 | background-repeat: no-repeat; 219 | display: inline; 220 | margin-top: 0; 221 | margin-bottom: 0; 222 | padding: 0; 223 | } 224 | 225 | #toc_button button { 226 | border-color: #ACACAC; 227 | border-style: none solid none none; 228 | border-width: 0 1px 0 0; 229 | height: 28px; 230 | margin: 0; 231 | padding-left: 30px; 232 | text-align: left; 233 | width: 230px; 234 | } 235 | 236 | li#jumpto_button { 237 | left: 230px; 238 | margin-left: 0; 239 | position: absolute; 240 | } 241 | 242 | li#jumpto_button select { 243 | height: 22px; 244 | margin: 5px 2px 0 10px; 245 | max-width: 300px; 246 | } 247 | 248 | /* @end */ 249 | 250 | /* @group Table of contents */ 251 | 252 | #tocContainer.isShowingTOC { 253 | border-right: 1px solid #ACACAC; 254 | display: block; 255 | overflow-x: hidden; 256 | overflow-y: auto; 257 | padding: 0; 258 | } 259 | 260 | #tocContainer { 261 | background-color: #E4EBF7; 262 | border-top: 1px solid #2B334F; 263 | bottom: 0; 264 | display: none; 265 | left: 0; 266 | overflow: hidden; 267 | position: absolute; 268 | top: 91px; 269 | width: 229px; 270 | } 271 | 272 | #tocContainer > ul#toc { 273 | font-size: 11px; 274 | margin: 0; 275 | padding: 12px 0 18px; 276 | width: 209px; 277 | -moz-user-select: none; 278 | -webkit-user-select: none; 279 | user-select: none; 280 | } 281 | 282 | #tocContainer > ul#toc > li { 283 | margin: 0; 284 | padding: 0 0 7px 30px; 285 | text-indent: -15px; 286 | } 287 | 288 | #tocContainer > ul#toc > li > .sectionName a { 289 | color: #000000; 290 | font-weight: bold; 291 | } 292 | 293 | #tocContainer > ul#toc > li > .sectionName a:hover { 294 | text-decoration: none; 295 | } 296 | 297 | #tocContainer > ul#toc li.children > ul { 298 | display: none; 299 | height: 0; 300 | } 301 | 302 | #tocContainer > ul#toc > li > ul { 303 | margin: 0; 304 | padding: 0; 305 | } 306 | 307 | #tocContainer > ul#toc > li > ul, ul#toc > li > ul > li { 308 | margin-left: 0; 309 | margin-bottom: 0; 310 | padding-left: 15px; 311 | } 312 | 313 | #tocContainer > ul#toc > li ul { 314 | list-style: none; 315 | margin-right: 0; 316 | padding-right: 0; 317 | } 318 | 319 | #tocContainer > ul#toc li.children.open > ul { 320 | display: block; 321 | height: auto; 322 | margin-left: -15px; 323 | padding-left: 0; 324 | } 325 | 326 | #tocContainer > ul#toc > li > ul, ul#toc > li > ul > li { 327 | margin-left: 0; 328 | padding-left: 15px; 329 | } 330 | 331 | #tocContainer li ul li { 332 | margin-top: 0.583em; 333 | overflow: hidden; 334 | text-overflow: ellipsis; 335 | white-space: nowrap; 336 | } 337 | 338 | #tocContainer li ul li span.sectionName { 339 | white-space: normal; 340 | } 341 | 342 | #tocContainer > ul#toc > li > ul > li > .sectionName a { 343 | font-weight: bold; 344 | } 345 | 346 | #tocContainer > ul#toc > li > ul a { 347 | color: #4F4F4F; 348 | } 349 | 350 | /* @end */ 351 | 352 | /* @group Index formatting */ 353 | 354 | .index-title { 355 | font-size: 13px; 356 | font-weight: normal; 357 | } 358 | 359 | .index-column { 360 | float: left; 361 | width: 30%; 362 | min-width: 200px; 363 | font-size: 11px; 364 | } 365 | 366 | .index-column ul { 367 | margin: 8px 0 0 0; 368 | padding: 0; 369 | list-style: none; 370 | } 371 | 372 | .index-column ul li { 373 | margin: 0 0 3px 0; 374 | padding: 0; 375 | } 376 | 377 | .hierarchy-column { 378 | min-width: 400px; 379 | } 380 | 381 | .hierarchy-column ul { 382 | margin: 3px 0 0 15px; 383 | } 384 | 385 | .hierarchy-column ul li { 386 | list-style-type: square; 387 | } 388 | 389 | /* @end */ 390 | 391 | /* @group Common formatting elements */ 392 | 393 | .title { 394 | font-weight: normal; 395 | font-size: 215%; 396 | margin-top:0; 397 | } 398 | 399 | .subtitle { 400 | font-weight: normal; 401 | font-size: 180%; 402 | color: #3C4C6C; 403 | border-bottom: 1px solid #5088C5; 404 | } 405 | 406 | .subsubtitle { 407 | font-weight: normal; 408 | font-size: 145%; 409 | height: 0.7em; 410 | } 411 | 412 | .note { 413 | border: 1px solid #5088C5; 414 | background-color: white; 415 | margin: 1.667em 0 1.75em 0; 416 | padding: 0 .667em .083em .750em; 417 | } 418 | 419 | .warning { 420 | border: 1px solid #5088C5; 421 | background-color: #F0F3F7; 422 | margin-bottom: 0.5em; 423 | padding: 0.3em 0.8em; 424 | } 425 | 426 | .bug { 427 | border: 1px solid #000; 428 | background-color: #ffffcc; 429 | margin-bottom: 0.5em; 430 | padding: 0.3em 0.8em; 431 | } 432 | 433 | .deprecated { 434 | color: #F60425; 435 | } 436 | 437 | /* @end */ 438 | 439 | /* @group Common layout */ 440 | 441 | .section { 442 | margin-top: 3em; 443 | } 444 | 445 | /* @end */ 446 | 447 | /* @group Object specification section */ 448 | 449 | .section-specification { 450 | margin-left: 2.5em; 451 | margin-right: 2.5em; 452 | font-size: 12px; 453 | } 454 | 455 | .section-specification table { 456 | margin-bottom: 0em; 457 | border-top: 1px solid #d6e0e5; 458 | } 459 | 460 | .section-specification td { 461 | vertical-align: top; 462 | border-bottom: 1px solid #d6e0e5; 463 | border-left-width: 0px; 464 | border-right-width: 0px; 465 | border-top-width: 0px; 466 | padding: .6em; 467 | } 468 | 469 | .section-specification .specification-title { 470 | font-weight: bold; 471 | } 472 | 473 | /* @end */ 474 | 475 | /* @group Tasks section */ 476 | 477 | .task-list { 478 | list-style-type: none; 479 | padding-left: 0px; 480 | } 481 | 482 | .task-list li { 483 | margin-bottom: 3px; 484 | } 485 | 486 | .task-item-suffix { 487 | color: #996; 488 | font-size: 12px; 489 | font-style: italic; 490 | margin-left: 0.5em; 491 | } 492 | 493 | span.tooltip span.tooltip { 494 | font-size: 1.0em; 495 | display: none; 496 | padding: 0.3em; 497 | border: 1px solid #aaa; 498 | background-color: #fdfec8; 499 | color: #000; 500 | text-align: left; 501 | } 502 | 503 | span.tooltip:hover span.tooltip { 504 | display: block; 505 | position: absolute; 506 | margin-left: 2em; 507 | } 508 | 509 | /* @end */ 510 | 511 | /* @group Method section */ 512 | 513 | .section-method { 514 | margin-top: 2.3em; 515 | } 516 | 517 | .method-title { 518 | margin-bottom: 1.5em; 519 | } 520 | 521 | .method-subtitle { 522 | margin-top: 0.7em; 523 | margin-bottom: 0.2em; 524 | } 525 | 526 | .method-subsection p { 527 | margin-top: 0.4em; 528 | margin-bottom: 0.8em; 529 | } 530 | 531 | .method-declaration { 532 | margin-top:1.182em; 533 | margin-bottom:.909em; 534 | } 535 | 536 | .method-declaration code { 537 | font:14px Courier, Consolas, monospace; 538 | color:#000; 539 | } 540 | 541 | .declaration { 542 | color: #000; 543 | } 544 | 545 | .argument-def { 546 | margin-top: 0.3em; 547 | margin-bottom: 0.3em; 548 | } 549 | 550 | .argument-def dd { 551 | margin-left: 1.25em; 552 | } 553 | 554 | .see-also-section ul { 555 | list-style-type: none; 556 | padding-left: 0px; 557 | margin-top: 0; 558 | } 559 | 560 | .see-also-section li { 561 | margin-bottom: 3px; 562 | } 563 | 564 | .declared-in-ref { 565 | color: #666; 566 | } 567 | 568 | #tocContainer.hideInXcode { 569 | display: none; 570 | border: 0px solid black; 571 | } 572 | 573 | #top_header.hideInXcode { 574 | display: none; 575 | } 576 | 577 | #contents.hideInXcode { 578 | border: 0px solid black; 579 | top: 0px; 580 | left: 0px; 581 | } 582 | 583 | /* @end */ 584 | 585 | -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/html/css/stylesPrint.css: -------------------------------------------------------------------------------- 1 | 2 | header { 3 | display: none; 4 | } 5 | 6 | div.main-navigation, div.navigation-top { 7 | display: none; 8 | } 9 | 10 | div#overview_contents, div#contents.isShowingTOC, div#contents { 11 | overflow: visible; 12 | position: relative; 13 | top: 0px; 14 | border: none; 15 | left: 0; 16 | } 17 | #tocContainer.isShowingTOC { 18 | display: none; 19 | } 20 | nav { 21 | display: none; 22 | } -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/html/hierarchy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TWSReleaseNotesView Hierarchy 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |

TWSReleaseNotesView

14 | Matteo Lallone 15 |
16 | 17 | 20 | 21 |
22 |
23 |
24 | 27 | 32 |
33 | 34 |
35 |

Class Hierarchy

36 | 37 | 56 | 57 |
58 | 59 | 60 | 61 |
62 | 65 | 75 |
76 |
77 | 78 | -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/html/img/button_bar_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesView Docs/html/img/button_bar_background.png -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/html/img/disclosure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesView Docs/html/img/disclosure.png -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/html/img/disclosure_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesView Docs/html/img/disclosure_open.png -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/html/img/library_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesView Docs/html/img/library_background.png -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/html/img/title_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesView Docs/html/img/title_background.png -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TWSReleaseNotesView Reference 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |

TWSReleaseNotesView

14 | Matteo Lallone 15 |
16 | 17 | 20 | 21 |
22 |
23 |
24 | 27 | 32 |
33 | 34 | 35 | 36 | 37 | 38 |
39 |

Class References

40 | 47 |
48 | 49 | 50 | 51 |
52 | 55 | 65 |
66 |
67 | 68 | -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleIdentifier 8 | it.matteolallone.TWSReleaseNotesView 9 | CFBundleName 10 | TWSReleaseNotesView Documentation 11 | CFBundleShortVersionString 12 | 1.0 13 | CFBundleVersion 14 | 1.0 15 | 16 | 17 | 18 | 19 | DocSetFeedName 20 | TWSReleaseNotesView Documentation 21 | 22 | DocSetMinimumXcodeVersion 23 | 3.0 24 | 25 | DocSetPublisherIdentifier 26 | it.matteolallone.documentation 27 | DocSetPublisherName 28 | Matteo Lallone 29 | NSHumanReadableCopyright 30 | Copyright © 2013 Matteo Lallone. All rights reserved. 31 | 32 | 33 | -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/Documents/Classes/TWSReleaseNotesDownloadOperation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TWSReleaseNotesDownloadOperation Class Reference 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |

TWSReleaseNotesView

16 | Matteo Lallone 17 |
18 | 19 | 22 | 61 |
62 | 103 |
104 |
105 | 106 | 112 | 117 |
118 | 119 |
120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 |
Inherits fromNSOperation
Declared inTWSReleaseNotesDownloadOperation.h
TWSReleaseNotesDownloadOperation.m
128 | 129 | 130 | 131 | 132 |
133 | 134 |

Overview

135 |

Use the TWSReleaseNotesDownloadOperation class to create an operation with the purpose of downloading the release notes text for a specified app, using the iTunes Search API.

136 | 137 |

The result of the operation is accessible in the completionBlock, using the releaseNotesText and the error properties.

138 |
139 | 140 | 141 | 142 | 143 | 144 |
145 | 146 |

Tasks

147 | 148 | 149 | 150 |

Getting main properties

151 | 152 |
    153 |
  • 154 | 155 |   releaseNotesText 156 |

    The downloaded release notes text.

    157 |
    158 | property 159 | 160 |
  • 161 | 162 |   error 163 |

    An error object associated to the failed operation.

    164 |
    165 | property 166 | 167 |
  • 168 |
169 | 170 | 171 | 172 |

Creating the operation

173 | 174 | 184 | 185 |
186 | 187 | 188 | 189 | 190 | 191 |
192 | 193 |

Properties

194 | 195 |
196 | 197 |

error

198 | 199 | 200 | 201 |
202 |

An error object associated to the failed operation.

203 |
204 | 205 | 206 | 207 |
@property (readonly, strong, nonatomic) NSError *error
208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 |
224 |

Declared In

225 | TWSReleaseNotesDownloadOperation.h
226 |
227 | 228 | 229 |
230 | 231 |
232 | 233 |

releaseNotesText

234 | 235 | 236 | 237 |
238 |

The downloaded release notes text.

239 |
240 | 241 | 242 | 243 |
@property (readonly, copy, nonatomic) NSString *releaseNotesText
244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 |
260 |

Declared In

261 | TWSReleaseNotesDownloadOperation.h
262 |
263 | 264 | 265 |
266 | 267 |
268 | 269 | 270 | 271 | 272 | 273 |
274 | 275 |

Instance Methods

276 | 277 |
278 | 279 |

initWithAppIdentifier:

280 | 281 | 282 | 283 |
284 |

Creates and operation with custom parameters.

285 |
286 | 287 | 288 | 289 |
- (id)initWithAppIdentifier:(NSString *)appIdentifier
290 | 291 | 292 | 293 |
294 |

Parameters

295 | 296 |
297 |
appIdentifier
298 |

The App Store app identifier for remote release notes retrieval.

299 |
300 | 301 |
302 | 303 | 304 | 305 |
306 |

Return Value

307 |

The initialized operation.

308 |
309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 |
321 |

Declared In

322 | TWSReleaseNotesDownloadOperation.h
323 |
324 | 325 | 326 |
327 | 328 |
329 | 330 | 331 |
332 | 338 | 347 |
348 |
349 | 440 | 441 | -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/Documents/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 3 | font-size: 13px; 4 | } 5 | 6 | code { 7 | font-family: Courier, Consolas, monospace; 8 | font-size: 13px; 9 | color: #666; 10 | } 11 | 12 | pre { 13 | font-family: Courier, Consolas, monospace; 14 | font-size: 13px; 15 | line-height: 18px; 16 | tab-interval: 0.5em; 17 | border: 1px solid #C7CFD5; 18 | background-color: #F1F5F9; 19 | color: #666; 20 | padding: 0.3em 1em; 21 | } 22 | 23 | ul { 24 | list-style-type: square; 25 | } 26 | 27 | li { 28 | margin-bottom: 10px; 29 | } 30 | 31 | a, a code { 32 | text-decoration: none; 33 | color: #36C; 34 | } 35 | 36 | a:hover, a:hover code { 37 | text-decoration: underline; 38 | color: #36C; 39 | } 40 | 41 | h2 { 42 | border-bottom: 1px solid #8391A8; 43 | color: #3C4C6C; 44 | font-size: 187%; 45 | font-weight: normal; 46 | margin-top: 1.75em; 47 | padding-bottom: 2px; 48 | } 49 | 50 | table { 51 | margin-bottom: 4em; 52 | border-collapse:collapse; 53 | vertical-align: middle; 54 | } 55 | 56 | td { 57 | border: 1px solid #9BB3CD; 58 | padding: .667em; 59 | font-size: 100%; 60 | } 61 | 62 | th { 63 | border: 1px solid #9BB3CD; 64 | padding: .3em .667em .3em .667em; 65 | background: #93A5BB; 66 | font-size: 103%; 67 | font-weight: bold; 68 | color: white; 69 | text-align: left; 70 | } 71 | 72 | /* @group Common page elements */ 73 | 74 | #top_header { 75 | height: 91px; 76 | left: 0; 77 | min-width: 598px; 78 | position: absolute; 79 | right: 0; 80 | top: 0; 81 | z-index: 900; 82 | } 83 | 84 | #footer { 85 | clear: both; 86 | padding-top: 20px; 87 | text-align: center; 88 | } 89 | 90 | #contents, #overview_contents { 91 | -webkit-overflow-scrolling: touch; 92 | border-top: 1px solid #2B334F; 93 | position: absolute; 94 | top: 91px; 95 | left: 0; 96 | right: 0; 97 | bottom: 0; 98 | overflow-x: hidden; 99 | overflow-y: auto; 100 | padding-left: 2em; 101 | padding-right: 2em; 102 | padding-top: 1em; 103 | min-width: 550px; 104 | } 105 | 106 | #contents.isShowingTOC { 107 | left: 230px; 108 | min-width: 320px; 109 | } 110 | 111 | .copyright { 112 | font-size: 12px; 113 | } 114 | 115 | .generator { 116 | font-size: 11px; 117 | } 118 | 119 | .main-navigation ul li { 120 | display: inline; 121 | margin-left: 15px; 122 | list-style: none; 123 | } 124 | 125 | .navigation-top { 126 | clear: both; 127 | float: right; 128 | } 129 | 130 | .navigation-bottom { 131 | clear: both; 132 | float: right; 133 | margin-top: 20px; 134 | margin-bottom: -10px; 135 | } 136 | 137 | .open > .disclosure { 138 | background-image: url("../img/disclosure_open.png"); 139 | } 140 | 141 | .disclosure { 142 | background: url("../img/disclosure.png") no-repeat scroll 0 0; 143 | } 144 | 145 | .disclosure, .nodisclosure { 146 | display: inline-block; 147 | height: 8px; 148 | margin-right: 5px; 149 | position: relative; 150 | width: 9px; 151 | } 152 | 153 | /* @end */ 154 | 155 | /* @group Header */ 156 | 157 | #top_header #library { 158 | background: url("../img/library_background.png") repeat-x 0 0 #485E78; 159 | background-color: #ccc; 160 | height: 35px; 161 | font-size: 115%; 162 | } 163 | 164 | #top_header #library #libraryTitle { 165 | color: #FFFFFF; 166 | margin-left: 15px; 167 | text-shadow: 0 -1px 0 #485E78; 168 | top: 8px; 169 | position: absolute; 170 | } 171 | 172 | #top_header #library #developerHome { 173 | color: #92979E; 174 | right: 15px; 175 | top: 8px; 176 | position: absolute; 177 | } 178 | 179 | #top_header #library a:hover { 180 | text-decoration: none; 181 | } 182 | 183 | #top_header #title { 184 | background: url("../img/title_background.png") repeat-x 0 0 #8A98A9; 185 | border-bottom: 1px solid #B6B6B6; 186 | height: 25px; 187 | overflow: hidden; 188 | } 189 | 190 | #top_header h1 { 191 | font-size: 115%; 192 | font-weight: normal; 193 | margin: 0; 194 | padding: 3px 0 2px; 195 | text-align: center; 196 | text-shadow: 0 1px 0 #D5D5D5; 197 | white-space: nowrap; 198 | } 199 | 200 | #headerButtons { 201 | background-color: #D8D8D8; 202 | background-image: url("../img/button_bar_background.png"); 203 | border-bottom: 1px solid #EDEDED; 204 | border-top: 1px solid #2B334F; 205 | font-size: 8pt; 206 | height: 28px; 207 | left: 0; 208 | list-style: none outside none; 209 | margin: 0; 210 | overflow: hidden; 211 | padding: 0; 212 | position: absolute; 213 | right: 0; 214 | top: 61px; 215 | } 216 | 217 | #headerButtons li { 218 | background-repeat: no-repeat; 219 | display: inline; 220 | margin-top: 0; 221 | margin-bottom: 0; 222 | padding: 0; 223 | } 224 | 225 | #toc_button button { 226 | border-color: #ACACAC; 227 | border-style: none solid none none; 228 | border-width: 0 1px 0 0; 229 | height: 28px; 230 | margin: 0; 231 | padding-left: 30px; 232 | text-align: left; 233 | width: 230px; 234 | } 235 | 236 | li#jumpto_button { 237 | left: 230px; 238 | margin-left: 0; 239 | position: absolute; 240 | } 241 | 242 | li#jumpto_button select { 243 | height: 22px; 244 | margin: 5px 2px 0 10px; 245 | max-width: 300px; 246 | } 247 | 248 | /* @end */ 249 | 250 | /* @group Table of contents */ 251 | 252 | #tocContainer.isShowingTOC { 253 | border-right: 1px solid #ACACAC; 254 | display: block; 255 | overflow-x: hidden; 256 | overflow-y: auto; 257 | padding: 0; 258 | } 259 | 260 | #tocContainer { 261 | background-color: #E4EBF7; 262 | border-top: 1px solid #2B334F; 263 | bottom: 0; 264 | display: none; 265 | left: 0; 266 | overflow: hidden; 267 | position: absolute; 268 | top: 91px; 269 | width: 229px; 270 | } 271 | 272 | #tocContainer > ul#toc { 273 | font-size: 11px; 274 | margin: 0; 275 | padding: 12px 0 18px; 276 | width: 209px; 277 | -moz-user-select: none; 278 | -webkit-user-select: none; 279 | user-select: none; 280 | } 281 | 282 | #tocContainer > ul#toc > li { 283 | margin: 0; 284 | padding: 0 0 7px 30px; 285 | text-indent: -15px; 286 | } 287 | 288 | #tocContainer > ul#toc > li > .sectionName a { 289 | color: #000000; 290 | font-weight: bold; 291 | } 292 | 293 | #tocContainer > ul#toc > li > .sectionName a:hover { 294 | text-decoration: none; 295 | } 296 | 297 | #tocContainer > ul#toc li.children > ul { 298 | display: none; 299 | height: 0; 300 | } 301 | 302 | #tocContainer > ul#toc > li > ul { 303 | margin: 0; 304 | padding: 0; 305 | } 306 | 307 | #tocContainer > ul#toc > li > ul, ul#toc > li > ul > li { 308 | margin-left: 0; 309 | margin-bottom: 0; 310 | padding-left: 15px; 311 | } 312 | 313 | #tocContainer > ul#toc > li ul { 314 | list-style: none; 315 | margin-right: 0; 316 | padding-right: 0; 317 | } 318 | 319 | #tocContainer > ul#toc li.children.open > ul { 320 | display: block; 321 | height: auto; 322 | margin-left: -15px; 323 | padding-left: 0; 324 | } 325 | 326 | #tocContainer > ul#toc > li > ul, ul#toc > li > ul > li { 327 | margin-left: 0; 328 | padding-left: 15px; 329 | } 330 | 331 | #tocContainer li ul li { 332 | margin-top: 0.583em; 333 | overflow: hidden; 334 | text-overflow: ellipsis; 335 | white-space: nowrap; 336 | } 337 | 338 | #tocContainer li ul li span.sectionName { 339 | white-space: normal; 340 | } 341 | 342 | #tocContainer > ul#toc > li > ul > li > .sectionName a { 343 | font-weight: bold; 344 | } 345 | 346 | #tocContainer > ul#toc > li > ul a { 347 | color: #4F4F4F; 348 | } 349 | 350 | /* @end */ 351 | 352 | /* @group Index formatting */ 353 | 354 | .index-title { 355 | font-size: 13px; 356 | font-weight: normal; 357 | } 358 | 359 | .index-column { 360 | float: left; 361 | width: 30%; 362 | min-width: 200px; 363 | font-size: 11px; 364 | } 365 | 366 | .index-column ul { 367 | margin: 8px 0 0 0; 368 | padding: 0; 369 | list-style: none; 370 | } 371 | 372 | .index-column ul li { 373 | margin: 0 0 3px 0; 374 | padding: 0; 375 | } 376 | 377 | .hierarchy-column { 378 | min-width: 400px; 379 | } 380 | 381 | .hierarchy-column ul { 382 | margin: 3px 0 0 15px; 383 | } 384 | 385 | .hierarchy-column ul li { 386 | list-style-type: square; 387 | } 388 | 389 | /* @end */ 390 | 391 | /* @group Common formatting elements */ 392 | 393 | .title { 394 | font-weight: normal; 395 | font-size: 215%; 396 | margin-top:0; 397 | } 398 | 399 | .subtitle { 400 | font-weight: normal; 401 | font-size: 180%; 402 | color: #3C4C6C; 403 | border-bottom: 1px solid #5088C5; 404 | } 405 | 406 | .subsubtitle { 407 | font-weight: normal; 408 | font-size: 145%; 409 | height: 0.7em; 410 | } 411 | 412 | .note { 413 | border: 1px solid #5088C5; 414 | background-color: white; 415 | margin: 1.667em 0 1.75em 0; 416 | padding: 0 .667em .083em .750em; 417 | } 418 | 419 | .warning { 420 | border: 1px solid #5088C5; 421 | background-color: #F0F3F7; 422 | margin-bottom: 0.5em; 423 | padding: 0.3em 0.8em; 424 | } 425 | 426 | .bug { 427 | border: 1px solid #000; 428 | background-color: #ffffcc; 429 | margin-bottom: 0.5em; 430 | padding: 0.3em 0.8em; 431 | } 432 | 433 | .deprecated { 434 | color: #F60425; 435 | } 436 | 437 | /* @end */ 438 | 439 | /* @group Common layout */ 440 | 441 | .section { 442 | margin-top: 3em; 443 | } 444 | 445 | /* @end */ 446 | 447 | /* @group Object specification section */ 448 | 449 | .section-specification { 450 | margin-left: 2.5em; 451 | margin-right: 2.5em; 452 | font-size: 12px; 453 | } 454 | 455 | .section-specification table { 456 | margin-bottom: 0em; 457 | border-top: 1px solid #d6e0e5; 458 | } 459 | 460 | .section-specification td { 461 | vertical-align: top; 462 | border-bottom: 1px solid #d6e0e5; 463 | border-left-width: 0px; 464 | border-right-width: 0px; 465 | border-top-width: 0px; 466 | padding: .6em; 467 | } 468 | 469 | .section-specification .specification-title { 470 | font-weight: bold; 471 | } 472 | 473 | /* @end */ 474 | 475 | /* @group Tasks section */ 476 | 477 | .task-list { 478 | list-style-type: none; 479 | padding-left: 0px; 480 | } 481 | 482 | .task-list li { 483 | margin-bottom: 3px; 484 | } 485 | 486 | .task-item-suffix { 487 | color: #996; 488 | font-size: 12px; 489 | font-style: italic; 490 | margin-left: 0.5em; 491 | } 492 | 493 | span.tooltip span.tooltip { 494 | font-size: 1.0em; 495 | display: none; 496 | padding: 0.3em; 497 | border: 1px solid #aaa; 498 | background-color: #fdfec8; 499 | color: #000; 500 | text-align: left; 501 | } 502 | 503 | span.tooltip:hover span.tooltip { 504 | display: block; 505 | position: absolute; 506 | margin-left: 2em; 507 | } 508 | 509 | /* @end */ 510 | 511 | /* @group Method section */ 512 | 513 | .section-method { 514 | margin-top: 2.3em; 515 | } 516 | 517 | .method-title { 518 | margin-bottom: 1.5em; 519 | } 520 | 521 | .method-subtitle { 522 | margin-top: 0.7em; 523 | margin-bottom: 0.2em; 524 | } 525 | 526 | .method-subsection p { 527 | margin-top: 0.4em; 528 | margin-bottom: 0.8em; 529 | } 530 | 531 | .method-declaration { 532 | margin-top:1.182em; 533 | margin-bottom:.909em; 534 | } 535 | 536 | .method-declaration code { 537 | font:14px Courier, Consolas, monospace; 538 | color:#000; 539 | } 540 | 541 | .declaration { 542 | color: #000; 543 | } 544 | 545 | .argument-def { 546 | margin-top: 0.3em; 547 | margin-bottom: 0.3em; 548 | } 549 | 550 | .argument-def dd { 551 | margin-left: 1.25em; 552 | } 553 | 554 | .see-also-section ul { 555 | list-style-type: none; 556 | padding-left: 0px; 557 | margin-top: 0; 558 | } 559 | 560 | .see-also-section li { 561 | margin-bottom: 3px; 562 | } 563 | 564 | .declared-in-ref { 565 | color: #666; 566 | } 567 | 568 | #tocContainer.hideInXcode { 569 | display: none; 570 | border: 0px solid black; 571 | } 572 | 573 | #top_header.hideInXcode { 574 | display: none; 575 | } 576 | 577 | #contents.hideInXcode { 578 | border: 0px solid black; 579 | top: 0px; 580 | left: 0px; 581 | } 582 | 583 | /* @end */ 584 | 585 | -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/Documents/css/stylesPrint.css: -------------------------------------------------------------------------------- 1 | 2 | header { 3 | display: none; 4 | } 5 | 6 | div.main-navigation, div.navigation-top { 7 | display: none; 8 | } 9 | 10 | div#overview_contents, div#contents.isShowingTOC, div#contents { 11 | overflow: visible; 12 | position: relative; 13 | top: 0px; 14 | border: none; 15 | left: 0; 16 | } 17 | #tocContainer.isShowingTOC { 18 | display: none; 19 | } 20 | nav { 21 | display: none; 22 | } -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/Documents/hierarchy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TWSReleaseNotesView Hierarchy 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |

TWSReleaseNotesView

14 | Matteo Lallone 15 |
16 | 17 | 20 | 21 |
22 |
23 |
24 | 27 | 32 |
33 | 34 |
35 |

Class Hierarchy

36 | 37 | 56 | 57 |
58 | 59 | 60 | 61 |
62 | 65 | 75 |
76 |
77 | 78 | -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/Documents/img/button_bar_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/Documents/img/button_bar_background.png -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/Documents/img/disclosure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/Documents/img/disclosure.png -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/Documents/img/disclosure_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/Documents/img/disclosure_open.png -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/Documents/img/library_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/Documents/img/library_background.png -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/Documents/img/title_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/Documents/img/title_background.png -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/Documents/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TWSReleaseNotesView Reference 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |

TWSReleaseNotesView

14 | Matteo Lallone 15 |
16 | 17 | 20 | 21 |
22 |
23 |
24 | 27 | 32 |
33 | 34 | 35 | 36 | 37 | 38 |
39 |

Class References

40 | 47 |
48 | 49 | 50 | 51 |
52 | 55 | 65 |
66 |
67 | 68 | -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/docSet.mom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/docSet.mom -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/docSet.skidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/docSet.skidx -------------------------------------------------------------------------------- /TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/docSet.toc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesView Docs/it.matteolallone.TWSReleaseNotesView.docset/Contents/Resources/docSet.toc -------------------------------------------------------------------------------- /TWSReleaseNotesView/TWSReleaseNotesDownloadOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // TWSReleaseNotesDownloadOperation.h 3 | // TWSReleaseNotesViewSample 4 | // 5 | // Created by Matteo Lallone on 03/08/13. 6 | // Copyright (c) 2013 Tapwings. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | Use the `TWSReleaseNotesDownloadOperation` class to create an operation with the purpose of downloading the release notes text for a specified app, using the iTunes Search API. 13 | 14 | The result of the operation is accessible in the `completionBlock`, using the and the properties. 15 | 16 | */ 17 | @interface TWSReleaseNotesDownloadOperation : NSOperation 18 | 19 | 20 | /** @name Getting main properties */ 21 | 22 | /// The downloaded release notes text. 23 | @property (readonly, copy, nonatomic) NSString *releaseNotesText; 24 | 25 | /// An error object associated to the failed operation. 26 | @property (readonly, strong, nonatomic) NSError *error; 27 | 28 | 29 | /** @name Creating the operation */ 30 | 31 | /** 32 | Creates and operation with custom parameters. 33 | @param appIdentifier The App Store app identifier for remote release notes retrieval. 34 | @return The initialized operation. 35 | */ 36 | - (id)initWithAppIdentifier:(NSString *)appIdentifier; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /TWSReleaseNotesView/TWSReleaseNotesDownloadOperation.m: -------------------------------------------------------------------------------- 1 | // 2 | // TWSReleaseNotesDownloadOperation.m 3 | // TWSReleaseNotesViewSample 4 | // 5 | // Created by Matteo Lallone on 03/08/13. 6 | // Copyright (c) 2013 Tapwings. All rights reserved. 7 | // 8 | 9 | #import "TWSReleaseNotesDownloadOperation.h" 10 | #import 11 | 12 | static NSString *const kTWSReleaseNotesDownloadOperationSearchURL = @"http://itunes.apple.com/lookup"; 13 | static NSString *const kTWSReleaseNotesDownloadOperationResultsArrayKey = @"results"; 14 | static NSString *const kTWSReleaseNotesDownloadOperationReleaseNotesKey = @"releaseNotes"; 15 | static NSString *const kTWSReleaseNotesDownloadOperationErrorDomain = @"com.tapwings.open.error.releaseNotes"; 16 | static const NSInteger kTWSReleaseNotesDownloadOperationDecodeErrorCode = 0; 17 | 18 | @interface TWSReleaseNotesDownloadOperation () 19 | 20 | @property (strong, nonatomic) NSURL *requestURL; 21 | @property (strong, nonatomic) NSURLConnection *urlConnection; 22 | @property (readwrite, strong, nonatomic) NSError *error; 23 | @property (readwrite, copy, nonatomic) NSString *releaseNotesText; 24 | @property (strong, nonatomic) NSMutableData *bufferData; 25 | @property (strong, nonatomic) NSData *appMetadata; 26 | @property (assign, nonatomic) BOOL isExecuting; 27 | @property (assign, nonatomic) BOOL isConcurrent; 28 | @property (assign, nonatomic) BOOL isFinished; 29 | 30 | - (void)extractReleaseNotes; 31 | 32 | @end 33 | 34 | @implementation TWSReleaseNotesDownloadOperation 35 | 36 | #pragma mark - Init - dealloc Methods 37 | 38 | - (id)initWithAppIdentifier:(NSString *)appIdentifier 39 | { 40 | self = [super init]; 41 | 42 | if (self) 43 | { 44 | // Setup request URL 45 | NSLocale *locale = [NSLocale currentLocale]; 46 | NSString *countryCode = [locale objectForKey:NSLocaleCountryCode]; 47 | _requestURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@?id=%@&country=%@", kTWSReleaseNotesDownloadOperationSearchURL, appIdentifier, countryCode]]; 48 | } 49 | 50 | return self; 51 | } 52 | 53 | #pragma mark - Instance Methods 54 | 55 | - (void)start 56 | { 57 | // Setup URL request 58 | NSURLRequest *request = [NSURLRequest requestWithURL:self.requestURL]; 59 | self.isExecuting = YES; 60 | self.isConcurrent = YES; 61 | self.isFinished = NO; 62 | 63 | // Setup URL connection 64 | [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 65 | self.urlConnection = [NSURLConnection connectionWithRequest:request delegate:self]; 66 | }]; 67 | } 68 | 69 | - (void)setIsExecuting:(BOOL)isExecuting 70 | { 71 | [self willChangeValueForKey:@"isExecuting"]; 72 | _isExecuting = isExecuting; 73 | [self didChangeValueForKey:@"isExecuting"]; 74 | 75 | // Toggle network activity indicator 76 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:isExecuting]; 77 | } 78 | 79 | - (void)setIsFinished:(BOOL)isFinished 80 | { 81 | [self willChangeValueForKey:@"isFinished"]; 82 | _isFinished = isFinished; 83 | [self didChangeValueForKey:@"isFinished"]; 84 | } 85 | 86 | - (void)cancel 87 | { 88 | [super cancel]; 89 | 90 | // Cancel URL connection 91 | [self.urlConnection cancel]; 92 | self.isFinished = YES; 93 | self.isExecuting = NO; 94 | } 95 | 96 | #pragma mark - Private Methods 97 | 98 | - (void)extractReleaseNotes 99 | { 100 | // Decode data 101 | NSError *decodeError; 102 | id rootObject = [NSJSONSerialization JSONObjectWithData:self.appMetadata options:NSJSONReadingAllowFragments error:&decodeError]; 103 | 104 | if (!decodeError && [rootObject isKindOfClass:[NSDictionary class]]) 105 | { 106 | NSDictionary *rootDictionary = (NSDictionary *)rootObject; 107 | id resultsObject = rootDictionary[kTWSReleaseNotesDownloadOperationResultsArrayKey]; 108 | 109 | if ([resultsObject isKindOfClass:[NSArray class]]) 110 | { 111 | NSArray *resultsArray = (NSArray *)resultsObject; 112 | if ([resultsArray count]) 113 | { 114 | id metadataObject = resultsArray[0]; 115 | 116 | if ([metadataObject isKindOfClass:[NSDictionary class]]) 117 | { 118 | NSDictionary *metadataDictionary = (NSDictionary *)metadataObject; 119 | id releaseNotesObject = metadataDictionary[kTWSReleaseNotesDownloadOperationReleaseNotesKey]; 120 | 121 | if ([releaseNotesObject isKindOfClass:[NSString class]]) 122 | { 123 | // Set release note text 124 | self.releaseNotesText = releaseNotesObject; 125 | 126 | self.isExecuting = NO; 127 | self.isFinished = YES; 128 | 129 | return; 130 | } 131 | } 132 | } 133 | } 134 | } 135 | 136 | decodeError = [NSError errorWithDomain:kTWSReleaseNotesDownloadOperationErrorDomain code:kTWSReleaseNotesDownloadOperationDecodeErrorCode userInfo:nil]; 137 | self.error = decodeError; 138 | self.isExecuting = NO; 139 | self.isFinished = YES; 140 | } 141 | 142 | #pragma mark - NSURLConnectionDelegate Methods 143 | 144 | - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response 145 | { 146 | return request; 147 | } 148 | 149 | - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse 150 | { 151 | return cachedResponse; 152 | } 153 | 154 | - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response 155 | { 156 | // Setup buffer 157 | self.bufferData = [NSMutableData data]; 158 | } 159 | 160 | - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 161 | { 162 | // Append data to buffer 163 | [self.bufferData appendData:data]; 164 | } 165 | 166 | - (void)connectionDidFinishLoading:(NSURLConnection *)connection 167 | { 168 | // Set release notes data 169 | self.appMetadata = self.bufferData; 170 | self.bufferData = nil; 171 | 172 | // Extract release notes text 173 | [self extractReleaseNotes]; 174 | } 175 | 176 | - (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error 177 | { 178 | // Set error 179 | self.error = error; 180 | 181 | self.isExecuting = NO; 182 | self.isFinished = YES; 183 | } 184 | 185 | @end 186 | -------------------------------------------------------------------------------- /TWSReleaseNotesView/TWSReleaseNotesView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TWSReleaseNotesView.h 3 | // TWSReleaseNotesViewSample 4 | // 5 | // Created by Matteo Lallone on 02/08/13. 6 | // Copyright (c) 2013 Tapwings. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | Use the `TWSReleaseNotesView` class to display a custom release notes view, to be shown when the app is opened for the first time after an update. 13 | 14 | If you want to check if the app is on its first launch, you can use the method. This method will check a previously stored app version string. If a stored app version string does not exist, it will return `YES`, storing the current version string. 15 | 16 | In order to check if the app was updated, the method can be used. This method will check a previously stored app version string. If a stored app version string is present but it does not match the current version string, it will return `YES`, storing the current version string. 17 | 18 | The release notes view can be initialized using the class method, if the release notes text is set directly when calling the method. If the release notes must be retrieved from the App Store, you must use the class method. 19 | 20 | The appearance of the view can be customized using its alpha, color and font properties, to be set before showing the release notes view with the instance method. 21 | */ 22 | @interface TWSReleaseNotesView : UIView 23 | 24 | 25 | /** @name Setting main properties */ 26 | 27 | /// The alpha value for the overlay to be applied to the container view. Default is `0.5f`. 28 | @property (assign, nonatomic) CGFloat overlayAlpha; 29 | 30 | /// The alpha value for the background color to be applied to the text container view. Default is `0.8f`. 31 | @property (assign, nonatomic) CGFloat textViewAlpha; 32 | 33 | /// The background color to be applied to the text container view. Default is `[UIColor blackColor]`. 34 | @property (strong, nonatomic) UIColor *textViewBackgroundColor; 35 | 36 | /// The dark separator color. Default is `[UIColor colorWithRed:16.0f/255.0f green:16.0f/255.0f blue:16.0f/255.0f alpha:1.0]`. 37 | @property (strong, nonatomic) UIColor *darkSeparatorColor; 38 | 39 | /// The light separator color. Default is `[UIColor colorWithRed:58.0f/255.0f green:58.0f/255.0f blue:65.0f/255.0f alpha:1.0]`. 40 | @property (strong, nonatomic) UIColor *lightSeparatorColor; 41 | 42 | /// The shadow color for the release notes view. Default is `[UIColor blackColor]`. 43 | @property (strong, nonatomic) UIColor *viewShadowColor; 44 | 45 | /// The shadow offset for the release notes view. Default is `(0.0f, 3.0f)`. 46 | @property (assign, nonatomic) CGSize viewShadowOffset; 47 | 48 | /// The shadow radius for the release notes view. Default is `3.0f`. 49 | @property (assign, nonatomic) CGFloat viewShadowRadius; 50 | 51 | /// The shadow opacity for the release notes view. Default is `1.0f`. 52 | @property (assign, nonatomic) CGFloat viewShadowOpacity; 53 | 54 | /// The font for the title label. Default is `[UIFont systemFontOfSize:16.0f]`. 55 | @property (strong, nonatomic) UIFont *titleFont; 56 | 57 | /// The color for the title label. Default is `[UIColor whiteColor]`. 58 | @property (strong, nonatomic) UIColor *titleColor; 59 | 60 | /// The shadow color for the title label. Default is `[UIColor blackColor]`. 61 | @property (strong, nonatomic) UIColor *titleShadowColor; 62 | 63 | /// The shadow offset for the title label. Default is `(0.0f, -1.0f)`. 64 | @property (assign, nonatomic) CGSize titleShadowOffset; 65 | 66 | /// The font for the release notes text view. Default is `[UIFont systemFontOfSize:14.0f]`. 67 | @property (strong, nonatomic) UIFont *releaseNotesFont; 68 | 69 | /// The color for the release notes text view. Default is `[UIColor whiteColor]`. 70 | @property (strong, nonatomic) UIColor *releaseNotesColor; 71 | 72 | /// The shadow color for the release notes text view. Default is `[UIColor blackColor]`. 73 | @property (strong, nonatomic) UIColor *releaseNotesShadowColor; 74 | 75 | /// The shadow offset for the release notes text view. Default is `(0.0f, -1.0f)`. 76 | @property (assign, nonatomic) CGSize releaseNotesShadowOffset; 77 | 78 | /// The font for the close button. Default is `[UIFont systemFontOfSize:16.0f]`. 79 | @property (strong, nonatomic) UIFont *closeButtonFont; 80 | 81 | /// The color for the close button. Default is `[UIColor whiteColor]`. 82 | @property (strong, nonatomic) UIColor *closeButtonColor; 83 | 84 | /// The shadow color for the close button. Default is `[UIColor blackColor]`. 85 | @property (strong, nonatomic) UIColor *closeButtonShadowColor; 86 | 87 | /// The shadow offset for the close button. Default is `(0.0f, -1.0f)`. 88 | @property (assign, nonatomic) CGSize closeButtonShadowOffset; 89 | 90 | 91 | /** @name Creating the release notes view */ 92 | 93 | /** 94 | Returns a release notes view initialized with custom parameters. 95 | @param releaseNotesTitle The title for the release notes view. 96 | @param releaseNotesText The release notes text. 97 | @param closeButtonTitle The title for the close button. 98 | @return The initialized release notes view. 99 | */ 100 | + (TWSReleaseNotesView *)viewWithReleaseNotesTitle:(NSString *)releaseNotesTitle text:(NSString *)releaseNotesText closeButtonTitle:(NSString *)closeButtonTitle; 101 | 102 | /** 103 | Creates a release notes view initialized with custom parameters and returns it in the completion block. 104 | @param appIdentifier The App Store app identifier for remote release notes retrieval. 105 | @param releaseNotesTitle The title for the release notes view. 106 | @param closeButtonTitle The title for the close button. 107 | @param completionBlock The block to be used as a completion handler. If the release notes retrieval is successful, a reference to the initialized release notes view is passed to the block. A `NSError` object is passed to the block otherwise. 108 | */ 109 | + (void)setupViewWithAppIdentifier:(NSString *)appIdentifier releaseNotesTitle:(NSString *)releaseNotesTitle closeButtonTitle:(NSString *)closeButtonTitle completionBlock:(void (^)(TWSReleaseNotesView *releaseView, NSString *releaseNoteText, NSError *error))completionBlock; 110 | 111 | 112 | /** @name Checking the app version */ 113 | 114 | /** 115 | Checks for app update state, using the `CFBundleVersion` key in the application `Info.plist`. 116 | @return Returns `YES` if a previous app version string was stored and if it does not match the current version string, `NO` otherwise. 117 | */ 118 | + (BOOL)isAppVersionUpdated; 119 | 120 | /** 121 | Checks if the app version key is currently stored or not. 122 | @return Returns `YES` if no previous app version string was stored, `NO` otherwise. 123 | */ 124 | + (BOOL)isAppOnFirstLaunch; 125 | 126 | 127 | /** @name Showing the release notes view */ 128 | 129 | /** 130 | Shows the release notes view in the specified container view. 131 | @param containerView The container view in which the release notes view must be shown. 132 | */ 133 | - (void)showInView:(UIView *)containerView; 134 | 135 | @end 136 | -------------------------------------------------------------------------------- /TWSReleaseNotesView/TWSReleaseNotesView.m: -------------------------------------------------------------------------------- 1 | // 2 | // TWSReleaseNotesView.m 3 | // TWSReleaseNotesViewSample 4 | // 5 | // Created by Matteo Lallone on 02/08/13. 6 | // Copyright (c) 2013 Tapwings. All rights reserved. 7 | // 8 | 9 | #import "TWSReleaseNotesView.h" 10 | #import 11 | #import "UIImage+ImageEffects.h" 12 | #import "TWSReleaseNotesDownloadOperation.h" 13 | 14 | @interface TWSUnselectableTextView : UITextView 15 | 16 | - (BOOL)canBecomeFirstResponder; 17 | 18 | @end 19 | 20 | @implementation TWSUnselectableTextView 21 | 22 | - (BOOL)canBecomeFirstResponder 23 | { 24 | return NO; 25 | } 26 | 27 | @end 28 | 29 | static NSString *const kTWSReleaseNotesViewVersionKey = @"com.tapwings.open.kTWSReleaseNotesViewControllerVersionKey"; 30 | static const CGFloat kTWSReleaseNotesViewDefaultOverlayAlpha = 0.5f; 31 | static const CGFloat kTWSReleaseNotesViewDefaultTextViewBackgroundAlpha = 0.8f; 32 | static const CGFloat kTWSReleaseNotesViewContainerViewCornerRadius = 3.0f; 33 | static const CGFloat kTWSReleaseNotesViewContainerViewWidth = 280.0f; 34 | static const CGFloat kTWSReleaseNotesViewContainerViewMinVerticalPadding = 60.0f; 35 | static const CGFloat kTWSReleaseNotesViewInnerContainerSidePadding = 6.0f; 36 | static const CGFloat kTWSReleaseNotesViewBlurredImageViewCornerRadius = 5.0f; 37 | static const CGFloat kTWSReleaseNotesViewTitleSidePadding = 6.0f; 38 | static const CGFloat kTWSReleaseNotesViewTitleLabelHeight = 44.0f; 39 | static const CGFloat kTWSReleaseNotesViewTextViewInsetHeight = 9.0f; 40 | static const CGFloat kTWSReleaseNotesViewButtonBoxHeight = 44.0f; 41 | static const CGFloat kTWSReleaseNotesViewSeparatorHeight = 1.0f; 42 | static const CGFloat kTWSReleaseNotesViewAnimationSpringScaleFactor = 0.05f; 43 | static const NSTimeInterval kTWSReleaseNotesViewTransitionDuration = 0.2f; 44 | 45 | @interface TWSReleaseNotesView () 46 | 47 | @property (copy, nonatomic) NSString *releaseNotesTitle; 48 | @property (copy, nonatomic) NSString *releaseNotesText; 49 | @property (copy, nonatomic) NSString *closeButtonTitle; 50 | @property (strong, nonatomic) UIView *popupView; 51 | @property (strong, nonatomic) UIImageView *backgroundBlurredImageView; 52 | @property (strong, nonatomic) UIView *backgroundOverlayView; 53 | @property (strong, nonatomic) UIView *textContainerView; 54 | @property (strong, nonatomic) UILabel *titleLabel; 55 | @property (strong, nonatomic) TWSUnselectableTextView *textView; 56 | @property (strong, nonatomic) UIButton *closeButton; 57 | 58 | - (id)initWithReleaseNotesTitle:(NSString *)releaseNotesTitle text:(NSString *)releaseNotesText closeButtonTitle:(NSString *)closeButtonTitle; 59 | - (void)setupSubviews; 60 | - (void)updateSubviewsLayoutInContainerView:(UIView *)containerView; 61 | - (void)prepareToShowInView:(UIView *)containerView; 62 | - (void)applyBlurredImageBackgroundFromView:(UIView *)view; 63 | - (UIView *)separatorInView:(UIView *)containerView belowView:(UIView *)topView; 64 | - (CGFloat)expectedReleaseNotesTextHeightWithWidth:(CGFloat)width; 65 | - (void)closeButtonTouchedDown:(id)sender; 66 | - (void)closeButtonTouchedUp:(id)sender; 67 | - (void)closeButtonDragExit:(id)sender; 68 | - (void)closeButtonDragEnter:(id)sender; 69 | - (void)dismiss; 70 | + (void)storeCurrentAppVersionString; 71 | 72 | @end 73 | 74 | @implementation TWSReleaseNotesView 75 | 76 | #pragma mark - Init - dealloc Methods 77 | 78 | - (id)initWithReleaseNotesTitle:(NSString *)releaseNotesTitle text:(NSString *)releaseNotesText closeButtonTitle:(NSString *)closeButtonTitle 79 | { 80 | self = [super init]; 81 | 82 | if (self) 83 | { 84 | // Setup user-defined properties 85 | _releaseNotesTitle = [releaseNotesTitle copy]; 86 | _releaseNotesText = [releaseNotesText copy]; 87 | _closeButtonTitle = [closeButtonTitle copy]; 88 | 89 | // Setup default properties 90 | _overlayAlpha = kTWSReleaseNotesViewDefaultOverlayAlpha; 91 | _textViewAlpha = kTWSReleaseNotesViewDefaultTextViewBackgroundAlpha; 92 | _textViewBackgroundColor = [UIColor blackColor]; 93 | _darkSeparatorColor = [UIColor colorWithRed:16.0f/255.0f green:16.0f/255.0f blue:16.0f/255.0f alpha:1.0]; 94 | _lightSeparatorColor = [UIColor colorWithRed:58.0f/255.0f green:58.0f/255.0f blue:65.0f/255.0f alpha:1.0]; 95 | _viewShadowColor = [UIColor blackColor]; 96 | _viewShadowOffset = CGSizeMake(0.0f, 3.0f); 97 | _viewShadowRadius = 3.0f; 98 | _viewShadowOpacity = 1.0f; 99 | _titleFont = [UIFont systemFontOfSize:16.0f]; 100 | _titleColor = [UIColor whiteColor]; 101 | _titleShadowColor = [UIColor blackColor]; 102 | _titleShadowOffset = CGSizeMake(0.0f, -1.0f); 103 | _releaseNotesFont = [UIFont systemFontOfSize:14.0f]; 104 | _releaseNotesColor = [UIColor whiteColor]; 105 | _releaseNotesShadowColor = [UIColor blackColor]; 106 | _releaseNotesShadowOffset = CGSizeMake(0.0f, -1.0f); 107 | _closeButtonFont = [UIFont systemFontOfSize:16.0f]; 108 | _closeButtonColor = [UIColor whiteColor]; 109 | _closeButtonShadowColor = [UIColor blackColor]; 110 | _closeButtonShadowOffset = CGSizeMake(0.0f, -1.0f); 111 | 112 | // Orientation change notification 113 | __weak TWSReleaseNotesView *weakSelf = self; 114 | [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidChangeStatusBarOrientationNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note){ 115 | double delayInSeconds = 0.01f; 116 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 117 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 118 | // Refresh blurred image background 119 | TWSReleaseNotesView *strongSelf = weakSelf; 120 | UIView *containerView = [strongSelf superview]; 121 | [strongSelf removeFromSuperview]; 122 | [strongSelf applyBlurredImageBackgroundFromView:containerView]; 123 | [strongSelf updateSubviewsLayoutInContainerView:containerView]; 124 | [containerView addSubview:strongSelf]; 125 | }); 126 | }]; 127 | 128 | // Setup subview hierarchy 129 | [self setupSubviews]; 130 | } 131 | 132 | return self; 133 | } 134 | 135 | - (void)dealloc 136 | { 137 | // Remove orientation change notification 138 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; 139 | } 140 | 141 | #pragma mark - Class Methods 142 | 143 | + (TWSReleaseNotesView *)viewWithReleaseNotesTitle:(NSString *)releaseNotesTitle text:(NSString *)releaseNotesText closeButtonTitle:(NSString *)closeButtonTitle 144 | { 145 | // Setup release controller 146 | TWSReleaseNotesView *releaseNotesView = [[TWSReleaseNotesView alloc] initWithReleaseNotesTitle:releaseNotesTitle text:releaseNotesText closeButtonTitle:closeButtonTitle]; 147 | return releaseNotesView; 148 | } 149 | 150 | + (void)setupViewWithAppIdentifier:(NSString *)appIdentifier releaseNotesTitle:(NSString *)releaseNotesTitle closeButtonTitle:(NSString *)closeButtonTitle completionBlock:(void (^)(TWSReleaseNotesView *, NSString *, NSError *))completionBlock 151 | { 152 | // Setup operation queue 153 | NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; 154 | operationQueue.maxConcurrentOperationCount = 1; 155 | TWSReleaseNotesDownloadOperation *operation = [[TWSReleaseNotesDownloadOperation alloc] initWithAppIdentifier:appIdentifier]; 156 | 157 | __weak TWSReleaseNotesDownloadOperation *weakOperation = operation; 158 | [operation setCompletionBlock:^{ 159 | TWSReleaseNotesDownloadOperation *strongOperation = weakOperation; 160 | if (completionBlock) 161 | { 162 | if (strongOperation.error) 163 | { 164 | NSError *error = strongOperation.error; 165 | 166 | [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 167 | // Perform completion block with error 168 | completionBlock(nil, nil, error); 169 | }]; 170 | } 171 | else 172 | { 173 | // Get release note text 174 | NSString *releaseNotesText = strongOperation.releaseNotesText; 175 | 176 | [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 177 | // Create and show release notes view 178 | TWSReleaseNotesView *releaseNotesView = [TWSReleaseNotesView viewWithReleaseNotesTitle:releaseNotesTitle text:releaseNotesText closeButtonTitle:closeButtonTitle]; 179 | 180 | // Perform completion block 181 | completionBlock(releaseNotesView, releaseNotesText, nil); 182 | }]; 183 | } 184 | } 185 | }]; 186 | 187 | // Add operation 188 | [operationQueue addOperation:operation]; 189 | } 190 | 191 | + (BOOL)isAppVersionUpdated 192 | { 193 | // Read stored version string and current version string 194 | NSString *previousAppVersion = [[NSUserDefaults standardUserDefaults] stringForKey:kTWSReleaseNotesViewVersionKey]; 195 | NSString *currentAppVersion = [[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"]; 196 | 197 | // Flag app as updated if a previous version string is found and it does not match with the current version string 198 | BOOL isUpdated = (previousAppVersion && ![previousAppVersion isEqualToString:currentAppVersion]) ? YES : NO; 199 | 200 | if (isUpdated || !previousAppVersion) 201 | { 202 | // Store current app version if needed 203 | [self storeCurrentAppVersionString]; 204 | } 205 | 206 | return isUpdated; 207 | } 208 | 209 | + (BOOL)isAppOnFirstLaunch 210 | { 211 | // Read stored version string 212 | NSString *previousAppVersion = [[NSUserDefaults standardUserDefaults] stringForKey:kTWSReleaseNotesViewVersionKey]; 213 | 214 | // Flag app as on first launch if no previous app string is found 215 | BOOL isFirstLaunch = (!previousAppVersion) ? YES : NO; 216 | 217 | if (isFirstLaunch) 218 | { 219 | // Store current app version if needed 220 | [self storeCurrentAppVersionString]; 221 | } 222 | 223 | return isFirstLaunch; 224 | } 225 | 226 | #pragma mark - Private Methods 227 | 228 | - (void)setupSubviews 229 | { 230 | // Main properties 231 | [self setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; 232 | 233 | // Main text container view 234 | _popupView = [[UIView alloc] initWithFrame:CGRectZero]; 235 | [_popupView setBackgroundColor:[UIColor clearColor]]; 236 | [_popupView setClipsToBounds:NO]; 237 | [_popupView.layer setCornerRadius:kTWSReleaseNotesViewContainerViewCornerRadius]; 238 | [_popupView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; 239 | [self addSubview:_popupView]; 240 | 241 | // Blurred background view 242 | _backgroundBlurredImageView = [[UIImageView alloc] initWithFrame:CGRectZero]; 243 | [_backgroundBlurredImageView setContentMode:UIViewContentModeCenter]; 244 | [_backgroundBlurredImageView setClipsToBounds:YES]; 245 | [_backgroundBlurredImageView.layer setCornerRadius:kTWSReleaseNotesViewBlurredImageViewCornerRadius]; 246 | [_backgroundBlurredImageView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; 247 | [_popupView addSubview:_backgroundBlurredImageView]; 248 | 249 | // Background overlay view 250 | _backgroundOverlayView = [[UIView alloc] initWithFrame:CGRectZero]; 251 | [_backgroundOverlayView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; 252 | [_backgroundOverlayView setClipsToBounds:YES]; 253 | [_backgroundOverlayView.layer setCornerRadius:kTWSReleaseNotesViewContainerViewCornerRadius]; 254 | [_backgroundBlurredImageView addSubview:_backgroundOverlayView]; 255 | 256 | // Main text container view 257 | _textContainerView = [[UIView alloc] initWithFrame:CGRectZero]; 258 | [_textContainerView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; 259 | [_textContainerView.layer setBorderWidth:1.0f]; 260 | [_textContainerView.layer setShadowOffset:CGSizeMake(0.0f, 1.0f)]; 261 | [_textContainerView.layer setShadowRadius:0.0f]; 262 | [_textContainerView.layer setShadowOpacity:1.0f]; 263 | [_popupView addSubview:_textContainerView]; 264 | 265 | // Title label 266 | _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 267 | [_titleLabel setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin]; 268 | [_titleLabel setBackgroundColor:[UIColor clearColor]]; 269 | [_titleLabel setNumberOfLines:2]; 270 | [_titleLabel.layer setShadowRadius:0.0f]; 271 | [_titleLabel.layer setShadowOpacity:1.0f]; 272 | [_titleLabel setTextAlignment:NSTextAlignmentCenter]; 273 | [_titleLabel setText:_releaseNotesTitle]; 274 | [_popupView addSubview:_titleLabel]; 275 | 276 | // Release notes text view 277 | _textView = [[TWSUnselectableTextView alloc] initWithFrame:CGRectZero]; 278 | [_textView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; 279 | [_textView setBackgroundColor:[UIColor clearColor]]; 280 | [_textView.layer setShadowRadius:0.0f]; 281 | [_textView.layer setShadowOpacity:1.0f]; 282 | [_textView setEditable:NO]; 283 | [_textView setText:_releaseNotesText]; 284 | [_popupView addSubview:_textView]; 285 | 286 | // Close button 287 | _closeButton = [UIButton buttonWithType:UIButtonTypeCustom]; 288 | [_closeButton setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin]; 289 | [_closeButton setTitle:_closeButtonTitle forState:UIControlStateNormal]; 290 | [_closeButton addTarget:self action:@selector(closeButtonTouchedUp:) forControlEvents:UIControlEventTouchUpInside]; 291 | [_closeButton addTarget:self action:@selector(closeButtonTouchedDown:) forControlEvents:UIControlEventTouchDown]; 292 | [_closeButton addTarget:self action:@selector(closeButtonDragExit:) forControlEvents:UIControlEventTouchDragExit]; 293 | [_closeButton addTarget:self action:@selector(closeButtonDragEnter:) forControlEvents:UIControlEventTouchDragEnter]; 294 | [_popupView addSubview:_closeButton]; 295 | } 296 | 297 | - (void)updateSubviewsLayoutInContainerView:(UIView *)containerView 298 | { 299 | // Update main properties 300 | CGRect containerBounds = [containerView bounds]; 301 | [self setFrame:containerBounds]; 302 | 303 | // Calculate text view height 304 | CGFloat textViewWidth = kTWSReleaseNotesViewContainerViewWidth - 2*kTWSReleaseNotesViewInnerContainerSidePadding; 305 | CGFloat textViewContentHeight = [self expectedReleaseNotesTextHeightWithWidth:textViewWidth]; 306 | 307 | // Calculate popup view vertical padding 308 | CGFloat popupViewExpectedHeight = kTWSReleaseNotesViewTitleLabelHeight + textViewContentHeight + kTWSReleaseNotesViewButtonBoxHeight + 2*kTWSReleaseNotesViewSeparatorHeight + 2*kTWSReleaseNotesViewInnerContainerSidePadding; 309 | CGFloat popupViewExpectedVerticalPadding = floorf((containerBounds.size.height - popupViewExpectedHeight) / 2.0f); 310 | CGFloat popupViewVerticalPadding = MAX(popupViewExpectedVerticalPadding, kTWSReleaseNotesViewContainerViewMinVerticalPadding); 311 | 312 | // Popup view 313 | [self.popupView setFrame:CGRectInset(containerBounds, floorf((containerBounds.size.width - kTWSReleaseNotesViewContainerViewWidth)/2.0f), popupViewVerticalPadding)]; 314 | [self.popupView.layer setShadowPath:[[UIBezierPath bezierPathWithRect:self.popupView.bounds] CGPath]]; 315 | 316 | // Background blurred image view 317 | [self.backgroundBlurredImageView setFrame:self.popupView.bounds]; 318 | 319 | // Background overlay view 320 | [self.backgroundOverlayView setFrame:self.popupView.bounds]; 321 | 322 | // Text container view 323 | [self.textContainerView setFrame:CGRectInset(self.popupView.bounds, kTWSReleaseNotesViewInnerContainerSidePadding, kTWSReleaseNotesViewInnerContainerSidePadding)]; 324 | 325 | // Title label frame 326 | CGRect titleLabelFrame = CGRectInset(self.textContainerView.frame, kTWSReleaseNotesViewTitleSidePadding, 0.0f); 327 | titleLabelFrame.size.height = kTWSReleaseNotesViewTitleLabelHeight; 328 | [self.titleLabel setFrame:titleLabelFrame]; 329 | 330 | // Top separator 331 | UIView *topSeparatorView = [self separatorInView:self.textContainerView belowView:self.titleLabel]; 332 | [topSeparatorView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin]; 333 | [self.textContainerView addSubview:topSeparatorView]; 334 | 335 | // Text view frame 336 | CGRect textViewFrame = self.textContainerView.frame; 337 | textViewFrame.origin.y = CGRectGetMinY(textViewFrame) + kTWSReleaseNotesViewTitleLabelHeight + 2*kTWSReleaseNotesViewSeparatorHeight; 338 | textViewFrame.size.height = self.textContainerView.frame.size.height - kTWSReleaseNotesViewTitleLabelHeight - 3*kTWSReleaseNotesViewSeparatorHeight - kTWSReleaseNotesViewButtonBoxHeight; 339 | [self.textView setFrame:textViewFrame]; 340 | 341 | // Bottom separator 342 | UIView *bottomSeparatorView = [self separatorInView:self.textContainerView belowView:self.textView]; 343 | [bottomSeparatorView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin]; 344 | [self.textContainerView addSubview:bottomSeparatorView]; 345 | 346 | // Close button 347 | CGRect closeButtonFrame = self.textContainerView.frame; 348 | closeButtonFrame.origin.y = CGRectGetMaxY(closeButtonFrame) - kTWSReleaseNotesViewButtonBoxHeight; 349 | closeButtonFrame.size.height = kTWSReleaseNotesViewButtonBoxHeight; 350 | [self.closeButton setFrame:closeButtonFrame]; 351 | } 352 | 353 | - (void)prepareToShowInView:(UIView *)containerView 354 | { 355 | // Update subviews layout 356 | [self updateSubviewsLayoutInContainerView:containerView]; 357 | 358 | // Initial properties for show animation 359 | [self setBackgroundColor:[UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.0f]]; 360 | 361 | [self.popupView setAlpha:0.0f]; 362 | [self.popupView setTransform:CGAffineTransformScale(CGAffineTransformIdentity, 0.0f, 0.0f)]; 363 | [self.popupView.layer setShadowColor:[self.viewShadowColor CGColor]]; 364 | [self.popupView.layer setShadowOffset:self.viewShadowOffset]; 365 | [self.popupView.layer setShadowRadius:self.viewShadowRadius]; 366 | [self.popupView.layer setShadowOpacity:self.viewShadowOpacity]; 367 | 368 | [self.backgroundOverlayView setBackgroundColor:[self.textViewBackgroundColor colorWithAlphaComponent:self.textViewAlpha]]; 369 | 370 | [self.textContainerView.layer setBorderColor:[self.darkSeparatorColor CGColor]]; 371 | [self.textContainerView.layer setShadowColor:[self.lightSeparatorColor CGColor]]; 372 | 373 | [self.titleLabel setFont:self.titleFont]; 374 | [self.titleLabel setTextColor:self.titleColor]; 375 | [self.titleLabel.layer setShadowColor:[self.titleShadowColor CGColor]]; 376 | [self.titleLabel.layer setShadowOffset:self.titleShadowOffset]; 377 | 378 | [self.textView setFont:self.releaseNotesFont]; 379 | [self.textView setTextColor:self.releaseNotesColor]; 380 | [self.textView.layer setShadowColor:[self.releaseNotesShadowColor CGColor]]; 381 | [self.textView.layer setShadowOffset:self.releaseNotesShadowOffset]; 382 | 383 | [self.closeButton.titleLabel setFont:self.closeButtonFont]; 384 | [self.closeButton setTitleColor:self.closeButtonColor forState:UIControlStateNormal]; 385 | [self.closeButton setTitleShadowColor:self.closeButtonShadowColor forState:UIControlStateNormal]; 386 | [self.closeButton.titleLabel setShadowOffset:self.closeButtonShadowOffset]; 387 | 388 | [self applyBlurredImageBackgroundFromView:containerView]; 389 | 390 | // Add to container view 391 | [containerView addSubview:self]; 392 | } 393 | 394 | - (void)applyBlurredImageBackgroundFromView:(UIView *)view 395 | { 396 | // Clone background image 397 | UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0.0f); 398 | [view.layer renderInContext:UIGraphicsGetCurrentContext()]; 399 | UIImage *cloneImage = UIGraphicsGetImageFromCurrentImageContext(); 400 | UIGraphicsEndImageContext(); 401 | 402 | // Apply blur effect 403 | UIImage *blurredImage = [cloneImage applySubtleEffect]; 404 | self.backgroundBlurredImageView.image = blurredImage; 405 | [self.backgroundBlurredImageView setNeedsDisplay]; 406 | } 407 | 408 | - (UIView *)separatorInView:(UIView *)containerView belowView:(UIView *)topView 409 | { 410 | // Setup separator view 411 | CGRect topViewFrame = [containerView convertRect:topView.frame fromView:[topView superview]]; 412 | CGRect separatorFrame = CGRectMake(containerView.bounds.origin.x, CGRectGetMaxY(topViewFrame), containerView.bounds.size.width, kTWSReleaseNotesViewSeparatorHeight); 413 | UIView *separatorView = [[UIView alloc] initWithFrame:separatorFrame]; 414 | [separatorView setBackgroundColor:self.darkSeparatorColor]; 415 | 416 | return separatorView; 417 | } 418 | 419 | - (CGFloat)expectedReleaseNotesTextHeightWithWidth:(CGFloat)width; 420 | { 421 | CGSize maximumLabelSize = CGSizeMake(width, MAXFLOAT); 422 | CGSize expectedLabelSize = [self.releaseNotesText sizeWithFont:self.releaseNotesFont constrainedToSize:maximumLabelSize lineBreakMode:NSLineBreakByWordWrapping]; 423 | return expectedLabelSize.height + 2*kTWSReleaseNotesViewTextViewInsetHeight; 424 | } 425 | 426 | - (void)closeButtonTouchedUp:(id)sender 427 | { 428 | // Un-highlight button on touch up 429 | [self.closeButton setBackgroundColor:[UIColor clearColor]]; 430 | 431 | // Dismiss release notes view 432 | [self dismiss]; 433 | } 434 | 435 | - (void)closeButtonTouchedDown:(id)sender 436 | { 437 | // Highlight button on touch down 438 | [self.closeButton setBackgroundColor:[UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.5f]]; 439 | } 440 | 441 | - (void)closeButtonDragExit:(id)sender 442 | { 443 | // Un-highlight button on exit 444 | [self.closeButton setBackgroundColor:[UIColor clearColor]]; 445 | } 446 | 447 | - (void)closeButtonDragEnter:(id)sender 448 | { 449 | // Highlight button on enter 450 | [self.closeButton setBackgroundColor:[UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.5f]]; 451 | } 452 | 453 | - (void)dismiss 454 | { 455 | // Dismiss release notes view 456 | [UIView animateWithDuration:kTWSReleaseNotesViewTransitionDuration/2.0f animations:^{ 457 | [self.popupView setTransform:CGAffineTransformScale(CGAffineTransformIdentity, 1.0f + kTWSReleaseNotesViewAnimationSpringScaleFactor, 1.0f + kTWSReleaseNotesViewAnimationSpringScaleFactor)]; 458 | } completion:^(BOOL finished){ 459 | if (finished) 460 | { 461 | [UIView animateWithDuration:kTWSReleaseNotesViewTransitionDuration animations:^{ 462 | [self setBackgroundColor:[UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.0f]]; 463 | [self.popupView setAlpha:0.0f]; 464 | [self.popupView setTransform:CGAffineTransformScale(CGAffineTransformIdentity, 0.0f, 0.0f)]; 465 | } completion:^(BOOL finished){ 466 | if (finished) 467 | { 468 | [self removeFromSuperview]; 469 | } 470 | }]; 471 | } 472 | }]; 473 | } 474 | 475 | + (void)storeCurrentAppVersionString 476 | { 477 | // Store current app version string in the user defaults 478 | NSString *currentAppVersion = [[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"]; 479 | [[NSUserDefaults standardUserDefaults] setObject:currentAppVersion forKey:kTWSReleaseNotesViewVersionKey]; 480 | [[NSUserDefaults standardUserDefaults] synchronize]; 481 | } 482 | 483 | #pragma mark - Instance Methods 484 | 485 | - (void)showInView:(UIView *)containerView 486 | { 487 | // Setup view before animation 488 | [self prepareToShowInView:containerView]; 489 | 490 | // Show release notes view 491 | [UIView animateWithDuration:kTWSReleaseNotesViewTransitionDuration animations:^{ 492 | [self setBackgroundColor:[UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:self.overlayAlpha]]; 493 | [self.popupView setAlpha:1.0f]; 494 | [self.popupView setTransform:CGAffineTransformScale(CGAffineTransformIdentity, 1.0f + kTWSReleaseNotesViewAnimationSpringScaleFactor, 1.0f + kTWSReleaseNotesViewAnimationSpringScaleFactor)]; 495 | } completion:^(BOOL finished){ 496 | if (finished) 497 | { 498 | [UIView animateWithDuration:kTWSReleaseNotesViewTransitionDuration/2.0f animations:^{ 499 | [self.popupView setTransform:CGAffineTransformScale(CGAffineTransformIdentity, 1.0f - kTWSReleaseNotesViewAnimationSpringScaleFactor, 1.0f - kTWSReleaseNotesViewAnimationSpringScaleFactor)]; 500 | } completion:^(BOOL finished){ 501 | if (finished) 502 | { 503 | [UIView animateWithDuration:kTWSReleaseNotesViewTransitionDuration/2.0f animations:^{ 504 | [self.popupView setTransform:CGAffineTransformIdentity]; 505 | }]; 506 | } 507 | }]; 508 | } 509 | }]; 510 | } 511 | 512 | @end 513 | -------------------------------------------------------------------------------- /TWSReleaseNotesView/UIImage+ImageEffects.h: -------------------------------------------------------------------------------- 1 | /* 2 | File: UIImage+ImageEffects.h 3 | Abstract: This is a category of UIImage that adds methods to apply blur and tint effects to an image. This is the code you’ll want to look out to find out how to use vImage to efficiently calculate a blur. 4 | Version: 1.0 5 | 6 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 7 | Inc. ("Apple") in consideration of your agreement to the following 8 | terms, and your use, installation, modification or redistribution of 9 | this Apple software constitutes acceptance of these terms. If you do 10 | not agree with these terms, please do not use, install, modify or 11 | redistribute this Apple software. 12 | 13 | In consideration of your agreement to abide by the following terms, and 14 | subject to these terms, Apple grants you a personal, non-exclusive 15 | license, under Apple's copyrights in this original Apple software (the 16 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 17 | Software, with or without modifications, in source and/or binary forms; 18 | provided that if you redistribute the Apple Software in its entirety and 19 | without modifications, you must retain this notice and the following 20 | text and disclaimers in all such redistributions of the Apple Software. 21 | Neither the name, trademarks, service marks or logos of Apple Inc. may 22 | be used to endorse or promote products derived from the Apple Software 23 | without specific prior written permission from Apple. Except as 24 | expressly stated in this notice, no other rights or licenses, express or 25 | implied, are granted by Apple herein, including but not limited to any 26 | patent rights that may be infringed by your derivative works or by other 27 | works in which the Apple Software may be incorporated. 28 | 29 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 30 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 31 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 32 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 33 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 34 | 35 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 36 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 37 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 38 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 39 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 40 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 41 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 42 | POSSIBILITY OF SUCH DAMAGE. 43 | 44 | Copyright (C) 2013 Apple Inc. All Rights Reserved. 45 | 46 | 47 | Copyright © 2013 Apple Inc. All rights reserved. 48 | WWDC 2013 License 49 | 50 | NOTE: This Apple Software was supplied by Apple as part of a WWDC 2013 51 | Session. Please refer to the applicable WWDC 2013 Session for further 52 | information. 53 | 54 | IMPORTANT: This Apple software is supplied to you by Apple Inc. 55 | ("Apple") in consideration of your agreement to the following terms, and 56 | your use, installation, modification or redistribution of this Apple 57 | software constitutes acceptance of these terms. If you do not agree with 58 | these terms, please do not use, install, modify or redistribute this 59 | Apple software. 60 | 61 | In consideration of your agreement to abide by the following terms, and 62 | subject to these terms, Apple grants you a non-exclusive license, under 63 | Apple's copyrights in this original Apple software (the "Apple 64 | Software"), to use, reproduce, modify and redistribute the Apple 65 | Software, with or without modifications, in source and/or binary forms; 66 | provided that if you redistribute the Apple Software in its entirety and 67 | without modifications, you must retain this notice and the following 68 | text and disclaimers in all such redistributions of the Apple Software. 69 | Neither the name, trademarks, service marks or logos of Apple Inc. may 70 | be used to endorse or promote products derived from the Apple Software 71 | without specific prior written permission from Apple. Except as 72 | expressly stated in this notice, no other rights or licenses, express or 73 | implied, are granted by Apple herein, including but not limited to any 74 | patent rights that may be infringed by your derivative works or by other 75 | works in which the Apple Software may be incorporated. 76 | 77 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES 78 | NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE 79 | IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR 80 | A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 81 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 82 | 83 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 84 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 85 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 86 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 87 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 88 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 89 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 90 | POSSIBILITY OF SUCH DAMAGE. 91 | 92 | EA1002 93 | 5/3/2013 94 | */ 95 | 96 | #import 97 | #import 98 | 99 | @interface UIImage (ImageEffects) 100 | 101 | - (UIImage *)applySubtleEffect; 102 | - (UIImage *)applyLightEffect; 103 | - (UIImage *)applyExtraLightEffect; 104 | - (UIImage *)applyDarkEffect; 105 | - (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor; 106 | 107 | - (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage; 108 | 109 | @end 110 | -------------------------------------------------------------------------------- /TWSReleaseNotesView/UIImage+ImageEffects.m: -------------------------------------------------------------------------------- 1 | /* 2 | File: UIImage+ImageEffects.m 3 | Abstract: This is a category of UIImage that adds methods to apply blur and tint effects to an image. This is the code you’ll want to look out to find out how to use vImage to efficiently calculate a blur. 4 | Version: 1.0 5 | 6 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 7 | Inc. ("Apple") in consideration of your agreement to the following 8 | terms, and your use, installation, modification or redistribution of 9 | this Apple software constitutes acceptance of these terms. If you do 10 | not agree with these terms, please do not use, install, modify or 11 | redistribute this Apple software. 12 | 13 | In consideration of your agreement to abide by the following terms, and 14 | subject to these terms, Apple grants you a personal, non-exclusive 15 | license, under Apple's copyrights in this original Apple software (the 16 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 17 | Software, with or without modifications, in source and/or binary forms; 18 | provided that if you redistribute the Apple Software in its entirety and 19 | without modifications, you must retain this notice and the following 20 | text and disclaimers in all such redistributions of the Apple Software. 21 | Neither the name, trademarks, service marks or logos of Apple Inc. may 22 | be used to endorse or promote products derived from the Apple Software 23 | without specific prior written permission from Apple. Except as 24 | expressly stated in this notice, no other rights or licenses, express or 25 | implied, are granted by Apple herein, including but not limited to any 26 | patent rights that may be infringed by your derivative works or by other 27 | works in which the Apple Software may be incorporated. 28 | 29 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 30 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 31 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 32 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 33 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 34 | 35 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 36 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 37 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 38 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 39 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 40 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 41 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 42 | POSSIBILITY OF SUCH DAMAGE. 43 | 44 | Copyright (C) 2013 Apple Inc. All Rights Reserved. 45 | 46 | 47 | Copyright © 2013 Apple Inc. All rights reserved. 48 | WWDC 2013 License 49 | 50 | NOTE: This Apple Software was supplied by Apple as part of a WWDC 2013 51 | Session. Please refer to the applicable WWDC 2013 Session for further 52 | information. 53 | 54 | IMPORTANT: This Apple software is supplied to you by Apple Inc. 55 | ("Apple") in consideration of your agreement to the following terms, and 56 | your use, installation, modification or redistribution of this Apple 57 | software constitutes acceptance of these terms. If you do not agree with 58 | these terms, please do not use, install, modify or redistribute this 59 | Apple software. 60 | 61 | In consideration of your agreement to abide by the following terms, and 62 | subject to these terms, Apple grants you a non-exclusive license, under 63 | Apple's copyrights in this original Apple software (the "Apple 64 | Software"), to use, reproduce, modify and redistribute the Apple 65 | Software, with or without modifications, in source and/or binary forms; 66 | provided that if you redistribute the Apple Software in its entirety and 67 | without modifications, you must retain this notice and the following 68 | text and disclaimers in all such redistributions of the Apple Software. 69 | Neither the name, trademarks, service marks or logos of Apple Inc. may 70 | be used to endorse or promote products derived from the Apple Software 71 | without specific prior written permission from Apple. Except as 72 | expressly stated in this notice, no other rights or licenses, express or 73 | implied, are granted by Apple herein, including but not limited to any 74 | patent rights that may be infringed by your derivative works or by other 75 | works in which the Apple Software may be incorporated. 76 | 77 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES 78 | NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE 79 | IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR 80 | A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 81 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 82 | 83 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 84 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 85 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 86 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 87 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 88 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 89 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 90 | POSSIBILITY OF SUCH DAMAGE. 91 | 92 | EA1002 93 | 5/3/2013 94 | */ 95 | 96 | #import "UIImage+ImageEffects.h" 97 | #import 98 | 99 | 100 | @implementation UIImage (ImageEffects) 101 | 102 | 103 | - (UIImage *)applySubtleEffect 104 | { 105 | UIColor *tintColor = [UIColor colorWithWhite:1.0 alpha:0.3]; 106 | return [self applyBlurWithRadius:5 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil]; 107 | } 108 | 109 | 110 | - (UIImage *)applyLightEffect 111 | { 112 | UIColor *tintColor = [UIColor colorWithWhite:1.0 alpha:0.3]; 113 | return [self applyBlurWithRadius:30 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil]; 114 | } 115 | 116 | 117 | - (UIImage *)applyExtraLightEffect 118 | { 119 | UIColor *tintColor = [UIColor colorWithWhite:0.97 alpha:0.82]; 120 | return [self applyBlurWithRadius:20 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil]; 121 | } 122 | 123 | 124 | - (UIImage *)applyDarkEffect 125 | { 126 | UIColor *tintColor = [UIColor colorWithWhite:0.11 alpha:0.73]; 127 | return [self applyBlurWithRadius:20 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil]; 128 | } 129 | 130 | 131 | - (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor 132 | { 133 | const CGFloat EffectColorAlpha = 0.6; 134 | UIColor *effectColor = tintColor; 135 | int componentCount = CGColorGetNumberOfComponents(tintColor.CGColor); 136 | if (componentCount == 2) { 137 | CGFloat b; 138 | if ([tintColor getWhite:&b alpha:NULL]) { 139 | effectColor = [UIColor colorWithWhite:b alpha:EffectColorAlpha]; 140 | } 141 | } 142 | else { 143 | CGFloat r, g, b; 144 | if ([tintColor getRed:&r green:&g blue:&b alpha:NULL]) { 145 | effectColor = [UIColor colorWithRed:r green:g blue:b alpha:EffectColorAlpha]; 146 | } 147 | } 148 | return [self applyBlurWithRadius:10 tintColor:effectColor saturationDeltaFactor:-1.0 maskImage:nil]; 149 | } 150 | 151 | 152 | - (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage 153 | { 154 | // Check pre-conditions. 155 | if (self.size.width < 1 || self.size.height < 1) { 156 | NSLog (@"*** error: invalid size: (%.2f x %.2f). Both dimensions must be >= 1: %@", self.size.width, self.size.height, self); 157 | return nil; 158 | } 159 | if (!self.CGImage) { 160 | NSLog (@"*** error: image must be backed by a CGImage: %@", self); 161 | return nil; 162 | } 163 | if (maskImage && !maskImage.CGImage) { 164 | NSLog (@"*** error: maskImage must be backed by a CGImage: %@", maskImage); 165 | return nil; 166 | } 167 | 168 | CGRect imageRect = { CGPointZero, self.size }; 169 | UIImage *effectImage = self; 170 | 171 | BOOL hasBlur = blurRadius > __FLT_EPSILON__; 172 | BOOL hasSaturationChange = fabs(saturationDeltaFactor - 1.) > __FLT_EPSILON__; 173 | if (hasBlur || hasSaturationChange) { 174 | UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]); 175 | CGContextRef effectInContext = UIGraphicsGetCurrentContext(); 176 | CGContextScaleCTM(effectInContext, 1.0, -1.0); 177 | CGContextTranslateCTM(effectInContext, 0, -self.size.height); 178 | CGContextDrawImage(effectInContext, imageRect, self.CGImage); 179 | 180 | vImage_Buffer effectInBuffer; 181 | effectInBuffer.data = CGBitmapContextGetData(effectInContext); 182 | effectInBuffer.width = CGBitmapContextGetWidth(effectInContext); 183 | effectInBuffer.height = CGBitmapContextGetHeight(effectInContext); 184 | effectInBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectInContext); 185 | 186 | UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]); 187 | CGContextRef effectOutContext = UIGraphicsGetCurrentContext(); 188 | vImage_Buffer effectOutBuffer; 189 | effectOutBuffer.data = CGBitmapContextGetData(effectOutContext); 190 | effectOutBuffer.width = CGBitmapContextGetWidth(effectOutContext); 191 | effectOutBuffer.height = CGBitmapContextGetHeight(effectOutContext); 192 | effectOutBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectOutContext); 193 | 194 | if (hasBlur) { 195 | // A description of how to compute the box kernel width from the Gaussian 196 | // radius (aka standard deviation) appears in the SVG spec: 197 | // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement 198 | // 199 | // For larger values of 's' (s >= 2.0), an approximation can be used: Three 200 | // successive box-blurs build a piece-wise quadratic convolution kernel, which 201 | // approximates the Gaussian kernel to within roughly 3%. 202 | // 203 | // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5) 204 | // 205 | // ... if d is odd, use three box-blurs of size 'd', centered on the output pixel. 206 | // 207 | CGFloat inputRadius = blurRadius * [[UIScreen mainScreen] scale]; 208 | NSUInteger radius = floor(inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5); 209 | if (radius % 2 != 1) { 210 | radius += 1; // force radius to be odd so that the three box-blur methodology works. 211 | } 212 | vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend); 213 | vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend); 214 | vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend); 215 | } 216 | BOOL effectImageBuffersAreSwapped = NO; 217 | if (hasSaturationChange) { 218 | CGFloat s = saturationDeltaFactor; 219 | CGFloat floatingPointSaturationMatrix[] = { 220 | 0.0722 + 0.9278 * s, 0.0722 - 0.0722 * s, 0.0722 - 0.0722 * s, 0, 221 | 0.7152 - 0.7152 * s, 0.7152 + 0.2848 * s, 0.7152 - 0.7152 * s, 0, 222 | 0.2126 - 0.2126 * s, 0.2126 - 0.2126 * s, 0.2126 + 0.7873 * s, 0, 223 | 0, 0, 0, 1, 224 | }; 225 | const int32_t divisor = 256; 226 | NSUInteger matrixSize = sizeof(floatingPointSaturationMatrix)/sizeof(floatingPointSaturationMatrix[0]); 227 | int16_t saturationMatrix[matrixSize]; 228 | for (NSUInteger i = 0; i < matrixSize; ++i) { 229 | saturationMatrix[i] = (int16_t)roundf(floatingPointSaturationMatrix[i] * divisor); 230 | } 231 | if (hasBlur) { 232 | vImageMatrixMultiply_ARGB8888(&effectOutBuffer, &effectInBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags); 233 | effectImageBuffersAreSwapped = YES; 234 | } 235 | else { 236 | vImageMatrixMultiply_ARGB8888(&effectInBuffer, &effectOutBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags); 237 | } 238 | } 239 | if (!effectImageBuffersAreSwapped) 240 | effectImage = UIGraphicsGetImageFromCurrentImageContext(); 241 | UIGraphicsEndImageContext(); 242 | 243 | if (effectImageBuffersAreSwapped) 244 | effectImage = UIGraphicsGetImageFromCurrentImageContext(); 245 | UIGraphicsEndImageContext(); 246 | } 247 | 248 | // Set up output context. 249 | UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]); 250 | CGContextRef outputContext = UIGraphicsGetCurrentContext(); 251 | CGContextScaleCTM(outputContext, 1.0, -1.0); 252 | CGContextTranslateCTM(outputContext, 0, -self.size.height); 253 | 254 | // Draw base image. 255 | CGContextDrawImage(outputContext, imageRect, self.CGImage); 256 | 257 | // Draw effect image. 258 | if (hasBlur) { 259 | CGContextSaveGState(outputContext); 260 | if (maskImage) { 261 | CGContextClipToMask(outputContext, imageRect, maskImage.CGImage); 262 | } 263 | CGContextDrawImage(outputContext, imageRect, effectImage.CGImage); 264 | CGContextRestoreGState(outputContext); 265 | } 266 | 267 | // Add in color tint. 268 | if (tintColor) { 269 | CGContextSaveGState(outputContext); 270 | CGContextSetFillColorWithColor(outputContext, tintColor.CGColor); 271 | CGContextFillRect(outputContext, imageRect); 272 | CGContextRestoreGState(outputContext); 273 | } 274 | 275 | // Output image is ready. 276 | UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext(); 277 | UIGraphicsEndImageContext(); 278 | 279 | return outputImage; 280 | } 281 | 282 | 283 | @end 284 | -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F819042C17AC0BE5006A0896 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F819042B17AC0BE5006A0896 /* UIKit.framework */; }; 11 | F819042E17AC0BE5006A0896 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F819042D17AC0BE5006A0896 /* Foundation.framework */; }; 12 | F819043017AC0BE5006A0896 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F819042F17AC0BE5006A0896 /* CoreGraphics.framework */; }; 13 | F819043617AC0BE5006A0896 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F819043417AC0BE5006A0896 /* InfoPlist.strings */; }; 14 | F819043817AC0BE5006A0896 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F819043717AC0BE5006A0896 /* main.m */; }; 15 | F819043C17AC0BE5006A0896 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F819043B17AC0BE5006A0896 /* AppDelegate.m */; }; 16 | F819043E17AC0BE5006A0896 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = F819043D17AC0BE5006A0896 /* Default.png */; }; 17 | F819044017AC0BE5006A0896 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F819043F17AC0BE5006A0896 /* Default@2x.png */; }; 18 | F819044217AC0BE5006A0896 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F819044117AC0BE5006A0896 /* Default-568h@2x.png */; }; 19 | F819044F17AC1520006A0896 /* RootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F819044D17AC1520006A0896 /* RootViewController.m */; }; 20 | F844C4D217BAEF3900021701 /* background@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F844C4CF17BAEF3900021701 /* background@2x.png */; }; 21 | F844C4D317BAEF3900021701 /* btn_bg_hl@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F844C4D017BAEF3900021701 /* btn_bg_hl@2x.png */; }; 22 | F844C4D417BAEF3900021701 /* btn_bg@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F844C4D117BAEF3900021701 /* btn_bg@2x.png */; }; 23 | F844C4D817BAF48B00021701 /* icon_7@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F844C4D517BAF48B00021701 /* icon_7@2x.png */; }; 24 | F844C4D917BAF48B00021701 /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = F844C4D617BAF48B00021701 /* icon.png */; }; 25 | F844C4DA17BAF48B00021701 /* icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F844C4D717BAF48B00021701 /* icon@2x.png */; }; 26 | F893E7FA17AD69FF005DB2FC /* RootViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F893E7F917AD69FF005DB2FC /* RootViewController.xib */; }; 27 | F8B6D83017BAE1F000C2ECB0 /* background.png in Resources */ = {isa = PBXBuildFile; fileRef = F8B6D82F17BAE1F000C2ECB0 /* background.png */; }; 28 | F8B6D83317BAED4400C2ECB0 /* btn_bg_hl.png in Resources */ = {isa = PBXBuildFile; fileRef = F8B6D83117BAED4400C2ECB0 /* btn_bg_hl.png */; }; 29 | F8B6D83417BAED4400C2ECB0 /* btn_bg.png in Resources */ = {isa = PBXBuildFile; fileRef = F8B6D83217BAED4400C2ECB0 /* btn_bg.png */; }; 30 | F8C4A09717B1395E003CA095 /* TWSReleaseNotesDownloadOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F8C4A09217B1395E003CA095 /* TWSReleaseNotesDownloadOperation.m */; }; 31 | F8C4A09817B1395E003CA095 /* TWSReleaseNotesView.m in Sources */ = {isa = PBXBuildFile; fileRef = F8C4A09417B1395E003CA095 /* TWSReleaseNotesView.m */; }; 32 | F8C4A09917B1395E003CA095 /* UIImage+ImageEffects.m in Sources */ = {isa = PBXBuildFile; fileRef = F8C4A09617B1395E003CA095 /* UIImage+ImageEffects.m */; }; 33 | F8E345D217AC3B74005478D6 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8E345D117AC3B74005478D6 /* QuartzCore.framework */; }; 34 | F8E345D417AC3B89005478D6 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8E345D317AC3B89005478D6 /* Accelerate.framework */; }; 35 | /* End PBXBuildFile section */ 36 | 37 | /* Begin PBXFileReference section */ 38 | F819042817AC0BE5006A0896 /* TWSReleaseNotesViewSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TWSReleaseNotesViewSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | F819042B17AC0BE5006A0896 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 40 | F819042D17AC0BE5006A0896 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 41 | F819042F17AC0BE5006A0896 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 42 | F819043317AC0BE5006A0896 /* TWSReleaseNotesViewSample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TWSReleaseNotesViewSample-Info.plist"; sourceTree = ""; }; 43 | F819043517AC0BE5006A0896 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 44 | F819043717AC0BE5006A0896 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 45 | F819043917AC0BE5006A0896 /* TWSReleaseNotesViewSample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TWSReleaseNotesViewSample-Prefix.pch"; sourceTree = ""; }; 46 | F819043A17AC0BE5006A0896 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 47 | F819043B17AC0BE5006A0896 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 48 | F819043D17AC0BE5006A0896 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 49 | F819043F17AC0BE5006A0896 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 50 | F819044117AC0BE5006A0896 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 51 | F819044C17AC1520006A0896 /* RootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RootViewController.h; sourceTree = ""; }; 52 | F819044D17AC1520006A0896 /* RootViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RootViewController.m; sourceTree = ""; }; 53 | F844C4CF17BAEF3900021701 /* background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "background@2x.png"; sourceTree = ""; }; 54 | F844C4D017BAEF3900021701 /* btn_bg_hl@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "btn_bg_hl@2x.png"; sourceTree = ""; }; 55 | F844C4D117BAEF3900021701 /* btn_bg@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "btn_bg@2x.png"; sourceTree = ""; }; 56 | F844C4D517BAF48B00021701 /* icon_7@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_7@2x.png"; sourceTree = ""; }; 57 | F844C4D617BAF48B00021701 /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon.png; sourceTree = ""; }; 58 | F844C4D717BAF48B00021701 /* icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon@2x.png"; sourceTree = ""; }; 59 | F893E7F917AD69FF005DB2FC /* RootViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RootViewController.xib; sourceTree = ""; }; 60 | F8B6D82F17BAE1F000C2ECB0 /* background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = background.png; sourceTree = ""; }; 61 | F8B6D83117BAED4400C2ECB0 /* btn_bg_hl.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = btn_bg_hl.png; sourceTree = ""; }; 62 | F8B6D83217BAED4400C2ECB0 /* btn_bg.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = btn_bg.png; sourceTree = ""; }; 63 | F8C4A09117B1395E003CA095 /* TWSReleaseNotesDownloadOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TWSReleaseNotesDownloadOperation.h; sourceTree = ""; }; 64 | F8C4A09217B1395E003CA095 /* TWSReleaseNotesDownloadOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TWSReleaseNotesDownloadOperation.m; sourceTree = ""; }; 65 | F8C4A09317B1395E003CA095 /* TWSReleaseNotesView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TWSReleaseNotesView.h; sourceTree = ""; }; 66 | F8C4A09417B1395E003CA095 /* TWSReleaseNotesView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TWSReleaseNotesView.m; sourceTree = ""; }; 67 | F8C4A09517B1395E003CA095 /* UIImage+ImageEffects.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+ImageEffects.h"; sourceTree = ""; }; 68 | F8C4A09617B1395E003CA095 /* UIImage+ImageEffects.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+ImageEffects.m"; sourceTree = ""; }; 69 | F8E345D117AC3B74005478D6 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 70 | F8E345D317AC3B89005478D6 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; 71 | /* End PBXFileReference section */ 72 | 73 | /* Begin PBXFrameworksBuildPhase section */ 74 | F819042517AC0BE5006A0896 /* Frameworks */ = { 75 | isa = PBXFrameworksBuildPhase; 76 | buildActionMask = 2147483647; 77 | files = ( 78 | F8E345D417AC3B89005478D6 /* Accelerate.framework in Frameworks */, 79 | F8E345D217AC3B74005478D6 /* QuartzCore.framework in Frameworks */, 80 | F819042C17AC0BE5006A0896 /* UIKit.framework in Frameworks */, 81 | F819042E17AC0BE5006A0896 /* Foundation.framework in Frameworks */, 82 | F819043017AC0BE5006A0896 /* CoreGraphics.framework in Frameworks */, 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | /* End PBXFrameworksBuildPhase section */ 87 | 88 | /* Begin PBXGroup section */ 89 | F819041F17AC0BE5006A0896 = { 90 | isa = PBXGroup; 91 | children = ( 92 | F819043117AC0BE5006A0896 /* TWSReleaseNotesViewControllerSample */, 93 | F819042A17AC0BE5006A0896 /* Frameworks */, 94 | F819042917AC0BE5006A0896 /* Products */, 95 | ); 96 | sourceTree = ""; 97 | }; 98 | F819042917AC0BE5006A0896 /* Products */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | F819042817AC0BE5006A0896 /* TWSReleaseNotesViewSample.app */, 102 | ); 103 | name = Products; 104 | sourceTree = ""; 105 | }; 106 | F819042A17AC0BE5006A0896 /* Frameworks */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | F8E345D317AC3B89005478D6 /* Accelerate.framework */, 110 | F819042F17AC0BE5006A0896 /* CoreGraphics.framework */, 111 | F819042D17AC0BE5006A0896 /* Foundation.framework */, 112 | F8E345D117AC3B74005478D6 /* QuartzCore.framework */, 113 | F819042B17AC0BE5006A0896 /* UIKit.framework */, 114 | ); 115 | name = Frameworks; 116 | sourceTree = ""; 117 | }; 118 | F819043117AC0BE5006A0896 /* TWSReleaseNotesViewControllerSample */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | F8C4A09017B1395E003CA095 /* TWSReleaseNotesView */, 122 | F819043A17AC0BE5006A0896 /* AppDelegate.h */, 123 | F819043B17AC0BE5006A0896 /* AppDelegate.m */, 124 | F819044C17AC1520006A0896 /* RootViewController.h */, 125 | F819044D17AC1520006A0896 /* RootViewController.m */, 126 | F893E7F917AD69FF005DB2FC /* RootViewController.xib */, 127 | F8B6D83517BAED4B00C2ECB0 /* Resources */, 128 | F819043217AC0BE5006A0896 /* Supporting Files */, 129 | ); 130 | name = TWSReleaseNotesViewControllerSample; 131 | path = TWSReleaseNotesViewSample; 132 | sourceTree = ""; 133 | }; 134 | F819043217AC0BE5006A0896 /* Supporting Files */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | F819043317AC0BE5006A0896 /* TWSReleaseNotesViewSample-Info.plist */, 138 | F819043417AC0BE5006A0896 /* InfoPlist.strings */, 139 | F819043717AC0BE5006A0896 /* main.m */, 140 | F819043917AC0BE5006A0896 /* TWSReleaseNotesViewSample-Prefix.pch */, 141 | F819043D17AC0BE5006A0896 /* Default.png */, 142 | F819043F17AC0BE5006A0896 /* Default@2x.png */, 143 | F819044117AC0BE5006A0896 /* Default-568h@2x.png */, 144 | ); 145 | name = "Supporting Files"; 146 | sourceTree = ""; 147 | }; 148 | F8B6D83517BAED4B00C2ECB0 /* Resources */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | F844C4D517BAF48B00021701 /* icon_7@2x.png */, 152 | F844C4D617BAF48B00021701 /* icon.png */, 153 | F844C4D717BAF48B00021701 /* icon@2x.png */, 154 | F844C4CF17BAEF3900021701 /* background@2x.png */, 155 | F844C4D017BAEF3900021701 /* btn_bg_hl@2x.png */, 156 | F844C4D117BAEF3900021701 /* btn_bg@2x.png */, 157 | F8B6D82F17BAE1F000C2ECB0 /* background.png */, 158 | F8B6D83117BAED4400C2ECB0 /* btn_bg_hl.png */, 159 | F8B6D83217BAED4400C2ECB0 /* btn_bg.png */, 160 | ); 161 | name = Resources; 162 | sourceTree = ""; 163 | }; 164 | F8C4A09017B1395E003CA095 /* TWSReleaseNotesView */ = { 165 | isa = PBXGroup; 166 | children = ( 167 | F8C4A09117B1395E003CA095 /* TWSReleaseNotesDownloadOperation.h */, 168 | F8C4A09217B1395E003CA095 /* TWSReleaseNotesDownloadOperation.m */, 169 | F8C4A09317B1395E003CA095 /* TWSReleaseNotesView.h */, 170 | F8C4A09417B1395E003CA095 /* TWSReleaseNotesView.m */, 171 | F8C4A09517B1395E003CA095 /* UIImage+ImageEffects.h */, 172 | F8C4A09617B1395E003CA095 /* UIImage+ImageEffects.m */, 173 | ); 174 | name = TWSReleaseNotesView; 175 | path = ../../TWSReleaseNotesView; 176 | sourceTree = ""; 177 | }; 178 | /* End PBXGroup section */ 179 | 180 | /* Begin PBXNativeTarget section */ 181 | F819042717AC0BE5006A0896 /* TWSReleaseNotesViewSample */ = { 182 | isa = PBXNativeTarget; 183 | buildConfigurationList = F819044517AC0BE5006A0896 /* Build configuration list for PBXNativeTarget "TWSReleaseNotesViewSample" */; 184 | buildPhases = ( 185 | F819042417AC0BE5006A0896 /* Sources */, 186 | F819042517AC0BE5006A0896 /* Frameworks */, 187 | F819042617AC0BE5006A0896 /* Resources */, 188 | ); 189 | buildRules = ( 190 | ); 191 | dependencies = ( 192 | ); 193 | name = TWSReleaseNotesViewSample; 194 | productName = TWSReleaseNotesViewControllerSample; 195 | productReference = F819042817AC0BE5006A0896 /* TWSReleaseNotesViewSample.app */; 196 | productType = "com.apple.product-type.application"; 197 | }; 198 | /* End PBXNativeTarget section */ 199 | 200 | /* Begin PBXProject section */ 201 | F819042017AC0BE5006A0896 /* Project object */ = { 202 | isa = PBXProject; 203 | attributes = { 204 | LastUpgradeCheck = 0460; 205 | ORGANIZATIONNAME = Tapwings; 206 | }; 207 | buildConfigurationList = F819042317AC0BE5006A0896 /* Build configuration list for PBXProject "TWSReleaseNotesViewSample" */; 208 | compatibilityVersion = "Xcode 3.2"; 209 | developmentRegion = English; 210 | hasScannedForEncodings = 0; 211 | knownRegions = ( 212 | en, 213 | ); 214 | mainGroup = F819041F17AC0BE5006A0896; 215 | productRefGroup = F819042917AC0BE5006A0896 /* Products */; 216 | projectDirPath = ""; 217 | projectRoot = ""; 218 | targets = ( 219 | F819042717AC0BE5006A0896 /* TWSReleaseNotesViewSample */, 220 | ); 221 | }; 222 | /* End PBXProject section */ 223 | 224 | /* Begin PBXResourcesBuildPhase section */ 225 | F819042617AC0BE5006A0896 /* Resources */ = { 226 | isa = PBXResourcesBuildPhase; 227 | buildActionMask = 2147483647; 228 | files = ( 229 | F8B6D83317BAED4400C2ECB0 /* btn_bg_hl.png in Resources */, 230 | F8B6D83417BAED4400C2ECB0 /* btn_bg.png in Resources */, 231 | F819043617AC0BE5006A0896 /* InfoPlist.strings in Resources */, 232 | F819043E17AC0BE5006A0896 /* Default.png in Resources */, 233 | F8B6D83017BAE1F000C2ECB0 /* background.png in Resources */, 234 | F819044017AC0BE5006A0896 /* Default@2x.png in Resources */, 235 | F819044217AC0BE5006A0896 /* Default-568h@2x.png in Resources */, 236 | F893E7FA17AD69FF005DB2FC /* RootViewController.xib in Resources */, 237 | F844C4D217BAEF3900021701 /* background@2x.png in Resources */, 238 | F844C4D317BAEF3900021701 /* btn_bg_hl@2x.png in Resources */, 239 | F844C4D417BAEF3900021701 /* btn_bg@2x.png in Resources */, 240 | F844C4D817BAF48B00021701 /* icon_7@2x.png in Resources */, 241 | F844C4D917BAF48B00021701 /* icon.png in Resources */, 242 | F844C4DA17BAF48B00021701 /* icon@2x.png in Resources */, 243 | ); 244 | runOnlyForDeploymentPostprocessing = 0; 245 | }; 246 | /* End PBXResourcesBuildPhase section */ 247 | 248 | /* Begin PBXSourcesBuildPhase section */ 249 | F819042417AC0BE5006A0896 /* Sources */ = { 250 | isa = PBXSourcesBuildPhase; 251 | buildActionMask = 2147483647; 252 | files = ( 253 | F819043817AC0BE5006A0896 /* main.m in Sources */, 254 | F819043C17AC0BE5006A0896 /* AppDelegate.m in Sources */, 255 | F8C4A09917B1395E003CA095 /* UIImage+ImageEffects.m in Sources */, 256 | F8C4A09717B1395E003CA095 /* TWSReleaseNotesDownloadOperation.m in Sources */, 257 | F819044F17AC1520006A0896 /* RootViewController.m in Sources */, 258 | F8C4A09817B1395E003CA095 /* TWSReleaseNotesView.m in Sources */, 259 | ); 260 | runOnlyForDeploymentPostprocessing = 0; 261 | }; 262 | /* End PBXSourcesBuildPhase section */ 263 | 264 | /* Begin PBXVariantGroup section */ 265 | F819043417AC0BE5006A0896 /* InfoPlist.strings */ = { 266 | isa = PBXVariantGroup; 267 | children = ( 268 | F819043517AC0BE5006A0896 /* en */, 269 | ); 270 | name = InfoPlist.strings; 271 | sourceTree = ""; 272 | }; 273 | /* End PBXVariantGroup section */ 274 | 275 | /* Begin XCBuildConfiguration section */ 276 | F819044317AC0BE5006A0896 /* Debug */ = { 277 | isa = XCBuildConfiguration; 278 | buildSettings = { 279 | ALWAYS_SEARCH_USER_PATHS = NO; 280 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 281 | CLANG_CXX_LIBRARY = "libc++"; 282 | CLANG_ENABLE_OBJC_ARC = YES; 283 | CLANG_WARN_CONSTANT_CONVERSION = YES; 284 | CLANG_WARN_EMPTY_BODY = YES; 285 | CLANG_WARN_ENUM_CONVERSION = YES; 286 | CLANG_WARN_INT_CONVERSION = YES; 287 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 288 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 289 | COPY_PHASE_STRIP = NO; 290 | GCC_C_LANGUAGE_STANDARD = gnu99; 291 | GCC_DYNAMIC_NO_PIC = NO; 292 | GCC_OPTIMIZATION_LEVEL = 0; 293 | GCC_PREPROCESSOR_DEFINITIONS = ( 294 | "DEBUG=1", 295 | "$(inherited)", 296 | ); 297 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 298 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 299 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 300 | GCC_WARN_UNUSED_VARIABLE = YES; 301 | IPHONEOS_DEPLOYMENT_TARGET = 6.1; 302 | ONLY_ACTIVE_ARCH = YES; 303 | SDKROOT = iphoneos; 304 | }; 305 | name = Debug; 306 | }; 307 | F819044417AC0BE5006A0896 /* Release */ = { 308 | isa = XCBuildConfiguration; 309 | buildSettings = { 310 | ALWAYS_SEARCH_USER_PATHS = NO; 311 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 312 | CLANG_CXX_LIBRARY = "libc++"; 313 | CLANG_ENABLE_OBJC_ARC = YES; 314 | CLANG_WARN_CONSTANT_CONVERSION = YES; 315 | CLANG_WARN_EMPTY_BODY = YES; 316 | CLANG_WARN_ENUM_CONVERSION = YES; 317 | CLANG_WARN_INT_CONVERSION = YES; 318 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 319 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 320 | COPY_PHASE_STRIP = YES; 321 | GCC_C_LANGUAGE_STANDARD = gnu99; 322 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 323 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 324 | GCC_WARN_UNUSED_VARIABLE = YES; 325 | IPHONEOS_DEPLOYMENT_TARGET = 6.1; 326 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 327 | SDKROOT = iphoneos; 328 | VALIDATE_PRODUCT = YES; 329 | }; 330 | name = Release; 331 | }; 332 | F819044617AC0BE5006A0896 /* Debug */ = { 333 | isa = XCBuildConfiguration; 334 | buildSettings = { 335 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 336 | GCC_PREFIX_HEADER = "TWSReleaseNotesViewSample/TWSReleaseNotesViewSample-Prefix.pch"; 337 | INFOPLIST_FILE = "TWSReleaseNotesViewSample/TWSReleaseNotesViewSample-Info.plist"; 338 | IPHONEOS_DEPLOYMENT_TARGET = 5.1; 339 | PRODUCT_NAME = TWSReleaseNotesViewSample; 340 | TARGETED_DEVICE_FAMILY = 1; 341 | WRAPPER_EXTENSION = app; 342 | }; 343 | name = Debug; 344 | }; 345 | F819044717AC0BE5006A0896 /* Release */ = { 346 | isa = XCBuildConfiguration; 347 | buildSettings = { 348 | CODE_SIGN_IDENTITY = "iPhone Distribution: Matteo Lallone (2BHJQK4UZU)"; 349 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Matteo Lallone (2BHJQK4UZU)"; 350 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 351 | GCC_PREFIX_HEADER = "TWSReleaseNotesViewSample/TWSReleaseNotesViewSample-Prefix.pch"; 352 | INFOPLIST_FILE = "TWSReleaseNotesViewSample/TWSReleaseNotesViewSample-Info.plist"; 353 | IPHONEOS_DEPLOYMENT_TARGET = 5.1; 354 | PRODUCT_NAME = TWSReleaseNotesViewSample; 355 | PROVISIONING_PROFILE = "9F71268A-3685-47BD-8770-868A43D83E6D"; 356 | "PROVISIONING_PROFILE[sdk=iphoneos*]" = "9F71268A-3685-47BD-8770-868A43D83E6D"; 357 | TARGETED_DEVICE_FAMILY = 1; 358 | WRAPPER_EXTENSION = app; 359 | }; 360 | name = Release; 361 | }; 362 | /* End XCBuildConfiguration section */ 363 | 364 | /* Begin XCConfigurationList section */ 365 | F819042317AC0BE5006A0896 /* Build configuration list for PBXProject "TWSReleaseNotesViewSample" */ = { 366 | isa = XCConfigurationList; 367 | buildConfigurations = ( 368 | F819044317AC0BE5006A0896 /* Debug */, 369 | F819044417AC0BE5006A0896 /* Release */, 370 | ); 371 | defaultConfigurationIsVisible = 0; 372 | defaultConfigurationName = Release; 373 | }; 374 | F819044517AC0BE5006A0896 /* Build configuration list for PBXNativeTarget "TWSReleaseNotesViewSample" */ = { 375 | isa = XCConfigurationList; 376 | buildConfigurations = ( 377 | F819044617AC0BE5006A0896 /* Debug */, 378 | F819044717AC0BE5006A0896 /* Release */, 379 | ); 380 | defaultConfigurationIsVisible = 0; 381 | defaultConfigurationName = Release; 382 | }; 383 | /* End XCConfigurationList section */ 384 | }; 385 | rootObject = F819042017AC0BE5006A0896 /* Project object */; 386 | } 387 | -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // TWSReleaseNotesViewSample 4 | // 5 | // Created by Matteo Lallone on 02/08/13. 6 | // Copyright (c) 2013 Tapwings. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // TWSReleaseNotesViewSample 4 | // 5 | // Created by Matteo Lallone on 02/08/13. 6 | // Copyright (c) 2013 Tapwings. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "RootViewController.h" 11 | 12 | @implementation AppDelegate 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 15 | { 16 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 17 | // Override point for customization after application launch. 18 | 19 | [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent]; 20 | RootViewController *rootController = [[RootViewController alloc] initWithNibName:@"RootViewController" bundle:nil]; 21 | self.window.rootViewController = rootController; 22 | 23 | self.window.backgroundColor = [UIColor whiteColor]; 24 | [self.window makeKeyAndVisible]; 25 | 26 | return YES; 27 | } 28 | 29 | - (void)applicationWillResignActive:(UIApplication *)application 30 | { 31 | // 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. 32 | // 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. 33 | } 34 | 35 | - (void)applicationDidEnterBackground:(UIApplication *)application 36 | { 37 | // 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. 38 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 39 | } 40 | 41 | - (void)applicationWillEnterForeground:(UIApplication *)application 42 | { 43 | // 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. 44 | } 45 | 46 | - (void)applicationDidBecomeActive:(UIApplication *)application 47 | { 48 | // 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. 49 | } 50 | 51 | - (void)applicationWillTerminate:(UIApplication *)application 52 | { 53 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/Default-568h@2x.png -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/Default.png -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/Default@2x.png -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/RootViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // RootViewController.h 3 | // TWSReleaseNotesViewSample 4 | // 5 | // Created by Matteo Lallone on 02/08/13. 6 | // Copyright (c) 2013 Tapwings. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface RootViewController : UIViewController 12 | 13 | @property (weak, nonatomic) IBOutlet UIButton *localButton; 14 | @property (weak, nonatomic) IBOutlet UIButton *remoteButton; 15 | - (IBAction)showLocalButtonPressed:(id)sender; 16 | - (IBAction)showRemoteButtonPressed:(id)sender; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/RootViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // RootViewController.m 3 | // TWSReleaseNotesViewSample 4 | // 5 | // Created by Matteo Lallone on 02/08/13. 6 | // Copyright (c) 2013 Tapwings. All rights reserved. 7 | // 8 | 9 | #import "RootViewController.h" 10 | #import "TWSReleaseNotesView.h" 11 | 12 | @interface RootViewController () 13 | 14 | - (void)showLocalReleaseNotesView; 15 | - (void)showRemoteReleaseNotesView; 16 | 17 | @end 18 | 19 | @implementation RootViewController 20 | 21 | #pragma mark - Init - dealloc Methods 22 | 23 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 24 | { 25 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 26 | if (self) { 27 | // Custom initialization 28 | } 29 | return self; 30 | } 31 | 32 | #pragma mark - View lifecycle 33 | 34 | - (void)viewDidLoad 35 | { 36 | [super viewDidLoad]; 37 | // Do any additional setup after loading the view from its nib. 38 | 39 | [self setWantsFullScreenLayout:YES]; 40 | 41 | BOOL bDoesSupportResizableImageWithCapInsets = [[[UIImage alloc] init] respondsToSelector:@selector(resizableImageWithCapInsets:resizingMode:)]; 42 | UIImage *buttonNormalImage = [UIImage imageNamed:@"btn_bg"], *buttonHighlightedImage = [UIImage imageNamed:@"btn_bg_hl"]; 43 | 44 | // Setup local button 45 | [self.localButton setBackgroundColor:[UIColor clearColor]]; 46 | if( bDoesSupportResizableImageWithCapInsets ) 47 | { 48 | buttonNormalImage = [buttonNormalImage resizableImageWithCapInsets:UIEdgeInsetsMake(0.0f, 6.0f, 0.0f, 6.0f) resizingMode:UIImageResizingModeStretch]; 49 | buttonHighlightedImage = [buttonHighlightedImage resizableImageWithCapInsets:UIEdgeInsetsMake(0.0f, 6.0f, 0.0f, 6.0f) resizingMode:UIImageResizingModeStretch]; 50 | } 51 | else 52 | { 53 | //iOS 5 compatibility 54 | buttonNormalImage = [buttonNormalImage resizableImageWithCapInsets:UIEdgeInsetsMake(0.0f, 6.0f, 0.0f, 6.0f)]; 55 | buttonHighlightedImage = [buttonHighlightedImage resizableImageWithCapInsets:UIEdgeInsetsMake(0.0f, 6.0f, 0.0f, 6.0f)]; 56 | } 57 | [self.localButton setBackgroundImage:buttonNormalImage forState:UIControlStateNormal]; 58 | [self.localButton setBackgroundImage:buttonHighlightedImage forState:UIControlStateHighlighted]; 59 | 60 | 61 | // Setup remote button 62 | [self.remoteButton setBackgroundColor:[UIColor clearColor]]; 63 | buttonNormalImage = [UIImage imageNamed:@"btn_bg"]; 64 | buttonHighlightedImage = [UIImage imageNamed:@"btn_bg_hl"]; 65 | if( bDoesSupportResizableImageWithCapInsets ) 66 | { 67 | buttonNormalImage = [buttonNormalImage resizableImageWithCapInsets:UIEdgeInsetsMake(0.0f, 6.0f, 0.0f, 6.0f) resizingMode:UIImageResizingModeStretch]; 68 | buttonHighlightedImage = [buttonHighlightedImage resizableImageWithCapInsets:UIEdgeInsetsMake(0.0f, 6.0f, 0.0f, 6.0f) resizingMode:UIImageResizingModeStretch]; 69 | } 70 | else 71 | { 72 | //iOS 5 compatibility 73 | buttonNormalImage = [buttonNormalImage resizableImageWithCapInsets:UIEdgeInsetsMake(0.0f, 6.0f, 0.0f, 6.0f)]; 74 | buttonHighlightedImage = [buttonHighlightedImage resizableImageWithCapInsets:UIEdgeInsetsMake(0.0f, 6.0f, 0.0f, 6.0f)]; 75 | } 76 | [self.remoteButton setBackgroundImage:buttonNormalImage forState:UIControlStateNormal]; 77 | [self.remoteButton setBackgroundImage:buttonHighlightedImage forState:UIControlStateHighlighted]; 78 | } 79 | 80 | - (void)didReceiveMemoryWarning 81 | { 82 | [super didReceiveMemoryWarning]; 83 | // Dispose of any resources that can be recreated. 84 | } 85 | 86 | - (void)viewDidAppear:(BOOL)animated 87 | { 88 | [super viewDidAppear:animated]; 89 | 90 | // Check for app update if app is not on first launch 91 | if (![TWSReleaseNotesView isAppOnFirstLaunch] && [TWSReleaseNotesView isAppVersionUpdated]) 92 | { 93 | [self showLocalReleaseNotesView]; 94 | } 95 | } 96 | 97 | #pragma mark - Private Methods 98 | 99 | - (void)showLocalReleaseNotesView 100 | { 101 | // Create the release notes view 102 | NSString *currentAppVersion = [[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"]; 103 | TWSReleaseNotesView *releaseNotesView = [TWSReleaseNotesView viewWithReleaseNotesTitle:[NSString stringWithFormat:@"What's new in version\n%@:", currentAppVersion] text:@"• Create custom stations of your favorite podcasts that update automatically with new episodes\n• Choose whether your stations begin playing with the newest or oldest unplayed episode\n• Your stations are stored in iCloud and kept up-to-date on all of your devices\n• Create an On-The-Go playlist with your own list of episodes\n• Playlists synced from iTunes now appear in the Podcasts app\n• The Now Playing view has been redesigned with easier to use playback controls\n• Addressed an issue with resuming playback when returning to the app\n• Additional performance and stability improvements" closeButtonTitle:@"Close"]; 104 | 105 | // Show the release notes view 106 | [releaseNotesView showInView:self.view]; 107 | } 108 | 109 | - (void)showRemoteReleaseNotesView 110 | { 111 | NSString *currentAppVersion = [[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"]; 112 | [TWSReleaseNotesView setupViewWithAppIdentifier:@"329670577" releaseNotesTitle:[NSString stringWithFormat:@"What's new in version %@:", currentAppVersion] closeButtonTitle:@"Close" completionBlock:^(TWSReleaseNotesView *releaseNotesView, NSString *releaseNotesText, NSError *error){ 113 | if (error) 114 | { 115 | NSLog(@"An error occurred: %@", [error localizedDescription]); 116 | } 117 | else 118 | { 119 | // Create and show release notes view 120 | [releaseNotesView showInView:self.view]; 121 | } 122 | }]; 123 | } 124 | 125 | #pragma mark - Control Methods 126 | 127 | - (IBAction)showLocalButtonPressed:(id)sender 128 | { 129 | [self showLocalReleaseNotesView]; 130 | } 131 | 132 | - (IBAction)showRemoteButtonPressed:(id)sender 133 | { 134 | [self showRemoteReleaseNotesView]; 135 | } 136 | 137 | @end 138 | -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/RootViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1552 5 | 12E55 6 | 3084 7 | 1187.39 8 | 626.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 2083 12 | 13 | 14 | IBProxyObject 15 | IBUIButton 16 | IBUIImageView 17 | IBUIView 18 | 19 | 20 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 21 | 22 | 23 | PluginDependencyRecalculationVersion 24 | 25 | 26 | 27 | 28 | IBFilesOwner 29 | IBCocoaTouchFramework 30 | 31 | 32 | IBFirstResponder 33 | IBCocoaTouchFramework 34 | 35 | 36 | 37 | 274 38 | 39 | 40 | 41 | 306 42 | {320, 568} 43 | 44 | 45 | _NS:9 46 | 5 47 | NO 48 | IBCocoaTouchFramework 49 | 50 | NSImage 51 | background.png 52 | 53 | 54 | 55 | 56 | 269 57 | {{60, 492}, {200, 36}} 58 | 59 | 60 | _NS:9 61 | 62 | 3 63 | MAA 64 | 65 | NO 66 | IBCocoaTouchFramework 67 | 0 68 | 0 69 | -2 70 | 0.0 71 | 0.0 72 | 0.0 73 | Remote release notes 74 | 75 | 3 76 | MQA 77 | 78 | 79 | 80 | 3 81 | MCAwAA 82 | 83 | 84 | 3 85 | MC41AA 86 | 87 | 88 | 1 89 | 15 90 | 91 | 92 | Helvetica 93 | 15 94 | 16 95 | 96 | 97 | 98 | 99 | 269 100 | {{60, 440}, {200, 36}} 101 | 102 | 103 | _NS:9 104 | 105 | NO 106 | IBCocoaTouchFramework 107 | 0 108 | 0 109 | -2 110 | 0.0 111 | 0.0 112 | 0.0 113 | Local release notes 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | {320, 568} 123 | 124 | 125 | _NS:9 126 | 127 | 3 128 | MQA 129 | 130 | 2 131 | 132 | 133 | 134 | IBUIScreenMetrics 135 | 136 | YES 137 | 138 | 139 | 140 | 141 | 142 | {320, 568} 143 | {568, 320} 144 | 145 | 146 | IBCocoaTouchFramework 147 | Retina 4 Full Screen 148 | 2 149 | 150 | IBCocoaTouchFramework 151 | 152 | 153 | 154 | 155 | 156 | 157 | view 158 | 159 | 160 | 161 | 3 162 | 163 | 164 | 165 | localButton 166 | 167 | 168 | 169 | 11 170 | 171 | 172 | 173 | remoteButton 174 | 175 | 176 | 177 | 12 178 | 179 | 180 | 181 | showRemoteButtonPressed: 182 | 183 | 184 | 7 185 | 186 | 10 187 | 188 | 189 | 190 | showLocalButtonPressed: 191 | 192 | 193 | 7 194 | 195 | 9 196 | 197 | 198 | 199 | 200 | 201 | 0 202 | 203 | 204 | 205 | 206 | 207 | -1 208 | 209 | 210 | File's Owner 211 | 212 | 213 | -2 214 | 215 | 216 | 217 | 218 | 2 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 4 229 | 230 | 231 | 232 | 233 | 5 234 | 235 | 236 | 237 | 238 | 7 239 | 240 | 241 | 242 | 243 | 244 | 245 | RootViewController 246 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 247 | UIResponder 248 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 249 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 250 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 251 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 252 | 253 | 254 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 12 263 | 264 | 265 | 0 266 | IBCocoaTouchFramework 267 | YES 268 | 3 269 | 270 | background.png 271 | {320, 568} 272 | 273 | 2083 274 | 275 | 276 | -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/TWSReleaseNotesViewSample-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ReleaseView 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIcons 12 | 13 | CFBundlePrimaryIcon 14 | 15 | CFBundleIconFiles 16 | 17 | icon@2x.png 18 | icon.png 19 | 20 | UIPrerenderedIcon 21 | 22 | 23 | UINewsstandIcon 24 | 25 | CFBundleIconFiles 26 | 27 | 28 | 29 | UINewsstandBindingEdge 30 | UINewsstandBindingEdgeLeft 31 | UINewsstandBindingType 32 | UINewsstandBindingTypeMagazine 33 | 34 | 35 | CFBundleIdentifier 36 | com.tapwings.${PRODUCT_NAME:rfc1034identifier} 37 | CFBundleInfoDictionaryVersion 38 | 6.0 39 | CFBundleName 40 | ${PRODUCT_NAME} 41 | CFBundlePackageType 42 | APPL 43 | CFBundleShortVersionString 44 | 1.2.0 45 | CFBundleSignature 46 | ???? 47 | CFBundleVersion 48 | 1.2.0 49 | LSRequiresIPhoneOS 50 | 51 | UIRequiredDeviceCapabilities 52 | 53 | armv7 54 | 55 | UISupportedInterfaceOrientations 56 | 57 | UIInterfaceOrientationPortrait 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/TWSReleaseNotesViewSample-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'TWSReleaseNotesViewSample' target in the 'TWSReleaseNotesViewSample' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_3_0 8 | #warning "This project uses features only available in iOS SDK 3.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/background.png -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/background@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/background@2x.png -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/btn_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/btn_bg.png -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/btn_bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/btn_bg@2x.png -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/btn_bg_hl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/btn_bg_hl.png -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/btn_bg_hl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/btn_bg_hl@2x.png -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/icon.png -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/icon@2x.png -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/icon_7@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/icon_7@2x.png -------------------------------------------------------------------------------- /TWSReleaseNotesViewSample/TWSReleaseNotesViewSample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // TWSReleaseNotesViewSample 4 | // 5 | // Created by Matteo Lallone on 02/08/13. 6 | // Copyright (c) 2013 Tapwings. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /TutorialImages/sampleProject01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TutorialImages/sampleProject01.png -------------------------------------------------------------------------------- /TutorialImages/sampleProject02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iGriever/TWSReleaseNotesView/HEAD/TutorialImages/sampleProject02.png --------------------------------------------------------------------------------