Browse Source

MasterDetail

master
marcoschmickler 2 years ago
parent
commit
bcf9dc8c72
  1. 13
      Podfile
  2. 202
      Pods/Nimbus/LICENSE
  3. 13
      Pods/Nimbus/README.mdown
  4. 37
      Pods/Nimbus/src/core/src/NIActions+Subclassing.h
  5. 422
      Pods/Nimbus/src/core/src/NIActions.h
  6. 247
      Pods/Nimbus/src/core/src/NIActions.m
  7. 92
      Pods/Nimbus/src/core/src/NIButtonUtilities.h
  8. 83
      Pods/Nimbus/src/core/src/NIButtonUtilities.m
  9. 172
      Pods/Nimbus/src/core/src/NICommonMetrics.h
  10. 69
      Pods/Nimbus/src/core/src/NICommonMetrics.m
  11. 194
      Pods/Nimbus/src/core/src/NIDebuggingTools.h
  12. 61
      Pods/Nimbus/src/core/src/NIDebuggingTools.m
  13. 92
      Pods/Nimbus/src/core/src/NIDeviceOrientation.h
  14. 76
      Pods/Nimbus/src/core/src/NIDeviceOrientation.m
  15. 54
      Pods/Nimbus/src/core/src/NIError.h
  16. 24
      Pods/Nimbus/src/core/src/NIError.m
  17. 421
      Pods/Nimbus/src/core/src/NIFoundationMethods.h
  18. 314
      Pods/Nimbus/src/core/src/NIFoundationMethods.m
  19. 48
      Pods/Nimbus/src/core/src/NIImageUtilities.h
  20. 24
      Pods/Nimbus/src/core/src/NIImageUtilities.m
  21. 316
      Pods/Nimbus/src/core/src/NIInMemoryCache.h
  22. 453
      Pods/Nimbus/src/core/src/NIInMemoryCache.m
  23. 101
      Pods/Nimbus/src/core/src/NINetworkActivity.h
  24. 171
      Pods/Nimbus/src/core/src/NINetworkActivity.m
  25. 58
      Pods/Nimbus/src/core/src/NINonEmptyCollectionTesting.h
  26. 35
      Pods/Nimbus/src/core/src/NINonEmptyCollectionTesting.m
  27. 65
      Pods/Nimbus/src/core/src/NINonRetainingCollections.h
  28. 36
      Pods/Nimbus/src/core/src/NINonRetainingCollections.m
  29. 20
      Pods/Nimbus/src/core/src/NIOperations+Subclassing.h
  30. 209
      Pods/Nimbus/src/core/src/NIOperations.h
  31. 111
      Pods/Nimbus/src/core/src/NIOperations.m
  32. 69
      Pods/Nimbus/src/core/src/NIPaths.h
  33. 61
      Pods/Nimbus/src/core/src/NIPaths.m
  34. 141
      Pods/Nimbus/src/core/src/NIPreprocessorMacros.h
  35. 74
      Pods/Nimbus/src/core/src/NIRuntimeClassModifications.h
  36. 37
      Pods/Nimbus/src/core/src/NIRuntimeClassModifications.m
  37. 257
      Pods/Nimbus/src/core/src/NISDKAvailability.h
  38. 72
      Pods/Nimbus/src/core/src/NISDKAvailability.m
  39. 224
      Pods/Nimbus/src/core/src/NISnapshotRotation.h
  40. 299
      Pods/Nimbus/src/core/src/NISnapshotRotation.m
  41. 84
      Pods/Nimbus/src/core/src/NIState.h
  42. 58
      Pods/Nimbus/src/core/src/NIState.m
  43. 164
      Pods/Nimbus/src/core/src/NIViewRecycler.h
  44. 111
      Pods/Nimbus/src/core/src/NIViewRecycler.m
  45. 26
      Pods/Nimbus/src/core/src/NimbusCore+Additions.h
  46. 120
      Pods/Nimbus/src/core/src/NimbusCore.h
  47. 26
      Pods/Nimbus/src/core/src/UIResponder+NimbusCore.h
  48. 49
      Pods/Nimbus/src/core/src/UIResponder+NimbusCore.m
  49. 123
      Pods/Nimbus/src/pagingscrollview/src/NIPagingScrollView+Subclassing.h
  50. 386
      Pods/Nimbus/src/pagingscrollview/src/NIPagingScrollView.h
  51. 760
      Pods/Nimbus/src/pagingscrollview/src/NIPagingScrollView.m
  52. 36
      Pods/Nimbus/src/pagingscrollview/src/NIPagingScrollViewPage.h
  53. 26
      Pods/Nimbus/src/pagingscrollview/src/NIPagingScrollViewPage.m
  54. 53
      Pods/Nimbus/src/pagingscrollview/src/NimbusPagingScrollView.h
  55. 170
      Pods/Nimbus/src/photos/src/NIPhotoAlbumScrollView.h
  56. 221
      Pods/Nimbus/src/photos/src/NIPhotoAlbumScrollView.m
  57. 93
      Pods/Nimbus/src/photos/src/NIPhotoAlbumScrollViewDataSource.h
  58. 55
      Pods/Nimbus/src/photos/src/NIPhotoAlbumScrollViewDelegate.h
  59. 162
      Pods/Nimbus/src/photos/src/NIPhotoScrollView.h
  60. 512
      Pods/Nimbus/src/photos/src/NIPhotoScrollView.m
  61. 41
      Pods/Nimbus/src/photos/src/NIPhotoScrollViewDelegate.h
  62. 31
      Pods/Nimbus/src/photos/src/NIPhotoScrollViewPhotoSize.h
  63. 168
      Pods/Nimbus/src/photos/src/NIPhotoScrubberView.h
  64. 465
      Pods/Nimbus/src/photos/src/NIPhotoScrubberView.m
  65. 201
      Pods/Nimbus/src/photos/src/NIToolbarPhotoViewController.h
  66. 533
      Pods/Nimbus/src/photos/src/NIToolbarPhotoViewController.m
  67. 122
      Pods/Nimbus/src/photos/src/NimbusPhotos.h
  68. 21
      Pods/WebBrowser/LICENSE.md
  69. 90
      Pods/WebBrowser/README.md
  70. 44
      Pods/WebBrowser/WebBrowser/InternationalControl.swift
  71. 39
      Pods/WebBrowser/WebBrowser/NavigationBarAppearance.swift
  72. 14
      Pods/WebBrowser/WebBrowser/Resources/LocalizedStrings/en.lproj/WebBrowser.strings
  73. 14
      Pods/WebBrowser/WebBrowser/Resources/LocalizedStrings/ja.lproj/WebBrowser.strings
  74. 14
      Pods/WebBrowser/WebBrowser/Resources/LocalizedStrings/ko.lproj/WebBrowser.strings
  75. 14
      Pods/WebBrowser/WebBrowser/Resources/LocalizedStrings/zh-Hans.lproj/WebBrowser.strings
  76. 14
      Pods/WebBrowser/WebBrowser/Resources/LocalizedStrings/zh-Hant.lproj/WebBrowser.strings
  77. 6
      Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/Contents.json
  78. 23
      Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/backIcon.imageset/Contents.json
  79. BIN
      Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/backIcon.imageset/backIcon.png
  80. BIN
      Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/backIcon.imageset/backIcon@2x.png
  81. BIN
      Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/backIcon.imageset/backIcon@3x.png
  82. 23
      Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/forwardIcon.imageset/Contents.json
  83. BIN
      Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/forwardIcon.imageset/forwardIcon.png
  84. BIN
      Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/forwardIcon.imageset/forwardIcon@2x.png
  85. BIN
      Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/forwardIcon.imageset/forwardIcon@3x.png
  86. 33
      Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/safariIcon.imageset/Contents.json
  87. BIN
      Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/safariIcon.imageset/safariIcon.png
  88. BIN
      Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/safariIcon.imageset/safariIcon@2x.png
  89. BIN
      Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/safariIcon.imageset/safariIcon@2x~iPad.png
  90. BIN
      Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/safariIcon.imageset/safariIcon@3x.png
  91. BIN
      Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/safariIcon.imageset/safariIcon~iPad.png
  92. 49
      Pods/WebBrowser/WebBrowser/SafariActivity.swift
  93. 30
      Pods/WebBrowser/WebBrowser/ToolbarAppearance.swift
  94. 38
      Pods/WebBrowser/WebBrowser/WebBrowser.swift
  95. 46
      Pods/WebBrowser/WebBrowser/WebBrowserDelegate.swift
  96. 412
      Pods/WebBrowser/WebBrowser/WebBrowserViewController.swift
  97. 46
      kplayer.xcodeproj/project.pbxproj
  98. 44
      kplayer.xcodeproj/xcuserdata/marcoschmickler.xcuserdatad/xcschemes/kplayer.xcscheme
  99. 37
      kplayer/AppDelegate.swift
  100. 25
      kplayer/PlayerApp.swift

13
Podfile

@ -6,11 +6,16 @@ use_frameworks!
target 'kplayer' do
pod 'Alamofire' #, '4.7'
pod 'HanekeSwift', :git => 'https://github.com/Haneke/HanekeSwift.git'
pod 'Nimbus/Photos'
pod 'Nimbus/PagingScrollView'
pod 'NVActivityIndicatorView'
pod 'WebBrowser'
pod 'FileBrowser'
pod 'ZIPFoundation'
end
post_install do |installer|
installer.generated_projects.each do |project|
project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
end
end
end
end

202
Pods/Nimbus/LICENSE

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

13
Pods/Nimbus/README.mdown

@ -1,13 +0,0 @@
Nimbus is an iOS framework whose feature set grows only as fast as its documentation.
[![Build Status](https://travis-ci.org/jverkoey/nimbus.svg)](https://travis-ci.org/jverkoey/nimbus)
Getting Started
---------------
- Visit the Nimbus website at [nimbuskit.info](http://nimbuskit.info).
- [Add Nimbus to your project](http://wiki.nimbuskit.info/Add-Nimbus-to-your-project).
- Follow Nimbus' development through its [version history](http://docs.nimbuskit.info/group___version-_history.html).
- See the [latest API diffs](http://docs.nimbuskit.info/group___version-9-3.html).
- Read the [Three20 Migration Guide](http://docs.nimbuskit.info/group___three20-_migration-_guide.html).
- Ask questions and get updates via the [Nimbus mailing list](http://groups.google.com/group/nimbusios).

37
Pods/Nimbus/src/core/src/NIActions+Subclassing.h

@ -1,37 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIActions.h"
@interface NIObjectActions : NSObject
@property (nonatomic, copy) NIActionBlock tapAction;
@property (nonatomic, copy) NIActionBlock detailAction;
@property (nonatomic, copy) NIActionBlock navigateAction;
@property (nonatomic) SEL tapSelector;
@property (nonatomic) SEL detailSelector;
@property (nonatomic) SEL navigateSelector;
@end
@interface NIActions ()
@property (nonatomic, weak) id target;
- (NIObjectActions *)actionForObjectOrClassOfObject:(id<NSObject>)object;
@end

422
Pods/Nimbus/src/core/src/NIActions.h

@ -1,422 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
/**
* For attaching actions to objects.
*
* @ingroup NimbusCore
* @defgroup Actions Actions
* @{
*/
/**
* @param object An action was performed on this object.
* @param target The target that was attached to the NIActions instance.
* @param indexPath The index path of the object.
*/
typedef BOOL (^NIActionBlock)(id object, id target, NSIndexPath* indexPath);
/**
* The attachable types of actions for NIAction.
*/
typedef NS_OPTIONS(NSUInteger, NIActionType) {
NIActionTypeNone = 0,
NIActionTypeTap = 1 << 0,
NIActionTypeDetail = 1 << 1,
NIActionTypeNavigate = 1 << 2,
};
/**
* The NIActions class provides a generic interface for attaching actions to objects.
*
* NIActions are used to implement user interaction in UITableViews and UICollectionViews via the
* corresponding classes (NITableViewActions and NICollectionViewActions) in the respective
* feature. NIActions separates the necessity
*
* <h3>Types of Actions</h3>
*
* The three primary types of actions are:
*
* - buttons,
* - detail views,
* - and pushing a new controller onto the navigation controller.
*
* <h3>Attaching Actions</h3>
*
* Actions may be attached to specific instances of objects or to entire classes of objects. When
* an action is attached to both a class of object and an instance of that class, only the instance
* action should be executed.
*
* All attachment methods return the object that was provided. This makes it simple to attach
* actions within an array creation statement.
*
* Actions come in two forms: blocks and selector invocations. Both can be attached to an object
* for each type of action and both will be executed, with the block being executed first. Blocks
* should be used for simple executions while selectors should be used when the action is complex.
*
* The following is an example of using NITableViewActions:
*
@code
NSArray *objects = @[
[NITitleCellObject objectWithTitle:@"Implicit tap handler"],
[self.actions attachToObject:[NITitleCellObject objectWithTitle:@"Explicit tap handler"]
tapBlock:
^BOOL(id object, id target) {
NSLog(@"Object was tapped with an explicit action: %@", object);
}]
];
[self.actions attachToClass:[NITitleCellObject class]
tapBlock:
^BOOL(id object, id target) {
NSLog(@"Object was tapped: %@", object);
}];
@endcode
*
*/
@interface NIActions : NSObject
// Designated initializer.
- (id)initWithTarget:(id)target;
#pragma mark Mapping Objects
- (id)attachToObject:(id<NSObject>)object tapBlock:(NIActionBlock)action;
- (id)attachToObject:(id<NSObject>)object detailBlock:(NIActionBlock)action;
- (id)attachToObject:(id<NSObject>)object navigationBlock:(NIActionBlock)action;
- (id)attachToObject:(id<NSObject>)object tapSelector:(SEL)selector;
- (id)attachToObject:(id<NSObject>)object detailSelector:(SEL)selector;
- (id)attachToObject:(id<NSObject>)object navigationSelector:(SEL)selector;
#pragma mark Mapping Classes
- (void)attachToClass:(Class)aClass tapBlock:(NIActionBlock)action;
- (void)attachToClass:(Class)aClass detailBlock:(NIActionBlock)action;
- (void)attachToClass:(Class)aClass navigationBlock:(NIActionBlock)action;
- (void)attachToClass:(Class)aClass tapSelector:(SEL)selector;
- (void)attachToClass:(Class)aClass detailSelector:(SEL)selector;
- (void)attachToClass:(Class)aClass navigationSelector:(SEL)selector;
#pragma mark Object State
- (BOOL)isObjectActionable:(id<NSObject>)object;
- (NIActionType)attachedActionTypesForObject:(id<NSObject>)object;
+ (id)objectFromKeyClass:(Class)keyClass map:(NSMutableDictionary *)map;
@end
#if defined __cplusplus
extern "C" {
#endif
/**
* Returns a block that pushes an instance of the controllerClass onto the navigation stack.
*
* Allocates an instance of the controller class and calls the init selector.
*
* The target property of the NIActions instance must be an instance of UIViewController
* with an attached navigationController.
*
* @param controllerClass The class of controller to instantiate.
*/
NIActionBlock NIPushControllerAction(Class controllerClass);
#if defined __cplusplus
};
#endif
/** The protocol for a data source that can be used with NIActions. */
@protocol NIActionsDataSource <NSObject>
/**
* The object located at the given indexPath.
*
* @param indexPath The index path of the requested object.
*/
- (id)objectAtIndexPath:(NSIndexPath *)indexPath;
@end
/** @name Creating Table View Actions */
/**
* Initializes a newly allocated table view actions object with the given controller.
*
* @attention This method is deprecated. Use the new method
* @link NIActions::initWithTarget: initWithTarget:@endlink.
*
* The controller is stored as a weak reference internally.
*
* @param controller The controller that will be used in action blocks.
* @fn NIActions::initWithController:
*/
/**
* Initializes a newly allocated table view actions object with the given target.
*
* This is the designated initializer.
*
* The target is stored as a weak reference internally.
*
* @param target The target that will be provided to action blocks and on which selectors will
* be performed.
* @fn NIActions::initWithTarget:
*/
/** @name Mapping Objects */
/**
* Attaches a tap action to the given object.
*
* A cell with an attached tap action will have its selectionStyle set to
* @c tableViewCellSelectionStyle when the cell is displayed.
*
* The action will be executed when the object's corresponding cell is tapped. The object argument
* of the block will be the object to which this action was attached. The target argument of the
* block will be this receiver's @c target.
*
* Return NO if the tap action is used to present a modal view controller. This provides a visual
* reminder to the user when the modal controller is dismissed as to which cell was tapped to invoke
* the modal controller.
*
* The tap action will be invoked first, followed by the navigation action if one is attached.
*
* @param object The object to attach the action to. This object must be contained within
* an NITableViewModel.
* @param action The tap action block.
* @returns The object that you attached this action to.
* @fn NIActions::attachToObject:tapBlock:
* @sa NIActions::attachToObject:tapSelector:
*/
/**
* Attaches a detail action to the given object.
*
* When a cell with a detail action is displayed, its accessoryType will be set to
* UITableViewCellAccessoryDetailDisclosureButton.
*
* When a cell's detail button is tapped, the detail action block will be executed. The return
* value of the block is ignored.
*
* @param object The object to attach the action to. This object must be contained within
* an NITableViewModel.
* @param action The detail action block.
* @returns The object that you attached this action to.
* @fn NIActions::attachToObject:detailBlock:
*/
/**
* Attaches a navigation action to the given object.
*
* When a cell with a navigation action is displayed, its accessoryType will be set to
* UITableViewCellAccessoryDisclosureIndicator if there is no detail action, otherwise the
* detail disclosure indicator takes precedence.
*
* When a cell with a navigation action is tapped the navigation block will be executed.
*
* If a tap action also exists for this object then the tap action will be executed first, followed
* by the navigation action.
*
* @param object The object to attach the action to. This object must be contained within
* an NITableViewModel.
* @param action The navigation action block.
* @returns The object that you attached this action to.
* @fn NIActions::attachToObject:navigationBlock:
*/
/**
* Attaches a tap selector to the given object.
*
* The method signature for the selector is:
@code
- (BOOL)didTapObject:(id)object;
@endcode
*
* A cell with an attached tap action will have its selectionStyle set to
* @c tableViewCellSelectionStyle when the cell is displayed.
*
* The selector will be performed on the action object's target when a cell with a tap selector is
* tapped, unless that selector does not exist on the @c target in which case nothing happens.
*
* If the selector invocation returns YES then the cell will be deselected immediately after the
* invocation completes its execution. If NO is returned then the cell's selection will remain.
*
* Return NO if the tap action is used to present a modal view controller. This provides a visual
* reminder to the user when the modal controller is dismissed as to which cell was tapped to invoke
* the modal controller.
*
* The tap action will be invoked first, followed by the navigation action if one is attached.
*
* @param object The object to attach the selector to. This object must be contained within
* an NITableViewModel.
* @param selector The selector that will be invoked by this action.
* @returns The object that you attached this action to.
* @fn NIActions::attachToObject:tapSelector:
* @sa NIActions::attachToObject:tapBlock:
*/
/**
* Attaches a detail selector to the given object.
*
* The method signature for the selector is:
@code
- (void)didTapObject:(id)object;
@endcode
*
* A cell with an attached tap action will have its selectionStyle set to
* @c tableViewCellSelectionStyle and its accessoryType set to
* @c UITableViewCellAccessoryDetailDisclosureButton when the cell is displayed.
*
* The selector will be performed on the action object's target when a cell with a detail selector's
* accessory indicator is tapped, unless that selector does not exist on the @c target in which
* case nothing happens.
*
* @param object The object to attach the selector to. This object must be contained within
* an NITableViewModel.
* @param selector The selector that will be invoked by this action.
* @returns The object that you attached this action to.
* @fn NIActions::attachToObject:detailSelector:
* @sa NIActions::attachToObject:detailBlock:
*/
/**
* Attaches a navigation selector to the given object.
*
* The method signature for the selector is:
@code
- (void)didTapObject:(id)object;
@endcode
*
* A cell with an attached navigation action will have its selectionStyle set to
* @c tableViewCellSelectionStyle and its accessoryType set to
* @c UITableViewCellAccessoryDetailDisclosureButton, unless it also has an attached detail action,
* in which case its accessoryType will be set to @c UITableViewCellAccessoryDisclosureIndicator
* when the cell is displayed.
*
* The selector will be performed on the action object's target when a cell with a navigation
* selector is tapped, unless that selector does not exist on the @c target in which case nothing
* happens.
*
* @param object The object to attach the selector to. This object must be contained within
* an NITableViewModel.
* @param selector The selector that will be invoked by this action.
* @returns The object that you attached this action to.
* @fn NIActions::attachToObject:navigationSelector:
* @sa NIActions::attachToObject:navigationBlock:
*/
/** @name Mapping Classes */
/**
* Attaches a tap block to a class.
*
* This method behaves similarly to attachToObject:tapBlock: except it attaches a tap action to
* all instances and subclassed instances of a given class.
*
* @param aClass The class to attach the action to.
* @param action The tap action block.
* @fn NIActions::attachToClass:tapBlock:
*/
/**
* Attaches a detail block to a class.
*
* This method behaves similarly to attachToObject:detailBlock: except it attaches a detail action
* to all instances and subclassed instances of a given class.
*
* @param aClass The class to attach the action to.
* @param action The detail action block.
* @fn NIActions::attachToClass:detailBlock:
*/
/**
* Attaches a navigation block to a class.
*
* This method behaves similarly to attachToObject:navigationBlock: except it attaches a navigation
* action to all instances and subclassed instances of a given class.
*
* @param aClass The class to attach the action to.
* @param action The navigation action block.
* @fn NIActions::attachToClass:navigationBlock:
*/
/**
* Attaches a tap selector to a class.
*
* This method behaves similarly to attachToObject:tapBlock: except it attaches a tap action to
* all instances and subclassed instances of a given class.
*
* @param aClass The class to attach the action to.
* @param selector The tap selector.
* @fn NIActions::attachToClass:tapSelector:
*/
/**
* Attaches a detail selector to a class.
*
* This method behaves similarly to attachToObject:detailBlock: except it attaches a detail action
* to all instances and subclassed instances of a given class.
*
* @param aClass The class to attach the action to.
* @param selector The tap selector.
* @fn NIActions::attachToClass:detailSelector:
*/
/**
* Attaches a navigation selector to a class.
*
* This method behaves similarly to attachToObject:navigationBlock: except it attaches a navigation
* action to all instances and subclassed instances of a given class.
*
* @param aClass The class to attach the action to.
* @param selector The tap selector.
* @fn NIActions::attachToClass:navigationSelector:
*/
/** @name Object State */
/**
* Returns whether or not the object has any actions attached to it.
*
* @fn NIActions::isObjectActionable:
*/
/**
* Returns a bitmask of flags indicating the types of actions attached to the provided object.
*
* @fn NIActions::attachedActionTypesForObject:
*/
/**
* Returns a mapped object from the given key class.
*
* If the key class is a subclass of any mapped key classes, the nearest ancestor class's mapped
* object will be returned and keyClass will be added to the map for future accesses.
*
* @param keyClass The key class that will be used to find the mapping in map.
* @param map A map of key classes to classes. May be modified if keyClass is a subclass of
* any existing key classes.
* @returns The mapped object if a match for keyClass was found in map. nil is returned
* otherwise.
* @fn NIActions::objectFromKeyClass:map:
*/
/**@}*/// End of Actions //////////////////////////////////////////////////////////////////////////

247
Pods/Nimbus/src/core/src/NIActions.m

@ -1,247 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIActions.h"
#import "NIActions+Subclassing.h"
#import <UIKit/UIKit.h>
#import "NIDebuggingTools.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
@interface NIActions ()
@property (nonatomic, strong) NSMutableDictionary* objectToAction;
@property (nonatomic, strong) NSMutableDictionary* classToAction;
@property (nonatomic, strong) NSMutableSet* objectSet;
@end
@implementation NIActions
- (id)initWithTarget:(id)target {
if ((self = [super init])) {
_target = target;
_objectToAction = [[NSMutableDictionary alloc] init];
_classToAction = [[NSMutableDictionary alloc] init];
_objectSet = [[NSMutableSet alloc] init];
}
return self;
}
- (id)init {
return [self initWithTarget:nil];
}
#pragma mark - Private
- (id)keyForObject:(id<NSObject>)object {
return @(object.hash);
}
//
// actionForObject: and actionForClass: are used when attaching actions to objects and classes and
// will always return an NIObjectActions object. These methods should not be used for determining
// whether an action is attached to a given object or class.
//
// actionForObjectOrClassOfObject: determines whether an action has been attached to an object
// or class of object and then returns the NIObjectActions or nil if no actions have been attached.
//
// Retrieves an NIObjectActions object for the given object or creates one if it doesn't yet exist
// so that actions may be attached.
- (NIObjectActions *)actionForObject:(id<NSObject>)object {
id key = [self keyForObject:object];
NIObjectActions* action = [self.objectToAction objectForKey:key];
if (nil == action) {
action = [[NIObjectActions alloc] init];
[self.objectToAction setObject:action forKey:key];
}
return action;
}
// Retrieves an NIObjectActions object for the given class or creates one if it doesn't yet exist
// so that actions may be attached.
- (NIObjectActions *)actionForClass:(Class)class {
NIObjectActions* action = [self.classToAction objectForKey:class];
if (nil == action) {
action = [[NIObjectActions alloc] init];
[self.classToAction setObject:action forKey:(id<NSCopying>)class];
}
return action;
}
// Fetches any attached actions for a given object.
- (NIObjectActions *)actionForObjectOrClassOfObject:(id<NSObject>)object {
id key = [self keyForObject:object];
NIObjectActions* action = [self.objectToAction objectForKey:key];
if (nil == action) {
action = [self.class objectFromKeyClass:object.class map:self.classToAction];
}
return action;
}
#pragma mark - Public
- (id)attachToObject:(id<NSObject>)object tapBlock:(NIActionBlock)action {
[self.objectSet addObject:object];
[self actionForObject:object].tapAction = action;
return object;
}
- (id)attachToObject:(id<NSObject>)object detailBlock:(NIActionBlock)action {
[self.objectSet addObject:object];
[self actionForObject:object].detailAction = action;
return object;
}
- (id)attachToObject:(id<NSObject>)object navigationBlock:(NIActionBlock)action {
[self.objectSet addObject:object];
[self actionForObject:object].navigateAction = action;
return object;
}
- (id)attachToObject:(id<NSObject>)object tapSelector:(SEL)selector {
[self.objectSet addObject:object];
[self actionForObject:object].tapSelector = selector;
return object;
}
- (id)attachToObject:(id<NSObject>)object detailSelector:(SEL)selector {
[self.objectSet addObject:object];
[self actionForObject:object].detailSelector = selector;
return object;
}
- (id)attachToObject:(id<NSObject>)object navigationSelector:(SEL)selector {
[self.objectSet addObject:object];
[self actionForObject:object].navigateSelector = selector;
return object;
}
- (void)attachToClass:(Class)aClass tapBlock:(NIActionBlock)action {
[self actionForClass:aClass].tapAction = action;
}
- (void)attachToClass:(Class)aClass detailBlock:(NIActionBlock)action {
[self actionForClass:aClass].detailAction = action;
}
- (void)attachToClass:(Class)aClass navigationBlock:(NIActionBlock)action {
[self actionForClass:aClass].navigateAction = action;
}
- (void)attachToClass:(Class)aClass tapSelector:(SEL)selector {
[self actionForClass:aClass].tapSelector = selector;
}
- (void)attachToClass:(Class)aClass detailSelector:(SEL)selector {
[self actionForClass:aClass].detailSelector = selector;
}
- (void)attachToClass:(Class)aClass navigationSelector:(SEL)selector {
[self actionForClass:aClass].navigateSelector = selector;
}
- (BOOL)isObjectActionable:(id<NSObject>)object {
return NIActionTypeNone != [self attachedActionTypesForObject:object];
}
- (NIActionType)attachedActionTypesForObject:(id<NSObject>)object {
if (nil == object) {
return NIActionTypeNone;
}
NIObjectActions* actions = [self actionForObjectOrClassOfObject:object];
NIActionType attachedActionTypes = 0;
if (actions.tapAction || actions.tapSelector) {
attachedActionTypes |= NIActionTypeTap;
}
if (actions.detailAction || actions.detailSelector) {
attachedActionTypes |= NIActionTypeDetail;
}
if (actions.navigateAction || actions.navigateSelector) {
attachedActionTypes |= NIActionTypeNavigate;
}
return attachedActionTypes;
}
+ (id)objectFromKeyClass:(Class)keyClass map:(NSMutableDictionary *)map {
id object = [map objectForKey:keyClass];
if (nil == object) {
// No mapping found for this key class, but it may be a subclass of another object that does
// have a mapping, so let's see what we can find.
Class superClass = nil;
for (Class class in map.allKeys) {
// We want to find the lowest node in the class hierarchy so that we pick the lowest ancestor
// in the hierarchy tree.
if ([keyClass isSubclassOfClass:class]
&& (nil == superClass || [class isSubclassOfClass:superClass])) {
superClass = class;
}
}
if (nil != superClass) {
object = [map objectForKey:superClass];
// Add this subclass to the map so that next time this result is instant.
[map setObject:object forKey:(id<NSCopying>)keyClass];
}
}
if (nil == object) {
// We couldn't find a mapping at all so let's add an empty mapping.
[map setObject:[NSNull class] forKey:(id<NSCopying>)keyClass];
} else if (object == [NSNull class]) {
// Don't return null mappings.
object = nil;
}
return object;
}
@end
@implementation NIObjectActions
@end
NIActionBlock NIPushControllerAction(Class controllerClass) {
return [^(id object, id target, NSIndexPath* indexPath) {
// You must initialize the actions object with initWithTarget: and pass a valid
// controller.
NIDASSERT(nil != target);
NIDASSERT([target isKindOfClass:[UIViewController class]]);
UIViewController *controller = target;
if (nil != controller && [controller isKindOfClass:[UIViewController class]]) {
// No navigation controller to push this new controller; this controller
// is going to be lost.
NIDASSERT(nil != controller.navigationController);
UIViewController* controllerToPush = [[controllerClass alloc] init];
[controller.navigationController pushViewController:controllerToPush
animated:YES];
}
return NO;
} copy];
}

92
Pods/Nimbus/src/core/src/NIButtonUtilities.h

@ -1,92 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#if defined __cplusplus
extern "C" {
#endif
/**
* For manipulating UIButton objects.
*
* @ingroup NimbusCore
* @defgroup Button-Utilities Button Utilities
* @{
*
* The methods provided here make it possible to specify different properties for different button
* states in a scalable way. For example, you can define a stylesheet class that has a number of
* class methods that return the various properties for buttons.
*
@code
@implementation Stylesheet
+ (UIImage *)backgroundImageForButtonWithState:(UIControlState)state {
if (state & UIControlStateHighlighted) {
return [UIImage imageNamed:@"button_highlighted"];
} else if (state == UIControlStateNormal) {
return [UIImage imageNamed:@"button"];
}
return nil;
}
@end
// The result of the implementation above will set the background images for the button's
// highlighted and default states.
NIApplyBackgroundImageSelectorToButton(@selector(backgroundImageForButtonWithState:),
[Stylesheet class],
button);
@endcode
*/
/**
* Sets the images for a button's states.
*
* @param selector A selector of the form:
* (UIImage *)imageWithControlState:(UIControlState)controlState
* @param target The target upon which the selector will be invoked.
* @param button The button object whose properties should be modified.
*/
void NIApplyImageSelectorToButton(SEL selector, id target, UIButton* button);
/**
* Sets the background images for a button's states.
*
* @param selector A selector of the form:
* (UIImage *)backgroundImageWithControlState:(UIControlState)controlState
* @param target The target upon which the selector will be invoked.
* @param button The button object whose properties should be modified.
*/
void NIApplyBackgroundImageSelectorToButton(SEL selector, id target, UIButton* button);
/**
* Sets the title colors for a button's states.
*
* @param selector A selector of the form:
* (UIColor *)colorWithControlState:(UIControlState)controlState
* @param target The target upon which the selector will be invoked.
* @param button The button object whose properties should be modified.
*/
void NIApplyTitleColorSelectorToButton(SEL selector, id target, UIButton* button);
/**@}*/// End of Button Utilities /////////////////////////////////////////////////////////////////
#if defined __cplusplus
};
#endif

83
Pods/Nimbus/src/core/src/NIButtonUtilities.m

@ -1,83 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIButtonUtilities.h"
void NIApplyImageSelectorToButton(SEL selector, id target, UIButton* button) {
typedef UIImage* (*ImageMethod)(id, SEL, UIControlState);
ImageMethod method = (ImageMethod)[target methodForSelector:selector];
UIImage* image = nil;
image = method(target, selector, UIControlStateNormal);
[button setImage:image forState:UIControlStateNormal];
image = method(target, selector, UIControlStateHighlighted);
[button setImage:image forState:UIControlStateHighlighted];
image = method(target, selector, UIControlStateDisabled);
[button setImage:image forState:UIControlStateDisabled];
image = method(target, selector, UIControlStateSelected);
[button setImage:image forState:UIControlStateSelected];
UIControlState selectedHighlightState = UIControlStateSelected | UIControlStateHighlighted;
image = method(target, selector, selectedHighlightState);
[button setImage:image forState:selectedHighlightState];
}
void NIApplyBackgroundImageSelectorToButton(SEL selector, id target, UIButton* button) {
typedef UIImage* (*ImageMethod)(id, SEL, UIControlState);
ImageMethod method = (ImageMethod)[target methodForSelector:selector];
UIImage* image = nil;
image = method(target, selector, UIControlStateNormal);
[button setBackgroundImage:image forState:UIControlStateNormal];
image = method(target, selector, UIControlStateHighlighted);
[button setBackgroundImage:image forState:UIControlStateHighlighted];
image = method(target, selector, UIControlStateDisabled);
[button setBackgroundImage:image forState:UIControlStateDisabled];
image = method(target, selector, UIControlStateSelected);
[button setBackgroundImage:image forState:UIControlStateSelected];
UIControlState selectedHighlightState = UIControlStateSelected | UIControlStateHighlighted;
image = method(target, selector, selectedHighlightState);
[button setBackgroundImage:image forState:selectedHighlightState];
}
void NIApplyTitleColorSelectorToButton(SEL selector, id target, UIButton* button) {
typedef UIColor* (*ColorMethod)(id, SEL, UIControlState);
ColorMethod method = (ColorMethod)[target methodForSelector:selector];
UIColor* color = nil;
color = method(target, selector, UIControlStateNormal);
[button setTitleColor:color forState:UIControlStateNormal];
color = method(target, selector, UIControlStateHighlighted);
[button setTitleColor:color forState:UIControlStateHighlighted];
color = method(target, selector, UIControlStateDisabled);
[button setTitleColor:color forState:UIControlStateDisabled];
color = method(target, selector, UIControlStateSelected);
[button setTitleColor:color forState:UIControlStateSelected];
UIControlState selectedHighlightState = UIControlStateSelected | UIControlStateHighlighted;
color = method(target, selector, selectedHighlightState);
[button setTitleColor:color forState:selectedHighlightState];
}

172
Pods/Nimbus/src/core/src/NICommonMetrics.h

@ -1,172 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "NIPreprocessorMacros.h"
#if defined __cplusplus
extern "C" {
#endif
/**
* For common system metrics.
*
* If you work with system metrics in any way it can be a pain in the ass to figure out what the
* exact metrics are. Figuring out how long it takes the status bar to animate is not something you
* should be spending your time on. The metrics in this file are provided as a means of unifying a
* number of system metrics for use in your applications.
*
* <h2>What Qualifies as a Common Metric</h2>
*
* Common metrics are system components, such as the dimensions of a toolbar in
* a particular orientation or the duration of a standard animation. This is
* not the place to put feature-specific metrics, such as the height of a photo scrubber
* view.
*
* <h2>Examples</h2>
*
* <h3>Positioning a Toolbar</h3>
*
* The following example updates the position and height of a toolbar when the device
* orientation is changing. This ensures that, in landscape mode on the iPhone, the toolbar
* is slightly shorter to accomodate the smaller height of the screen.
*
* @code
* - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
* duration:(NSTimeInterval)duration {
* [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
*
* CGRect toolbarFrame = self.toolbar.frame;
* toolbarFrame.size.height = NIToolbarHeightForOrientation(toInterfaceOrientation);
* toolbarFrame.origin.y = self.view.bounds.size.height - toolbarFrame.size.height;
* self.toolbar.frame = toolbarFrame;
* }
* @endcode
*
* @ingroup NimbusCore
* @defgroup Common-Metrics Common Metrics
* @{
*/
#ifndef UIViewAutoresizingFlexibleMargins
#define UIViewAutoresizingFlexibleMargins (UIViewAutoresizingFlexibleLeftMargin \
| UIViewAutoresizingFlexibleTopMargin \
| UIViewAutoresizingFlexibleRightMargin \
| UIViewAutoresizingFlexibleBottomMargin)
#endif
#ifndef UIViewAutoresizingFlexibleDimensions
#define UIViewAutoresizingFlexibleDimensions (UIViewAutoresizingFlexibleWidth \
| UIViewAutoresizingFlexibleHeight)
#endif
#ifndef UIViewAutoresizingNavigationBar
#define UIViewAutoresizingNavigationBar (UIViewAutoresizingFlexibleWidth \
| UIViewAutoresizingFlexibleBottomMargin)
#endif
#ifndef UIViewAutoresizingToolbar
#define UIViewAutoresizingToolbar (UIViewAutoresizingFlexibleWidth \
| UIViewAutoresizingFlexibleTopMargin)
#endif
/**
* The recommended number of points for a minimum tappable area.
*
* Value: 44
*/
CGFloat NIMinimumTapDimension(void);
/**
* Fetch the height of a toolbar in a given orientation.
*
* On the iPhone:
* - Portrait: 44
* - Landscape: 33
*
* On the iPad: always 44
*/
CGFloat NIToolbarHeightForOrientation(UIInterfaceOrientation orientation);
/**
* The animation curve used when changing the status bar's visibility.
*
* This is the curve of the animation used by
* <code>-[[UIApplication sharedApplication] setStatusBarHidden:withAnimation:].</code>
*
* Value: UIViewAnimationCurveEaseIn
*/
UIViewAnimationCurve NIStatusBarAnimationCurve(void);
/**
* The animation duration used when changing the status bar's visibility.
*
* This is the duration of the animation used by
* <code>-[[UIApplication sharedApplication] setStatusBarHidden:withAnimation:].</code>
*
* Value: 0.3 seconds
*/
NSTimeInterval NIStatusBarAnimationDuration(void);
/**
* The animation curve used when the status bar's bounds change (when a call is received,
* for example).
*
* Value: UIViewAnimationCurveEaseInOut
*/
UIViewAnimationCurve NIStatusBarBoundsChangeAnimationCurve(void);
/**
* The animation duration used when the status bar's bounds change (when a call is received,
* for example).
*
* Value: 0.35 seconds
*/
NSTimeInterval NIStatusBarBoundsChangeAnimationDuration(void);
/**
* Get the status bar's current height.
*
* If the status bar is hidden this will return 0.
*
* This is generally 20 when the status bar is its normal height.
*/
CGFloat NIStatusBarHeight(void) NI_EXTENSION_UNAVAILABLE_IOS("");
/**
* The animation duration when the device is rotating to a new orientation.
*
* Value: 0.4 seconds if the device is being rotated 90 degrees.
* 0.8 seconds if the device is being rotated 180 degrees.
*
* @param isFlippingUpsideDown YES if the device is being flipped upside down.
*/
NSTimeInterval NIDeviceRotationDuration(BOOL isFlippingUpsideDown);
/**
* The padding around a standard cell in a table view.
*
* Value: 10 pixels on all sides.
*/
UIEdgeInsets NICellContentPadding(void);
#if defined __cplusplus
};
#endif
/**@}*/// End of Common Metrics ///////////////////////////////////////////////////////////////////

69
Pods/Nimbus/src/core/src/NICommonMetrics.m

@ -1,69 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NICommonMetrics.h"
#import "NISDKAvailability.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
CGFloat NIMinimumTapDimension(void) {
return 44;
}
CGFloat NIToolbarHeightForOrientation(UIInterfaceOrientation orientation) {
return (NIIsPad()
? 44
: (UIInterfaceOrientationIsPortrait(orientation)
? 44
: 33));
}
UIViewAnimationCurve NIStatusBarAnimationCurve(void) {
return UIViewAnimationCurveEaseIn;
}
NSTimeInterval NIStatusBarAnimationDuration(void) {
return 0.3;
}
UIViewAnimationCurve NIStatusBarBoundsChangeAnimationCurve(void) {
return UIViewAnimationCurveEaseInOut;
}
NSTimeInterval NIStatusBarBoundsChangeAnimationDuration(void) {
return 0.35;
}
CGFloat NIStatusBarHeight(void) {
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
// We take advantage of the fact that the status bar will always be wider than it is tall
// in order to avoid having to check the status bar orientation.
CGFloat statusBarHeight = MIN(statusBarFrame.size.width, statusBarFrame.size.height);
return statusBarHeight;
}
NSTimeInterval NIDeviceRotationDuration(BOOL isFlippingUpsideDown) {
return isFlippingUpsideDown ? 0.8 : 0.4;
}
UIEdgeInsets NICellContentPadding(void) {
return UIEdgeInsetsMake(10, 10, 10, 10);
}

194
Pods/Nimbus/src/core/src/NIDebuggingTools.h

@ -1,194 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Forked from Three20 June 10, 2011 - Copyright 2009-2011 Facebook
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
/**
* For inspecting code and writing to logs in debug builds.
*
* Nearly all of the following macros will only do anything if the DEBUG macro is defined.
* The recommended way to enable the debug tools is to specify DEBUG in the "Preprocessor Macros"
* field in your application's Debug target settings. Be careful not to set this for your release
* or app store builds because this will enable code that may cause your app to be rejected.
*
*
* <h2>Debug Assertions</h2>
*
* Debug assertions are a lightweight "sanity check". They won't crash the app, nor will they
* be included in release builds. They <i>will</i> halt the app's execution when debugging so
* that you can inspect the values that caused the failure.
*
* @code
* NIDASSERT(statement);
* @endcode
*
* If <i>statement</i> is false, the statement will be written to the log and if a debugger is
* attached, the app will break on the assertion line.
*
*
* <h2>Debug Logging</h2>
*
* @code
* NIDPRINT(@"formatted log text %d", param1);
* @endcode
*
* Print the given formatted text to the log.
*
* @code
* NIDPRINTMETHODNAME();
* @endcode
*
* Print the current method name to the log.
*
* @code
* NIDCONDITIONLOG(statement, @"formatted log text %d", param1);
* @endcode
*
* If statement is true, then the formatted text will be written to the log.
*
* @code
* NIDINFO/NIDWARNING/NIDERROR(@"formatted log text %d", param1);
* @endcode
*
* Will only write the formatted text to the log if NIMaxLogLevel is greater than the respective
* NID* method's log level. See below for log levels.
*
* The default maximum log level is NILOGLEVEL_WARNING.
*
* <h3>Turning up the log level while the app is running</h3>
*
* NIMaxLogLevel is declared a non-const extern so that you can modify it at runtime. This can
* be helpful for turning logging on while the application is running.
*
* @code
* NIMaxLogLevel = NILOGLEVEL_INFO;
* @endcode
*
* @ingroup NimbusCore
* @defgroup Debugging-Tools Debugging Tools
* @{
*/
#if defined(DEBUG) || defined(NI_DEBUG)
/**
* Assertions that only fire when DEBUG is defined.
*
* An assertion is like a programmatic breakpoint. Use it for sanity checks to save headache while
* writing your code.
*/
#import <TargetConditionals.h>
#if defined __cplusplus
extern "C" {
#endif
int NIIsInDebugger(void);
#if defined __cplusplus
}
#endif
#if TARGET_IPHONE_SIMULATOR
// We leave the __asm__ in this macro so that when a break occurs, we don't have to step out of
// a "breakInDebugger" function.
#define NIDASSERT(xx) { if (!(xx)) { NIDPRINT(@"NIDASSERT failed: %s", #xx); \
if (NIDebugAssertionsShouldBreak && NIIsInDebugger()) { __asm__("int $3\n" : : ); } } \
} ((void)0)
#else
#define NIDASSERT(xx) { if (!(xx)) { NIDPRINT(@"NIDASSERT failed: %s", #xx); \
if (NIDebugAssertionsShouldBreak && NIIsInDebugger()) { raise(SIGTRAP); } } \
} ((void)0)
#endif // #if TARGET_IPHONE_SIMULATOR
#else
#define NIDASSERT(xx) ((void)0)
#endif // #if defined(DEBUG) || defined(NI_DEBUG)
#define NILOGLEVEL_INFO 5
#define NILOGLEVEL_WARNING 3
#define NILOGLEVEL_ERROR 1
/**
* The maximum log level to output for Nimbus debug logs.
*
* This value may be changed at run-time.
*
* The default value is NILOGLEVEL_WARNING.
*/
extern NSInteger NIMaxLogLevel;
/**
* Whether or not debug assertions should halt program execution like a breakpoint when they fail.
*
* An example of when this is used is in unit tests, when failure cases are tested that will
* fire debug assertions.
*
* The default value is YES.
*/
extern BOOL NIDebugAssertionsShouldBreak;
/**
* Only writes to the log when DEBUG is defined.
*
* This log method will always write to the log, regardless of log levels. It is used by all
* of the other logging methods in Nimbus' debugging library.
*/
#if defined(DEBUG) || defined(NI_DEBUG)
#define NIDPRINT(xx, ...) NSLog(@"%s(%d): " xx, __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define NIDPRINT(xx, ...) ((void)0)
#endif // #if defined(DEBUG) || defined(NI_DEBUG)
/**
* Write the containing method's name to the log using NIDPRINT.
*/
#define NIDPRINTMETHODNAME() NIDPRINT(@"%s", __PRETTY_FUNCTION__)
#if defined(DEBUG) || defined(NI_DEBUG)
/**
* Only writes to the log if condition is satisified.
*
* This macro powers the level-based loggers. It can also be used for conditionally enabling
* families of logs.
*/
#define NIDCONDITIONLOG(condition, xx, ...) { if ((condition)) { NIDPRINT(xx, ##__VA_ARGS__); } \
} ((void)0)
#else
#define NIDCONDITIONLOG(condition, xx, ...) ((void)0)
#endif // #if defined(DEBUG) || defined(NI_DEBUG)
/**
* Only writes to the log if NIMaxLogLevel >= NILOGLEVEL_ERROR.
*/
#define NIDERROR(xx, ...) NIDCONDITIONLOG((NILOGLEVEL_ERROR <= NIMaxLogLevel), xx, ##__VA_ARGS__)
/**
* Only writes to the log if NIMaxLogLevel >= NILOGLEVEL_WARNING.
*/
#define NIDWARNING(xx, ...) NIDCONDITIONLOG((NILOGLEVEL_WARNING <= NIMaxLogLevel), \
xx, ##__VA_ARGS__)
/**
* Only writes to the log if NIMaxLogLevel >= NILOGLEVEL_INFO.
*/
#define NIDINFO(xx, ...) NIDCONDITIONLOG((NILOGLEVEL_INFO <= NIMaxLogLevel), xx, ##__VA_ARGS__)
/**@}*/// End of Debugging Tools //////////////////////////////////////////////////////////////////

61
Pods/Nimbus/src/core/src/NIDebuggingTools.m

@ -1,61 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
// Copyright 2009-2011 Facebook
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIDebuggingTools.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
NSInteger NIMaxLogLevel = NILOGLEVEL_WARNING;
BOOL NIDebugAssertionsShouldBreak = YES;
#if defined(DEBUG) || defined(NI_DEBUG)
#import <unistd.h>
#import <sys/sysctl.h>
// From: http://developer.apple.com/mac/library/qa/qa2004/qa1361.html
int NIIsInDebugger(void) {
int mib[4];
struct kinfo_proc info;
size_t size;
// Initialize the flags so that, if sysctl fails for some bizarre
// reason, we get a predictable result.
info.kp_proc.p_flag = 0;
// Initialize mib, which tells sysctl the info we want, in this case
// we're looking for information about a specific process ID.
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
// Call sysctl.
size = sizeof(info);
sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
// We're being debugged if the P_TRACED flag is set.
return (info.kp_proc.p_flag & P_TRACED) != 0;
}
#endif // #if defined(DEBUG) || defined(NI_DEBUG)

92
Pods/Nimbus/src/core/src/NIDeviceOrientation.h

@ -1,92 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Forked from Three20 June 10, 2011 - Copyright 2009-2011 Facebook
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "NIPreprocessorMacros.h"
#if defined __cplusplus
extern "C" {
#endif
/**
* For dealing with device orientations.
*
* <h2>Examples</h2>
*
* <h3>Use NIIsSupportedOrientation to Enable Autorotation</h3>
*
* @code
* - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
* return NIIsSupportedOrientation(toInterfaceOrientation);
* }
* @endcode
*
* @ingroup NimbusCore
* @defgroup Device-Orientation Device Orientation
* @{
*/
/**
* For use in shouldAutorotateToInterfaceOrientation:
*
* On iPhone/iPod touch:
*
* Returns YES if the orientation is portrait, landscape left, or landscape right.
* This helps to ignore upside down and flat orientations.
*
* On iPad:
*
* Always returns YES.
*/
BOOL NIIsSupportedOrientation(UIInterfaceOrientation orientation);
/**
* Returns the application's current interface orientation.
*
* This is simply a convenience method for [UIApplication sharedApplication].statusBarOrientation.
*
* @returns The current interface orientation.
*/
UIInterfaceOrientation NIInterfaceOrientation(void) NI_EXTENSION_UNAVAILABLE_IOS("");
/**
* Returns YES if the device is a phone and the orientation is landscape.
*
* This is a useful check for phone landscape mode which often requires
* additional logic to handle the smaller vertical real estate.
*
* @returns YES if the device is a phone and orientation is landscape.
*/
BOOL NIIsLandscapePhoneOrientation(UIInterfaceOrientation orientation);
/**
* Creates an affine transform for the given device orientation.
*
* This is useful for creating a transformation matrix for a view that has been added
* directly to the window and doesn't automatically have its transformation modified.
*/
CGAffineTransform NIRotateTransformForOrientation(UIInterfaceOrientation orientation);
#if defined __cplusplus
};
#endif
/**@}*/// End of Device Orientation ///////////////////////////////////////////////////////////////

76
Pods/Nimbus/src/core/src/NIDeviceOrientation.m

@ -1,76 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Forked from Three20 June 10, 2011 - Copyright 2009-2011 Facebook
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIDeviceOrientation.h"
#import <QuartzCore/QuartzCore.h>
#import "NIDebuggingTools.h"
#import "NISDKAvailability.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
BOOL NIIsSupportedOrientation(UIInterfaceOrientation orientation) {
if (NIIsPad()) {
return YES;
} else {
switch (orientation) {
case UIInterfaceOrientationPortrait:
case UIInterfaceOrientationLandscapeLeft:
case UIInterfaceOrientationLandscapeRight:
return YES;
default:
return NO;
}
}
}
UIInterfaceOrientation NIInterfaceOrientation(void) {
UIInterfaceOrientation orient = [UIApplication sharedApplication].statusBarOrientation;
// This code used to use the Three20 navigator to find the currently visible view controller and
// fall back to checking its orientation if we didn't know the status bar's orientation.
// It's unclear when this was actually necessary, though, so this assertion is here to try
// to find that case. If this assertion fails then the repro case needs to be analyzed and
// this method made more robust to handle that case.
NIDASSERT(UIDeviceOrientationUnknown != orient);
return orient;
}
BOOL NIIsLandscapePhoneOrientation(UIInterfaceOrientation orientation) {
return NIIsPhone() && UIInterfaceOrientationIsLandscape(orientation);
}
CGAffineTransform NIRotateTransformForOrientation(UIInterfaceOrientation orientation) {
if (orientation == UIInterfaceOrientationLandscapeLeft) {
return CGAffineTransformMakeRotation((CGFloat)(M_PI * 1.5));
} else if (orientation == UIInterfaceOrientationLandscapeRight) {
return CGAffineTransformMakeRotation((CGFloat)(M_PI / 2.0));
} else if (orientation == UIInterfaceOrientationPortraitUpsideDown) {
return CGAffineTransformMakeRotation((CGFloat)(-M_PI));
} else {
return CGAffineTransformIdentity;
}
}

54
Pods/Nimbus/src/core/src/NIError.h

@ -1,54 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
/**
* For defining various error types used throughout the Nimbus framework.
*
* @ingroup NimbusCore
* @defgroup Errors Errors
* @{
*/
/** The Nimbus error domain. */
extern NSString* const NINimbusErrorDomain;
/** The key used for images in the error's userInfo. */
extern NSString* const NIImageErrorKey;
/** NSError codes in NINimbusErrorDomain. */
typedef enum {
/** The image is too small to be used. */
NIImageTooSmall = 1,
} NINimbusErrorDomainCode;
/**@}*/// End of Errors ///////////////////////////////////////////////////////////////////////////
/**
* <h3>Example</h3>
*
* @code
* error = [NSError errorWithDomain: NINimbusErrorDomain
* code: NIImageTooSmall
* userInfo: [NSDictionary dictionaryWithObject: image
* forKey: NIImageErrorKey]];
* @endcode
*
* @enum NINimbusErrorDomainCode
*/

24
Pods/Nimbus/src/core/src/NIError.m

@ -1,24 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIError.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
NSString* const NINimbusErrorDomain = @"com.nimbus.error";
NSString* const NIImageErrorKey = @"image";

421
Pods/Nimbus/src/core/src/NIFoundationMethods.h

@ -1,421 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "NIPreprocessorMacros.h"
#if defined __cplusplus
extern "C" {
#endif
/**
* For filling in gaps in Apple's Foundation framework.
*
* @ingroup NimbusCore
* @defgroup Foundation-Methods Foundation Methods
* @{
*
* Utility methods save time and headache. You've probably written dozens of your own. Nimbus
* hopes to provide an ever-growing set of convenience methods that compliment the Foundation
* framework's functionality.
*/
#pragma mark - NSInvocation Methods
/**
* Construct an NSInvocation with an instance of an object and a selector
*
* @return an NSInvocation that will call the given selector on the given target
*/
NSInvocation* NIInvocationWithInstanceTarget(NSObject* target, SEL selector);
/**
* This method is deprecated. Please use NIInvocationWithInstanceTarget([object class], selector)
* instead.
*/
NSInvocation* NIInvocationWithClassTarget(Class targetClass, SEL selector) __NI_DEPRECATED_METHOD;
#pragma mark - CGRect Methods
/**
* For manipulating CGRects.
*
* @defgroup CGRect-Methods CGRect Methods
* @{
*
* These methods provide additional means of modifying the edges of CGRects beyond the basics
* included in CoreGraphics.
*/
/**
* Modifies only the right and bottom edges of a CGRect.
*
* @return a CGRect with dx and dy subtracted from the width and height.
*
* Example result: CGRectMake(x, y, w - dx, h - dy)
*/
CGRect NIRectContract(CGRect rect, CGFloat dx, CGFloat dy);
/**
* Modifies only the right and bottom edges of a CGRect.
*
* @return a CGRect with dx and dy added to the width and height.
*
* Example result: CGRectMake(x, y, w + dx, h + dy)
*/
CGRect NIRectExpand(CGRect rect, CGFloat dx, CGFloat dy);
/**
* Modifies only the top and left edges of a CGRect.
*
* @return a CGRect whose origin has been offset by dx, dy, and whose size has been
* contracted by dx, dy.
*
* Example result: CGRectMake(x + dx, y + dy, w - dx, h - dy)
*/
CGRect NIRectShift(CGRect rect, CGFloat dx, CGFloat dy);
/**
* Inverse of UIEdgeInsetsInsetRect.
*
* Example result: CGRectMake(x - left, y - top,
* w + left + right, h + top + bottom)
*/
CGRect NIEdgeInsetsOutsetRect(CGRect rect, UIEdgeInsets outsets);
/**
* Returns the x position that will center size within containerSize.
*
* Example result: floorf((containerSize.width - size.width) / 2.f)
*/
CGFloat NICenterX(CGSize containerSize, CGSize size);
/**
* Returns the y position that will center size within containerSize.
*
* Example result: floorf((containerSize.height - size.height) / 2.f)
*/
CGFloat NICenterY(CGSize containerSize, CGSize size);
/**
* Returns a rect that will center viewToCenter within containerView.
*
* @return a CGPoint that will center viewToCenter within containerView.
*/
CGRect NIFrameOfCenteredViewWithinView(UIView* viewToCenter, UIView* containerView);
/**
* Returns the size of the string with given UILabel properties.
*/
CGSize NISizeOfStringWithLabelProperties(NSString *string, CGSize constrainedToSize, UIFont *font, NSLineBreakMode lineBreakMode, NSInteger numberOfLines);
/**@}*/
#pragma mark - NSRange Methods
/**
* For manipulating NSRange.
*
* @defgroup NSRange-Methods NSRange Methods
* @{
*/
/**
* Create an NSRange object from a CFRange object.
*
* @return an NSRange object with the same values as the CFRange object.
*
* @attention This has the potential to behave unexpectedly because it converts the
* CFRange's long values to unsigned integers. Nimbus will fire off a debug
* assertion at runtime if the value will be chopped or the sign will change.
* Even though the assertion will fire, the method will still chop or change
* the sign of the values so you should take care to fix this.
*/
NSRange NIMakeNSRangeFromCFRange(CFRange range);
/**@}*/
#pragma mark - NSData Methods
/**
* For manipulating NSData.
*
* @defgroup NSData-Methods NSData Methods
* @{
*/
/**
* Calculates an md5 hash of the data using CC_MD5.
*/
NSString* NIMD5HashFromData(NSData* data);
/**
* Calculates a sha1 hash of the data using CC_SHA1.
*/
NSString* NISHA1HashFromData(NSData* data);
/**@}*/
#pragma mark - NSString Methods
/**
* For manipulating NSStrings.
*
* @defgroup NSString-Methods NSString Methods
* @{
*/
/**
* Calculates an md5 hash of the string using CC_MD5.
*
* Treats the string as UTF8.
*/
NSString* NIMD5HashFromString(NSString* string);
/**
* Calculates a sha1 hash of the string using CC_SHA1.
*
* Treats the string as UTF8.
*/
NSString* NISHA1HashFromString(NSString* string);
/**
* Returns a Boolean value indicating whether the string is a NSString object that contains only
* whitespace and newlines.
*/
BOOL NIIsStringWithWhitespaceAndNewlines(NSString* string);
/**
* Compares two strings expressing software versions.
*
* The comparison is (except for the development version provisions noted below) lexicographic
* string comparison. So as long as the strings being compared use consistent version formats,
* a variety of schemes are supported. For example "3.02" < "3.03" and "3.0.2" < "3.0.3". If you
* mix such schemes, like trying to compare "3.02" and "3.0.3", the result may not be what you
* expect.
*
* Development versions are also supported by adding an "a" character and more version info after
* it. For example "3.0a1" or "3.01a4". The way these are handled is as follows: if the parts
* before the "a" are different, the parts after the "a" are ignored. If the parts before the "a"
* are identical, the result of the comparison is the result of NUMERICALLY comparing the parts
* after the "a". If the part after the "a" is empty, it is treated as if it were "0". If one
* string has an "a" and the other does not (e.g. "3.0" and "3.0a1") the one without the "a"
* is newer.
*
* Examples (?? means undefined):
* @htmlonly
* <pre>
* "3.0" = "3.0"
* "3.0a2" = "3.0a2"
* "3.0" > "2.5"
* "3.1" > "3.0"
* "3.0a1" < "3.0"
* "3.0a1" < "3.0a4"
* "3.0a2" < "3.0a19" <-- numeric, not lexicographic
* "3.0a" < "3.0a1"
* "3.02" < "3.03"
* "3.0.2" < "3.0.3"
* "3.00" ?? "3.0"
* "3.02" ?? "3.0.3"
* "3.02" ?? "3.0.2"
* </pre>
* @endhtmlonly
*/
NSComparisonResult NICompareVersionStrings(NSString* string1, NSString* string2);
/**
* Parses a URL query string into a dictionary where the values are arrays.
*
* A query string is one that looks like &param1=value1&param2=value2...
*
* The resulting NSDictionary will contain keys for each parameter name present in the query.
* The value for each key will be an NSArray which may be empty if the key is simply present
* in the query. Otherwise each object in the array with be an NSString corresponding to a value
* in the query for that parameter.
*/
NSDictionary* NIQueryDictionaryFromStringUsingEncoding(NSString* string, NSStringEncoding encoding);
/**
* Returns a string that has been escaped for use as a URL parameter.
*/
NSString* NIStringByAddingPercentEscapesForURLParameterString(NSString* parameter);
/**
* Appends a dictionary of query parameters to a string, adding the ? character if necessary.
*/
NSString* NIStringByAddingQueryDictionaryToString(NSString* string, NSDictionary* query);
/**@}*/
#pragma mark - CGFloat Methods
/**
* For manipulating CGFloat.
*
* @defgroup CGFloat-Methods CGFloat Methods
* @{
*
* These methods provide math functions on CGFloats. They could easily be replaced with <tgmath.h>
* but that is currently (Xcode 5.0) incompatible with CLANG_ENABLE_MODULES (on by default for
* many projects/targets). We'll use CG_INLINE because this really should be completely inline.
*/
#if CGFLOAT_IS_DOUBLE
#define NI_CGFLOAT_EPSILON DBL_EPSILON
#else
#define NI_CGFLOAT_EPSILON FLT_EPSILON
#endif
/**
* fabs()/fabsf() sized for CGFloat
*/
CG_INLINE CGFloat NICGFloatAbs(CGFloat x) {
#if CGFLOAT_IS_DOUBLE
return (CGFloat)fabs(x);
#else
return (CGFloat)fabsf(x);
#endif
}
/**
* floor()/floorf() sized for CGFloat
*/
CG_INLINE CGFloat NICGFloatFloor(CGFloat x) {
#if CGFLOAT_IS_DOUBLE
return (CGFloat)floor(x);
#else
return (CGFloat)floorf(x);
#endif
}
/**
* ceil()/ceilf() sized for CGFloat
*/
CG_INLINE CGFloat NICGFloatCeil(CGFloat x) {
#if CGFLOAT_IS_DOUBLE
return (CGFloat)ceil(x);
#else
return (CGFloat)ceilf(x);
#endif
}
/**
* round()/roundf() sized for CGFloat
*/
CG_INLINE CGFloat NICGFloatRound(CGFloat x) {
#if CGFLOAT_IS_DOUBLE
return (CGFloat)round(x);
#else
return (CGFloat)roundf(x);
#endif
}
/**
* sqrt()/sqrtf() sized for CGFloat
*/
CG_INLINE CGFloat NICGFloatSqRt(CGFloat x) {
#if CGFLOAT_IS_DOUBLE
return (CGFloat)sqrt(x);
#else
return (CGFloat)sqrtf(x);
#endif
}
/**
* copysign()/copysignf() sized for CGFloat
*/
CG_INLINE CGFloat NICGFloatCopySign(CGFloat x, CGFloat y) {
#if CGFLOAT_IS_DOUBLE
return (CGFloat)copysign(x, y);
#else
return (CGFloat)copysignf(x, y);
#endif
}
/**
* pow()/powf() sized for CGFloat
*/
CG_INLINE CGFloat NICGFloatPow(CGFloat x, CGFloat y) {
#if CGFLOAT_IS_DOUBLE
return (CGFloat)pow(x, y);
#else
return (CGFloat)powf(x, y);
#endif
}
/**
* cos()/cosf() sized for CGFloat
*/
CG_INLINE CGFloat NICGFloatCos(CGFloat x) {
#if CGFLOAT_IS_DOUBLE
return (CGFloat)cos(x);
#else
return (CGFloat)cosf(x);
#endif
}
/**@}*/
#pragma mark - General Purpose Methods
/**
* For general purpose foundation type manipulation.
*
* @defgroup General-Purpose-Methods General Purpose Methods
* @{
*/
/**
* Deprecated method. Use NIBoundf instead.
*/
CGFloat boundf(CGFloat value, CGFloat min, CGFloat max) __NI_DEPRECATED_METHOD; // Use NIBoundf instead. MAINTENANCE: Remove by Feb 28, 2014.
/**
* Deprecated method. Use NIBoundi instead.
*/
NSInteger boundi(NSInteger value, NSInteger min, NSInteger max) __NI_DEPRECATED_METHOD; // Use NIBoundi instead. MAINTENANCE: Remove by Feb 28, 2014.
/**
* Bounds a given value within the min and max values.
*
* If max < min then value will be min.
*
* @returns min <= result <= max
*/
CGFloat NIBoundf(CGFloat value, CGFloat min, CGFloat max);
/**
* Bounds a given value within the min and max values.
*
* If max < min then value will be min.
*
* @returns min <= result <= max
*/
NSInteger NIBoundi(NSInteger value, NSInteger min, NSInteger max);
/**@}*/
#if defined __cplusplus
};
#endif
/**@}*/// End of Foundation Methods ///////////////////////////////////////////////////////////////

314
Pods/Nimbus/src/core/src/NIFoundationMethods.m

@ -1,314 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIFoundationMethods.h"
#import "NIDebuggingTools.h"
#import <CommonCrypto/CommonDigest.h>
#import <objc/runtime.h>
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
#pragma mark - NSInvocation
NSInvocation* NIInvocationWithInstanceTarget(NSObject *targetObject, SEL selector) {
NSMethodSignature* sig = [targetObject methodSignatureForSelector:selector];
NSInvocation* inv = [NSInvocation invocationWithMethodSignature:sig];
[inv setTarget:targetObject];
[inv setSelector:selector];
return inv;
}
// Deprecated. Please delete on the next minor version upgrade.
NSInvocation* NIInvocationWithClassTarget(Class targetClass, SEL selector) {
return NIInvocationWithInstanceTarget((NSObject *)targetClass, selector);
}
#pragma mark - CGRect
CGRect NIRectContract(CGRect rect, CGFloat dx, CGFloat dy) {
return CGRectMake(rect.origin.x, rect.origin.y, rect.size.width - dx, rect.size.height - dy);
}
CGRect NIRectExpand(CGRect rect, CGFloat dx, CGFloat dy) {
return CGRectMake(rect.origin.x, rect.origin.y, rect.size.width + dx, rect.size.height + dy);
}
CGRect NIRectShift(CGRect rect, CGFloat dx, CGFloat dy) {
return CGRectOffset(NIRectContract(rect, dx, dy), dx, dy);
}
CGRect NIEdgeInsetsOutsetRect(CGRect rect, UIEdgeInsets outsets) {
return CGRectMake(rect.origin.x - outsets.left,
rect.origin.y - outsets.top,
rect.size.width + outsets.left + outsets.right,
rect.size.height + outsets.top + outsets.bottom);
}
CGFloat NICenterX(CGSize containerSize, CGSize size) {
return NICGFloatFloor((containerSize.width - size.width) / 2.f);
}
CGFloat NICenterY(CGSize containerSize, CGSize size) {
return NICGFloatFloor((containerSize.height - size.height) / 2.f);
}
CGRect NIFrameOfCenteredViewWithinView(UIView* viewToCenter, UIView* containerView) {
CGPoint origin;
CGSize containerViewSize = containerView.bounds.size;
CGSize viewSize = viewToCenter.frame.size;
origin.x = NICenterX(containerViewSize, viewSize);
origin.y = NICenterY(containerViewSize, viewSize);
return CGRectMake(origin.x, origin.y, viewSize.width, viewSize.height);
}
CGSize NISizeOfStringWithLabelProperties(NSString *string, CGSize constrainedToSize, UIFont *font, NSLineBreakMode lineBreakMode, NSInteger numberOfLines) {
if (string.length == 0) {
return CGSizeZero;
}
CGFloat lineHeight = font.lineHeight;
CGSize size = CGSizeZero;
if (numberOfLines == 1) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
size = [string sizeWithFont:font forWidth:constrainedToSize.width lineBreakMode:lineBreakMode];
#pragma clang diagnostic pop
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
size = [string sizeWithFont:font constrainedToSize:constrainedToSize lineBreakMode:lineBreakMode];
#pragma clang diagnostic pop
if (numberOfLines > 0) {
size.height = MIN(size.height, numberOfLines * lineHeight);
}
}
return size;
}
#pragma mark - NSRange
NSRange NIMakeNSRangeFromCFRange(CFRange range) {
// CFRange stores its values in signed longs, but we're about to copy the values into
// unsigned integers, let's check whether we're about to lose any information.
NIDASSERT(range.location >= 0 && range.location <= NSIntegerMax);
NIDASSERT(range.length >= 0 && range.length <= NSIntegerMax);
return NSMakeRange(range.location, range.length);
}
#pragma mark - NSData
NSString* NIMD5HashFromData(NSData* data) {
unsigned char result[CC_MD5_DIGEST_LENGTH];
bzero(result, sizeof(result));
CC_MD5_CTX md5Context;
CC_MD5_Init(&md5Context);
size_t bytesHashed = 0;
while (bytesHashed < [data length]) {
CC_LONG updateSize = 1024 * 1024;
if (([data length] - bytesHashed) < (size_t)updateSize) {
updateSize = (CC_LONG)([data length] - bytesHashed);
}
CC_MD5_Update(&md5Context, (char *)[data bytes] + bytesHashed, updateSize);
bytesHashed += updateSize;
}
CC_MD5_Final(result, &md5Context);
return [NSString stringWithFormat:
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11], result[12], result[13], result[14],
result[15]
];
}
NSString* NISHA1HashFromData(NSData* data) {
unsigned char result[CC_SHA1_DIGEST_LENGTH];
bzero(result, sizeof(result));
CC_SHA1_CTX sha1Context;
CC_SHA1_Init(&sha1Context);
size_t bytesHashed = 0;
while (bytesHashed < [data length]) {
CC_LONG updateSize = 1024 * 1024;
if (([data length] - bytesHashed) < (size_t)updateSize) {
updateSize = (CC_LONG)([data length] - bytesHashed);
}
CC_SHA1_Update(&sha1Context, (char *)[data bytes] + bytesHashed, updateSize);
bytesHashed += updateSize;
}
CC_SHA1_Final(result, &sha1Context);
return [NSString stringWithFormat:
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11], result[12], result[13], result[14],
result[15], result[16], result[17], result[18], result[19]
];
}
#pragma mark - NSString
NSString* NIMD5HashFromString(NSString* string) {
return NIMD5HashFromData([string dataUsingEncoding:NSUTF8StringEncoding]);
}
NSString* NISHA1HashFromString(NSString* string) {
return NISHA1HashFromData([string dataUsingEncoding:NSUTF8StringEncoding]);
}
BOOL NIIsStringWithWhitespaceAndNewlines(NSString* string) {
NSCharacterSet* notWhitespaceAndNewlines = [[NSCharacterSet whitespaceAndNewlineCharacterSet] invertedSet];
return [string isKindOfClass:[NSString class]] && [string rangeOfCharacterFromSet:notWhitespaceAndNewlines].length == 0;
}
NSComparisonResult NICompareVersionStrings(NSString* string1, NSString* string2) {
NSArray *oneComponents = [string1 componentsSeparatedByString:@"a"];
NSArray *twoComponents = [string2 componentsSeparatedByString:@"a"];
// The parts before the "a"
NSString *oneMain = [oneComponents objectAtIndex:0];
NSString *twoMain = [twoComponents objectAtIndex:0];
// If main parts are different, return that result, regardless of alpha part
NSComparisonResult mainDiff;
if ((mainDiff = [oneMain compare:twoMain]) != NSOrderedSame) {
return mainDiff;
}
// At this point the main parts are the same; just deal with alpha stuff
// If one has an alpha part and the other doesn't, the one without is newer
if ([oneComponents count] < [twoComponents count]) {
return NSOrderedDescending;
} else if ([oneComponents count] > [twoComponents count]) {
return NSOrderedAscending;
} else if ([oneComponents count] == 1) {
// Neither has an alpha part, and we know the main parts are the same
return NSOrderedSame;
}
// At this point the main parts are the same and both have alpha parts. Compare the alpha parts
// numerically. If it's not a valid number (including empty string) it's treated as zero.
NSNumber *oneAlpha = [NSNumber numberWithInt:[[oneComponents objectAtIndex:1] intValue]];
NSNumber *twoAlpha = [NSNumber numberWithInt:[[twoComponents objectAtIndex:1] intValue]];
return [oneAlpha compare:twoAlpha];
}
NSDictionary* NIQueryDictionaryFromStringUsingEncoding(NSString* string, NSStringEncoding encoding) {
NSCharacterSet* delimiterSet = [NSCharacterSet characterSetWithCharactersInString:@"&;"];
NSMutableDictionary* pairs = [NSMutableDictionary dictionary];
NSScanner* scanner = [[NSScanner alloc] initWithString:string];
while (![scanner isAtEnd]) {
NSString* pairString = nil;
[scanner scanUpToCharactersFromSet:delimiterSet intoString:&pairString];
[scanner scanCharactersFromSet:delimiterSet intoString:NULL];
NSArray* kvPair = [pairString componentsSeparatedByString:@"="];
if (kvPair.count == 1 || kvPair.count == 2) {
NSString* key = [kvPair[0] stringByReplacingPercentEscapesUsingEncoding:encoding];
NSMutableArray* values = pairs[key];
if (nil == values) {
values = [NSMutableArray array];
pairs[key] = values;
}
if (kvPair.count == 1) {
[values addObject:[NSNull null]];
} else if (kvPair.count == 2) {
NSString* value = [kvPair[1] stringByReplacingPercentEscapesUsingEncoding:encoding];
[values addObject:value];
}
}
}
return [pairs copy];
}
NSString* NIStringByAddingPercentEscapesForURLParameterString(NSString* parameter) {
CFStringRef buffer = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(__bridge CFStringRef)parameter,
NULL,
(__bridge CFStringRef)@"!*'();:@&=+$,/?%#[]",
kCFStringEncodingUTF8);
NSString* result = [NSString stringWithString:(__bridge NSString *)buffer];
CFRelease(buffer);
return result;
}
NSString* NIStringByAddingQueryDictionaryToString(NSString* string, NSDictionary* query) {
NSMutableArray* pairs = [NSMutableArray array];
for (NSString* key in [query keyEnumerator]) {
NSString* value = NIStringByAddingPercentEscapesForURLParameterString([query objectForKey:key]);
NSString* pair = [NSString stringWithFormat:@"%@=%@", key, value];
[pairs addObject:pair];
}
NSString* params = [pairs componentsJoinedByString:@"&"];
if ([string rangeOfString:@"?"].location == NSNotFound) {
return [string stringByAppendingFormat:@"?%@", params];
} else {
return [string stringByAppendingFormat:@"&%@", params];
}
}
#pragma mark - General Purpose
// Deprecated.
CGFloat boundf(CGFloat value, CGFloat min, CGFloat max) {
return NIBoundf(value, min, max);
}
// Deprecated.
NSInteger boundi(NSInteger value, NSInteger min, NSInteger max) {
return NIBoundi(value, min, max);
}
CGFloat NIBoundf(CGFloat value, CGFloat min, CGFloat max) {
if (max < min) {
max = min;
}
CGFloat bounded = value;
if (bounded > max) {
bounded = max;
}
if (bounded < min) {
bounded = min;
}
return bounded;
}
NSInteger NIBoundi(NSInteger value, NSInteger min, NSInteger max) {
if (max < min) {
max = min;
}
NSInteger bounded = value;
if (bounded > max) {
bounded = max;
}
if (bounded < min) {
bounded = min;
}
return bounded;
}

48
Pods/Nimbus/src/core/src/NIImageUtilities.h

@ -1,48 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#if defined __cplusplus
extern "C" {
#endif
/**
* For manipulating UIImage objects.
*
* @ingroup NimbusCore
* @defgroup Image-Utilities Image Utilities
* @{
*/
/**
* Returns an image that is stretchable from the center.
*
* A common use of this method is to create an image that has rounded corners for a button
* and then assign a stretchable version of that image to a UIButton.
*
* This stretches the middle vertical and horizontal line of pixels, so use care when
* stretching images that have gradients. For example, an image with a vertical gradient
* can be stretched horizontally, but will look odd if stretched vertically.
*/
UIImage* NIStretchableImageFromImage(UIImage* image);
/**@}*/// End of Image Utilities //////////////////////////////////////////////////////////////////
#if defined __cplusplus
};
#endif

24
Pods/Nimbus/src/core/src/NIImageUtilities.m

@ -1,24 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIImageUtilities.h"
UIImage* NIStretchableImageFromImage(UIImage* image) {
const CGSize size = image.size;
NSInteger midX = (NSInteger)(size.width / 2.f);
NSInteger midY = (NSInteger)(size.height / 2.f);
return [image stretchableImageWithLeftCapWidth:midX topCapHeight:midY];
}

316
Pods/Nimbus/src/core/src/NIInMemoryCache.h

@ -1,316 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#import "NIPreprocessorMacros.h"
/**
* For storing and accessing objects in memory.
*
* The base class, NIMemoryCache, is a generic object store that may be used for anything that
* requires support for expiration.
*
* @ingroup NimbusCore
* @defgroup In-Memory-Caches In-Memory Caches
* @{
*/
/**
* An in-memory cache for storing objects with expiration support.
*
* The Nimbus in-memory object cache allows you to store objects in memory with an expiration
* date attached. Objects with expiration dates drop out of the cache when they have expired.
*/
@interface NIMemoryCache : NSObject
// Designated initializer.
- (id)initWithCapacity:(NSUInteger)capacity;
- (NSUInteger)count;
- (void)storeObject:(id)object withName:(NSString *)name;
- (void)storeObject:(id)object withName:(NSString *)name expiresAfter:(NSDate *)expirationDate;
- (void)removeObjectWithName:(NSString *)name;
- (void)removeAllObjectsWithPrefix:(NSString *)prefix;
- (void)removeAllObjects;
- (id)objectWithName:(NSString *)name;
- (BOOL)containsObjectWithName:(NSString *)name;
- (NSDate *)dateOfLastAccessWithName:(NSString *)name;
- (NSString *)nameOfLeastRecentlyUsedObject;
- (NSString *)nameOfMostRecentlyUsedObject;
- (void)reduceMemoryUsage;
// Subclassing
- (BOOL)shouldSetObject:(id)object withName:(NSString *)name previousObject:(id)previousObject;
- (void)didSetObject:(id)object withName:(NSString *)name;
- (void)willRemoveObject:(id)object withName:(NSString *)name;
// Deprecated method. Use shouldSetObject:withName:previousObject: instead.
- (BOOL)willSetObject:(id)object withName:(NSString *)name previousObject:(id)previousObject __NI_DEPRECATED_METHOD;
@end
/**
* An in-memory cache for storing images with caps on the total number of pixels.
*
* When reduceMemoryUsage is called, the least recently used images are removed from the cache
* until the numberOfPixels is below maxNumberOfPixelsUnderStress.
*
* When an image is added to the cache that causes the memory usage to pass the max, the
* least recently used images are removed from the cache until the numberOfPixels is below
* maxNumberOfPixels.
*
* By default the image memory cache has no limit to its pixel count. You must explicitly
* set this value in your application.
*
* @attention If the cache is too small to fit the newly added image, then all images
* will end up being removed including the one being added.
*
* @see Nimbus::imageMemoryCache
* @see Nimbus::setImageMemoryCache:
*/
@interface NIImageMemoryCache : NIMemoryCache
@property (nonatomic, readonly) unsigned long long numberOfPixels;
@property (nonatomic) unsigned long long maxNumberOfPixels; // Default: 0 (unlimited)
@property (nonatomic) unsigned long long maxNumberOfPixelsUnderStress; // Default: 0 (unlimited)
@end
/**@}*/// End of In-Memory Cache //////////////////////////////////////////////////////////////////
/** @name Creating an In-Memory Cache */
/**
* Initializes a newly allocated cache with the given capacity.
*
* @returns An in-memory cache initialized with the given capacity.
* @fn NIMemoryCache::initWithCapacity:
*/
/** @name Storing Objects in the Cache */
/**
* Stores an object in the cache.
*
* The object will be stored without an expiration date. The object will stay in the cache until
* it's bumped out due to the cache's memory limit.
*
* @param object The object being stored in the cache.
* @param name The name used as a key to store this object.
* @fn NIMemoryCache::storeObject:withName:
*/
/**
* Stores an object in the cache with an expiration date.
*
* If an object is stored with an expiration date that has already passed then the object will
* not be stored in the cache and any existing object will be removed. The rationale behind this
* is that the object would be removed from the cache the next time it was accessed anyway.
*
* @param object The object being stored in the cache.
* @param name The name used as a key to store this object.
* @param expirationDate A date after which this object is no longer valid in the cache.
* @fn NIMemoryCache::storeObject:withName:expiresAfter:
*/
/** @name Removing Objects from the Cache */
/**
* Removes an object from the cache with the given name.
*
* @param name The name used as a key to store this object.
* @fn NIMemoryCache::removeObjectWithName:
*/
/**
* Removes all objects from the cache with a given prefix.
*
* This method requires a scan of the cache entries.
*
* @param prefix Any object name that has this prefix will be removed from the cache.
* @fn NIMemoryCache::removeAllObjectsWithPrefix:
*/
/**
* Removes all objects from the cache, regardless of expiration dates.
*
* This will completely clear out the cache and all objects in the cache will be released.
*
* @fn NIMemoryCache::removeAllObjects
*/
/** @name Accessing Objects in the Cache */
/**
* Retrieves an object from the cache.
*
* If the object has expired then the object will be removed from the cache and nil will be
* returned.
*
* @returns The object stored in the cache. The object is retained and autoreleased to
* ensure that it survives this run loop if you then remove it from the cache.
* @fn NIMemoryCache::objectWithName:
*/
/**
* Returns a Boolean value that indicates whether an object with the given name is present
* in the cache.
*
* Does not update the access time of the object.
*
* If the object has expired then the object will be removed from the cache and NO will be
* returned.
*
* @returns YES if an object with the given name is present in the cache and has not expired,
* otherwise NO.
* @fn NIMemoryCache::containsObjectWithName:
*/
/**
* Returns the date that the object with the given name was last accessed.
*
* Does not update the access time of the object.
*
* If the object has expired then the object will be removed from the cache and nil will be
* returned.
*
* @returns The last access date of the object if it exists and has not expired, nil
* otherwise.
* @fn NIMemoryCache::dateOfLastAccessWithName:
*/
/**
* Retrieve the name of the object that was least recently used.
*
* This will not update the access time of the object.
*
* If the cache is empty, returns nil.
*
* @fn NIMemoryCache::nameOfLeastRecentlyUsedObject
*/
/**
* Retrieve the key with the most fresh access.
*
* This will not update the access time of the object.
*
* If the cache is empty, returns nil.
*
* @fn NIMemoryCache::nameOfMostRecentlyUsedObject
*/
/** @name Reducing Memory Usage Explicitly */
/**
* Removes all expired objects from the cache.
*
* Subclasses may add additional functionality to this implementation.
* Subclasses should call super in order to prune expired objects.
*
* This will be called when <code>UIApplicationDidReceiveMemoryWarningNotification</code>
* is posted.
*
* @fn NIMemoryCache::reduceMemoryUsage
*/
/** @name Querying an In-Memory Cache */
/**
* Returns the number of objects currently in the cache.
*
* @returns The number of objects currently in the cache.
* @fn NIMemoryCache::count
*/
/**
* @name Subclassing
*
* The following methods are provided to aid in subclassing and are not meant to be
* used externally.
*/
/**
* An object is about to be stored in the cache.
*
* @param object The object that is about to be stored in the cache.
* @param name The cache name for the object.
* @param previousObject The object previously stored in the cache. This may be the
* same as object.
* @returns YES If object is allowed to be stored in the cache.
* @fn NIMemoryCache::shouldSetObject:withName:previousObject:
*/
/**
* This method is deprecated. Please use shouldSetObject:withName:previousObject: instead.
*
* @fn NIMemoryCache::willSetObject:withName:previousObject:
*/
/**
* An object has been stored in the cache.
*
* @param object The object that was stored in the cache.
* @param name The cache name for the object.
* @fn NIMemoryCache::didSetObject:withName:
*/
/**
* An object is about to be removed from the cache.
*
* @param object The object about to removed from the cache.
* @param name The cache name for the object about to be removed.
* @fn NIMemoryCache::willRemoveObject:withName:
*/
// NIImageMemoryCache
/** @name Querying an In-Memory Image Cache */
/**
* Returns the total number of pixels being stored in the cache.
*
* @returns The total number of pixels being stored in the cache.
* @fn NIImageMemoryCache::numberOfPixels
*/
/** @name Setting the Maximum Number of Pixels */
/**
* The maximum number of pixels this cache may ever store.
*
* Defaults to 0, which is special cased to represent an unlimited number of pixels.
*
* @returns The maximum number of pixels this cache may ever store.
* @fn NIImageMemoryCache::maxNumberOfPixels
*/
/**
* The maximum number of pixels this cache may store after a call to reduceMemoryUsage.
*
* Defaults to 0, which is special cased to represent an unlimited number of pixels.
*
* @returns The maximum number of pixels this cache may store after a call
* to reduceMemoryUsage.
* @fn NIImageMemoryCache::maxNumberOfPixelsUnderStress
*/

453
Pods/Nimbus/src/core/src/NIInMemoryCache.m

@ -1,453 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIInMemoryCache.h"
#import "NIDebuggingTools.h"
#import "NIPreprocessorMacros.h"
#import <UIKit/UIKit.h>
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
@interface NIMemoryCache()
// Mapping from a name (usually a URL) to an internal object.
@property (nonatomic, strong) NSMutableDictionary* cacheMap;
// A linked list of least recently used cache objects. Most recently used is the tail.
@property (nonatomic, strong) NSMutableOrderedSet* lruCacheObjects;
@end
/**
* @brief A single cache item's information.
*
* Used in expiration calculations and for storing the actual cache object.
*/
@interface NIMemoryCacheInfo : NSObject
/**
* @brief The name used to store this object in the cache.
*/
@property (nonatomic, copy) NSString* name;
/**
* @brief The object stored in the cache.
*/
@property (nonatomic, strong) id object;
/**
* @brief The date after which the image is no longer valid and should be removed from the cache.
*/
@property (nonatomic, strong) NSDate* expirationDate;
/**
* @brief The last time this image was accessed.
*
* This property is updated every time the image is fetched from or stored into the cache. It
* is used when the memory peak has been reached as a fast means of removing least-recently-used
* images. When the memory limit is reached, we sort the cache based on the last access times and
* then prune images until we're under the memory limit again.
*/
@property (nonatomic, strong) NSDate* lastAccessTime;
/**
* @brief Determine whether this cache entry has past its expiration date.
*
* @returns YES if an expiration date has been specified and the expiration date has been passed.
* NO in all other cases. Notably if there is no expiration date then this object will
* never expire.
*/
- (BOOL)hasExpired;
@end
@implementation NIMemoryCache
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (id)init {
return [self initWithCapacity:0];
}
- (id)initWithCapacity:(NSUInteger)capacity {
if ((self = [super init])) {
_cacheMap = [[NSMutableDictionary alloc] initWithCapacity:capacity];
_lruCacheObjects = [NSMutableOrderedSet orderedSet];
// Automatically reduce memory usage when we get a memory warning.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(reduceMemoryUsage)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
}
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:
@"<%@"
@" lruObjects: %@"
@" cache map: %@"
@">",
[super description],
self.lruCacheObjects,
self.cacheMap];
}
#pragma mark - Internal
- (void)updateAccessTimeForInfo:(NIMemoryCacheInfo *)info {
@synchronized(self) {
NIDASSERT(nil != info);
if (nil == info) {
return; // COV_NF_LINE
}
info.lastAccessTime = [NSDate date];
[self.lruCacheObjects removeObject:info];
[self.lruCacheObjects addObject:info];
}
}
- (NIMemoryCacheInfo *)cacheInfoForName:(NSString *)name {
NIMemoryCacheInfo* info;
@synchronized(self) {
info = self.cacheMap[name];
}
return info;
}
- (void)setCacheInfo:(NIMemoryCacheInfo *)info forName:(NSString *)name {
@synchronized(self) {
NIDASSERT(nil != name);
if (nil == name) {
return;
}
// Storing in the cache counts as an access of the object, so we update the access time.
[self updateAccessTimeForInfo:info];
id previousObject = [self cacheInfoForName:name].object;
if ([self shouldSetObject:info.object withName:name previousObject:previousObject]) {
self.cacheMap[name] = info;
[self didSetObject:info.object withName:name];
}
}
}
- (void)removeCacheInfoForName:(NSString *)name {
@synchronized(self) {
NIDASSERT(nil != name);
if (nil == name) {
return;
}
NIMemoryCacheInfo* cacheInfo = [self cacheInfoForName:name];
[self willRemoveObject:cacheInfo.object withName:name];
[self.lruCacheObjects removeObject:cacheInfo];
[self.cacheMap removeObjectForKey:name];
}
}
#pragma mark - Subclassing
// Deprecated method.
- (BOOL)willSetObject:(id)object withName:(NSString *)name previousObject:(id)previousObject {
return [self shouldSetObject:object withName:name previousObject:previousObject];
}
- (BOOL)shouldSetObject:(id)object withName:(NSString *)name previousObject:(id)previousObject {
// Allow anything to be stored.
return YES;
}
- (void)didSetObject:(id)object withName:(NSString *)name {
// No-op
}
- (void)willRemoveObject:(id)object withName:(NSString *)name {
// No-op
}
#pragma mark - Public
- (void)storeObject:(id)object withName:(NSString *)name {
@synchronized(self) {
[self storeObject:object withName:name expiresAfter:nil];
}
}
- (void)storeObject:(id)object withName:(NSString *)name expiresAfter:(NSDate *)expirationDate {
@synchronized(self) {
// Don't store nil objects in the cache.
if (nil == object) {
return;
}
if (nil != expirationDate && [[NSDate date] timeIntervalSinceDate:expirationDate] >= 0) {
// The object being stored is already expired so remove the object from the cache altogether.
[self removeObjectWithName:name];
// We're done here.
return;
}
NIMemoryCacheInfo* info = [self cacheInfoForName:name];
// Create a new cache entry.
if (nil == info) {
info = [[NIMemoryCacheInfo alloc] init];
info.name = name;
}
// Store the object in the cache item.
info.object = object;
// Override any existing expiration date.
info.expirationDate = expirationDate;
// Commit the changes to the cache.
[self setCacheInfo:info forName:name];
}
}
- (id)objectWithName:(NSString *)name {
@synchronized(self) {
NIMemoryCacheInfo* info = [self cacheInfoForName:name];
id object = nil;
if (nil != info) {
if ([info hasExpired]) {
[self removeObjectWithName:name];
} else {
// Update the access time whenever we fetch an object from the cache.
[self updateAccessTimeForInfo:info];
object = info.object;
}
}
return object;
}
}
- (BOOL)containsObjectWithName:(NSString *)name {
@synchronized(self) {
NIMemoryCacheInfo* info = [self cacheInfoForName:name];
if ([info hasExpired]) {
[self removeObjectWithName:name];
return NO;
}
return (nil != info);
}
}
- (NSDate *)dateOfLastAccessWithName:(NSString *)name {
@synchronized(self) {
NIMemoryCacheInfo* info = [self cacheInfoForName:name];
if ([info hasExpired]) {
[self removeObjectWithName:name];
return nil;
}
return [info lastAccessTime];
}
}
- (NSString *)nameOfLeastRecentlyUsedObject {
@synchronized(self) {
NIMemoryCacheInfo* info = [self.lruCacheObjects firstObject];
if ([info hasExpired]) {
[self removeObjectWithName:info.name];
return nil;
}
return info.name;
}
}
- (NSString *)nameOfMostRecentlyUsedObject {
@synchronized(self) {
NIMemoryCacheInfo* info = [self.lruCacheObjects lastObject];
if ([info hasExpired]) {
[self removeObjectWithName:info.name];
return nil;
}
return info.name;
}
}
- (void)removeObjectWithName:(NSString *)name {
@synchronized(self) {
[self removeCacheInfoForName:name];
}
}
- (void)removeAllObjectsWithPrefix:(NSString *)prefix {
@synchronized(self) {
// Assertions fire if you try to modify the object you're iterating over, so we make a copy.
for (NSString* name in [self.cacheMap copy]) {
if ([name hasPrefix:prefix]) {
[self removeObjectWithName:name];
}
}
}
}
- (void)removeAllObjects {
@synchronized(self) {
[self.cacheMap removeAllObjects];
[self.lruCacheObjects removeAllObjects];
}
}
- (void)reduceMemoryUsage {
@synchronized(self) {
// Assertions fire if you try to modify the object you're iterating over, so we make a copy.
for (id name in [self.cacheMap copy]) {
NIMemoryCacheInfo* info = [self cacheInfoForName:name];
if ([info hasExpired]) {
[self removeCacheInfoForName:name];
}
}
}
}
- (NSUInteger)count {
@synchronized(self) {
return self.cacheMap.count;
}
}
@end
@implementation NIMemoryCacheInfo
- (BOOL)hasExpired {
return (nil != _expirationDate
&& [[NSDate date] timeIntervalSinceDate:_expirationDate] >= 0);
}
- (NSString *)description {
return [NSString stringWithFormat:
@"<%@"
@" name: %@"
@" object: %@"
@" expiration date: %@"
@" last access time: %@"
@">",
[super description],
self.name,
self.object,
self.expirationDate,
self.lastAccessTime];
}
@end
@interface NIImageMemoryCache()
@property (nonatomic, assign) unsigned long long numberOfPixels;
@end
@implementation NIImageMemoryCache
- (unsigned long long)numberOfPixelsUsedByImage:(UIImage *)image {
@synchronized(self) {
if (nil == image) {
return 0;
}
return (unsigned long long)(image.size.width * image.size.height * [image scale] * [image scale]);
}
}
- (void)removeAllObjects {
@synchronized(self) {
[super removeAllObjects];
self.numberOfPixels = 0;
}
}
- (void)reduceMemoryUsage {
@synchronized(self) {
// Remove all expired images first.
[super reduceMemoryUsage];
if (self.maxNumberOfPixelsUnderStress > 0) {
// Remove the least recently used images by iterating over the linked list.
while (self.numberOfPixels > self.maxNumberOfPixelsUnderStress) {
NIMemoryCacheInfo* info = [self.lruCacheObjects firstObject];
[self removeCacheInfoForName:info.name];
}
}
}
}
- (BOOL)shouldSetObject:(id)object withName:(NSString *)name previousObject:(id)previousObject {
@synchronized(self) {
NIDASSERT(nil == object || [object isKindOfClass:[UIImage class]]);
if (![object isKindOfClass:[UIImage class]]) {
return NO;
}
_numberOfPixels -= [self numberOfPixelsUsedByImage:previousObject];
_numberOfPixels += [self numberOfPixelsUsedByImage:object];
return YES;
}
}
- (void)didSetObject:(id)object withName:(NSString *)name {
@synchronized(self) {
// Reduce the cache size after the object has been set in case the cache size is smaller
// than the object that's being added and we need to remove this object right away.
if (self.maxNumberOfPixels > 0) {
// Remove least recently used images until we satisfy our memory constraints.
while (self.numberOfPixels > self.maxNumberOfPixels) {
NIMemoryCacheInfo* info = [self.lruCacheObjects firstObject];
[self removeCacheInfoForName:info.name];
}
}
}
}
- (void)willRemoveObject:(id)object withName:(NSString *)name {
@synchronized(self) {
NIDASSERT(nil == object || [object isKindOfClass:[UIImage class]]);
if (nil == object || ![object isKindOfClass:[UIImage class]]) {
return;
}
self.numberOfPixels -= [self numberOfPixelsUsedByImage:object];
}
}
@end

101
Pods/Nimbus/src/core/src/NINetworkActivity.h

@ -1,101 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Forked from Three20 July 2, 2011 - Copyright 2009-2011 Facebook
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#import "NIPreprocessorMacros.h"
#if defined __cplusplus
extern "C" {
#endif
/**
* For showing network activity in the device's status bar.
*
* @ingroup NimbusCore
* @defgroup Network-Activity Network Activity
* @{
*
* Two methods for keeping track of all active network tasks. These methods are threadsafe
* and act as a simple counter. When the counter is positive, the network activity indicator
* is displayed.
*/
/**
* Increment the number of active network tasks.
*
* The status bar activity indicator will be spinning while there are active tasks.
*
* This method is threadsafe.
*/
void NINetworkActivityTaskDidStart(void) NI_EXTENSION_UNAVAILABLE_IOS("");
/**
* Decrement the number of active network tasks.
*
* The status bar activity indicator will be spinning while there are active tasks.
*
* This method is threadsafe.
*/
void NINetworkActivityTaskDidFinish(void);
/**
* @name For Debugging Only
* @{
*
* Methods that will only do anything interesting if the DEBUG preprocessor macro is defined.
*/
/**
* Enable network activity debugging.
*
* @attention This won't do anything unless the DEBUG preprocessor macro is defined.
*
* The Nimbus network activity methods will only work correctly if they are the only methods to
* touch networkActivityIndicatorVisible. If you are using another library that touches
* networkActivityIndicatorVisible then the network activity indicator might not accurately
* represent its state.
*
* When enabled, the networkActivityIndicatorVisible method on UIApplication will be swizzled
* with a debugging method that checks the global network task count and verifies that state
* is maintained correctly. If it is found that networkActivityIndicatorVisible is being accessed
* directly, then an assertion will be fired.
*
* If debugging was previously enabled, this does nothing.
*/
void NIEnableNetworkActivityDebugging(void);
/**
* Disable network activity debugging.
*
* @attention This won't do anything unless the DEBUG preprocessor macro is defined.
*
* When disabled, the networkActivityIndicatorVisible will be restored if this was previously
* enabled, otherwise this method does nothing.
*
* If debugging wasn't previously enabled, this does nothing.
*/
void NIDisableNetworkActivityDebugging(void);
/**@}*/// End of For Debugging Only
#if defined __cplusplus
};
#endif
/**@}*/// End of Network Activity /////////////////////////////////////////////////////////////////

171
Pods/Nimbus/src/core/src/NINetworkActivity.m

@ -1,171 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NINetworkActivity.h"
#import "NIDebuggingTools.h"
#import "NIPreprocessorMacros.h"
#if defined(DEBUG) || defined(NI_DEBUG)
#import "NIRuntimeClassModifications.h"
#endif
#import <pthread.h>
#import <UIKit/UIKit.h>
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
static int gNetworkTaskCount = 0;
static pthread_mutex_t gMutex = PTHREAD_MUTEX_INITIALIZER;
static const NSTimeInterval kDelayBeforeDisablingActivity = 0.1;
static NSTimer* gScheduledDelayTimer = nil;
@interface NINetworkActivity : NSObject
@end
@implementation NINetworkActivity
// Called after a certain amount of time has passed since all network activity has stopped.
// By delaying the turnoff of the network activity we avoid "flickering" effects when network
// activity is starting and stopping rapidly.
+ (void)disableNetworkActivity NI_EXTENSION_UNAVAILABLE_IOS("") {
pthread_mutex_lock(&gMutex);
if (nil != gScheduledDelayTimer) {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
gScheduledDelayTimer = nil;
}
pthread_mutex_unlock(&gMutex);
}
@end
void NINetworkActivityTaskDidStart(void) {
pthread_mutex_lock(&gMutex);
BOOL enableNetworkActivityIndicator = (0 == gNetworkTaskCount);
++gNetworkTaskCount;
[gScheduledDelayTimer invalidate];
gScheduledDelayTimer = nil;
if (enableNetworkActivityIndicator) {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}
pthread_mutex_unlock(&gMutex);
}
void NINetworkActivityTaskDidFinish(void) {
pthread_mutex_lock(&gMutex);
--gNetworkTaskCount;
// If this asserts, you don't have enough stop requests to match your start requests.
NIDASSERT(gNetworkTaskCount >= 0);
gNetworkTaskCount = MAX(0, gNetworkTaskCount);
if (gNetworkTaskCount == 0) {
[gScheduledDelayTimer invalidate];
gScheduledDelayTimer = nil;
// Ensure that the timer is scheduled on the main loop, otherwise it will die when the thread
// dies.
dispatch_async(dispatch_get_main_queue(), ^{
pthread_mutex_lock(&gMutex);
gScheduledDelayTimer = [NSTimer scheduledTimerWithTimeInterval:kDelayBeforeDisablingActivity
target:[NINetworkActivity class]
selector:@selector(disableNetworkActivity)
userInfo:nil
repeats:NO];
pthread_mutex_unlock(&gMutex);
});
}
pthread_mutex_unlock(&gMutex);
}
#pragma mark - Network Activity Debugging
#if defined(DEBUG) || defined(NI_DEBUG)
static BOOL gNetworkActivityDebuggingEnabled = NO;
void NISwizzleMethodsForNetworkActivityDebugging(void);
@implementation UIApplication (NimbusNetworkActivityDebugging)
- (void)nimbusDebugSetNetworkActivityIndicatorVisible:(BOOL)visible {
// This method will only be used when swizzled, so this will actually call
// setNetworkActivityIndicatorVisible:
[self nimbusDebugSetNetworkActivityIndicatorVisible:visible];
// Sanity check that this method isn't being called directly when debugging isn't enabled.
NIDASSERT(gNetworkActivityDebuggingEnabled);
// If either of the following assertions fail then you should look at the call stack to
// determine what code is erroneously calling setNetworkActivityIndicatorVisible: directly.
if (visible) {
// The only time we should be enabling the network activity indicator is when the task
// count is one.
NIDASSERT(1 == gNetworkTaskCount);
} else {
// The only time we should be disabling the network activity indicator is when the task
// count is zero.
NIDASSERT(0 == gNetworkTaskCount);
}
}
@end
void NISwizzleMethodsForNetworkActivityDebugging(void) {
NISwapInstanceMethods([UIApplication class],
@selector(setNetworkActivityIndicatorVisible:),
@selector(nimbusDebugSetNetworkActivityIndicatorVisible:));
}
void NIEnableNetworkActivityDebugging(void) {
if (!gNetworkActivityDebuggingEnabled) {
gNetworkActivityDebuggingEnabled = YES;
NISwizzleMethodsForNetworkActivityDebugging();
}
}
void NIDisableNetworkActivityDebugging(void) {
if (gNetworkActivityDebuggingEnabled) {
gNetworkActivityDebuggingEnabled = NO;
NISwizzleMethodsForNetworkActivityDebugging();
}
}
#else // #ifndef DEBUG
void NIEnableNetworkActivityDebugging(void) {
// No-op
}
void NIDisableNetworkActivityDebugging(void) {
// No-op
}
#endif // #if defined(DEBUG) || defined(NI_DEBUG)

58
Pods/Nimbus/src/core/src/NINonEmptyCollectionTesting.h

@ -1,58 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Forked from Three20 June 10, 2011 - Copyright 2009-2011 Facebook
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#if defined __cplusplus
extern "C" {
#endif
/**
* For testing whether a collection is of a certain type and is non-empty.
*
* @ingroup NimbusCore
* @defgroup Non-Empty-Collection-Testing Non-Empty Collection Testing
* @{
*
* Simply calling -count on an object may not yield the expected results when enumerating it if
* certain assumptions are also made about the object's type. For example, if a JSON response
* returns a dictionary when you expected an array, casting the result to an NSArray and
* calling count will yield a positive value, but objectAtIndex: will crash the application.
* These methods provide a safer check for non-emptiness of collections.
*/
/**
* Tests if an object is a non-nil array which is not empty.
*/
BOOL NIIsArrayWithObjects(id object);
/**
* Tests if an object is a non-nil set which is not empty.
*/
BOOL NIIsSetWithObjects(id object);
/**
* Tests if an object is a non-nil string which is not empty.
*/
BOOL NIIsStringWithAnyText(id object);
#if defined __cplusplus
};
#endif
/**@}*/// End of Non-Empty Collection Testing /////////////////////////////////////////////////////

35
Pods/Nimbus/src/core/src/NINonEmptyCollectionTesting.m

@ -1,35 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Forked from Three20 June 10, 2011 - Copyright 2009-2011 Facebook
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NINonEmptyCollectionTesting.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
BOOL NIIsArrayWithObjects(id object) {
return [object isKindOfClass:[NSArray class]] && [(NSArray*)object count] > 0;
}
BOOL NIIsSetWithObjects(id object) {
return [object isKindOfClass:[NSSet class]] && [(NSSet*)object count] > 0;
}
BOOL NIIsStringWithAnyText(id object) {
return [object isKindOfClass:[NSString class]] && [(NSString*)object length] > 0;
}

65
Pods/Nimbus/src/core/src/NINonRetainingCollections.h

@ -1,65 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Forked from Three20 June 10, 2011 - Copyright 2009-2011 Facebook
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#if defined __cplusplus
extern "C" {
#endif
/**
* For collections that don't retain their objects.
*
* @ingroup NimbusCore
* @defgroup Non-Retaining-Collections Non-Retaining Collections
* @{
*
* Non-retaining collections have historically been used when we needed more than one delegate
* in an object. However, NSNotificationCenter is a much better solution for n > 1 delegates.
* Using a non-retaining collection is dangerous, so if you must use one, use it with extreme care.
* The danger primarily lies in the fact that by all appearances the collection should still
* operate like a regular collection, so this might lead to a lot of developer error if the
* developer assumes that the collection does, in fact, retain the object.
*/
/**
* Creates a mutable array which does not retain references to the objects it contains.
*
* Typically used with arrays of delegates.
*/
NSMutableArray* NICreateNonRetainingMutableArray(void);
/**
* Creates a mutable dictionary which does not retain references to the values it contains.
*
* Typically used with dictionaries of delegates.
*/
NSMutableDictionary* NICreateNonRetainingMutableDictionary(void);
/**
* Creates a mutable set which does not retain references to the values it contains.
*
* Typically used with sets of delegates.
*/
NSMutableSet* NICreateNonRetainingMutableSet(void);
#if defined __cplusplus
};
#endif
/**@}*/// End of Non-Retaining Collections ////////////////////////////////////////////////////////

36
Pods/Nimbus/src/core/src/NINonRetainingCollections.m

@ -1,36 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Forked from Three20 June 9, 2011 - Copyright 2009-2011 Facebook
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NINonRetainingCollections.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
NSMutableArray* NICreateNonRetainingMutableArray(void) {
return (__bridge_transfer NSMutableArray *)CFArrayCreateMutable(nil, 0, nil);
}
NSMutableDictionary* NICreateNonRetainingMutableDictionary(void) {
return (__bridge_transfer NSMutableDictionary *)CFDictionaryCreateMutable(nil, 0, nil, nil);
}
NSMutableSet* NICreateNonRetainingMutableSet(void) {
return (__bridge_transfer NSMutableSet *)CFSetCreateMutable(nil, 0, nil);
}

20
Pods/Nimbus/src/core/src/NIOperations+Subclassing.h

@ -1,20 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIOperations.h"
@interface NIOperation()
@property (strong) NSError* lastError;
@end

209
Pods/Nimbus/src/core/src/NIOperations.h

@ -1,209 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "NIPreprocessorMacros.h" /* for weak */
@class NIOperation;
typedef void (^NIOperationBlock)(NIOperation* operation);
typedef void (^NIOperationDidFailBlock)(NIOperation* operation, NSError* error);
/**
* For writing code that runs concurrently.
*
* @ingroup NimbusCore
* @defgroup Operations Operations
*
* This collection of NSOperation implementations is meant to provide a set of common
* operations that might be used in an application to offload complex processing to a separate
* thread.
*/
@protocol NIOperationDelegate;
/**
* A base implementation of an NSOperation that supports traditional delegation and blocks.
*
* <h2>Subclassing</h2>
*
* A subclass should call the operationDid* methods to notify the delegate on the main thread
* of changes in the operation's state. Calling these methods will notify the delegate and the
* blocks if provided.
*
* @ingroup Operations
*/
@interface NIOperation : NSOperation
@property (weak) id<NIOperationDelegate> delegate;
@property (readonly, strong) NSError* lastError;
@property (assign) NSInteger tag;
@property (copy) NIOperationBlock didStartBlock;
@property (copy) NIOperationBlock didFinishBlock;
@property (copy) NIOperationDidFailBlock didFailWithErrorBlock;
@property (copy) NIOperationBlock willFinishBlock;
- (void)didStart;
- (void)didFinish;
- (void)didFailWithError:(NSError *)error;
- (void)willFinish;
@end
/**
* The delegate protocol for an NIOperation.
*
* @ingroup Operations
*/
@protocol NIOperationDelegate <NSObject>
@optional
/** @name [NIOperationDelegate] State Changes */
/** The operation has started executing. */
- (void)nimbusOperationDidStart:(NIOperation *)operation;
/**
* The operation is about to complete successfully.
*
* This will not be called if the operation fails.
*
* This will be called from within the operation's runloop and must be thread safe.
*/
- (void)nimbusOperationWillFinish:(NIOperation *)operation;
/**
* The operation has completed successfully.
*
* This will not be called if the operation fails.
*/
- (void)nimbusOperationDidFinish:(NIOperation *)operation;
/**
* The operation failed in some way and has completed.
*
* operationDidFinish: will not be called.
*/
- (void)nimbusOperationDidFail:(NIOperation *)operation withError:(NSError *)error;
@end
// NIOperation
/** @name Delegation */
/**
* The delegate through which changes are notified for this operation.
*
* All delegate methods are performed on the main thread.
*
* @fn NIOperation::delegate
*/
/** @name Post-Operation Properties */
/**
* The error last passed to the didFailWithError notification.
*
* @fn NIOperation::lastError
*/
/** @name Identification */
/**
* A simple tagging mechanism for identifying operations.
*
* @fn NIOperation::tag
*/
/** @name Blocks */
/**
* The operation has started executing.
*
* Performed on the main thread.
*
* @fn NIOperation::didStartBlock
*/
/**
* The operation has completed successfully.
*
* This will not be called if the operation fails.
*
* Performed on the main thread.
*
* @fn NIOperation::didFinishBlock
*/
/**
* The operation failed in some way and has completed.
*
* didFinishBlock will not be executed.
*
* Performed on the main thread.
*
* @fn NIOperation::didFailWithErrorBlock
*/
/**
* The operation is about to complete successfully.
*
* This will not be called if the operation fails.
*
* Performed in the operation's thread.
*
* @fn NIOperation::willFinishBlock
*/
/**
* @name Subclassing
*
* The following methods are provided to aid in subclassing and are not meant to be
* used externally.
*/
/**
* On the main thread, notify the delegate that the operation has begun.
*
* @fn NIOperation::didStart
*/
/**
* On the main thread, notify the delegate that the operation has finished.
*
* @fn NIOperation::didFinish
*/
/**
* On the main thread, notify the delegate that the operation has failed.
*
* @fn NIOperation::didFailWithError:
*/
/**
* In the operation's thread, notify the delegate that the operation will finish successfully.
*
* @fn NIOperation::willFinish
*/

111
Pods/Nimbus/src/core/src/NIOperations.m

@ -1,111 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIOperations.h"
#import "NIDebuggingTools.h"
#import "NIPreprocessorMacros.h"
#import "NIOperations+Subclassing.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
@implementation NIOperation
- (void)dealloc {
// For an unknown reason these block objects are not released when the NIOperation is deallocated
// with ARC enabled.
_didStartBlock = nil;
_didFinishBlock = nil;
_didFailWithErrorBlock = nil;
_willFinishBlock = nil;
}
#pragma mark - Initiate delegate notification from the NSOperation
- (void)didStart {
[self performSelectorOnMainThread:@selector(onMainThreadOperationDidStart)
withObject:nil
waitUntilDone:[NSThread isMainThread]];
}
- (void)didFinish {
[self performSelectorOnMainThread:@selector(onMainThreadOperationDidFinish)
withObject:nil
waitUntilDone:[NSThread isMainThread]];
}
- (void)didFailWithError:(NSError *)error {
self.lastError = error;
[self performSelectorOnMainThread:@selector(onMainThreadOperationDidFailWithError:)
withObject:error
waitUntilDone:[NSThread isMainThread]];
}
- (void)willFinish {
if ([self.delegate respondsToSelector:@selector(nimbusOperationWillFinish:)]) {
[self.delegate nimbusOperationWillFinish:self];
}
if (nil != self.willFinishBlock) {
self.willFinishBlock(self);
}
}
#pragma mark - Main Thread
- (void)onMainThreadOperationDidStart {
// This method should only be called on the main thread.
NIDASSERT([NSThread isMainThread]);
if ([self.delegate respondsToSelector:@selector(nimbusOperationDidStart:)]) {
[self.delegate nimbusOperationDidStart:self];
}
if (nil != self.didStartBlock) {
self.didStartBlock(self);
}
}
- (void)onMainThreadOperationDidFinish {
// This method should only be called on the main thread.
NIDASSERT([NSThread isMainThread]);
if ([self.delegate respondsToSelector:@selector(nimbusOperationDidFinish:)]) {
[self.delegate nimbusOperationDidFinish:self];
}
if (nil != self.didFinishBlock) {
self.didFinishBlock(self);
}
}
- (void)onMainThreadOperationDidFailWithError:(NSError *)error {
// This method should only be called on the main thread.
NIDASSERT([NSThread isMainThread]);
if ([self.delegate respondsToSelector:@selector(nimbusOperationDidFail:withError:)]) {
[self.delegate nimbusOperationDidFail:self withError:error];
}
if (nil != self.didFailWithErrorBlock) {
self.didFailWithErrorBlock(self, error);
}
}
@end

69
Pods/Nimbus/src/core/src/NIPaths.h

@ -1,69 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Forked from Three20 June 10, 2011 - Copyright 2009-2011 Facebook
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#if defined __cplusplus
extern "C" {
#endif
/**
* For creating standard system paths.
*
* @ingroup NimbusCore
* @defgroup Paths Paths
* @{
*/
/**
* Create a path with the given bundle and the relative path appended.
*
* @param bundle The bundle to append relativePath to. If nil, [NSBundle mainBundle]
* will be used.
* @param relativePath The relative path to append to the bundle's path.
*
* @returns The bundle path concatenated with the given relative path.
*/
NSString* NIPathForBundleResource(NSBundle* bundle, NSString* relativePath);
/**
* Create a path with the documents directory and the relative path appended.
*
* @returns The documents path concatenated with the given relative path.
*/
NSString* NIPathForDocumentsResource(NSString* relativePath);
/**
* Create a path with the Library directory and the relative path appended.
*
* @returns The Library path concatenated with the given relative path.
*/
NSString* NIPathForLibraryResource(NSString* relativePath);
/**
* Create a path with the caches directory and the relative path appended.
*
* @returns The caches path concatenated with the given relative path.
*/
NSString* NIPathForCachesResource(NSString* relativePath);
#if defined __cplusplus
};
#endif
/**@}*/// End of Paths ////////////////////////////////////////////////////////////////////////////

61
Pods/Nimbus/src/core/src/NIPaths.m

@ -1,61 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Forked from Three20 June 10, 2011 - Copyright 2009-2011 Facebook
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIPaths.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
NSString* NIPathForBundleResource(NSBundle* bundle, NSString* relativePath) {
NSString* resourcePath = [(nil == bundle ? [NSBundle mainBundle] : bundle) resourcePath];
return [resourcePath stringByAppendingPathComponent:relativePath];
}
NSString* NIPathForDocumentsResource(NSString* relativePath) {
static NSString* documentsPath = nil;
if (nil == documentsPath) {
NSArray* dirs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
documentsPath = [dirs objectAtIndex:0];
}
return [documentsPath stringByAppendingPathComponent:relativePath];
}
NSString* NIPathForLibraryResource(NSString* relativePath) {
static NSString* libraryPath = nil;
if (nil == libraryPath) {
NSArray* dirs = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
NSUserDomainMask,
YES);
libraryPath = [dirs objectAtIndex:0];
}
return [libraryPath stringByAppendingPathComponent:relativePath];
}
NSString* NIPathForCachesResource(NSString* relativePath) {
static NSString* cachesPath = nil;
if (nil == cachesPath) {
NSArray* dirs = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
NSUserDomainMask,
YES);
cachesPath = [dirs objectAtIndex:0];
}
return [cachesPath stringByAppendingPathComponent:relativePath];
}

141
Pods/Nimbus/src/core/src/NIPreprocessorMacros.h

@ -1,141 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Forked from Three20 June 10, 2011 - Copyright 2009-2011 Facebook
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#pragma mark - Preprocessor Macros
/**
* Preprocessor macros are added to Nimbus with care. Macros hide functionality and are difficult
* to debug, so most macros found in Nimbus are one-liners or compiler utilities.
*
* <h2>Creating Byte- and Hex-based Colors</h2>
*
* Nimbus provides the RGBCOLOR and RGBACOLOR macros for easily creating UIColor objects
* with byte and hex values.
*
* <h3>Examples</h3>
*
@code
UIColor* color = RGBCOLOR(255, 128, 64); // Fully opaque orange
UIColor* color = RGBACOLOR(255, 128, 64, 0.5); // Orange with 50% transparency
UIColor* color = RGBCOLOR(0xFF, 0x7A, 0x64); // Hexadecimal color
@endcode
*
* <h3>Why it exists</h3>
*
* There is no easy way to create UIColor objects using 0 - 255 range values or hexadecimal. This
* leads to code like this being written:
*
@code
UIColor* color = [UIColor colorWithRed:128.f/255.0f green:64.f/255.0f blue:32.f/255.0f alpha:1]
@endcode
*
* <h2>Avoid requiring the -all_load and -force_load flags</h2>
*
* Categories can introduce the need for the -all_load and -force_load because of the fact that
* the application will not load these categories on startup without them. This is due to the way
* Xcode deals with .m files that only contain categories: it doesn't load them without the
* -all_load or -force_load flag specified.
*
* There is, however, a way to force Xcode into loading the category .m file. If you provide an
* empty class implementation in the .m file then your app will pick up the category
* implementation.
*
* Example in plain UIKit:
*
@code
@interface BogusClass
@end
@implementation BogusClass
@end
@implementation UIViewController (MyCustomCategory)
...
@end
@endcode
*
* NI_FIX_CATEGORY_BUG is a Nimbus macro that you include in your category `.m` file to save you
* the trouble of having to write a bogus class for every category. Just be sure that the name you
* provide to the macro is unique across your project or you will encounter duplicate symbol errors
* when linking.
*
@code
NI_FIX_CATEGORY_BUG(UIViewController_MyCustomCategory);
@implementation UIViewController (MyCustomCategory)
...
@end
@endcode
*
* @ingroup NimbusCore
* @defgroup Preprocessor-Macros Preprocessor Macros
* @{
*/
/**
* Mark a method or property as deprecated to the compiler.
*
* Any use of a deprecated method or property will flag a warning when compiling.
*
* Borrowed from Apple's AvailabiltyInternal.h header.
*
* @htmlonly
* <pre>
* __AVAILABILITY_INTERNAL_DEPRECATED __attribute__((deprecated))
* </pre>
* @endhtmlonly
*/
#define __NI_DEPRECATED_METHOD __attribute__((deprecated))
/**
* Mark APIs as unavailable in app extensions.
*
* Use of unavailable methods, classes, or functions produces a compile error when built as part
* of an app extension target. If the method, class or function using the unavailable API has also
* been marked as unavailable in app extensions, the error will be suppressed.
*/
#ifdef NS_EXTENSION_UNAVAILABLE_IOS
#define NI_EXTENSION_UNAVAILABLE_IOS(msg) NS_EXTENSION_UNAVAILABLE_IOS(msg)
#else
#define NI_EXTENSION_UNAVAILABLE_IOS(msg)
#endif
/**
* Force a category to be loaded when an app starts up.
*
* Add this macro before each category implementation, so we don't have to use
* -all_load or -force_load to load object files from static libraries that only contain
* categories and no classes.
* See http://developer.apple.com/library/mac/#qa/qa2006/qa1490.html for more info.
*/
#define NI_FIX_CATEGORY_BUG(name) @interface NI_FIX_CATEGORY_BUG_##name : NSObject @end \
@implementation NI_FIX_CATEGORY_BUG_##name @end
/**
* Creates an opaque UIColor object from a byte-value color definition.
*/
#define RGBCOLOR(r,g,b) [UIColor colorWithRed:(r)/255.0f green:(g)/255.0f blue:(b)/255.0f alpha:1]
/**
* Creates a UIColor object from a byte-value color definition and alpha transparency.
*/
#define RGBACOLOR(r,g,b,a) [UIColor colorWithRed:(r)/255.0f green:(g)/255.0f blue:(b)/255.0f alpha:(a)]
/**@}*/// End of Preprocessor Macros //////////////////////////////////////////////////////////////

74
Pods/Nimbus/src/core/src/NIRuntimeClassModifications.h

@ -1,74 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Forked from Three20 June 10, 2011 - Copyright 2009-2011 Facebook
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#if defined __cplusplus
extern "C" {
#endif
/**
* For modifying class implementations at runtime.
*
* @ingroup NimbusCore
* @defgroup Runtime-Class-Modifications Runtime Class Modifications
* @{
*
* @attention Please use caution when modifying class implementations at runtime.
* Apple is prone to rejecting apps for gratuitous use of method swapping.
* In particular, avoid swapping any NSObject methods such as dealloc, init,
* and retain/release on UIKit classes.
*
* See example: @link ExampleRuntimeDebugging.m Runtime Debugging with Method Swizzling@endlink
*/
/**
* Swap two class instance method implementations.
*
* Use this method when you would like to replace an existing method implementation in a class
* with your own implementation at runtime. In practice this is often used to replace the
* implementations of UIKit classes where subclassing isn't an adequate solution.
*
* This will only work for methods declared with a -.
*
* After calling this method, any calls to originalSel will actually call newSel and vice versa.
*
* Uses method_exchangeImplementations to accomplish this.
*/
void NISwapInstanceMethods(Class cls, SEL originalSel, SEL newSel);
/**
* Swap two class method implementations.
*
* Use this method when you would like to replace an existing method implementation in a class
* with your own implementation at runtime. In practice this is often used to replace the
* implementations of UIKit classes where subclassing isn't an adequate solution.
*
* This will only work for methods declared with a +.
*
* After calling this method, any calls to originalSel will actually call newSel and vice versa.
*
* Uses method_exchangeImplementations to accomplish this.
*/
void NISwapClassMethods(Class cls, SEL originalSel, SEL newSel);
#if defined __cplusplus
};
#endif
/**@}*/// End of Runtime Class Modifications //////////////////////////////////////////////////////

37
Pods/Nimbus/src/core/src/NIRuntimeClassModifications.m

@ -1,37 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Forked from Three20 June 10, 2011 - Copyright 2009-2011 Facebook
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIRuntimeClassModifications.h"
#import <objc/runtime.h>
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
void NISwapInstanceMethods(Class cls, SEL originalSel, SEL newSel) {
Method originalMethod = class_getInstanceMethod(cls, originalSel);
Method newMethod = class_getInstanceMethod(cls, newSel);
method_exchangeImplementations(originalMethod, newMethod);
}
void NISwapClassMethods(Class cls, SEL originalSel, SEL newSel) {
Method originalMethod = class_getClassMethod(cls, originalSel);
Method newMethod = class_getClassMethod(cls, newSel);
method_exchangeImplementations(originalMethod, newMethod);
}

257
Pods/Nimbus/src/core/src/NISDKAvailability.h

@ -1,257 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Forked from Three20 June 10, 2011 - Copyright 2009-2011 Facebook
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "NIPreprocessorMacros.h"
/**
* For checking SDK feature availibility.
*
* @ingroup NimbusCore
* @defgroup SDK-Availability SDK Availability
* @{
*
* NIIOS macros are defined in parallel to their __IPHONE_ counterparts as a consistently-defined
* means of checking __IPHONE_OS_VERSION_MAX_ALLOWED.
*
* For example:
*
* @htmlonly
* <pre>
* #if __IPHONE_OS_VERSION_MAX_ALLOWED >= NIIOS_3_2
* // This code will only compile on versions >= iOS 3.2
* #endif
* </pre>
* @endhtmlonly
*/
/**
* Released on July 11, 2008
*/
#define NIIOS_2_0 20000
/**
* Released on September 9, 2008
*/
#define NIIOS_2_1 20100
/**
* Released on November 21, 2008
*/
#define NIIOS_2_2 20200
/**
* Released on June 17, 2009
*/
#define NIIOS_3_0 30000
/**
* Released on September 9, 2009
*/
#define NIIOS_3_1 30100
/**
* Released on April 3, 2010
*/
#define NIIOS_3_2 30200
/**
* Released on June 21, 2010
*/
#define NIIOS_4_0 40000
/**
* Released on September 8, 2010
*/
#define NIIOS_4_1 40100
/**
* Released on November 22, 2010
*/
#define NIIOS_4_2 40200
/**
* Released on March 9, 2011
*/
#define NIIOS_4_3 40300
/**
* Released on October 12, 2011.
*/
#define NIIOS_5_0 50000
/**
* Released on March 7, 2012.
*/
#define NIIOS_5_1 50100
/**
* Released on September 19, 2012.
*/
#define NIIOS_6_0 60000
/**
* Released on January 28, 2013.
*/
#define NIIOS_6_1 60100
/**
* Released on September 18, 2013
*/
#define NIIOS_7_0 70000
#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_2_0
#define kCFCoreFoundationVersionNumber_iPhoneOS_2_0 478.23
#endif
#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_2_1
#define kCFCoreFoundationVersionNumber_iPhoneOS_2_1 478.26
#endif
#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_2_2
#define kCFCoreFoundationVersionNumber_iPhoneOS_2_2 478.29
#endif
#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_3_0
#define kCFCoreFoundationVersionNumber_iPhoneOS_3_0 478.47
#endif
#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_3_1
#define kCFCoreFoundationVersionNumber_iPhoneOS_3_1 478.52
#endif
#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_3_2
#define kCFCoreFoundationVersionNumber_iPhoneOS_3_2 478.61
#endif
#ifndef kCFCoreFoundationVersionNumber_iOS_4_0
#define kCFCoreFoundationVersionNumber_iOS_4_0 550.32
#endif
#ifndef kCFCoreFoundationVersionNumber_iOS_4_1
#define kCFCoreFoundationVersionNumber_iOS_4_1 550.38
#endif
#ifndef kCFCoreFoundationVersionNumber_iOS_4_2
#define kCFCoreFoundationVersionNumber_iOS_4_2 550.52
#endif
#ifndef kCFCoreFoundationVersionNumber_iOS_4_3
#define kCFCoreFoundationVersionNumber_iOS_4_3 550.52
#endif
#ifndef kCFCoreFoundationVersionNumber_iOS_5_0
#define kCFCoreFoundationVersionNumber_iOS_5_0 675.00
#endif
#ifndef kCFCoreFoundationVersionNumber_iOS_5_1
#define kCFCoreFoundationVersionNumber_iOS_5_1 690.10
#endif
#ifndef kCFCoreFoundationVersionNumber_iOS_6_0
#define kCFCoreFoundationVersionNumber_iOS_6_0 793.00
#endif
#ifndef kCFCoreFoundationVersionNumber_iOS_6_1
#define kCFCoreFoundationVersionNumber_iOS_6_1 793.00
#endif
#if defined(__cplusplus)
extern "C" {
#endif
/**
* Checks whether the device the app is currently running on is an iPad or not.
*
* @returns YES if the device is an iPad.
*/
BOOL NIIsPad(void);
/**
* Checks whether the device the app is currently running on is an
* iPhone/iPod touch or not.
*
* @returns YES if the device is an iPhone or iPod touch.
*/
BOOL NIIsPhone(void);
/**
* Checks whether the device supports tint colors on all UIViews.
*
* @returns YES if all UIView instances on the device respond to tintColor.
*/
BOOL NIIsTintColorGloballySupported(void);
/**
* Checks whether the device's OS version is at least the given version number.
*
* Useful for runtime checks of the device's version number.
*
* @param versionNumber Any value of kCFCoreFoundationVersionNumber.
*
* @attention Apple recommends using respondsToSelector where possible to check for
* feature support. Use this method as a last resort.
*/
BOOL NIDeviceOSVersionIsAtLeast(double versionNumber);
/**
* Fetch the screen's scale.
*/
CGFloat NIScreenScale(void);
/**
* Returns YES if the screen is a retina display, NO otherwise.
*/
BOOL NIIsRetina(void);
/**
* This method is now deprecated. Use [UIPopoverController class] instead.
*
* MAINTENANCE: Remove by Feb 28, 2014.
*/
Class NIUIPopoverControllerClass(void) __NI_DEPRECATED_METHOD;
/**
* This method is now deprecated. Use [UITapGestureRecognizer class] instead.
*
* MAINTENANCE: Remove by Feb 28, 2014.
*/
Class NIUITapGestureRecognizerClass(void) __NI_DEPRECATED_METHOD;
#if defined(__cplusplus)
} // extern "C"
#endif
#pragma mark Building with Old SDKs
// Define methods that were introduced in iOS 7.0.
#if __IPHONE_OS_VERSION_MAX_ALLOWED < NIIOS_7_0
@interface UIViewController (Nimbus7SDKAvailability)
@property (nonatomic, assign) UIRectEdge edgesForExtendedLayout;
- (void)setNeedsStatusBarAppearanceUpdate;
@end
#endif
/**@}*/// End of SDK Availability /////////////////////////////////////////////////////////////////

72
Pods/Nimbus/src/core/src/NISDKAvailability.m

@ -1,72 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Forked from Three20 June 10, 2011 - Copyright 2009-2011 Facebook
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NimbusCore.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
#if __IPHONE_OS_VERSION_MAX_ALLOWED < NIIOS_6_0
const UIImageResizingMode UIImageResizingModeStretch = -1;
#endif
BOOL NIIsPad(void) {
static NSInteger isPad = -1;
if (isPad < 0) {
isPad = ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) ? 1 : 0;
}
return isPad > 0;
}
BOOL NIIsPhone(void) {
static NSInteger isPhone = -1;
if (isPhone < 0) {
isPhone = ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) ? 1 : 0;
}
return isPhone > 0;
}
BOOL NIIsTintColorGloballySupported(void) {
static NSInteger isTintColorGloballySupported = -1;
if (isTintColorGloballySupported < 0) {
UIView* view = [[UIView alloc] init];
isTintColorGloballySupported = [view respondsToSelector:@selector(tintColor)];
}
return isTintColorGloballySupported > 0;
}
BOOL NIDeviceOSVersionIsAtLeast(double versionNumber) {
return kCFCoreFoundationVersionNumber >= versionNumber;
}
CGFloat NIScreenScale(void) {
return [[UIScreen mainScreen] scale];
}
BOOL NIIsRetina(void) {
return NIScreenScale() > 1.f;
}
Class NIUIPopoverControllerClass(void) {
return [UIPopoverController class];
}
Class NIUITapGestureRecognizerClass(void) {
return [UITapGestureRecognizer class];
}

224
Pods/Nimbus/src/core/src/NISnapshotRotation.h

@ -1,224 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
/**
* An object designed to easily implement snapshot rotation.
*
* Snapshot rotation involves taking two screenshots of a UIView: the "before" and the "after"
* state of the rotation. These two images are then cross-faded during the rotation, creating an
* animation that minimizes visual artifacts that would otherwise be noticed when rotation occurs.
*
* This feature will only function on iOS 6.0 and higher. On older iOS versions the view will rotate
* just as it always has.
*
* This functionality has been adopted from WWDC 2012 session 240 "Polishing Your Interface
* Rotations".
*
* @ingroup NimbusCore
* @defgroup Snapshot-Rotation Snapshot Rotation
* @{
*/
@protocol NISnapshotRotationDelegate;
/**
* The NISnapshotRotation class provides support for implementing snapshot-based rotations on views.
*
* You must call this object's rotation methods from your controller in order for the rotation
* object to implement the rotation animations correctly.
*/
@interface NISnapshotRotation : NSObject
// Designated initializer.
- (id)initWithDelegate:(id<NISnapshotRotationDelegate>)delegate;
@property (nonatomic, weak) id<NISnapshotRotationDelegate> delegate;
@property (nonatomic, readonly, assign) CGRect frameBeforeRotation;
@property (nonatomic, readonly, assign) CGRect frameAfterRotation;
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration;
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration;
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation;
@end
/**
* The NITableViewSnapshotRotation class implements the fixedInsetsForSnapshotRotation: delegate
* method and forwards all other delegate methods along.
*
* If you are rotating a UITableView you can instantiate a NITableViewSnapshotRotation object and
* use it just like you would a snapshot rotation object. The NITableViewSnapshotRotation class
* intercepts the fixedInsetsForSnapshotRotation: method and returns insets that map to the
* dimensions of the content view for the first visible cell in the table view.
*
* The assigned delegate only needs to implement containerViewForSnapshotRotation: and
* rotatingViewForSnapshotRotation:.
*/
@interface NITableViewSnapshotRotation : NISnapshotRotation
@end
/**
* The methods declared by the NISnapshotRotation protocol allow the adopting delegate to respond to
* messages from the NISnapshotRotation class and thus implement snapshot rotations.
*/
@protocol NISnapshotRotationDelegate <NSObject>
@required
/** @name Accessing Rotation Views */
/**
* Tells the delegate to return the container view of the rotating view.
*
* This is often the controller's self.view. This view must not be the same as the rotatingView and
* rotatingView must be in the subview tree of containerView.
*
* @sa NISnapshotRotation::rotatingViewForSnapshotRotation:
*/
- (UIView *)containerViewForSnapshotRotation:(NISnapshotRotation *)snapshotRotation;
/**
* Tells the delegate to return the rotating view.
*
* The rotating view is the view that will be snapshotted during the rotation.
*
* This view must not be the same as the containerView and must be in the subview tree of
* containerView.
*
* @sa NISnapshotRotation::containerViewForSnapshotRotation:
*/
- (UIView *)rotatingViewForSnapshotRotation:(NISnapshotRotation *)snapshotRotation;
@optional
/** @name Configuring Fixed Insets */
/**
* Asks the delegate to return the insets of the rotating view that should be fixed during rotation.
*
* This method will only be called on iOS 6.0 and higher.
*
* The returned insets will denote which parts of the snapshotted images will not stretch during
* the rotation animation.
*/
- (UIEdgeInsets)fixedInsetsForSnapshotRotation:(NISnapshotRotation *)snapshotRotation;
@end
#if defined __cplusplus
extern "C" {
#endif
/**
* Returns an opaque UIImage snapshot of the given view.
*
* This method takes into account the offset of scrollable views and captures whatever is currently
* in the frame of the view.
*
* @param view A snapshot will be taken of this view.
* @returns A UIImage with the snapshot of @c view.
*/
UIImage* NISnapshotOfView(UIView* view);
/**
* Returns a UIImageView with an image snapshot of the given view.
*
* The frame of the returned view is set to match the frame of @c view.
*
* @param view A snapshot will be taken of this view.
* @returns A UIImageView with the snapshot of @c view and matching frame.
*/
UIImageView* NISnapshotViewOfView(UIView* view);
/**
* Returns a UIImage snapshot of the given view with transparency.
*
* This method takes into account the offset of scrollable views and captures whatever is currently
* in the frame of the view.
*
* @param view A snapshot will be taken of this view.
* @returns A UIImage with the snapshot of @c view.
*/
UIImage* NISnapshotOfViewWithTransparency(UIView* view);
/**
* Returns a UIImageView with an image snapshot with transparency of the given view.
*
* The frame of the returned view is set to match the frame of @c view.
*
* @param view A snapshot will be taken of this view.
* @returns A UIImageView with the snapshot of @c view and matching frame.
*/
UIImageView* NISnapshotViewOfViewWithTransparency(UIView* view);
#if defined __cplusplus
}
#endif
/**
* @}
*/
/** @name Creating a Snapshot Rotation Object */
/**
* Initializes a newly allocated rotation object with a given delegate.
*
* @param delegate A delegate that implements the NISnapshotRotation protocol.
* @returns A NISnapshotRotation object initialized with @c delegate.
* @fn NISnapshotRotation::initWithDelegate:
*/
/** @name Accessing the Delegate */
/**
* The delegate of the snapshot rotation object.
*
* The delegate must adopt the NISnapshotRotation protocol. The NISnapshotRotation class, which does
* not retain the delegate, invokes each protocol method the delegate implements.
*
* @fn NISnapshotRotation::delegate
*/
/** @name Implementing UIViewController Autorotation */
/**
* Prepares the animation for a rotation by taking a snapshot of the rotatingView in its current
* state.
*
* This method must be called from your UIViewController implementation.
*
* @fn NISnapshotRotation::willRotateToInterfaceOrientation:duration:
*/
/**
* Crossfades between the initial and final snapshots.
*
* This method must be called from your UIViewController implementation.
*
* @fn NISnapshotRotation::willAnimateRotationToInterfaceOrientation:duration:
*/
/**
* Finalizes the rotation animation by removing the snapshot views from the container view.
*
* This method must be called from your UIViewController implementation.
*
* @fn NISnapshotRotation::didRotateFromInterfaceOrientation:
*/

299
Pods/Nimbus/src/core/src/NISnapshotRotation.m

@ -1,299 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// This code was originally found in Apple's WWDC Session 240 on
// "Polishing Your Interface Rotations" and has been repurposed into a reusable class.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NISnapshotRotation.h"
#import "NIDebuggingTools.h"
#import "NISDKAvailability.h"
#import <QuartzCore/QuartzCore.h>
#import <objc/runtime.h>
#if __IPHONE_OS_VERSION_MIN_REQUIRED < NIIOS_6_0
#error "Nimbus Snapshot Rotation requires iOS 6 or higher."
#endif
UIImage* NISnapshotOfViewWithTransparencyOption(UIView* view, BOOL transparency);
UIImage* NISnapshotOfViewWithTransparencyOption(UIView* view, BOOL transparency) {
// Passing 0 as the last argument ensures that the image context will match the current device's
// scaling mode.
UIGraphicsBeginImageContextWithOptions(view.bounds.size, !transparency, 0);
CGContextRef cx = UIGraphicsGetCurrentContext();
// Views that can scroll do so by modifying their bounds. We want to capture the part of the view
// that is currently in the frame, so we offset by the bounds of the view accordingly.
CGContextTranslateCTM(cx, -view.bounds.origin.x, -view.bounds.origin.y);
BOOL didDraw = NO;
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= NIIOS_7_0
if ([view respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) {
didDraw = [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
}
#endif
if (!didDraw) {
[view.layer renderInContext:cx];
}
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
UIImage* NISnapshotOfView(UIView* view) {
return NISnapshotOfViewWithTransparencyOption(view, NO);
}
UIImageView* NISnapshotViewOfView(UIView* view) {
UIImage* image = NISnapshotOfView(view);
UIImageView* snapshotView = [[UIImageView alloc] initWithImage:image];
snapshotView.frame = view.frame;
return snapshotView;
}
UIImage* NISnapshotOfViewWithTransparency(UIView* view) {
return NISnapshotOfViewWithTransparencyOption(view, YES);
}
UIImageView* NISnapshotViewOfViewWithTransparency(UIView* view) {
UIImage* image = NISnapshotOfViewWithTransparency(view);
UIImageView* snapshotView = [[UIImageView alloc] initWithImage:image];
snapshotView.frame = view.frame;
return snapshotView;
}
@interface NISnapshotRotation()
@property (nonatomic, assign) BOOL isSupportedOS;
@property (nonatomic, assign) CGRect frameBeforeRotation;
@property (nonatomic, assign) CGRect frameAfterRotation;
@property (nonatomic, strong) UIImageView* snapshotViewBeforeRotation;
@property (nonatomic, strong) UIImageView* snapshotViewAfterRotation;
@end
@implementation NISnapshotRotation
- (id)initWithDelegate:(id<NISnapshotRotationDelegate>)delegate {
if ((self = [super init])) {
_delegate = delegate;
// Check whether this feature is supported or not.
UIImage* image = [[UIImage alloc] init];
_isSupportedOS = [image respondsToSelector:@selector(resizableImageWithCapInsets:resizingMode:)];
}
return self;
}
- (id)init {
return [self initWithDelegate:nil];
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if (!self.isSupportedOS) {
return;
}
UIView* containerView = [self.delegate containerViewForSnapshotRotation:self];
UIView* rotationView = [self.delegate rotatingViewForSnapshotRotation:self];
// The container view must not be the same as the rotation view.
NIDASSERT(containerView != rotationView);
if (containerView == rotationView) {
return;
}
self.frameBeforeRotation = rotationView.frame;
self.snapshotViewBeforeRotation = NISnapshotViewOfViewWithTransparency(rotationView);
[containerView insertSubview:self.snapshotViewBeforeRotation aboveSubview:rotationView];
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if (!self.isSupportedOS) {
return;
}
UIView* containerView = [self.delegate containerViewForSnapshotRotation:self];
UIView* rotationView = [self.delegate rotatingViewForSnapshotRotation:self];
// The container view must not be the same as the rotation view.
NIDASSERT(containerView != rotationView);
if (containerView == rotationView) {
return;
}
self.frameAfterRotation = rotationView.frame;
[UIView setAnimationsEnabled:NO];
self.snapshotViewAfterRotation = NISnapshotViewOfViewWithTransparency(rotationView);
// Set the new frame while maintaining the old frame's height.
self.snapshotViewAfterRotation.frame = CGRectMake(self.frameBeforeRotation.origin.x,
self.frameBeforeRotation.origin.y,
self.frameBeforeRotation.size.width,
self.snapshotViewAfterRotation.frame.size.height);
UIImage* imageBeforeRotation = self.snapshotViewBeforeRotation.image;
UIImage* imageAfterRotation = self.snapshotViewAfterRotation.image;
if ([self.delegate respondsToSelector:@selector(fixedInsetsForSnapshotRotation:)]) {
UIEdgeInsets fixedInsets = [self.delegate fixedInsetsForSnapshotRotation:self];
imageBeforeRotation = [imageBeforeRotation resizableImageWithCapInsets:fixedInsets resizingMode:UIImageResizingModeStretch];
imageAfterRotation = [imageAfterRotation resizableImageWithCapInsets:fixedInsets resizingMode:UIImageResizingModeStretch];
}
self.snapshotViewBeforeRotation.image = imageBeforeRotation;
self.snapshotViewAfterRotation.image = imageAfterRotation;
[UIView setAnimationsEnabled:YES];
if (imageAfterRotation.size.height < imageBeforeRotation.size.height) {
self.snapshotViewAfterRotation.alpha = 0;
[containerView insertSubview:self.snapshotViewAfterRotation aboveSubview:self.snapshotViewBeforeRotation];
self.snapshotViewAfterRotation.alpha = 1;
} else {
[containerView insertSubview:self.snapshotViewAfterRotation belowSubview:self.snapshotViewBeforeRotation];
self.snapshotViewBeforeRotation.alpha = 0;
}
self.snapshotViewAfterRotation.frame = self.frameAfterRotation;
self.snapshotViewBeforeRotation.frame = CGRectMake(self.frameAfterRotation.origin.x,
self.frameAfterRotation.origin.y,
self.frameAfterRotation.size.width,
self.snapshotViewBeforeRotation.frame.size.height);
rotationView.hidden = YES;
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
if (!self.isSupportedOS) {
return;
}
UIView* containerView = [self.delegate containerViewForSnapshotRotation:self];
UIView* rotationView = [self.delegate rotatingViewForSnapshotRotation:self];
// The container view must not be the same as the rotation view.
NIDASSERT(containerView != rotationView);
if (containerView == rotationView) {
return;
}
[self.snapshotViewBeforeRotation removeFromSuperview];
[self.snapshotViewAfterRotation removeFromSuperview];
self.snapshotViewBeforeRotation = nil;
self.snapshotViewAfterRotation = nil;
rotationView.hidden = NO;
}
@end
@interface NITableViewSnapshotRotation() <NISnapshotRotationDelegate>
@property (nonatomic, weak) id<NISnapshotRotationDelegate> forwardingDelegate;
@end
@implementation NITableViewSnapshotRotation
- (void)setDelegate:(id<NISnapshotRotationDelegate>)delegate {
if (delegate == self) {
[super setDelegate:delegate];
} else {
self.forwardingDelegate = delegate;
}
}
- (id)init {
if ((self = [super init])) {
self.delegate = self;
}
return self;
}
#pragma mark - Forward Invocations
- (BOOL)shouldForwardSelectorToDelegate:(SEL)selector {
struct objc_method_description description;
// Only forward the selector if it's part of the protocol.
description = protocol_getMethodDescription(@protocol(NISnapshotRotationDelegate), selector, NO, YES);
BOOL isSelectorInProtocol = (description.name != NULL && description.types != NULL);
return (isSelectorInProtocol && [self.forwardingDelegate respondsToSelector:selector]);
}
- (BOOL)respondsToSelector:(SEL)selector {
if ([super respondsToSelector:selector] == YES) {
return YES;
} else {
return [self shouldForwardSelectorToDelegate:selector];
}
}
- (id)forwardingTargetForSelector:(SEL)selector {
if ([self shouldForwardSelectorToDelegate:selector]) {
return self.forwardingDelegate;
} else {
return nil;
}
}
#pragma mark - NISnapshotRotation
- (UIView *)containerViewForSnapshotRotation:(NISnapshotRotation *)snapshotRotation {
return [self.forwardingDelegate containerViewForSnapshotRotation:snapshotRotation];
}
- (UIView *)rotatingViewForSnapshotRotation:(NISnapshotRotation *)snapshotRotation {
return [self.forwardingDelegate rotatingViewForSnapshotRotation:snapshotRotation];
}
- (UIEdgeInsets)fixedInsetsForSnapshotRotation:(NISnapshotRotation *)snapshotRotation {
UIEdgeInsets insets = UIEdgeInsetsZero;
// Find the right edge of the content view in the coordinate space of the UITableView.
UIView* rotatingView = [self.forwardingDelegate rotatingViewForSnapshotRotation:snapshotRotation];
NIDASSERT([rotatingView isKindOfClass:[UITableView class]]);
if ([rotatingView isKindOfClass:[UITableView class]]) {
UITableView* tableView = (UITableView *)rotatingView;
NSArray* visibleCells = tableView.visibleCells;
if (visibleCells.count > 0) {
UIView* contentView = [[visibleCells objectAtIndex:0] contentView];
CGFloat contentViewRightEdge = [tableView convertPoint:CGPointMake(contentView.bounds.size.width, 0) fromView:contentView].x;
CGFloat fixedRightWidth = tableView.bounds.size.width - contentViewRightEdge;
CGFloat fixedLeftWidth = MIN(snapshotRotation.frameAfterRotation.size.width, snapshotRotation.frameBeforeRotation.size.width) - fixedRightWidth - 1;
insets = UIEdgeInsetsMake(0, fixedLeftWidth, 0, fixedRightWidth);
}
}
return insets;
}
@end

84
Pods/Nimbus/src/core/src/NIState.h

@ -1,84 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
@class NIImageMemoryCache;
/**
* For modifying Nimbus state information.
*
* @ingroup NimbusCore
* @defgroup Core-State State
* @{
*
* The Nimbus core provides a common layer of features used by nearly all of the libraries in
* the Nimbus ecosystem. Here you will find methods for accessing and setting the global image
* cache amongst other things.
*/
/**
* The Nimbus state interface.
*
* @ingroup Core-State
*/
@interface Nimbus : NSObject
#pragma mark Accessing Global State /** @name Accessing Global State */
/**
* Access the global image memory cache.
*
* If a cache hasn't been assigned via Nimbus::setGlobalImageMemoryCache: then one will be created
* automatically.
*
* @remarks The default image cache has no upper limit on its memory consumption. It is
* up to you to specify an upper limit in your application.
*/
+ (NIImageMemoryCache *)imageMemoryCache;
/**
* Access the global network operation queue.
*
* The global network operation queue exists to be used for asynchronous network requests if
* you choose. By defining a global operation queue in the core of Nimbus, we can ensure that
* all libraries that depend on core will use the same network operation queue unless configured
* otherwise.
*
* If an operation queue hasn't been assigned via Nimbus::setGlobalNetworkOperationQueue: then
* one will be created automatically with the default iOS settings.
*/
+ (NSOperationQueue *)networkOperationQueue;
#pragma mark Modifying Global State /** @name Modifying Global State */
/**
* Set the global image memory cache.
*
* The cache will be retained and the old cache released.
*/
+ (void)setImageMemoryCache:(NIImageMemoryCache *)imageMemoryCache;
/**
* Set the global network operation queue.
*
* The queue will be retained and the old queue released.
*/
+ (void)setNetworkOperationQueue:(NSOperationQueue *)queue;
@end
/**@}*/// End of State ////////////////////////////////////////////////////////////////////////////

58
Pods/Nimbus/src/core/src/NIState.m

@ -1,58 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIState.h"
#import "NIInMemoryCache.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
static NIImageMemoryCache* sNimbusGlobalMemoryCache = nil;
static NSOperationQueue* sNimbusGlobalOperationQueue = nil;
@implementation Nimbus
+ (void)setImageMemoryCache:(NIImageMemoryCache *)imageMemoryCache {
if (sNimbusGlobalMemoryCache != imageMemoryCache) {
sNimbusGlobalMemoryCache = nil;
sNimbusGlobalMemoryCache = imageMemoryCache;
}
}
+ (NIImageMemoryCache *)imageMemoryCache {
if (nil == sNimbusGlobalMemoryCache) {
sNimbusGlobalMemoryCache = [[NIImageMemoryCache alloc] init];
}
return sNimbusGlobalMemoryCache;
}
+ (void)setNetworkOperationQueue:(NSOperationQueue *)queue {
if (sNimbusGlobalOperationQueue != queue) {
sNimbusGlobalOperationQueue = nil;
sNimbusGlobalOperationQueue = queue;
}
}
+ (NSOperationQueue *)networkOperationQueue {
if (nil == sNimbusGlobalOperationQueue) {
sNimbusGlobalOperationQueue = [[NSOperationQueue alloc] init];
}
return sNimbusGlobalOperationQueue;
}
@end

164
Pods/Nimbus/src/core/src/NIViewRecycler.h

@ -1,164 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
/**
* For recycling views in scroll views.
*
* @ingroup NimbusCore
* @defgroup Core-View-Recycling View Recyling
* @{
*
* View recycling is an important aspect of iOS memory management and performance when building
* scroll views. UITableView uses view recycling via the table cell dequeue mechanism.
* NIViewRecycler implements this recycling functionality, allowing you to implement recycling
* mechanisms in your own views and controllers.
*
*
* <h2>Example Use</h2>
*
* Imagine building a UITableView. We'll assume that a viewRecycler object exists in the view.
*
* Views are usually recycled once they are no longer on screen, so within a did scroll event
* we might have code like the following:
*
@code
for (UIView<NIRecyclableView>* view in visibleViews) {
if (![self isVisible:view]) {
[viewRecycler recycleView:view];
[view removeFromSuperview];
}
}
@endcode
*
* This will take the views that are no longer visible and add them to the recycler. At a later
* point in that same didScroll code we will check if there are any new views that are visible.
* This is when we try to dequeue a recycled view from the recycler.
*
@code
UIView<NIRecyclableView>* view = [viewRecycler dequeueReusableViewWithIdentifier:reuseIdentifier];
if (nil == view) {
// Allocate a new view that conforms to the NIRecyclableView protocol.
view = [[[...]] autorelease];
}
[self addSubview:view];
@endcode
*
*/
@protocol NIRecyclableView;
/**
* An object for efficiently reusing views by recycling and dequeuing them from a pool of views.
*
* This sort of object is likely what UITableView and NIPagingScrollView use to recycle their views.
*/
@interface NIViewRecycler : NSObject
- (UIView<NIRecyclableView> *)dequeueReusableViewWithIdentifier:(NSString *)reuseIdentifier;
- (void)recycleView:(UIView<NIRecyclableView> *)view;
- (void)removeAllViews;
@end
/**
* The NIRecyclableView protocol defines a set of optional methods that a view may implement to
* handle being added to a NIViewRecycler.
*/
@protocol NIRecyclableView <NSObject>
@optional
/**
* The identifier used to categorize views into buckets for reuse.
*
* Views will be reused when a new view is requested with a matching identifier.
*
* If the reuseIdentifier is nil then the class name will be used.
*/
@property (nonatomic, copy) NSString* reuseIdentifier;
/**
* Called immediately after the view has been dequeued from the recycled view pool.
*/
- (void)prepareForReuse;
@end
/**
* A simple implementation of the NIRecyclableView protocol as a UIView.
*
* This class can be used as a base class for building recyclable views if specific reuse
* identifiers are necessary, e.g. when the same class might have different implementations
* depending on the reuse identifier.
*
* Assuming functionality is consistent for a given class it is simpler not to have a
* reuseIdentifier, making the view recycler use the class name as the reuseIdentifier. In this case
* subclassing this class is overkill.
*/
@interface NIRecyclableView : UIView <NIRecyclableView>
// Designated initializer.
- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier;
@property (nonatomic, copy) NSString* reuseIdentifier;
@end
/**@}*/ // End of View Recyling
/**
* Dequeues a reusable view from the recycled views pool if one exists, otherwise returns nil.
*
* @fn NIViewRecycler::dequeueReusableViewWithIdentifier:
* @param reuseIdentifier Often the name of the class of view you wish to fetch.
*/
/**
* Adds a given view to the recycled views pool.
*
* @fn NIViewRecycler::recycleView:
* @param view The view to recycle. The reuse identifier will be retrieved from the view
* via the NIRecyclableView protocol.
*/
/**
* Removes all of the views from the recycled views pool.
*
* @fn NIViewRecycler::removeAllViews
*/
/**
* Initializes a newly allocated view with the given reuse identifier.
*
* This is the designated initializer.
*
* @fn NIRecyclableView::initWithReuseIdentifier:
* @param reuseIdentifier The identifier that will be used to group this view in the view
* recycler.
*/
/**
* This view's reuse identifier.
*
* Used by NIViewRecycler to pool this view into a group of similar recycled views.
*
* @fn NIRecyclableView::reuseIdentifier
*/

111
Pods/Nimbus/src/core/src/NIViewRecycler.m

@ -1,111 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIViewRecycler.h"
#import "NimbusCore.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
@interface NIViewRecycler()
@property (nonatomic, strong) NSMutableDictionary* reuseIdentifiersToRecycledViews;
@end
@implementation NIViewRecycler
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (id)init {
if ((self = [super init])) {
_reuseIdentifiersToRecycledViews = [[NSMutableDictionary alloc] init];
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:@selector(reduceMemoryUsage)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
}
return self;
}
#pragma mark - Memory Warnings
- (void)reduceMemoryUsage {
[self removeAllViews];
}
#pragma mark - Public
- (UIView<NIRecyclableView> *)dequeueReusableViewWithIdentifier:(NSString *)reuseIdentifier {
NSMutableArray* views = [_reuseIdentifiersToRecycledViews objectForKey:reuseIdentifier];
UIView<NIRecyclableView>* view = [views lastObject];
if (nil != view) {
[views removeLastObject];
if ([view respondsToSelector:@selector(prepareForReuse)]) {
[view prepareForReuse];
}
}
return view;
}
- (void)recycleView:(UIView<NIRecyclableView> *)view {
NIDASSERT([view isKindOfClass:[UIView class]]);
NSString* reuseIdentifier = nil;
if ([view respondsToSelector:@selector(reuseIdentifier)]) {
reuseIdentifier = [view reuseIdentifier];;
}
if (nil == reuseIdentifier) {
reuseIdentifier = NSStringFromClass([view class]);
}
NIDASSERT(nil != reuseIdentifier);
if (nil == reuseIdentifier) {
return;
}
NSMutableArray* views = [_reuseIdentifiersToRecycledViews objectForKey:reuseIdentifier];
if (nil == views) {
views = [[NSMutableArray alloc] init];
[_reuseIdentifiersToRecycledViews setObject:views forKey:reuseIdentifier];
}
[views addObject:view];
}
- (void)removeAllViews {
[_reuseIdentifiersToRecycledViews removeAllObjects];
}
@end
@implementation NIRecyclableView
- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier {
if ((self = [super initWithFrame:CGRectZero])) {
_reuseIdentifier = reuseIdentifier;
}
return self;
}
- (id)initWithFrame:(CGRect)frame {
return [self initWithReuseIdentifier:nil];
}
@end

26
Pods/Nimbus/src/core/src/NimbusCore+Additions.h

@ -1,26 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// All category documentation is found in the source files due to limitations of Doxygen.
// Look for the documentation in the Classes tab of the documentation.
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "NimbusCore.h"
// Additions
#import "UIResponder+NimbusCore.h"

120
Pods/Nimbus/src/core/src/NimbusCore.h

@ -1,120 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
/**
* @defgroup NimbusCore Nimbus Core
*
* <div id="github" feature="core"></div>
*
* Nimbus' Core defines the foundation upon which all other Nimbus features are built.
* Within the core you will find common elements used to build iOS applications
* including in-memory caches, path manipulation, and SDK availability. These features form
* the foundation upon which all other Nimbus libraries are built.
*
* <h2>How Features are Added to the Core</h2>
*
* As a general rule of thumb, if something is used between multiple independent libraries or
* applications with little variation, it likely qualifies to be added to the Core.
*
* <h3>Exceptions</h3>
*
* Standalone user interface components are <i>rarely</i> acceptable features to add to the Core.
* For example: photo viewers, pull to refresh, launchers, attributed labels.
*
* Nimbus is not UIKit: we don't have the privilege of being an assumed cost on every iOS
* device. Developers must carefully weigh whether it is worth adding a Nimbus feature - along
* with its dependencies - over building the feature themselves or using another library. This
* means that an incredible amount of care must be placed into deciding what gets added to the
* Core.
*
* <h2>How Features are Removed from the Core</h2>
*
* It is inevitable that certain aspects of the Core will grow and develop over time. If a
* feature gets to the point where the value of being a separate library is greater than the
* overhead of managing such a library, then the feature should be considered for removal
* from the Core.
*
* Great care must be taken to ensure that Nimbus doesn't become a framework composed of
* hundreds of miniscule libraries.
*
* <h2>Common autoresizing masks</h2>
*
* Nimbus provides the following macros: UIViewAutoresizingFlexibleMargins,
* UIViewAutoresizingFlexibleDimensions, UIViewAutoresizingNavigationBar, and
* UIViewAutoresizingToolbarBar.
*
@code
// Create a view that fills its superview's bounds.
UIView* contentView = [[UIView alloc] initWithFrame:self.view.bounds];
contentView.autoresizingMask = UIViewAutoresizingFlexibleDimensions;
[self.view addSubview:contentView];
// Create a view that is always centered in the superview's bounds.
UIView* centeredView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
centeredView.autoresizingMask = UIViewAutoresizingFlexibleMargins;
// Center the view within the superview however you choose.
[self.view addSubview:centeredView];
// Create a navigation bar that stays fixed to the top.
UINavigationBar* navBar = [[UINavigationBar alloc] initWithFrame:CGRectZero];
[navBar sizeToFit];
navBar.autoresizingMask = UIViewAutoresizingNavigationBar;
[self.view addSubview:navBar];
// Create a toolbar that stays fixed to the bottom.
UIToolbar* toolBar = [[UIToolbar alloc] initWithFrame:CGRectZero];
[toolBar sizeToFit];
toolBar.autoresizingMask = UIViewAutoresizingToolbarBar;
[self.view addSubview:toolBar];
@endcode
*
* <h3>Why they exist</h3>
*
* Using the existing UIViewAutoresizing flags can be tedious for common flags.
*
* For example, to make a view have flexible margins you would need to write four flags:
*
@code
view.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin
| UIViewAutoresizingFlexibleTopMargin
| UIViewAutoresizingFlexibleRightMargin
| UIViewAutoresizingFlexibleBottomMargin);
@endcode
*/
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "NIActions.h"
#import "NIButtonUtilities.h"
#import "NICommonMetrics.h"
#import "NIDebuggingTools.h"
#import "NIDeviceOrientation.h"
#import "NIError.h"
#import "NIFoundationMethods.h"
#import "NIImageUtilities.h"
#import "NIInMemoryCache.h"
#import "NINetworkActivity.h"
#import "NINonEmptyCollectionTesting.h"
#import "NINonRetainingCollections.h"
#import "NIOperations.h"
#import "NIPaths.h"
#import "NIPreprocessorMacros.h"
#import "NIRuntimeClassModifications.h"
#import "NISDKAvailability.h"
#import "NISnapshotRotation.h"
#import "NIState.h"
#import "NIViewRecycler.h"

26
Pods/Nimbus/src/core/src/UIResponder+NimbusCore.h

@ -1,26 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// +currentFirstResponder originally written by Jakob Egger, adapted by Jeff Verkoeyen.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <UIKit/UIKit.h>
// Documentation for these additions is found in the .m file.
@interface UIResponder (NimbusCore)
+ (instancetype)nimbus_currentFirstResponder;
@end

49
Pods/Nimbus/src/core/src/UIResponder+NimbusCore.m

@ -1,49 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// +currentFirstResponder originally written by Jakob Egger, adapted by Jeff Verkoeyen.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "UIResponder+NimbusCore.h"
#import "NIPreprocessorMacros.h"
// Adapted from http://stackoverflow.com/questions/5029267/is-there-any-way-of-asking-an-ios-view-which-of-its-children-has-first-responder/14135456#14135456
static __weak id sCurrentFirstResponder = nil;
NI_FIX_CATEGORY_BUG(UIResponderNimbusCore)
/**
* For working with UIResponders.
*/
@implementation UIResponder (NimbusCore)
/**
* Returns the current first responder by sending an action from the UIApplication.
*
* The implementation was adapted from http://stackoverflow.com/questions/5029267/is-there-any-way-of-asking-an-ios-view-which-of-its-children-has-first-responder/14135456#14135456
*/
+ (instancetype)nimbus_currentFirstResponder {
sCurrentFirstResponder = nil;
[[UIApplication sharedApplication] sendAction:@selector(nimbus_findFirstResponder:)
to:nil from:nil forEvent:nil];
return sCurrentFirstResponder;
}
- (void)nimbus_findFirstResponder:(id)sender {
sCurrentFirstResponder = self;
}
@end

123
Pods/Nimbus/src/pagingscrollview/src/NIPagingScrollView+Subclassing.h

@ -1,123 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIPagingScrollView.h"
// Methods that are meant to be subclassed.
@interface NIPagingScrollView (Subclassing)
// Meant to be subclassed. Default implementations are stubs.
- (void)willDisplayPage:(UIView<NIPagingScrollViewPage> *)pageView;
- (void)didRecyclePage:(UIView<NIPagingScrollViewPage> *)pageView;
- (void)didReloadNumberOfPages;
- (void)didChangeCenterPageIndexFrom:(NSInteger)from to:(NSInteger)to;
// Meant to be subclassed.
- (UIView<NIPagingScrollViewPage> *)loadPageAtIndex:(NSInteger)pageIndex;
#pragma mark Accessing Child Views
- (UIScrollView *)scrollView;
- (NSMutableSet *)visiblePages; // Set of UIView<NIPagingScrollViewPage>*
@end
// Methods that are not meant to be subclassed.
@interface NIPagingScrollView (ProtectedMethods)
- (void)setCenterPageIndexIvar:(NSInteger)centerPageIndex;
- (void)recyclePageAtIndex:(NSInteger)pageIndex;
- (void)displayPageAtIndex:(NSInteger)pageIndex;
- (CGFloat)pageScrollableDimension;
- (void)layoutVisiblePages;
@end
/**
* Called before the page is about to be shown and after its frame has been set.
*
* Meant to be subclassed. By default this method does nothing.
*
* @fn NIPagingScrollView::willDisplayPage:
*/
/**
* Called immediately after the page is removed from the paging scroll view.
*
* Meant to be subclassed. By default this method does nothing.
*
* @fn NIPagingScrollView::didRecyclePage:
*/
/**
* Called immediately after the data source has been queried for its number of
* pages.
*
* Meant to be subclassed. By default this method does nothing.
*
* @fn NIPagingScrollView::didReloadNumberOfPages
*/
/**
* Called when the visible page has changed.
*
* Meant to be subclassed. By default this method does nothing.
*
* @fn NIPagingScrollView::didChangeCenterPageIndexFrom:to:
*/
/**
* Called when a page needs to be loaded before it is displayed.
*
* By default this method asks the data source for the page at the given index.
* A subclass may chose to modify the page index using a transformation method
* before calling super.
*
* @fn NIPagingScrollView::loadPageAtIndex:
*/
/**
* Sets the centerPageIndex ivar without side effects.
*
* @fn NIPagingScrollView::setCenterPageIndexIvar:
*/
/**
* Recycles the page at the given index.
*
* @fn NIPagingScrollView::recyclePageAtIndex:
*/
/**
* Displays the page at the given index.
*
* @fn NIPagingScrollView::displayPageAtIndex:
*/
/**
* Returns the page's scrollable dimension.
*
* This is the width of the paging scroll view for horizontal scroll views, or
* the height of the paging scroll view for vertical scroll views.
*
* @fn NIPagingScrollView::pageScrollableDimension
*/
/**
* Updates the frames of all visible pages based on their page indices.
*
* @fn NIPagingScrollView::layoutVisiblePages
*/

386
Pods/Nimbus/src/pagingscrollview/src/NIPagingScrollView.h

@ -1,386 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
// Copyright 2012 Manu Cornet (vertical layouts)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "NimbusCore.h"
/**
* numberOfPages will be this value until reloadData is called.
*/
extern const NSInteger NIPagingScrollViewUnknownNumberOfPages;
/**
* The default number of pixels on the side of each page.
*
* Value: 10
*/
extern const CGFloat NIPagingScrollViewDefaultPageMargin;
typedef enum {
NIPagingScrollViewHorizontal = 0,
NIPagingScrollViewVertical,
} NIPagingScrollViewType;
@protocol NIPagingScrollViewDataSource;
@protocol NIPagingScrollViewDelegate;
@protocol NIPagingScrollViewPage;
@class NIViewRecycler;
/**
* The NIPagingScrollView class provides a UITableView-like interface for loading pages via a data
* source.
*
* @ingroup NimbusPagingScrollView
*/
@interface NIPagingScrollView : UIView <UIScrollViewDelegate>
#pragma mark Data Source
- (void)reloadData;
@property (nonatomic, weak) id<NIPagingScrollViewDataSource> dataSource;
@property (nonatomic, weak) id<NIPagingScrollViewDelegate> delegate;
// It is highly recommended that you use this method to manage view recycling.
- (UIView<NIPagingScrollViewPage> *)dequeueReusablePageWithIdentifier:(NSString *)identifier;
#pragma mark State
- (UIView<NIPagingScrollViewPage> *)centerPageView;
@property (nonatomic) NSInteger centerPageIndex; // Use moveToPageAtIndex:animated: to animate to a given page.
@property (nonatomic, readonly) NSInteger numberOfPages;
#pragma mark Configuring Presentation
// Controls the border between pages.
@property (nonatomic) CGFloat pageMargin;
// Used to make the view smaller than the frame of the paging scroll view, thus showing
// neighboring pages, either horizontally or vertically depending on the configuration
// of the view.
@property (nonatomic) CGFloat pageInset;
@property (nonatomic) NIPagingScrollViewType type; // Default: NIPagingScrollViewHorizontal
#pragma mark Visible Pages
- (BOOL)hasNext;
- (BOOL)hasPrevious;
- (void)moveToNextAnimated:(BOOL)animated;
- (void)moveToPreviousAnimated:(BOOL)animated;
- (BOOL)moveToPageAtIndex:(NSInteger)pageIndex animated:(BOOL)animated updateVisiblePagesWhileScrolling:(BOOL)updateVisiblePagesWhileScrolling;
// Short form for moveToPageAtIndex:pageIndex animated:animated updateVisiblePagesWhileScrolling:NO
- (BOOL)moveToPageAtIndex:(NSInteger)pageIndex animated:(BOOL)animated;
#pragma mark Rotating the Scroll View
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration;
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration;
@end
/**
* The delegate for NIPagingScrollView.
*
* @ingroup NimbusPagingScrollView
*/
@protocol NIPagingScrollViewDelegate <UIScrollViewDelegate>
@optional
#pragma mark Scrolling and Zooming /** @name [NIPhotoAlbumScrollViewDelegate] Scrolling and Zooming */
/**
* The user is scrolling between two photos.
*/
- (void)pagingScrollViewDidScroll:(NIPagingScrollView *)pagingScrollView;
#pragma mark Changing Pages /** @name [NIPagingScrollViewDelegate] Changing Pages */
/**
* The current page will change.
*
* pagingScrollView.centerPageIndex will reflect the old page index, not the new
* page index.
*/
- (void)pagingScrollViewWillChangePages:(NIPagingScrollView *)pagingScrollView;
/**
* The current page has changed.
*
* pagingScrollView.centerPageIndex will reflect the changed page index.
*/
- (void)pagingScrollViewDidChangePages:(NIPagingScrollView *)pagingScrollView;
@end
/**
* The data source for NIPagingScrollView.
*
* @ingroup NimbusPagingScrollView
*/
@protocol NIPagingScrollViewDataSource <NSObject>
@required
#pragma mark Fetching Required Album Information /** @name [NIPagingScrollViewDataSource] Fetching Required Album Information */
/**
* Fetches the total number of pages in the scroll view.
*
* The value returned in this method will be cached by the scroll view until reloadData
* is called again.
*/
- (NSInteger)numberOfPagesInPagingScrollView:(NIPagingScrollView *)pagingScrollView;
/**
* Fetches a page that will be displayed at the given page index.
*
* You should always try to reuse pages by calling dequeueReusablePageWithIdentifier: on the
* paging scroll view before allocating a new page.
*/
- (UIView<NIPagingScrollViewPage> *)pagingScrollView:(NIPagingScrollView *)pagingScrollView pageViewForIndex:(NSInteger)pageIndex;
@end
/**
* The protocol that a paging scroll view page should implement.
*
* By providing a protocol instead of a UIView base class we allow more flexibility when
* building pages.
*
* @ingroup NimbusPagingScrollView
*/
@protocol NIPagingScrollViewPage <NIRecyclableView>
@required
/**
* The index of this page view.
*/
@property (nonatomic, assign) NSInteger pageIndex;
@optional
/**
* Called after the page has gone off-screen.
*
* This method should be used to reset any state information after a page goes off-screen.
* For example, in the Nimbus photo viewer we reset the zoom scale so that if the photo
* was zoomed in it will fit on the screen again when the user flips back and forth between
* two pages.
*/
- (void)pageDidDisappear;
/**
* Called when the frame of the page is going to change.
*
* Use this method to maintain any state that may be affected by the frame changing.
* The Nimbus photo viewer uses this method to save and restore the zoom and center
* point. This makes the photo always appear to rotate around the center point of the screen
* rather than the center of the photo.
*/
- (void)setFrameAndMaintainState:(CGRect)frame;
@end
/** @name Data Source */
/**
* The data source for this page album view.
*
* This is the only means by which this paging view acquires any information about the
* album to be displayed.
*
* @fn NIPagingScrollView::dataSource
*/
/**
* Force the view to reload its data by asking the data source for information.
*
* This must be called at least once after dataSource has been set in order for the view
* to gather any presentable information.
*
* This method is cheap because we only fetch new information about the currently displayed
* pages. If the number of pages shrinks then the current center page index will be decreased
* accordingly.
*
* @fn NIPagingScrollView::reloadData
*/
/**
* Dequeues a reusable page from the set of recycled pages.
*
* If no pages have been recycled for the given identifier then this will return nil.
* In this case it is your responsibility to create a new page.
*
* @fn NIPagingScrollView::dequeueReusablePageWithIdentifier:
*/
/**
* The delegate for this paging view.
*
* Any user interactions or state changes are sent to the delegate through this property.
*
* @fn NIPagingScrollView::delegate
*/
/** @name Configuring Presentation */
/**
* The number of pixels on either side of each page.
*
* The space between each page will be 2x this value.
*
* By default this is NIPagingScrollViewDefaultPageMargin.
*
* @fn NIPagingScrollView::pageMargin
*/
/**
* The type of paging scroll view to display.
*
* This property allows you to configure whether you want a horizontal or vertical paging scroll
* view. You should set this property before you present the scroll view and not modify it after.
*
* By default this is NIPagingScrollViewHorizontal.
*
* @fn NIPagingScrollView::type
*/
/** @name State */
/**
* The current center page view.
*
* If no pages exist then this will return nil.
*
* @fn NIPagingScrollView::centerPageView
*/
/**
* The current center page index.
*
* This is a zero-based value. If you intend to use this in a label such as "page ## of n" be
* sure to add one to this value.
*
* Setting this value directly will center the new page without any animation.
*
* @fn NIPagingScrollView::centerPageIndex
*/
/**
* Change the center page index with optional animation.
*
* This method is deprecated in favor of
* @link NIPagingScrollView::moveToPageAtIndex:animated: moveToPageAtIndex:animated:@endlink
*
* @fn NIPagingScrollView::setCenterPageIndex:animated:
*/
/**
* The total number of pages in this paging view, as gathered from the data source.
*
* This value is cached after reloadData has been called.
*
* Until reloadData is called the first time, numberOfPages will be
* NIPagingScrollViewUnknownNumberOfPages.
*
* @fn NIPagingScrollView::numberOfPages
*/
/** @name Changing the Visible Page */
/**
* Returns YES if there is a next page.
*
* @fn NIPagingScrollView::hasNext
*/
/**
* Returns YES if there is a previous page.
*
* @fn NIPagingScrollView::hasPrevious
*/
/**
* Move to the next page if there is one.
*
* @fn NIPagingScrollView::moveToNextAnimated:
*/
/**
* Move to the previous page if there is one.
*
* @fn NIPagingScrollView::moveToPreviousAnimated:
*/
/**
* Move to the given page index with optional animation.
*
* @returns NO if a page change animation is already in effect and we couldn't change the page
* again.
* @fn NIPagingScrollView::moveToPageAtIndex:animated:
*/
/**
* Move to the given page index with optional animation and option to enable page updates while
* scrolling.
*
* NOTE: Passing YES for moveToPageAtIndex:animated:updateVisiblePagesWhileScrolling will cause
* every page from the present page to the destination page to be loaded. This has the potential to
* cause choppy animations.
*
* @param updateVisiblePagesWhileScrolling If YES, will query the data source for any pages
* that become visible while the animation occurs.
* @returns NO if a page change animation is already in effect and we couldn't change the page
* again.
* @fn NIPagingScrollView::moveToPageAtIndex:animated:updateVisiblePagesWhileScrolling:
*/
/** @name Rotating the Scroll View */
/**
* Stores the current state of the scroll view in preparation for rotation.
*
* This must be called in conjunction with willAnimateRotationToInterfaceOrientation:duration:
* in the methods by the same name from the view controller containing this view.
*
* @fn NIPagingScrollView::willRotateToInterfaceOrientation:duration:
*/
/**
* Updates the frame of the scroll view while maintaining the current visible page's state.
*
* @fn NIPagingScrollView::willAnimateRotationToInterfaceOrientation:duration:
*/
/** @name Subclassing */
/**
* The internal scroll view.
*
* Meant to be used by subclasses only.
*
* @fn NIPagingScrollView::pagingScrollView
*/
/**
* The set of currently visible pages.
*
* Meant to be used by subclasses only.
*
* @fn NIPagingScrollView::visiblePages
*/

760
Pods/Nimbus/src/pagingscrollview/src/NIPagingScrollView.m

@ -1,760 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
// Copyright 2012 Manu Cornet (vertical layouts)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIPagingScrollView.h"
#import "NIPagingScrollView+Subclassing.h"
#import "NimbusCore.h"
#import <objc/runtime.h>
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
const NSInteger NIPagingScrollViewUnknownNumberOfPages = -1;
const CGFloat NIPagingScrollViewDefaultPageMargin = 10;
const CGFloat NIPagingScrollViewDefaultPageInset = 0;
@implementation NIPagingScrollView {
NIViewRecycler* _viewRecycler;
UIScrollView* _scrollView;
NSMutableSet* _visiblePages;
// Animating to Pages
NSInteger _animatingToPageIndex;
BOOL _isKillingAnimation;
NSInteger _queuedAnimationPageIndex;
BOOL _shouldUpdateVisiblePagesWhileScrolling;
// Rotation State
NSInteger _firstVisiblePageIndexBeforeRotation;
CGFloat _percentScrolledIntoFirstVisiblePage;
}
- (void)commonInit {
// Default state.
self.pageMargin = NIPagingScrollViewDefaultPageMargin;
self.pageInset = NIPagingScrollViewDefaultPageInset;
self.type = NIPagingScrollViewHorizontal;
// Internal state
_animatingToPageIndex = -1;
_firstVisiblePageIndexBeforeRotation = -1;
_percentScrolledIntoFirstVisiblePage = -1;
_centerPageIndex = -1;
_numberOfPages = NIPagingScrollViewUnknownNumberOfPages;
_viewRecycler = [[NIViewRecycler alloc] init];
// The internal scroll view that powers this paging scroll view.
_scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
_scrollView.pagingEnabled = YES;
_scrollView.scrollsToTop = NO;
// Allows the scroll view to show adjacent pages...
_scrollView.clipsToBounds = NO;
// ...while still clipping contents to the bounds of the paging scroll view.
self.clipsToBounds = YES;
_scrollView.autoresizingMask = UIViewAutoresizingFlexibleDimensions;
_scrollView.delegate = self;
_scrollView.showsVerticalScrollIndicator = NO;
_scrollView.showsHorizontalScrollIndicator = NO;
[self addSubview:_scrollView];
}
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
[self commonInit];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
[self commonInit];
}
return self;
}
#pragma mark - Page Layout
- (void)layoutSubviews {
[super layoutSubviews];
_scrollView.frame = [self frameForPagingScrollView];
// Retain the current position.
CGPoint offset = [self frameForPageAtIndex:_centerPageIndex].origin;
_scrollView.contentOffset = [self contentOffsetFromPageOffset:offset];
_scrollView.contentSize = [self contentSizeForPagingScrollView];
[self layoutVisiblePages];
}
// The following three methods are from Apple's ImageScrollView example application and have
// been used here because they are well-documented and concise.
- (CGRect)frameForPagingScrollView {
CGRect frame = self.bounds;
if (NIPagingScrollViewHorizontal == self.type) {
// We make the paging scroll view a little bit wider on the side edges so that there
// there is space between the pages when flipping through them.
frame = CGRectInset(frame, self.pageInset - self.pageMargin, 0);
} else if (NIPagingScrollViewVertical == self.type) {
frame = CGRectInset(frame, 0, self.pageInset - self.pageMargin);
}
return frame;
}
- (CGRect)frameForPageAtIndex:(NSInteger)pageIndex {
// We have to use our paging scroll view's bounds, not frame, to calculate the page
// placement. When the device is in landscape orientation, the frame will still be in
// portrait because the pagingScrollView is the root view controller's view, so its
// frame is in window coordinate space, which is never rotated. Its bounds, however,
// will be in landscape because it has a rotation transform applied.
CGRect bounds = _scrollView.bounds;
CGRect pageFrame = bounds;
if (NIPagingScrollViewHorizontal == self.type) {
pageFrame.origin.x = (bounds.size.width * pageIndex);
// We need to counter the extra spacing added to the paging scroll view in
// frameForPagingScrollView.
pageFrame = CGRectInset(pageFrame, self.pageMargin, 0);
} else if (NIPagingScrollViewVertical == self.type) {
pageFrame.origin.y = (bounds.size.height * pageIndex);
pageFrame = CGRectInset(pageFrame, 0, self.pageMargin);
}
return pageFrame;
}
- (CGSize)contentSizeForPagingScrollView {
// We use the paging scroll view's bounds to calculate the contentSize, for the same reason
// outlined above.
CGRect bounds = _scrollView.bounds;
if (NIPagingScrollViewHorizontal == self.type) {
return CGSizeMake(bounds.size.width * self.numberOfPages, bounds.size.height);
} else if (NIPagingScrollViewVertical == self.type) {
return CGSizeMake(bounds.size.width, bounds.size.height * self.numberOfPages);
}
return CGSizeZero;
}
- (CGPoint)contentOffsetFromPageOffset:(CGPoint)offset {
if (NIPagingScrollViewHorizontal == self.type) {
offset.x -= self.pageMargin;
} else if (NIPagingScrollViewVertical == self.type) {
offset.y -= self.pageMargin;
}
return offset;
}
- (CGFloat)pageScrollableDimension {
if (NIPagingScrollViewHorizontal == self.type) {
return _scrollView.bounds.size.width;
} else if (NIPagingScrollViewVertical == self.type) {
return _scrollView.bounds.size.height;
}
return 0;
}
- (CGPoint)contentOffsetFromOffset:(CGFloat)offset {
if (NIPagingScrollViewHorizontal == self.type) {
return CGPointMake(offset, 0);
} else if (NIPagingScrollViewVertical == self.type) {
return CGPointMake(0, offset);
}
return CGPointMake(0, 0);
}
- (CGFloat)scrolledPageOffset {
if (NIPagingScrollViewHorizontal == self.type) {
return _scrollView.contentOffset.x;
} else if (NIPagingScrollViewVertical == self.type) {
return _scrollView.contentOffset.y;
}
return 0;
}
#pragma mark - Visible Page Management
- (BOOL)isDisplayingPageForIndex:(NSInteger)pageIndex {
BOOL foundPage = NO;
// There will never be more than a handful (3 without insets) of visible pages in this array, so this lookup is
// effectively O(C) constant time.
for (UIView <NIPagingScrollViewPage>* page in _visiblePages) {
if (page.pageIndex == pageIndex) {
foundPage = YES;
break;
}
}
return foundPage;
}
- (NSInteger)currentVisiblePageIndex {
CGPoint contentOffset = _scrollView.contentOffset;
CGSize boundsSize = _scrollView.bounds.size;
if (NIPagingScrollViewHorizontal == self.type) {
// Whatever image is currently displayed in the center of the screen is the currently
// visible image.
return NIBoundi((NSInteger)(NICGFloatFloor((contentOffset.x + boundsSize.width / 2) / boundsSize.width)
+ 0.5f),
0, self.numberOfPages - 1);
} else if (NIPagingScrollViewVertical == self.type) {
return NIBoundi((NSInteger)(NICGFloatFloor((contentOffset.y + boundsSize.height / 2) / boundsSize.height)
+ 0.5f),
0, self.numberOfPages - 1);
}
return 0;
}
- (NSRange)rangeOfVisiblePages {
if (0 >= self.numberOfPages) {
return NSMakeRange(0, 0);
}
NSInteger visibleRange = 1;
if (_pageInset != 0) {
CGSize boundsSize = _scrollView.bounds.size;
CGSize frameSize = self.frame.size;
visibleRange = (NSInteger)ceil(frameSize.width / (boundsSize.width + _pageMargin));
}
NSInteger currentVisiblePageIndex = [self currentVisiblePageIndex];
NSInteger firstVisiblePageIndex = NIBoundi(currentVisiblePageIndex - visibleRange, 0, self.numberOfPages - 1);
NSInteger lastVisiblePageIndex = NIBoundi(currentVisiblePageIndex + visibleRange, 0, self.numberOfPages - 1);
return NSMakeRange(firstVisiblePageIndex, lastVisiblePageIndex - firstVisiblePageIndex + 1);
}
- (void)willDisplayPage:(UIView<NIPagingScrollViewPage> *)pageView atIndex:(NSInteger)pageIndex {
pageView.pageIndex = pageIndex;
pageView.frame = [self frameForPageAtIndex:pageIndex];
[self willDisplayPage:pageView];
}
- (void)resetPage:(id<NIPagingScrollViewPage>)page {
if ([page respondsToSelector:@selector(pageDidDisappear)]) {
[page pageDidDisappear];
}
}
- (void)resetSurroundingPages {
for (id<NIPagingScrollViewPage> page in _visiblePages) {
if (page.pageIndex != self.centerPageIndex) {
[self resetPage:page];
}
}
}
- (UIView<NIPagingScrollViewPage> *)dequeueReusablePageWithIdentifier:(NSString *)identifier {
NIDASSERT(nil != identifier);
if (nil == identifier) {
return nil;
}
return (UIView<NIPagingScrollViewPage> *)[_viewRecycler dequeueReusableViewWithIdentifier:identifier];
}
- (UIView<NIPagingScrollViewPage> *)loadPageAtIndex:(NSInteger)pageIndex {
UIView<NIPagingScrollViewPage>* page = [self.dataSource pagingScrollView:self pageViewForIndex:pageIndex];
NIDASSERT([page isKindOfClass:[UIView class]]);
NIDASSERT([page conformsToProtocol:@protocol(NIPagingScrollViewPage)]);
if (nil == page || ![page isKindOfClass:[UIView class]]
|| ![page conformsToProtocol:@protocol(NIPagingScrollViewPage)]) {
// Bail out! This page is malformed.
return nil;
}
return page;
}
- (void)displayPageAtIndex:(NSInteger)pageIndex {
UIView<NIPagingScrollViewPage>* page = [self loadPageAtIndex:pageIndex];
if (nil == page) {
return;
}
// This will only be called once, before the page is shown.
[self willDisplayPage:page atIndex:pageIndex];
[_scrollView addSubview:page];
[_visiblePages addObject:page];
}
- (void)recyclePageAtIndex:(NSInteger)pageIndex {
for (UIView<NIPagingScrollViewPage>* page in [_visiblePages copy]) {
if (page.pageIndex == pageIndex) {
[_viewRecycler recycleView:page];
[page removeFromSuperview];
[self didRecyclePage:page];
[_visiblePages removeObject:page];
}
}
}
- (void)preloadOffscreenPages {
NSRange rangeOfVisiblePages = [self rangeOfVisiblePages];
for (NSUInteger pageIndex = rangeOfVisiblePages.location;
pageIndex < NSMaxRange(rangeOfVisiblePages); ++pageIndex) {
if (![self isDisplayingPageForIndex:pageIndex]) {
[self displayPageAtIndex:pageIndex];
}
}
}
- (void)updateVisiblePagesShouldNotifyDelegate:(BOOL)shouldNotifyDelegate {
// Before updating _centerPageIndex, notify delegate
if (shouldNotifyDelegate && (self.numberOfPages > 0) &&
([self currentVisiblePageIndex] != self.centerPageIndex) &&
[self.delegate respondsToSelector:@selector(pagingScrollViewWillChangePages:)]) {
[self.delegate pagingScrollViewWillChangePages:self];
}
NSRange rangeOfVisiblePages = [self rangeOfVisiblePages];
// Recycle no-longer-visible pages. We copy _visiblePages because we may modify it while we're
// iterating over it.
for (UIView<NIPagingScrollViewPage>* page in [_visiblePages copy]) {
if (!NSLocationInRange(page.pageIndex, rangeOfVisiblePages)) {
[_viewRecycler recycleView:page];
[page removeFromSuperview];
[self didRecyclePage:page];
[_visiblePages removeObject:page];
}
}
NSInteger oldCenterPageIndex = self.centerPageIndex;
if (self.numberOfPages > 0) {
_centerPageIndex = [self currentVisiblePageIndex];
[self didChangeCenterPageIndexFrom:oldCenterPageIndex to:_centerPageIndex];
if (_pageInset != 0) {
// Load all visible insetted pages immediately.
[self preloadOffscreenPages];
} else {
// Prioritize displaying the currently visible page.
if (![self isDisplayingPageForIndex:_centerPageIndex]) {
[self displayPageAtIndex:_centerPageIndex];
}
// Add missing pages after displaying the current page.
[self performSelector:@selector(preloadOffscreenPages)
withObject:nil
afterDelay:0];
}
} else {
_centerPageIndex = -1;
}
if (shouldNotifyDelegate && oldCenterPageIndex != _centerPageIndex
&& [self.delegate respondsToSelector:@selector(pagingScrollViewDidChangePages:)]) {
[self.delegate pagingScrollViewDidChangePages:self];
}
}
- (void)layoutVisiblePages {
for (UIView<NIPagingScrollViewPage>* page in _visiblePages) {
CGRect pageFrame = [self frameForPageAtIndex:page.pageIndex];
if ([page respondsToSelector:@selector(setFrameAndMaintainState:)]) {
[page setFrameAndMaintainState:pageFrame];
} else {
[page setFrame:pageFrame];
}
}
}
#pragma mark - UIView
- (void)setFrame:(CGRect)frame {
// We have to modify this method because it eventually leads to changing the content offset
// programmatically. When this happens we end up getting a scrollViewDidScroll: message
// during which we do not want to modify the visible pages because this is handled elsewhere.
[super setFrame:frame];
_scrollView.contentSize = [self contentSizeForPagingScrollView];
[self layoutVisiblePages];
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *view = [super hitTest:point withEvent:event];
// We must forward hits for the scrollView or else the smaller frame when
// it is inset will prevent touches outside the scrollView bounds.
if (view == self) {
return _scrollView;
}
return view;
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[self updateVisiblePagesShouldNotifyDelegate:YES];
_isKillingAnimation = NO;
if ([self.delegate respondsToSelector:_cmd]) {
[self.delegate scrollViewWillBeginDragging:scrollView];
}
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if ([scrollView isTracking] && [scrollView isDragging]) {
if ([self.delegate respondsToSelector:@selector(pagingScrollViewDidScroll:)]) {
[self.delegate pagingScrollViewDidScroll:self];
}
}
if (_shouldUpdateVisiblePagesWhileScrolling
&& ![scrollView isTracking] && ![scrollView isDragging]) {
[self updateVisiblePagesShouldNotifyDelegate:YES];
}
if ([self.delegate respondsToSelector:_cmd]) {
[self.delegate scrollViewDidScroll:scrollView];
}
if (_isKillingAnimation) {
// The content size is calculated based on the number of pages and the scroll view frame.
CGPoint offset = [self frameForPageAtIndex:_centerPageIndex].origin;
offset = [self contentOffsetFromPageOffset:offset];
_scrollView.contentOffset = offset;
}
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
_isKillingAnimation = NO;
if (!decelerate) {
[self updateVisiblePagesShouldNotifyDelegate:YES];
[self resetSurroundingPages];
}
if ([self.delegate respondsToSelector:_cmd]) {
[self.delegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self updateVisiblePagesShouldNotifyDelegate:YES];
[self resetSurroundingPages];
if ([self.delegate respondsToSelector:_cmd]) {
[self.delegate scrollViewDidEndDecelerating:scrollView];
}
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
if (_animatingToPageIndex >= 0) {
[self didAnimateToPage:_animatingToPageIndex];
if ([self.delegate respondsToSelector:_cmd]) {
[self.delegate scrollViewDidEndScrollingAnimation:scrollView];
}
}
}
#pragma mark - Forward UIScrollViewDelegate Methods
- (BOOL)shouldForwardSelectorToDelegate:(SEL)aSelector {
struct objc_method_description description;
// Only forward the selector if it's part of the UIScrollViewDelegate protocol.
description = protocol_getMethodDescription(@protocol(UIScrollViewDelegate),
aSelector,
NO,
YES);
BOOL isSelectorInScrollViewDelegate = (description.name != NULL && description.types != NULL);
return (isSelectorInScrollViewDelegate
&& [self.delegate respondsToSelector:aSelector]);
}
- (BOOL)respondsToSelector:(SEL)aSelector {
if ([super respondsToSelector:aSelector] == YES) {
return YES;
} else {
return [self shouldForwardSelectorToDelegate:aSelector];
}
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([self shouldForwardSelectorToDelegate:aSelector]) {
return self.delegate;
} else {
return nil;
}
}
#pragma mark - Subclassing
- (void)willDisplayPage:(UIView<NIPagingScrollViewPage> *)pageView {
// No-op.
}
- (void)didRecyclePage:(UIView<NIPagingScrollViewPage> *)pageView {
// No-op
}
- (void)didReloadNumberOfPages {
// No-op
}
- (void)didChangeCenterPageIndexFrom:(NSInteger)from to:(NSInteger)to {
// No-op
}
- (void)setCenterPageIndexIvar:(NSInteger)centerPageIndex {
_centerPageIndex = centerPageIndex;
}
#pragma mark - Public
- (void)reloadData {
_animatingToPageIndex = -1;
NIDASSERT(nil != _dataSource);
// Remove any visible pages from the view before we release the sets.
for (UIView<NIPagingScrollViewPage>* page in _visiblePages) {
[_viewRecycler recycleView:page];
[(UIView *)page removeFromSuperview];
[self didRecyclePage:page];
}
_visiblePages = nil;
// If there is no data source then we can't do anything particularly interesting.
if (nil == _dataSource) {
_scrollView.contentSize = self.bounds.size;
_scrollView.contentOffset = CGPointZero;
// May as well just get rid of all the views then.
[_viewRecycler removeAllViews];
return;
}
_visiblePages = [[NSMutableSet alloc] init];
// Cache the number of pages.
_numberOfPages = [_dataSource numberOfPagesInPagingScrollView:self];
_scrollView.frame = [self frameForPagingScrollView];
_scrollView.contentSize = [self contentSizeForPagingScrollView];
[self didReloadNumberOfPages];
NSInteger oldCenterPageIndex = _centerPageIndex;
if (oldCenterPageIndex >= 0) {
_centerPageIndex = NIBoundi(_centerPageIndex, 0, self.numberOfPages - 1);
if (![_scrollView isTracking] && ![_scrollView isDragging]) {
// The content size is calculated based on the number of pages and the scroll view frame.
CGPoint offset = [self frameForPageAtIndex:_centerPageIndex].origin;
offset = [self contentOffsetFromPageOffset:offset];
_scrollView.contentOffset = offset;
_isKillingAnimation = YES;
}
}
// Begin requesting the page information from the data source.
[self updateVisiblePagesShouldNotifyDelegate:NO];
}
- (void)willRotateToInterfaceOrientation: (UIInterfaceOrientation)toInterfaceOrientation
duration: (NSTimeInterval)duration {
// Here, our pagingScrollView bounds have not yet been updated for the new interface
// orientation. This is a good place to calculate the content offset that we will
// need in the new orientation.
CGFloat offset = [self scrolledPageOffset];
CGFloat pageScrollableDimension = [self pageScrollableDimension];
if (offset >= 0) {
_firstVisiblePageIndexBeforeRotation = (NSInteger)NICGFloatFloor(offset / pageScrollableDimension);
_percentScrolledIntoFirstVisiblePage = ((offset
- (_firstVisiblePageIndexBeforeRotation * pageScrollableDimension))
/ pageScrollableDimension);
} else {
_firstVisiblePageIndexBeforeRotation = 0;
_percentScrolledIntoFirstVisiblePage = offset / pageScrollableDimension;
}
}
- (void)willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation)toInterfaceOrientation
duration: (NSTimeInterval)duration {
// Recalculate contentSize based on current orientation.
_scrollView.contentSize = [self contentSizeForPagingScrollView];
[self layoutVisiblePages];
// Adjust contentOffset to preserve page location based on values collected prior to location.
CGFloat pageScrollableDimension = [self pageScrollableDimension];
CGFloat newOffset = ((_firstVisiblePageIndexBeforeRotation * pageScrollableDimension)
+ (_percentScrolledIntoFirstVisiblePage * pageScrollableDimension));
_scrollView.contentOffset = [self contentOffsetFromOffset:newOffset];
}
- (BOOL)hasNext {
return (self.centerPageIndex < self.numberOfPages - 1);
}
- (BOOL)hasPrevious {
return self.centerPageIndex > 0;
}
- (void)didAnimateToPage:(NSInteger)pageIndex {
_shouldUpdateVisiblePagesWhileScrolling = NO;
_animatingToPageIndex = -1;
if (_queuedAnimationPageIndex >= 0 && _queuedAnimationPageIndex != pageIndex) {
[self moveToPageAtIndex:_queuedAnimationPageIndex animated:YES];
return;
}
// Reset the content offset once the animation completes, just to be sure that the
// viewer sits on a page bounds even if we rotate the device while animating.
CGPoint offset = [self frameForPageAtIndex:pageIndex].origin;
offset = [self contentOffsetFromPageOffset:offset];
_scrollView.contentOffset = offset;
[self updateVisiblePagesShouldNotifyDelegate:YES];
}
- (BOOL)moveToPageAtIndex:(NSInteger)pageIndex animated:(BOOL)animated {
return [self moveToPageAtIndex:pageIndex animated:animated updateVisiblePagesWhileScrolling:NO];
}
- (BOOL)moveToPageAtIndex:(NSInteger)pageIndex animated:(BOOL)animated updateVisiblePagesWhileScrolling:(BOOL)updateVisiblePagesWhileScrolling {
if (_animatingToPageIndex >= 0) {
// Don't allow re-entry for sliding animations.
_queuedAnimationPageIndex = pageIndex;
return NO;
}
_shouldUpdateVisiblePagesWhileScrolling = updateVisiblePagesWhileScrolling;
_isKillingAnimation = NO;
_queuedAnimationPageIndex = -1;
CGPoint offset = [self frameForPageAtIndex:pageIndex].origin;
offset = [self contentOffsetFromPageOffset:offset];
// The paging scroll view won't actually animate if the offsets are identical.
animated = animated && !CGPointEqualToPoint(offset, _scrollView.contentOffset);
if (animated) {
_animatingToPageIndex = pageIndex;
}
[_scrollView setContentOffset:offset animated:animated];
if (!animated) {
[self resetSurroundingPages];
[self didAnimateToPage:pageIndex];
}
return YES;
}
- (void)moveToNextAnimated:(BOOL)animated {
if ([self hasNext]) {
NSInteger pageIndex = self.centerPageIndex + 1;
[self moveToPageAtIndex:pageIndex animated:animated];
}
}
- (void)moveToPreviousAnimated:(BOOL)animated {
if ([self hasPrevious]) {
NSInteger pageIndex = self.centerPageIndex - 1;
[self moveToPageAtIndex:pageIndex animated:animated];
}
}
- (UIView<NIPagingScrollViewPage> *)centerPageView {
for (UIView<NIPagingScrollViewPage>* page in _visiblePages) {
if (page.pageIndex == self.centerPageIndex) {
return page;
}
}
return nil;
}
- (void)setCenterPageIndex:(NSInteger)centerPageIndex {
[self moveToPageAtIndex:centerPageIndex animated:NO];
}
- (void)setPageMargin:(CGFloat)pageMargin {
_pageMargin = pageMargin;
[self setNeedsLayout];
}
- (void)setPageInset:(CGFloat)pageInset {
_pageInset = pageInset;
[self setNeedsLayout];
}
- (void)setType:(NIPagingScrollViewType)type {
if (_type != type) {
_type = type;
_scrollView.scrollsToTop = (type == NIPagingScrollViewVertical);
}
}
- (UIScrollView *)scrollView {
return _scrollView;
}
- (NSMutableSet *)visiblePages {
return _visiblePages;
}
@end

36
Pods/Nimbus/src/pagingscrollview/src/NIPagingScrollViewPage.h

@ -1,36 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <UIKit/UIKit.h>
#import "NIPagingScrollView.h"
/**
* A skeleton implementation of a page view.
*
* This view simply implements the required properties of NIPagingScrollViewPage.
*
* @ingroup NimbusPagingScrollView
*/
@interface NIPagingScrollViewPage : NIRecyclableView <NIPagingScrollViewPage>
@property (nonatomic) NSInteger pageIndex;
@end
/**
* The page index.
*
* @fn NIPagingScrollViewPage::pageIndex
*/

26
Pods/Nimbus/src/pagingscrollview/src/NIPagingScrollViewPage.m

@ -1,26 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIPagingScrollViewPage.h"
#import "NimbusCore.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
@implementation NIPagingScrollViewPage
@end

53
Pods/Nimbus/src/pagingscrollview/src/NimbusPagingScrollView.h

@ -1,53 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
/**
* @defgroup NimbusPagingScrollView Nimbus Paging Scroll View
* @{
*
* <div id="github" feature="pagingscrollview"></div>
*
* A paging scroll view is a UIScrollView that scrolls horizontally and shows a series of
* pages that are efficiently recycled.
*
* The Nimbus paging scroll view is powered by a datasource that allows you to separate the
* data from the view. This makes it easy to efficiently recycle pages and only create as many
* pages of content as may be visible at any given point in time. Nimbus' implementation also
* provides helpful features such as keeping the center page centered when the device changes
* orientation.
*
* Paging scroll views are commonly used in many iOS applications. For example, Nimbus' Photos
* feature uses a paging scroll view to power its NIPhotoAlbumScrollView.
*
* <h2>Building a Component with NIPagingScrollView</h2>
*
* NIPagingScrollView works much like a UITableView in that you must implement a data source
* and optionally a delegate. The data source fetches information about the contents of the
* paging scroll view, such as the total number of pages and the view for a given page when it
* is required. The views that you return for pages must conform to the NIPagingScrollViewPage
* protocol. This is similar to UITableViewCell, but rather than subclass a view you can simply
* implement a protocol. If you would prefer not to implement the protocol, you can subclass
* NIPageView which implements the required methods of NIPagingScrollViewPage.
*/
/**@}*/
#import "NIPagingScrollView.h"
#import "NIPagingScrollViewPage.h"
#import "NimbusCore.h"

170
Pods/Nimbus/src/photos/src/NIPhotoAlbumScrollView.h

@ -1,170 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIPhotoScrollViewDelegate.h"
#import "NIPhotoAlbumScrollViewDataSource.h"
#import "NIPhotoAlbumScrollViewDelegate.h"
#import "NimbusPagingScrollView.h"
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
/**
* A paged scroll view that shows a collection of photos.
*
* @ingroup NimbusPhotos
*
* This view provides a light-weight implementation of a photo viewer, complete with
* pinch-to-zoom and swiping to change photos. It is designed to perform well with
* large sets of photos and large images that are loaded from either the network or
* disk.
*
* It is intended for this view to be used in conjunction with a view controller that
* implements the data source protocol and presents any required chrome.
*
* @see NIToolbarPhotoViewController
*/
@interface NIPhotoAlbumScrollView : NIPagingScrollView <NIPhotoScrollViewDelegate>
#pragma mark Data Source
// For use in your pagingScrollView:pageForIndex: data source implementation.
- (UIView<NIPagingScrollViewPage> *)pagingScrollView:(NIPagingScrollView *)pagingScrollView pageViewForIndex:(NSInteger)pageIndex;
@property (nonatomic, weak) id<NIPhotoAlbumScrollViewDataSource> dataSource;
@property (nonatomic, weak) id<NIPhotoAlbumScrollViewDelegate> delegate;
#pragma mark Configuring Functionality
@property (nonatomic, assign, getter=isZoomingEnabled) BOOL zoomingIsEnabled;
@property (nonatomic, assign, getter=isZoomingAboveOriginalSizeEnabled) BOOL zoomingAboveOriginalSizeIsEnabled;
@property (nonatomic, strong) UIColor* photoViewBackgroundColor;
#pragma mark Configuring Presentation
@property (nonatomic, strong) UIImage* loadingImage;
#pragma mark Notifying the View of Loaded Photos
- (void)didLoadPhoto: (UIImage *)image
atIndex: (NSInteger)photoIndex
photoSize: (NIPhotoScrollViewPhotoSize)photoSize;
@end
/** @name Data Source */
/**
* The data source for this photo album view.
*
* This is the only means by which this photo album view acquires any information about the
* album to be displayed.
*
* @fn NIPhotoAlbumScrollView::dataSource
*/
/**
* Use this method in your implementation of NIPhotoAlbumScrollViewDataSource's
* pagingScrollView:pageForIndex:.
*
* Example:
*
@code
- (id<NIPagingScrollViewPage>)pagingScrollView:(NIPagingScrollView *)pagingScrollView pageForIndex:(NSInteger)pageIndex {
return [self.photoAlbumView pagingScrollView:pagingScrollView pageForIndex:pageIndex];
}
@endcode
*
* Automatically uses the paging scroll view's page recycling methods and creates
* NIPhotoScrollViews as needed.
*
* @fn NIPhotoAlbumScrollView::pagingScrollView:pageForIndex:
*/
/**
* The delegate for this photo album view.
*
* Any user interactions or state changes are sent to the delegate through this property.
*
* @fn NIPhotoAlbumScrollView::delegate
*/
/** @name Configuring Functionality */
/**
* Whether zooming is enabled or not.
*
* Regardless of whether this is enabled, only original-sized images will be zoomable.
* This is because we often don't know how large the final image is so we can't
* calculate min and max zoom amounts correctly.
*
* By default this is YES.
*
* @fn NIPhotoAlbumScrollView::zoomingIsEnabled
*/
/**
* Whether small photos can be zoomed at least until they fit the screen.
*
* @see NIPhotoScrollView::zoomingAboveOriginalSizeIsEnabled
*
* By default this is YES.
*
* @fn NIPhotoAlbumScrollView::zoomingAboveOriginalSizeIsEnabled
*/
/**
* The background color of each photo's view.
*
* By default this is [UIColor blackColor].
*
* @fn NIPhotoAlbumScrollView::photoViewBackgroundColor
*/
/** @name Configuring Presentation */
/**
* An image that is displayed while the photo is loading.
*
* This photo will be presented if no image is returned in the data source's implementation
* of photoAlbumScrollView:photoAtIndex:photoSize:isLoading:.
*
* Zooming is disabled when showing a loading image, regardless of the state of zoomingIsEnabled.
*
* By default this is nil.
*
* @fn NIPhotoAlbumScrollView::loadingImage
*/
/** @name Notifying the View of Loaded Photos */
/**
* Notify the scroll view that a photo has been loaded at a given index.
*
* You should notify the completed loading of thumbnails as well. Calling this method
* is fairly lightweight and will only update the images of the visible pages. Err on the
* side of calling this method too much rather than too little.
*
* The photo at the given index will only be replaced with the given image if photoSize
* is of a higher quality than the currently-displayed photo's size.
*
* @fn NIPhotoAlbumScrollView::didLoadPhoto:atIndex:photoSize:
*/

221
Pods/Nimbus/src/photos/src/NIPhotoAlbumScrollView.m

@ -1,221 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIPhotoAlbumScrollView.h"
#import "NIPagingScrollView+Subclassing.h"
#import "NIPhotoScrollView.h"
#import "NIPhotoAlbumScrollViewDataSource.h"
#import "NimbusCore.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
@implementation NIPhotoAlbumScrollView {
// Configurable Properties
UIImage* _loadingImage;
BOOL _zoomingIsEnabled;
BOOL _zoomingAboveOriginalSizeIsEnabled;
}
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Default state.
self.zoomingIsEnabled = YES;
self.zoomingAboveOriginalSizeIsEnabled = YES;
}
return self;
}
- (void)setBackgroundColor:(UIColor *)backgroundColor {
[super setBackgroundColor:backgroundColor];
self.scrollView.backgroundColor = backgroundColor;
}
- (void)notifyDelegatePhotoDidLoadAtIndex:(NSInteger)photoIndex {
if (photoIndex == (self.centerPageIndex + 1)
&& [self.delegate respondsToSelector:@selector(photoAlbumScrollViewDidLoadNextPhoto:)]) {
[self.delegate photoAlbumScrollViewDidLoadNextPhoto:self];
} else if (photoIndex == (self.centerPageIndex - 1)
&& [self.delegate respondsToSelector:@selector(photoAlbumScrollViewDidLoadPreviousPhoto:)]) {
[self.delegate photoAlbumScrollViewDidLoadPreviousPhoto:self];
}
}
#pragma mark - Visible Page Management
- (void)willDisplayPage:(NIPhotoScrollView *)page {
// When we ask the data source for the image we expect the following to happen:
// 1) If the data source has any image at this index, it should return it and set the
// photoSize accordingly.
// 2) If the returned photo is not the highest quality available, the data source should
// start loading the high quality photo and set isLoading to YES.
// 3) If no photo was available, then the data source should start loading the photo
// at its highest available quality and nil should be returned. The loadingImage property
// will be displayed until the image is loaded. isLoading should be set to YES.
NIPhotoScrollViewPhotoSize photoSize = NIPhotoScrollViewPhotoSizeUnknown;
BOOL isLoading = NO;
CGSize originalPhotoDimensions = CGSizeZero;
UIImage* image = [self.dataSource photoAlbumScrollView: self
photoAtIndex: page.pageIndex
photoSize: &photoSize
isLoading: &isLoading
originalPhotoDimensions: &originalPhotoDimensions];
page.photoDimensions = originalPhotoDimensions;
// Only mark the view as loading if the center image is loading.
page.loading = (page.pageIndex == self.centerPageIndex) && isLoading;
if (nil == image) {
page.zoomingIsEnabled = NO;
[page setImage:self.loadingImage photoSize:NIPhotoScrollViewPhotoSizeUnknown];
} else {
BOOL updateImage = photoSize > page.photoSize;
if (updateImage) {
[page setImage:image photoSize:photoSize];
}
// Configure this after the image is set otherwise if the page's image isn't there
// e.g. (after prepareForReuse), zooming will always be disabled
page.zoomingIsEnabled = ([self isZoomingEnabled]
&& (NIPhotoScrollViewPhotoSizeOriginal == photoSize));
if (updateImage && NIPhotoScrollViewPhotoSizeOriginal == photoSize) {
[self notifyDelegatePhotoDidLoadAtIndex:page.pageIndex];
}
}
}
- (void)didRecyclePage:(UIView<NIPagingScrollViewPage> *)page {
// Give the data source the opportunity to kill any asynchronous operations for this
// now-recycled page.
if ([self.dataSource respondsToSelector:
@selector(photoAlbumScrollView:stopLoadingPhotoAtIndex:)]) {
[self.dataSource photoAlbumScrollView: self
stopLoadingPhotoAtIndex: page.pageIndex];
}
}
#pragma mark - NIPhotoScrollViewDelegate
- (void)photoScrollViewDidDoubleTapToZoom: (NIPhotoScrollView *)photoScrollView
didZoomIn: (BOOL)didZoomIn {
if ([self.delegate respondsToSelector:@selector(photoAlbumScrollView:didZoomIn:)]) {
[self.delegate photoAlbumScrollView:self didZoomIn:didZoomIn];
}
}
#pragma mark - Public
- (UIView<NIPagingScrollViewPage> *)pagingScrollView:(NIPagingScrollView *)pagingScrollView
pageViewForIndex:(NSInteger)pageIndex {
UIView<NIPagingScrollViewPage>* pageView = nil;
NSString* reuseIdentifier = @"photo";
pageView = [pagingScrollView dequeueReusablePageWithIdentifier:reuseIdentifier];
if (nil == pageView) {
pageView = [[NIPhotoScrollView alloc] init];
pageView.reuseIdentifier = reuseIdentifier;
pageView.backgroundColor = self.photoViewBackgroundColor;
}
NIPhotoScrollView* photoScrollView = (NIPhotoScrollView *)pageView;
photoScrollView.photoScrollViewDelegate = self;
photoScrollView.zoomingAboveOriginalSizeIsEnabled = [self isZoomingAboveOriginalSizeEnabled];
return pageView;
}
- (void)didLoadPhoto: (UIImage *)image
atIndex: (NSInteger)pageIndex
photoSize: (NIPhotoScrollViewPhotoSize)photoSize {
// This modifies the UI and therefor MUST be executed on the main thread.
NIDASSERT([NSThread isMainThread]);
for (NIPhotoScrollView* page in self.visiblePages) {
if (page.pageIndex == pageIndex) {
// Only replace the photo if it's of a higher quality than one we're already showing.
if (photoSize > page.photoSize) {
page.loading = NO;
[page setImage:image photoSize:photoSize];
page.zoomingIsEnabled = ([self isZoomingEnabled]
&& (NIPhotoScrollViewPhotoSizeOriginal == photoSize));
// Notify the delegate that the photo has been loaded.
if (NIPhotoScrollViewPhotoSizeOriginal == photoSize) {
[self notifyDelegatePhotoDidLoadAtIndex:pageIndex];
}
}
break;
}
}
}
- (void)setZoomingAboveOriginalSizeIsEnabled:(BOOL)enabled {
_zoomingAboveOriginalSizeIsEnabled = enabled;
for (NIPhotoScrollView* page in self.visiblePages) {
page.zoomingAboveOriginalSizeIsEnabled = enabled;
}
}
- (void)setPhotoViewBackgroundColor:(UIColor *)photoViewBackgroundColor {
if (_photoViewBackgroundColor != photoViewBackgroundColor) {
_photoViewBackgroundColor = photoViewBackgroundColor;
for (UIView<NIPagingScrollViewPage>* page in self.visiblePages) {
page.backgroundColor = photoViewBackgroundColor;
}
}
}
- (BOOL)hasNext {
return (self.centerPageIndex < self.numberOfPages - 1);
}
- (BOOL)hasPrevious {
return self.centerPageIndex > 0;
}
- (id<NIPhotoAlbumScrollViewDataSource>)dataSource {
NIDASSERT([[super dataSource] conformsToProtocol:@protocol(NIPhotoAlbumScrollViewDataSource)]);
return (id<NIPhotoAlbumScrollViewDataSource>)[super dataSource];
}
- (void)setDataSource:(id<NIPhotoAlbumScrollViewDataSource>)dataSource {
[super setDataSource:(id<NIPagingScrollViewDataSource>)dataSource];
}
- (id<NIPhotoAlbumScrollViewDelegate>)delegate {
id<NIPagingScrollViewDelegate> superDelegate = [super delegate];
NIDASSERT(nil == superDelegate
|| [superDelegate conformsToProtocol:@protocol(NIPhotoAlbumScrollViewDelegate)]);
return (id<NIPhotoAlbumScrollViewDelegate>)superDelegate;
}
- (void)setDelegate:(id<NIPhotoAlbumScrollViewDelegate>)delegate {
[super setDelegate:(id<NIPhotoAlbumScrollViewDelegate>)delegate];
}
@end

93
Pods/Nimbus/src/photos/src/NIPhotoAlbumScrollViewDataSource.h

@ -1,93 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIPhotoScrollViewPhotoSize.h"
#import "NimbusPagingScrollView.h"
#import <Foundation/Foundation.h>
@class NIPhotoAlbumScrollView;
/**
* The photo album scroll data source.
*
* @ingroup NimbusPhotos
*
* This data source emphasizes speed and memory efficiency by requesting images only when
* they're needed and encouraging immediate responses from the data source implementation.
*
* @see NIPhotoAlbumScrollView
*/
@protocol NIPhotoAlbumScrollViewDataSource <NIPagingScrollViewDataSource>
@required
#pragma mark Fetching Required Album Information /** @name [NIPhotoAlbumScrollViewDataSource] Fetching Required Album Information */
/**
* Fetches the highest-quality image available for the photo at the given index.
*
* Your goal should be to make this implementation return as fast as possible. Avoid
* hitting the disk or blocking on a network request. Aim to load images asynchronously.
*
* If you already have the highest-quality image in memory (like in an NIImageMemoryCache),
* then you can simply return the image and set photoSize to be
* NIPhotoScrollViewPhotoSizeOriginal.
*
* If the highest-quality image is not available when this method is called then you should
* spin off an asynchronous operation to load the image and set isLoading to YES.
*
* If you have a thumbnail in memory but not the full-size image yet, then you should return
* the thumbnail, set isLoading to YES, and set photoSize to NIPhotoScrollViewPhotoSizeThumbnail.
*
* Once the high-quality image finishes loading, call didLoadPhoto:atIndex:photoSize: with
* the image.
*
* This method will be called to prefetch the next and previous photos in the scroll view.
* The currently displayed photo will always be requested first.
*
* @attention The photo scroll view does not hold onto the UIImages for very long at all.
* It is up to the controller to decide on an adequate caching policy to ensure
* that images are kept in memory through the life of the photo album.
* In your implementation of the data source you should prioritize thumbnails
* being kept in memory over full-size images. When a memory warning is received,
* the original photos should be relinquished from memory first.
*/
- (UIImage *)photoAlbumScrollView: (NIPhotoAlbumScrollView *)photoAlbumScrollView
photoAtIndex: (NSInteger)photoIndex
photoSize: (NIPhotoScrollViewPhotoSize *)photoSize
isLoading: (BOOL *)isLoading
originalPhotoDimensions: (CGSize *)originalPhotoDimensions;
@optional
#pragma mark Optimizing Data Retrieval /** @name [NIPhotoAlbumScrollViewDataSource] Optimizing Data Retrieval */
/**
* Called when you should cancel any asynchronous loading requests for the given photo.
*
* When a photo is not immediately visible this method is called to allow the data
* source to minimize the number of active asynchronous operations in place.
*
* This method is optional, though recommended because it focuses the device's processing
* power on the most immediately accessible photos.
*/
- (void)photoAlbumScrollView: (NIPhotoAlbumScrollView *)photoAlbumScrollView
stopLoadingPhotoAtIndex: (NSInteger)photoIndex;
@end

55
Pods/Nimbus/src/photos/src/NIPhotoAlbumScrollViewDelegate.h

@ -1,55 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#import "NimbusPagingScrollView.h"
@class NIPhotoAlbumScrollView;
/**
* The photo album scroll view delegate.
*
* @ingroup Photos-Protocols
* @see NIPhotoAlbumScrollView
*/
@protocol NIPhotoAlbumScrollViewDelegate <NIPagingScrollViewDelegate>
@optional
#pragma mark Scrolling and Zooming /** @name [NIPhotoAlbumScrollViewDelegate] Scrolling and Zooming */
/**
* The user double-tapped to zoom in or out.
*/
- (void)photoAlbumScrollView: (NIPhotoAlbumScrollView *)photoAlbumScrollView
didZoomIn: (BOOL)didZoomIn;
#pragma mark Data Availability /** @name [NIPhotoAlbumScrollViewDelegate] Data Availability */
/**
* The next photo in the album has been loaded and is ready to be displayed.
*/
- (void)photoAlbumScrollViewDidLoadNextPhoto:(NIPhotoAlbumScrollView *)photoAlbumScrollView;
/**
* The previous photo in the album has been loaded and is ready to be displayed.
*/
- (void)photoAlbumScrollViewDidLoadPreviousPhoto:(NIPhotoAlbumScrollView *)photoAlbumScrollView;
@end

162
Pods/Nimbus/src/photos/src/NIPhotoScrollView.h

@ -1,162 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIPagingScrollViewPage.h"
#import "NIPhotoScrollViewPhotoSize.h"
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@protocol NIPhotoScrollViewDelegate;
@class NICenteringScrollView;
/**
* A single photo view that supports zooming and rotation.
*
* @ingroup NimbusPhotos
*/
@interface NIPhotoScrollView : UIView <UIScrollViewDelegate, NIPagingScrollViewPage>
#pragma mark Configuring Functionality
@property (nonatomic, assign, getter=isZoomingEnabled) BOOL zoomingIsEnabled; // default: yes
@property (nonatomic, assign, getter=isZoomingAboveOriginalSizeEnabled) BOOL zoomingAboveOriginalSizeIsEnabled; // default: yes
@property (nonatomic, assign, getter=isDoubleTapToZoomEnabled) BOOL doubleTapToZoomIsEnabled; // default: yes
@property (nonatomic, assign) CGFloat maximumScale; // default: 0 (autocalculate)
@property (nonatomic, weak) id<NIPhotoScrollViewDelegate> photoScrollViewDelegate;
#pragma mark State
- (UIImage *)image;
- (NIPhotoScrollViewPhotoSize)photoSize;
- (void)setImage:(UIImage *)image photoSize:(NIPhotoScrollViewPhotoSize)photoSize;
@property (nonatomic, assign, getter = isLoading) BOOL loading;
@property (nonatomic, assign) NSInteger pageIndex;
@property (nonatomic, assign) CGSize photoDimensions;
@property (nonatomic, readonly, strong) UITapGestureRecognizer* doubleTapGestureRecognizer;
@end
/** @name Configuring Functionality */
/**
* Whether the photo is allowed to be zoomed.
*
* By default this is YES.
*
* @fn NIPhotoScrollView::zoomingIsEnabled
*/
/**
* Whether small photos can be zoomed at least until they fit the screen.
*
* If this is disabled, images smaller than the view size can not be zoomed in beyond
* their original dimensions.
*
* If this is enabled, images smaller than the view size can be zoomed in only until
* they fit the view bounds.
*
* The default behavior in Photos.app allows small photos to be zoomed in.
*
* @attention This will allow photos to be zoomed in even if they don't have any more
* pixels to show, causing the photo to blur. This can look ok for photographs,
* but might not look ok for software design mockups.
*
* By default this is YES.
*
* @fn NIPhotoScrollView::zoomingAboveOriginalSizeIsEnabled
*/
/**
* Whether double-tapping zooms in and out of the image.
*
* Available on iOS 3.2 and later.
*
* By default this is YES.
*
* @fn NIPhotoScrollView::doubleTapToZoomIsEnabled
*/
/**
* The maximum scale of the image.
*
* By default this is 0, meaning the view will automatically determine the maximum scale.
* Setting this to a non-zero value will override the automatically-calculated maximum scale.
*
* @fn NIPhotoScrollView::maximumScale
*/
/**
* The photo scroll view delegate.
*
* @fn NIPhotoScrollView::photoScrollViewDelegate
*/
/** @name State */
/**
* The currently-displayed photo.
*
* @fn NIPhotoScrollView::image
*/
/**
* Set a new photo with a specific size.
*
* If image is nil then the photoSize will be overridden as NIPhotoScrollViewPhotoSizeUnknown.
*
* Resets the current zoom levels and zooms to fit the image.
*
* @fn NIPhotoScrollView::setImage:photoSize:
*/
/**
* The index of this photo within a photo album.
*
* @fn NIPhotoScrollView::pageIndex
*/
/**
* The current size of the photo.
*
* This is used to replace the photo only with successively higher-quality versions.
*
* @fn NIPhotoScrollView::photoSize
*/
/**
* The largest dimensions of the photo.
*
* This is used to show the thumbnail at the final image size in case the final image size
* is smaller than the album's frame. Without this value we have to assume that the thumbnail
* will take up the full screen. If the final image doesn't take up the full screen, then
* the photo view will appear to "snap" to the smaller full-size image when the final image
* does load.
*
* CGSizeZero is used to signify an unknown final photo dimension.
*
* @fn NIPhotoScrollView::photoDimensions
*/
/**
* The gesture recognizer for double-tapping zooms in and out of the image.
*
* This is used mainly for setting up dependencies between gesture recognizers.
*
* @fn NIPhotoScrollView::doubleTapGestureRecognizer
*/

512
Pods/Nimbus/src/photos/src/NIPhotoScrollView.m

@ -1,512 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIPhotoScrollView.h"
#import "NIPhotoScrollViewDelegate.h"
#import "NimbusCore.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
/**
* A UIScrollView that centers the zooming view's frame as the user zooms.
*
* We must update the zooming view's frame within the scroll view's layoutSubviews,
* thus why we've subclassed UIScrollView.
*/
@interface NICenteringScrollView : UIScrollView
@end
@implementation NICenteringScrollView
#pragma mark - UIView
- (void)layoutSubviews {
[super layoutSubviews];
// Center the image as it becomes smaller than the size of the screen.
UIView* zoomingSubview = [self.delegate viewForZoomingInScrollView:self];
CGSize boundsSize = self.bounds.size;
CGRect frameToCenter = zoomingSubview.frame;
// Center horizontally.
if (frameToCenter.size.width < boundsSize.width) {
frameToCenter.origin.x = NICGFloatFloor((boundsSize.width - frameToCenter.size.width) / 2);
} else {
frameToCenter.origin.x = 0;
}
// Center vertically.
if (frameToCenter.size.height < boundsSize.height) {
frameToCenter.origin.y = NICGFloatFloor((boundsSize.height - frameToCenter.size.height) / 2);
} else {
frameToCenter.origin.y = 0;
}
zoomingSubview.frame = frameToCenter;
}
@end
@interface NIPhotoScrollView ()
@property (nonatomic, assign) NIPhotoScrollViewPhotoSize photoSize;
- (void)setMaxMinZoomScalesForCurrentBounds;
@end
@implementation NIPhotoScrollView {
// The photo view to be zoomed.
UIImageView* _imageView;
// The scroll view.
NICenteringScrollView* _scrollView;
UIActivityIndicatorView* _loadingView;
// Photo Information
NIPhotoScrollViewPhotoSize _photoSize;
CGSize _photoDimensions;
// Configurable State
BOOL _zoomingIsEnabled;
BOOL _zoomingAboveOriginalSizeIsEnabled;
UITapGestureRecognizer* _doubleTapGestureRecognizer;
}
@synthesize reuseIdentifier;
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Default configuration.
self.zoomingIsEnabled = YES;
self.zoomingAboveOriginalSizeIsEnabled = YES;
self.doubleTapToZoomIsEnabled = YES;
// Autorelease so that we don't have to worry about releasing the subviews in dealloc.
_scrollView = [[NICenteringScrollView alloc] initWithFrame:self.bounds];
_scrollView.autoresizingMask = (UIViewAutoresizingFlexibleWidth
| UIViewAutoresizingFlexibleHeight);
_loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
[_loadingView sizeToFit];
_loadingView.frame = NIFrameOfCenteredViewWithinView(_loadingView, self);
_loadingView.autoresizingMask = UIViewAutoresizingFlexibleMargins;
// We implement viewForZoomingInScrollView: and return the image view for zooming.
_scrollView.delegate = self;
// Disable the scroll indicators.
_scrollView.showsVerticalScrollIndicator = NO;
_scrollView.showsHorizontalScrollIndicator = NO;
// Photo viewers should feel sticky when you're panning around, not smooth and slippery
// like a UITableView.
_scrollView.decelerationRate = UIScrollViewDecelerationRateFast;
// Ensure that empty areas of the scroll view are draggable.
self.backgroundColor = [UIColor blackColor];
_scrollView.backgroundColor = self.backgroundColor;
_imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
[_scrollView addSubview:_imageView];
[self addSubview:_scrollView];
[self addSubview:_loadingView];
}
return self;
}
- (void)setBackgroundColor:(UIColor *)backgroundColor {
[super setBackgroundColor:backgroundColor];
_scrollView.backgroundColor = backgroundColor;
}
#pragma mark - UIScrollView
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return _imageView;
}
#pragma mark - Gesture Recognizers
- (CGRect)rectAroundPoint:(CGPoint)point atZoomScale:(CGFloat)zoomScale {
NIDASSERT(zoomScale > 0);
// Define the shape of the zoom rect.
CGSize boundsSize = self.bounds.size;
// Modify the size according to the requested zoom level.
// For example, if we're zooming in to 0.5 zoom, then this will increase the bounds size
// by a factor of two.
CGSize scaledBoundsSize = CGSizeMake(boundsSize.width / zoomScale,
boundsSize.height / zoomScale);
CGRect rect = CGRectMake(point.x - scaledBoundsSize.width / 2,
point.y - scaledBoundsSize.height / 2,
scaledBoundsSize.width,
scaledBoundsSize.height);
// When the image is zoomed out there is a bit of empty space around the image due
// to the fact that it's centered on the screen. When we created the rect around the
// point we need to take this "space" into account.
// 1: get the frame of the image in this view's coordinates.
CGRect imageScaledFrame = [self convertRect:_imageView.frame toView:self];
// 2: Offset the frame by the excess amount. This will ensure that the zoomed location
// is always centered on the tap location. We only allow positive values because a
// negative value implies that there isn't actually any offset.
rect = CGRectOffset(rect, -MAX(0, imageScaledFrame.origin.x), -MAX(0, imageScaledFrame.origin.y));
return rect;
}
- (void)didDoubleTap:(UITapGestureRecognizer *)tapGesture {
BOOL isCompletelyZoomedIn = (_scrollView.maximumZoomScale <= _scrollView.zoomScale + FLT_EPSILON);
BOOL didZoomIn;
_scrollView.scrollEnabled = true;
if (isCompletelyZoomedIn) {
// Zoom the photo back out.
[_scrollView setZoomScale:_scrollView.minimumZoomScale animated:YES];
didZoomIn = NO;
} else {
// Zoom into the tap point.
CGPoint tapCenter = [tapGesture locationInView:_imageView];
CGRect maxZoomRect = [self rectAroundPoint:tapCenter atZoomScale:_scrollView.maximumZoomScale];
[_scrollView zoomToRect:maxZoomRect animated:YES];
didZoomIn = YES;
}
if ([self.photoScrollViewDelegate respondsToSelector:
@selector(photoScrollViewDidDoubleTapToZoom:didZoomIn:)]) {
[self.photoScrollViewDelegate photoScrollViewDidDoubleTapToZoom:self didZoomIn:didZoomIn];
}
}
#pragma mark - NIPagingScrollViewPage
- (void)prepareForReuse {
_imageView.image = nil;
self.photoSize = NIPhotoScrollViewPhotoSizeUnknown;
_scrollView.zoomScale = 1;
_scrollView.contentSize = self.bounds.size;
}
- (void)pageDidDisappear {
_scrollView.zoomScale = _scrollView.minimumZoomScale;
}
#pragma mark - Public
- (void)setImage:(UIImage *)image photoSize:(NIPhotoScrollViewPhotoSize)photoSize {
_imageView.image = image;
[_imageView sizeToFit];
if (nil == image) {
self.photoSize = NIPhotoScrollViewPhotoSizeUnknown;
} else {
self.photoSize = photoSize;
}
// The min/max zoom values assume that the content size is the image size. The max zoom will
// be a value that allows the image to be seen at a 1-to-1 pixel resolution, while the min
// zoom will be small enough to fit the image on the screen perfectly.
if (nil != image) {
_scrollView.contentSize = image.size;
} else {
_scrollView.contentSize = self.bounds.size;
}
[self setMaxMinZoomScalesForCurrentBounds];
// Start off with the image fully-visible on the screen.
_scrollView.zoomScale = _scrollView.minimumZoomScale;
[self setNeedsLayout];
}
- (void)setLoading:(BOOL)loading {
_loading = loading;
if (loading) {
[_loadingView startAnimating];
} else {
[_loadingView stopAnimating];
}
}
- (UIImage *)image {
return _imageView.image;
}
- (void)setZoomingIsEnabled:(BOOL)enabled {
_zoomingIsEnabled = enabled;
if (nil != _imageView.image) {
[self setMaxMinZoomScalesForCurrentBounds];
// Fit the image on screen.
_scrollView.zoomScale = _scrollView.minimumZoomScale;
// Disable zoom bouncing if zooming is disabled, otherwise the view will allow pinching.
_scrollView.bouncesZoom = enabled;
} else {
// Reset to the defaults if there is no set image yet.
_scrollView.zoomScale = 1;
_scrollView.minimumZoomScale = 1;
_scrollView.maximumZoomScale = 1;
_scrollView.bouncesZoom = NO;
}
}
- (void)setDoubleTapToZoomIsEnabled:(BOOL)enabled {
if (enabled && nil == _doubleTapGestureRecognizer) {
_doubleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didDoubleTap:)];
_doubleTapGestureRecognizer.numberOfTapsRequired = 2;
[self addGestureRecognizer:_doubleTapGestureRecognizer];
}
_doubleTapGestureRecognizer.enabled = enabled;
}
- (BOOL)isDoubleTapToZoomEnabled {
return [_doubleTapGestureRecognizer isEnabled];
}
- (CGFloat)scaleForSize:(CGSize)size boundsSize:(CGSize)boundsSize useMinimalScale:(BOOL)minimalScale {
CGFloat xScale = boundsSize.width / size.width; // The scale needed to perfectly fit the image width-wise.
CGFloat yScale = boundsSize.height / size.height; // The scale needed to perfectly fit the image height-wise.
CGFloat minScale = minimalScale ? MIN(xScale, yScale) : MAX(xScale, yScale); // Use the minimum of these to allow the image to become fully visible, or the maximum to get fullscreen size
return minScale;
}
/**
* Calculate the min and max scale for the given dimensions and photo size.
*
* minScale will fit the photo to the bounds, unless it is too small in which case it will
* show the image at a 1-to-1 resolution.
*
* maxScale will be whatever value shows the image at a 1-to-1 resolution, UNLESS
* isZoomingAboveOriginalSizeEnabled is enabled, in which case maxScale will be calculated
* such that the image completely fills the bounds.
*
* Exception: If the photo size is unknown (this is a loading image, for example) then
* the minimum scale will be set without considering the screen scale. This allows the
* loading image to draw with its own image scale if it's a high-res @2x image.
*/
- (void)minAndMaxScaleForDimensions: (CGSize)dimensions
boundsSize: (CGSize)boundsSize
photoScale: (CGFloat)photoScale
photoSize: (NIPhotoScrollViewPhotoSize)photoSize
minScale: (CGFloat *)pMinScale
maxScale: (CGFloat *)pMaxScale {
NIDASSERT(nil != pMinScale);
NIDASSERT(nil != pMaxScale);
if (nil == pMinScale
|| nil == pMaxScale) {
return;
}
CGFloat minScale = [self scaleForSize: dimensions
boundsSize: boundsSize
useMinimalScale: YES];
// On high resolution screens we have double the pixel density, so we will be seeing
// every pixel if we limit the maximum zoom scale to 0.5.
// If the photo size is unknown, it's likely that we're showing the loading image and
// don't want to shrink it down with the zoom because it should be a scaled image.
CGFloat maxScale = ((NIPhotoScrollViewPhotoSizeUnknown == photoSize)
? 1
: (photoScale / NIScreenScale()));
if (NIPhotoScrollViewPhotoSizeThumbnail != photoSize) {
// Don't let minScale exceed maxScale. (If the image is smaller than the screen, we
// don't want to force it to be zoomed.)
// todo marco minScale = MIN(minScale, maxScale);
}
// At this point if the image is small, then minScale and maxScale will be the same because
// we don't want to allow the photo to be zoomed.
// If zooming above the original size IS enabled, however, expand the max zoom to
// whatever value would make the image fit the view perfectly.
if ([self isZoomingAboveOriginalSizeEnabled]) {
CGFloat idealMaxScale = [self scaleForSize: dimensions
boundsSize: boundsSize
useMinimalScale: NO];
maxScale = MAX(maxScale, idealMaxScale);
}
*pMinScale = minScale;
*pMaxScale = maxScale;
}
- (void)setMaxMinZoomScalesForCurrentBounds {
CGSize imageSize = _imageView.bounds.size;
// Avoid crashing if the image has no dimensions.
if (imageSize.width <= 0 || imageSize.height <= 0) {
_scrollView.maximumZoomScale = 1;
_scrollView.minimumZoomScale = 1;
return;
}
// The following code is from Apple's ImageScrollView example application and has been used
// here because it is well-documented and concise.
CGSize boundsSize = _scrollView.bounds.size;
CGFloat minScale = 0;
CGFloat maxScale = 0;
// Calculate the min/max scale for the image to be presented.
[self minAndMaxScaleForDimensions: imageSize
boundsSize: boundsSize
photoScale: _imageView.image.scale
photoSize: self.photoSize
minScale: &minScale
maxScale: &maxScale];
// When we show thumbnails for images that are too small for the bounds, we try to use
// the known photo dimensions to scale the minimum scale to match what the final image
// would be. This avoids any "snapping" effects from stretching the thumbnail too large.
if ((NIPhotoScrollViewPhotoSizeThumbnail == self.photoSize)
&& !CGSizeEqualToSize(self.photoDimensions, CGSizeZero)) {
CGFloat scaleToFitOriginal = 0;
CGFloat originalMaxScale = 0;
// Calculate the original-sized image's min/max scale.
[self minAndMaxScaleForDimensions: self.photoDimensions
boundsSize: boundsSize
photoScale: _imageView.image.scale
photoSize: NIPhotoScrollViewPhotoSizeOriginal
minScale: &scaleToFitOriginal
maxScale: &originalMaxScale];
if (scaleToFitOriginal + FLT_EPSILON >= (1.0 / NIScreenScale())) {
// If the final image will be smaller than the view then we want to use that
// scale as the "true" scale and adjust it relatively to the thumbnail's dimensions.
// This ensures that the thumbnail will always be the same visual size as the original
// image, giving us that sexy "crisping" effect when the thumbnail is loaded.
CGFloat relativeSize = self.photoDimensions.width / imageSize.width;
minScale = scaleToFitOriginal * relativeSize;
}
}
// If zooming is disabled then we flatten the range for zooming to only allow the min zoom.
if (self.isZoomingEnabled && NIPhotoScrollViewPhotoSizeOriginal == self.photoSize && self.maximumScale > 0) {
_scrollView.maximumZoomScale = self.maximumScale;
} else {
_scrollView.maximumZoomScale = self.isZoomingEnabled ? maxScale : minScale;
}
_scrollView.minimumZoomScale = minScale;
}
#pragma mark Saving/Restoring Offset and Scale
// Parts of the following code are from Apple's ImageScrollView example application and
// have been used here because they are well-documented and concise.
// Fetch the visual center point of this view in the image view's coordinate space.
- (CGPoint)pointToCenterAfterRotation {
CGRect bounds = _scrollView.bounds;
CGPoint boundsCenter = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
return [self convertPoint:boundsCenter toView:_imageView];
}
- (CGFloat)scaleToRestoreAfterRotation {
CGFloat contentScale = _scrollView.zoomScale;
// If we're at the minimum zoom scale, preserve that by returning 0, which
// will be converted to the minimum allowable scale when the scale is restored.
if (contentScale <= _scrollView.minimumZoomScale + FLT_EPSILON) {
contentScale = 0;
}
return contentScale;
}
- (CGPoint)maximumContentOffset {
CGSize contentSize = _scrollView.contentSize;
CGSize boundsSize = _scrollView.bounds.size;
return CGPointMake(contentSize.width - boundsSize.width,
contentSize.height - boundsSize.height);
}
- (CGPoint)minimumContentOffset {
return CGPointZero;
}
- (void)restoreCenterPoint:(CGPoint)oldCenter scale:(CGFloat)oldScale {
// Step 1: restore zoom scale, making sure it is within the allowable range.
_scrollView.zoomScale = NIBoundf(oldScale,
_scrollView.minimumZoomScale, _scrollView.maximumZoomScale);
// Step 2: restore center point, making sure it is within the allowable range.
// 2a: convert our desired center point back to the scroll view's coordinate space from the
// image's coordinate space.
CGPoint boundsCenter = [self convertPoint:oldCenter fromView:_imageView];
// 2b: calculate the content offset that would yield that center point
CGPoint offset = CGPointMake(boundsCenter.x - _scrollView.bounds.size.width / 2.0f,
boundsCenter.y - _scrollView.bounds.size.height / 2.0f);
// 2c: restore offset, adjusted to be within the allowable range
CGPoint maxOffset = [self maximumContentOffset];
CGPoint minOffset = [self minimumContentOffset];
offset.x = NIBoundf(offset.x, minOffset.x, maxOffset.x);
offset.y = NIBoundf(offset.y, minOffset.y, maxOffset.y);
_scrollView.contentOffset = offset;
}
#pragma mark Saving/Restoring Offset and Scale
- (void)setFrameAndMaintainState:(CGRect)frame {
CGPoint restorePoint = [self pointToCenterAfterRotation];
CGFloat restoreScale = [self scaleToRestoreAfterRotation];
self.frame = frame;
[self setMaxMinZoomScalesForCurrentBounds];
[self restoreCenterPoint:restorePoint scale:restoreScale];
[_scrollView setNeedsLayout];
}
@end

41
Pods/Nimbus/src/photos/src/NIPhotoScrollViewDelegate.h

@ -1,41 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
@class NIPhotoScrollView;
/**
* The photo scroll view delegate.
*
* @ingroup NimbusPhotos
*/
@protocol NIPhotoScrollViewDelegate <NSObject>
@optional
#pragma mark Zooming /** @name [NIPhotoScrollViewDelegate] Zooming */
/**
* The user has double-tapped the photo to zoom either in or out.
*
* @param photoScrollView The photo scroll view that was tapped.
* @param didZoomIn YES if the photo was zoomed in. NO if the photo was zoomed out.
*/
- (void)photoScrollViewDidDoubleTapToZoom: (NIPhotoScrollView *)photoScrollView
didZoomIn: (BOOL)didZoomIn;
@end

31
Pods/Nimbus/src/photos/src/NIPhotoScrollViewPhotoSize.h

@ -1,31 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
/**
* Contextual information about the size of the photo.
*/
typedef enum {
// Unknown photo size.
NIPhotoScrollViewPhotoSizeUnknown,
// A smaller version of the image.
NIPhotoScrollViewPhotoSizeThumbnail,
// The full-size image.
NIPhotoScrollViewPhotoSizeOriginal,
} NIPhotoScrollViewPhotoSize;

168
Pods/Nimbus/src/photos/src/NIPhotoScrubberView.h

@ -1,168 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "NIPreprocessorMacros.h" /* for weak */
@protocol NIPhotoScrubberViewDataSource;
@protocol NIPhotoScrubberViewDelegate;
/**
* A control built for quickly skimming through a collection of images.
*
* @ingroup NimbusPhotos
*
* The user interacts with the scrubber by "scrubbing" their finger along the control,
* or more simply, touching the control and moving their finger along a single axis.
* Scrubbers can be seen in the Photos.app on the iPad.
*
* The thumbnails displayed in a scrubber will be a subset of the overall set of photos.
* The wider the scrubber, the more thumbnails will be shown. The displayed thumbnails will
* be chosen at constant intervals in the album, with a larger "selected" thumbnail image
* that will show whatever image is currently selected. This larger thumbnail will be
* positioned relatively within the scrubber to show the user what the current selection
* is in a physically intuitive way.
*
* This view is a completely independent view from the photo scroll view so you can choose
* to use this in your already built photo viewer.
*
* @image html scrubber1.png "Screenshot of NIPhotoScrubberView on the iPad."
*
* @see NIPhotoScrubberViewDataSource
* @see NIPhotoScrubberViewDelegate
*/
@interface NIPhotoScrubberView : UIView
#pragma mark Data Source /** @name Data Source */
/**
* The data source for this scrubber view.
*/
@property (nonatomic, weak) id<NIPhotoScrubberViewDataSource> dataSource;
/**
* Forces the scrubber view to reload all of its data.
*
* This must be called at least once after dataSource has been set in order for the view
* to gather any presentable information.
*
* This method is expensive. It will reset the state of the view and remove all existing
* thumbnails before requesting the new information from the data source.
*/
- (void)reloadData;
/**
* Notify the scrubber view that a thumbnail has been loaded at a given index.
*
* This method is cheap, so do not be afraid to call it whenever a thumbnail loads.
* It will only modify visible thumbnails.
*/
- (void)didLoadThumbnail: (UIImage *)image
atIndex: (NSInteger)photoIndex;
#pragma mark Delegate /** @name Delegate */
/**
* The delegate for this scrubber view.
*/
@property (nonatomic, weak) id<NIPhotoScrubberViewDelegate> delegate;
#pragma mark Accessing Selection /** @name Accessing Selection */
/**
* The selected photo index.
*/
@property (nonatomic, assign) NSInteger selectedPhotoIndex;
/**
* Set the selected photo with animation.
*/
- (void)setSelectedPhotoIndex:(NSInteger)photoIndex animated:(BOOL)animated;
@end
/**
* The data source for the photo scrubber.
*
* @ingroup NimbusPhotos
*
* <h2>Performance Considerations</h2>
*
* A scrubber view's purpose is for instantly flipping through an album of photos. As such,
* it's crucial that your implementation of the data source performs blazingly fast. When
* the scrubber requests a thumbnail from you you should *not* be hitting the disk or blocking
* on a network call. If you don't have the thumbnail available at that exact moment, fire
* off an asynchronous load request (using NIReadFileFromDiskOperation or NIHTTPRequest)
* and return nil. Once the thumbnail is loaded, call didLoadThumbnail:atIndex: to notify
* the scrubber that it can display the thumbnail now.
*
* It is not recommended to use high-res images for your scrubber thumbnails. This is because
* the scrubber will keep a large set of images in memory and if you're giving it
* high-resolution images then you'll find that your app quickly burns through memory.
* If you don't have access to thumbnails from whatever API you're using then you should consider
* not using a scrubber.
*
* @see NIPhotoScrubberView
*/
@protocol NIPhotoScrubberViewDataSource <NSObject>
@required
#pragma mark Fetching Required Information /** @name Fetching Required Information */
/**
* Fetches the total number of photos in the scroll view.
*
* The value returned in this method will be cached by the scroll view until reloadData
* is called again.
*/
- (NSInteger)numberOfPhotosInScrubberView:(NIPhotoScrubberView *)photoScrubberView;
/**
* Fetch the thumbnail image for the given photo index.
*
* Please read and understand the performance considerations for this data source.
*/
- (UIImage *)photoScrubberView: (NIPhotoScrubberView *)photoScrubberView
thumbnailAtIndex: (NSInteger)thumbnailIndex;
@end
/**
* The delegate for the photo scrubber.
*
* @ingroup NimbusPhotos
*
* Sends notifications of state changes.
*
* @see NIPhotoScrubberView
*/
@protocol NIPhotoScrubberViewDelegate <NSObject>
@optional
#pragma mark Selection Changes /** @name Selection Changes */
/**
* The photo scrubber changed its selection.
*
* Use photoScrubberView.selectedPhotoIndex to access the current selection.
*/
- (void)photoScrubberViewDidChangeSelection:(NIPhotoScrubberView *)photoScrubberView;
@end

465
Pods/Nimbus/src/photos/src/NIPhotoScrubberView.m

@ -1,465 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIPhotoScrubberView.h"
#import "NimbusCore.h"
#import <QuartzCore/QuartzCore.h>
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
static const NSInteger NIPhotoScrubberViewUnknownTag = -1;
@interface NIPhotoScrubberView()
/**
* @internal
*
* A method to encapsulate initilization logic that can be shared by different init methods.
*/
- (void)initializeScrubber;
/**
* @internal
*
* A lightweight method for updating all of the visible thumbnails in the scrubber.
*
* This method will force the scrubber to lay itself out, calculate how many thumbnails might
* be visible, and then lay out the thumbnails and fetch any thumbnail images it can find.
*
* This method should never take much time to run, so it can safely be used in layoutSubviews.
*/
- (void)updateVisiblePhotos;
/**
* @internal
*
* Returns a new, autoreleased image view in the style of this photo scrubber.
*
* This implementation returns an image with a 1px solid white border and a black background.
*/
- (UIImageView *)photoView;
@end
@implementation NIPhotoScrubberView {
NSMutableArray* _visiblePhotoViews;
NSMutableSet* _recycledPhotoViews;
UIView* _containerView;
UIImageView* _selectionView;
// State
NSInteger _selectedPhotoIndex;
// Cached data source values
NSInteger _numberOfPhotos;
// Cached display values
NSInteger _numberOfVisiblePhotos;
}
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
[self initializeScrubber];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
[self initializeScrubber];
}
return self;
}
- (void)initializeScrubber {
// Only one finger should be allowed to interact with the scrubber at a time.
self.multipleTouchEnabled = NO;
_containerView = [[UIView alloc] init];
_containerView.layer.borderColor = [UIColor colorWithWhite:1 alpha:0.1f].CGColor;
_containerView.layer.borderWidth = 1;
_containerView.backgroundColor = [UIColor colorWithWhite:1 alpha:0.3f];
_containerView.userInteractionEnabled = NO;
[self addSubview:_containerView];
_selectionView = [self photoView];
[self addSubview:_selectionView];
_selectedPhotoIndex = -1;
}
#pragma mark - View Creation
- (UIImageView *)photoView {
UIImageView* imageView = [[UIImageView alloc] init];
imageView.layer.borderColor = [UIColor whiteColor].CGColor;
imageView.layer.borderWidth = 1;
imageView.backgroundColor = [UIColor blackColor];
imageView.clipsToBounds = YES;
imageView.userInteractionEnabled = NO;
imageView.contentMode = UIViewContentModeScaleAspectFill;
imageView.tag = NIPhotoScrubberViewUnknownTag;
return imageView;
}
#pragma mark - Layout
- (CGSize)photoSize {
CGSize boundsSize = self.bounds.size;
// These numbers are roughly estimated from the Photos.app's scrubber.
CGFloat photoWidth = NICGFloatFloor(boundsSize.height / 0.6f);
CGFloat photoHeight = NICGFloatFloor(photoWidth * 0.75f);
return CGSizeMake(photoWidth, photoHeight);
}
- (CGSize)selectionSize {
CGSize boundsSize = self.bounds.size;
// These numbers are roughly estimated from the Photos.app's scrubber.
CGFloat selectionWidth = NICGFloatFloor(boundsSize.height / 0.3f);
CGFloat selectionHeight = NICGFloatFloor(selectionWidth * 0.75f);
return CGSizeMake(selectionWidth, selectionHeight);
}
// The amount of space on either side of the scrubber's left and right edges.
- (CGFloat)horizontalMargins {
CGSize photoSize = [self photoSize];
return NICGFloatFloor(photoSize.width / 2);
}
- (CGFloat)spaceBetweenPhotos {
return 1;
}
// The maximum number of pixels that the scrubber can utilize. The scrubber layer's border
// is contained within this width and must be considered when laying out the thumbnails.
- (CGFloat)maxContentWidth {
CGSize boundsSize = self.bounds.size;
CGFloat horizontalMargins = [self horizontalMargins];
CGFloat maxContentWidth = (boundsSize.width
- horizontalMargins * 2);
return maxContentWidth;
}
- (NSInteger)numberOfVisiblePhotos {
CGSize photoSize = [self photoSize];
CGFloat spaceBetweenPhotos = [self spaceBetweenPhotos];
// Here's where we take into account the container layer's border because we don't want to
// display thumbnails on top of the border.
CGFloat maxContentWidth = ([self maxContentWidth]
- _containerView.layer.borderWidth * 2);
NSInteger numberOfPhotosThatFit = (NSInteger)floor((maxContentWidth + spaceBetweenPhotos)
/ (photoSize.width + spaceBetweenPhotos));
return MIN(_numberOfPhotos, numberOfPhotosThatFit);
}
- (CGRect)frameForSelectionAtIndex:(NSInteger)photoIndex {
CGSize photoSize = [self photoSize];
CGSize selectionSize = [self selectionSize];
CGFloat containerWidth = _containerView.bounds.size.width;
// TODO (jverkoey July 21, 2011): I need to figure out why this is necessary.
// Basically, when there are a lot of photos it seems like the selection frame
// slowly gets offset from the thumbnail frame it's supposed to be representing until by the end
// it's off the right edge by a noticeable amount. Trimming off some fat from the right
// edge seems to fix this.
if (_numberOfVisiblePhotos < _numberOfPhotos) {
containerWidth -= photoSize.width / 2;
}
// Calculate the offset into the container view based on index/numberOfPhotos.
CGFloat relativeOffset = NICGFloatFloor((((CGFloat)photoIndex * containerWidth)
/ (CGFloat)MAX(1, _numberOfPhotos)));
return CGRectMake(NICGFloatFloor(_containerView.frame.origin.x
+ relativeOffset
+ photoSize.width / 2 - selectionSize.width / 2),
NICGFloatFloor(_containerView.center.y - selectionSize.height / 2),
selectionSize.width, selectionSize.height);
}
- (CGRect)frameForThumbAtIndex:(NSInteger)thumbIndex {
CGSize photoSize = [self photoSize];
CGFloat spaceBetweenPhotos = [self spaceBetweenPhotos];
return CGRectMake(_containerView.layer.borderWidth
+ (photoSize.width + spaceBetweenPhotos) * thumbIndex,
_containerView.layer.borderWidth,
photoSize.width, photoSize.height);
}
- (void)layoutSubviews {
[super layoutSubviews];
CGSize boundsSize = self.bounds.size;
CGSize photoSize = [self photoSize];
CGFloat spaceBetweenPhotos = [self spaceBetweenPhotos];
CGFloat maxContentWidth = [self maxContentWidth];
// Update the total number of visible photos.
_numberOfVisiblePhotos = [self numberOfVisiblePhotos];
// Hide views if there isn't any interesting information to show.
_containerView.hidden = (0 == _numberOfVisiblePhotos);
_selectionView.hidden = (_selectedPhotoIndex < 0 || _containerView.hidden);
// Calculate the container width using the number of visible photos.
CGFloat containerWidth = ((_numberOfVisiblePhotos * photoSize.width)
+ (MAX(0, _numberOfVisiblePhotos - 1) * spaceBetweenPhotos)
+ _containerView.layer.borderWidth * 2);
// Then we center the container in the content area.
CGFloat containerMargins = MAX(0, NICGFloatFloor((maxContentWidth - containerWidth) / 2));
CGFloat horizontalMargins = [self horizontalMargins];
CGFloat containerHeight = photoSize.height + _containerView.layer.borderWidth * 2;
CGFloat containerLeftMargin = horizontalMargins + containerMargins;
CGFloat containerTopMargin = NICGFloatFloor((boundsSize.height - containerHeight) / 2);
_containerView.frame = CGRectMake(containerLeftMargin,
containerTopMargin,
containerWidth,
containerHeight);
// Don't bother updating the selected photo index if there isn't a selection; the
// selection view will be hidden anyway.
if (_selectedPhotoIndex >= 0) {
_selectionView.frame = [self frameForSelectionAtIndex:_selectedPhotoIndex];
}
// Update the frames for all of the thumbnails.
[self updateVisiblePhotos];
}
// Transforms an index into the number of visible photos into an index into the total
// number of photos.
- (NSInteger)photoIndexAtScrubberIndex:(NSInteger)scrubberIndex {
return (NSInteger)(NICGFloatCeil((CGFloat)(scrubberIndex * _numberOfPhotos)
/ (CGFloat)_numberOfVisiblePhotos)
+ 0.5f);
}
- (void)updateVisiblePhotos {
if (nil == self.dataSource) {
return;
}
// This will update the number of visible photos if the layout did indeed change.
[self layoutIfNeeded];
// Recycle any views that we no longer need.
while ([_visiblePhotoViews count] > (NSUInteger)_numberOfVisiblePhotos) {
UIView* photoView = [_visiblePhotoViews lastObject];
[photoView removeFromSuperview];
[_recycledPhotoViews addObject:photoView];
[_visiblePhotoViews removeLastObject];
}
// Lay out the visible photos.
for (NSUInteger ix = 0; ix < (NSUInteger)_numberOfVisiblePhotos; ++ix) {
UIImageView* photoView = nil;
// We must first get the photo view at this index.
// If there aren't enough visible photo views then try to recycle another view.
if (ix >= [_visiblePhotoViews count]) {
photoView = [_recycledPhotoViews anyObject];
if (nil == photoView) {
// Couldn't recycle the view, so create a new one.
photoView = [self photoView];
} else {
[_recycledPhotoViews removeObject:photoView];
}
[_containerView addSubview:photoView];
[_visiblePhotoViews addObject:photoView];
} else {
photoView = [_visiblePhotoViews objectAtIndex:ix];
}
NSInteger photoIndex = [self photoIndexAtScrubberIndex:ix];
// Only request the thumbnail if this thumbnail's photo index has changed. Otherwise
// we assume that this photo either already has the thumbnail or it's still loading.
if (photoView.tag != photoIndex) {
photoView.tag = photoIndex;
UIImage* image = [self.dataSource photoScrubberView:self thumbnailAtIndex:photoIndex];
photoView.image = image;
if (_selectedPhotoIndex == photoIndex) {
_selectionView.image = image;
}
}
photoView.frame = [self frameForThumbAtIndex:ix];
}
}
#pragma mark - Changing Selection
- (NSInteger)photoIndexAtPoint:(CGPoint)point {
NSInteger photoIndex;
if (point.x <= 0) {
// Beyond the left edge
photoIndex = 0;
} else if (point.x >= _containerView.bounds.size.width) {
// Beyond the right edge
photoIndex = (_numberOfPhotos - 1);
} else {
// Somewhere in between
photoIndex = (NSInteger)(NICGFloatFloor((point.x / _containerView.bounds.size.width) * _numberOfPhotos)
+ 0.5f);
}
return photoIndex;
}
- (void)updateSelectionWithPoint:(CGPoint)point {
NSInteger photoIndex = [self photoIndexAtPoint:point];
if (photoIndex != _selectedPhotoIndex) {
[self setSelectedPhotoIndex:photoIndex];
if ([self.delegate respondsToSelector:@selector(photoScrubberViewDidChangeSelection:)]) {
[self.delegate photoScrubberViewDidChangeSelection:self];
}
}
}
#pragma mark - UIResponder
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
UITouch* touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:_containerView];
[self updateSelectionWithPoint:touchPoint];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
UITouch* touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:_containerView];
[self updateSelectionWithPoint:touchPoint];
}
#pragma mark - Public
- (void)didLoadThumbnail: (UIImage *)image
atIndex: (NSInteger)photoIndex {
for (UIImageView* thumbView in _visiblePhotoViews) {
if (thumbView.tag == photoIndex) {
thumbView.image = image;
break;
}
}
// Update the selected thumbnail if it's the one that just received a photo.
if (_selectedPhotoIndex == photoIndex) {
_selectionView.image = image;
}
}
- (void)reloadData {
NIDASSERT(nil != _dataSource);
// Remove any visible photos from the view before we release the sets.
for (UIView* photoView in _visiblePhotoViews) {
[photoView removeFromSuperview];
}
// If there is no data source then we can't do anything particularly interesting.
if (nil == _dataSource) {
return;
}
_visiblePhotoViews = [[NSMutableArray alloc] init];
_recycledPhotoViews = [[NSMutableSet alloc] init];
// Cache the number of photos.
_numberOfPhotos = [_dataSource numberOfPhotosInScrubberView:self];
[self setNeedsLayout];
// This will call layoutIfNeeded and layoutSubviews will then be called because we
// set the needsLayout flag.
[self updateVisiblePhotos];
}
- (void)setSelectedPhotoIndex:(NSInteger)photoIndex animated:(BOOL)animated {
if (_selectedPhotoIndex != photoIndex) {
// Don't animate the selection if it was previously invalid.
animated = animated && (_selectedPhotoIndex >= 0);
_selectedPhotoIndex = photoIndex;
if (animated) {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.2];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[UIView setAnimationBeginsFromCurrentState:YES];
}
_selectionView.frame = [self frameForSelectionAtIndex:_selectedPhotoIndex];
if (animated) {
[UIView commitAnimations];
}
_selectionView.image = [self.dataSource photoScrubberView: self
thumbnailAtIndex: _selectedPhotoIndex];
}
}
- (void)setSelectedPhotoIndex:(NSInteger)photoIndex {
[self setSelectedPhotoIndex:photoIndex animated:NO];
}
@end

201
Pods/Nimbus/src/photos/src/NIToolbarPhotoViewController.h

@ -1,201 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "NIPhotoAlbumScrollView.h"
#import "NIPhotoScrubberView.h"
@class NIPhotoAlbumScrollView;
/**
* A simple photo album view controller implementation with a toolbar.
*
* @ingroup NimbusPhotos
*
* This controller does not implement the photo album data source, it simply implements
* some of the most common UI elements that are associated with a photo viewer.
*
* For an example of implementing the data source, see the photos examples in the
* examples directory.
*
* <h2>Implementing Delegate Methods</h2>
*
* This view controller already implements NIPhotoAlbumScrollViewDelegate. If you want to
* implement methods of this delegate you should take care to call the super implementation
* if necessary. The following methods have implementations in this class:
*
* - photoAlbumScrollViewDidScroll:
* - photoAlbumScrollView:didZoomIn:
* - photoAlbumScrollViewDidChangePages:
*
*
* <h2>Recommended Configurations</h2>
*
* <h3>Default: Zooming enabled with translucent toolbar</h3>
*
* The default settings are good for showing a photo album that takes up the entire screen.
* The photos will be visible beneath the toolbar because it is translucent. The chrome will
* be hidden whenever the user starts interacting with the photos.
*
* @code
* toolbarIsTranslucent = YES;
* hidesChromeWhenScrolling = YES;
* chromeCanBeHidden = YES;
* @endcode
*
* <h3>Zooming disabled with opaque toolbar</h3>
*
* The following settings are good for viewing photo albums when you want to keep the chrome
* visible at all times without zooming enabled.
*
* @code
* toolbarIsTranslucent = NO;
* chromeCanBeHidden = NO;
* photoAlbumView.zoomingIsEnabled = NO;
* @endcode
*/
@interface NIToolbarPhotoViewController : UIViewController <NIPhotoAlbumScrollViewDelegate, NIPhotoScrubberViewDelegate>
#pragma mark Configuring Functionality
@property (nonatomic, assign, getter=isToolbarTranslucent) BOOL toolbarIsTranslucent; // default: yes
@property (nonatomic, assign) BOOL hidesChromeWhenScrolling; // default: yes
@property (nonatomic, assign) BOOL chromeCanBeHidden; // default: yes
@property (nonatomic, assign) BOOL animateMovingToNextAndPreviousPhotos; // default: no
@property (nonatomic, assign, getter=isScrubberEnabled) BOOL scrubberIsEnabled; // default: ipad yes - iphone no
#pragma mark Views
@property (nonatomic, readonly, strong) UIToolbar* toolbar;
@property (nonatomic, readonly, strong) NIPhotoAlbumScrollView* photoAlbumView;
@property (nonatomic, readonly, strong) NIPhotoScrubberView* photoScrubberView;
- (void)refreshChromeState;
#pragma mark Toolbar Buttons
@property (nonatomic, readonly, strong) UIBarButtonItem* nextButton;
@property (nonatomic, readonly, strong) UIBarButtonItem* previousButton;
#pragma mark Subclassing
- (void)setChromeVisibility:(BOOL)isVisible animated:(BOOL)animated;
- (void)setChromeTitle;
@end
/** @name Configuring Functionality */
/**
* Whether the toolbar is translucent and shows photos beneath it or not.
*
* If this is enabled, the toolbar will be translucent and the photo view will
* take up the entire view controller's bounds.
*
* If this is disabled, the photo will only occupy the remaining space above the
* toolbar. The toolbar will also not be hidden when the chrome is dismissed. This is by design
* because dismissing the toolbar when photos can't be displayed beneath it would leave
* an empty space below the album.
*
* By default this is YES.
*
* @fn NIToolbarPhotoViewController::toolbarIsTranslucent
*/
/**
* Whether or not to hide the chrome when the user begins interacting with the photo.
*
* If this is enabled, then the chrome will be hidden when the user starts swiping from
* one photo to another.
*
* The chrome is the toolbar and the system status bar.
*
* By default this is YES.
*
* @attention This will be set to NO if toolbarCanBeHidden is set to NO.
*
* @fn NIToolbarPhotoViewController::hidesChromeWhenScrolling
*/
/**
* Whether or not to allow hiding the chrome.
*
* If this is enabled then the user will be able to single-tap to dismiss or show the
* toolbar.
*
* The chrome is the toolbar and the system status bar.
*
* If this is disabled then the chrome will always be visible.
*
* By default this is YES.
*
* @attention Setting this to NO will also disable hidesToolbarWhenScrolling.
*
* @fn NIToolbarPhotoViewController::chromeCanBeHidden
*/
/**
* Whether to animate moving to a next or previous photo when the user taps the button.
*
* By default this is NO.
*
* @fn NIToolbarPhotoViewController::animateMovingToNextAndPreviousPhotos
*/
/**
* Whether to show a scrubber in the toolbar instead of next/previous buttons.
*
* By default this is YES on the iPad and NO on the iPhone.
*
* @fn NIToolbarPhotoViewController::scrubberIsEnabled
*/
/** @name Views */
/**
* The toolbar view.
*
* @fn NIToolbarPhotoViewController::toolbar
*/
/**
* The photo album view.
*
* @fn NIToolbarPhotoViewController::photoAlbumView
*/
/**
* The photo scrubber view.
*
* @fn NIToolbarPhotoViewController::photoScrubberView
*/
/** @name Toolbar Buttons */
/**
* The 'next' button.
*
* @fn NIToolbarPhotoViewController::nextButton
*/
/**
* The 'previous' button.
*
* @fn NIToolbarPhotoViewController::previousButton
*/

533
Pods/Nimbus/src/photos/src/NIToolbarPhotoViewController.m

@ -1,533 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "NIToolbarPhotoViewController.h"
#import "NIPhotoAlbumScrollView.h"
#import "NimbusCore.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "Nimbus requires ARC support."
#endif
@implementation NIToolbarPhotoViewController {
// Views
UIToolbar* _toolbar;
NIPhotoAlbumScrollView* _photoAlbumView;
// Scrubber View
NIPhotoScrubberView* _photoScrubberView;
// Toolbar Buttons
UIBarButtonItem* _nextButton;
UIBarButtonItem* _previousButton;
// Gestures
UITapGestureRecognizer* _tapGesture;
// State
BOOL _isAnimatingChrome;
BOOL _isChromeHidden;
BOOL _prefersStatusBarHidden;
// Configuration
BOOL _toolbarIsTranslucent;
BOOL _hidesChromeWhenScrolling;
BOOL _chromeCanBeHidden;
BOOL _animateMovingToNextAndPreviousPhotos;
BOOL _scrubberIsEnabled;
}
- (void)shutdown_NIToolbarPhotoViewController {
_toolbar = nil;
_photoAlbumView = nil;
_nextButton = nil;
_previousButton = nil;
_photoScrubberView = nil;
_tapGesture = nil;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
// Default Configuration Settings
self.toolbarIsTranslucent = YES;
self.hidesChromeWhenScrolling = YES;
self.chromeCanBeHidden = YES;
self.animateMovingToNextAndPreviousPhotos = NO;
if ([self respondsToSelector:@selector(setAutomaticallyAdjustsScrollViewInsets:)]) {
self.automaticallyAdjustsScrollViewInsets = NO;
}
// The scrubber is better use of the extra real estate on the iPad.
// If you ask me, though, the scrubber works pretty well on the iPhone too. It's up
// to you if you want to use it in your own implementations.
self.scrubberIsEnabled = NIIsPad();
// Allow the photos to display beneath the status bar.
self.wantsFullScreenLayout = YES;
}
return self;
}
- (void)addTapGestureToView {
if ([self isViewLoaded]) {
if (nil == _tapGesture) {
_tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTap)];
[self.photoAlbumView addGestureRecognizer:_tapGesture];
}
}
_tapGesture.enabled = YES;
}
- (void)updateToolbarItems {
UIBarItem* flexibleSpace =
[[UIBarButtonItem alloc] initWithBarButtonSystemItem: UIBarButtonSystemItemFlexibleSpace
target: nil
action: nil];
if ([self isScrubberEnabled]) {
_nextButton = nil;
_previousButton = nil;
if (nil == _photoScrubberView) {
CGRect scrubberFrame = CGRectMake(0, 0,
self.toolbar.bounds.size.width,
self.toolbar.bounds.size.height);
_photoScrubberView = [[NIPhotoScrubberView alloc] initWithFrame:scrubberFrame];
_photoScrubberView.autoresizingMask = (UIViewAutoresizingFlexibleWidth
| UIViewAutoresizingFlexibleHeight);
_photoScrubberView.delegate = self;
}
UIBarButtonItem* scrubberItem =
[[UIBarButtonItem alloc] initWithCustomView:self.photoScrubberView];
self.toolbar.items = [NSArray arrayWithObjects:
flexibleSpace, scrubberItem, flexibleSpace,
nil];
[_photoScrubberView setSelectedPhotoIndex:self.photoAlbumView.centerPageIndex];
} else {
_photoScrubberView = nil;
if (nil == _nextButton) {
UIImage* nextIcon = [UIImage imageWithContentsOfFile:
NIPathForBundleResource(nil, @"NimbusPhotos.bundle/gfx/next.png")];
// We weren't able to find the next or previous icons in your application's resources.
// Ensure that you've dragged the NimbusPhotos.bundle from src/photos/resources into your
// application with the "Create Folder References" option selected. You can verify that
// you've done this correctly by expanding the NimbusPhotos.bundle file in your project
// and verifying that the 'gfx' directory is blue. Also verify that the bundle is being
// copied in the Copy Bundle Resources phase.
NIDASSERT(nil != nextIcon);
_nextButton = [[UIBarButtonItem alloc] initWithImage: nextIcon
style: UIBarButtonItemStylePlain
target: self
action: @selector(didTapNextButton)];
}
if (nil == _previousButton) {
UIImage* previousIcon = [UIImage imageWithContentsOfFile:
NIPathForBundleResource(nil, @"NimbusPhotos.bundle/gfx/previous.png")];
// We weren't able to find the next or previous icons in your application's resources.
// Ensure that you've dragged the NimbusPhotos.bundle from src/photos/resources into your
// application with the "Create Folder References" option selected. You can verify that
// you've done this correctly by expanding the NimbusPhotos.bundle file in your project
// and verifying that the 'gfx' directory is blue. Also verify that the bundle is being
// copied in the Copy Bundle Resources phase.
NIDASSERT(nil != previousIcon);
_previousButton = [[UIBarButtonItem alloc] initWithImage: previousIcon
style: UIBarButtonItemStylePlain
target: self
action: @selector(didTapPreviousButton)];
}
self.toolbar.items = [NSArray arrayWithObjects:
flexibleSpace, self.previousButton,
flexibleSpace, self.nextButton,
flexibleSpace,
nil];
}
}
- (void)loadView {
[super loadView];
self.view.backgroundColor = [UIColor blackColor];
CGRect bounds = self.view.bounds;
// Toolbar Setup
CGFloat toolbarHeight = NIToolbarHeightForOrientation(NIInterfaceOrientation());
CGRect toolbarFrame = CGRectMake(0, bounds.size.height - toolbarHeight,
bounds.size.width, toolbarHeight);
_toolbar = [[UIToolbar alloc] initWithFrame:toolbarFrame];
_toolbar.barStyle = UIBarStyleBlack;
_toolbar.translucent = self.toolbarIsTranslucent;
_toolbar.autoresizingMask = (UIViewAutoresizingFlexibleWidth
| UIViewAutoresizingFlexibleTopMargin);
[self updateToolbarItems];
// Photo Album View Setup
CGRect photoAlbumFrame = bounds;
if (!self.toolbarIsTranslucent) {
photoAlbumFrame = NIRectContract(bounds, 0, toolbarHeight);
}
_photoAlbumView = [[NIPhotoAlbumScrollView alloc] initWithFrame:photoAlbumFrame];
_photoAlbumView.autoresizingMask = (UIViewAutoresizingFlexibleWidth
| UIViewAutoresizingFlexibleHeight);
_photoAlbumView.delegate = self;
[self.view addSubview:_photoAlbumView];
[self.view addSubview:_toolbar];
if (self.hidesChromeWhenScrolling || self.chromeCanBeHidden) {
[self addTapGestureToView];
}
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[UIApplication sharedApplication] setStatusBarStyle: (NIIsPad()
? UIStatusBarStyleBlackOpaque
: UIStatusBarStyleBlackTranslucent)
animated: animated];
UINavigationBar* navBar = self.navigationController.navigationBar;
navBar.barStyle = UIBarStyleBlack;
navBar.translucent = self.toolbarIsTranslucent;
_previousButton.enabled = [self.photoAlbumView hasPrevious];
_nextButton.enabled = [self.photoAlbumView hasNext];
}
#if __IPHONE_OS_VERSION_MIN_REQUIRED < NIIOS_6_0
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return NIIsSupportedOrientation(toInterfaceOrientation);
}
#endif
- (void)willRotateToInterfaceOrientation: (UIInterfaceOrientation)toInterfaceOrientation
duration: (NSTimeInterval)duration {
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
[self.photoAlbumView willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}
- (void)willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation)toInterfaceOrientation
duration: (NSTimeInterval)duration {
[self.photoAlbumView willAnimateRotationToInterfaceOrientation: toInterfaceOrientation
duration: duration];
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation
duration:duration];
CGRect toolbarFrame = self.toolbar.frame;
toolbarFrame.size.height = NIToolbarHeightForOrientation(toInterfaceOrientation);
toolbarFrame.origin.y = self.view.bounds.size.height - toolbarFrame.size.height;
self.toolbar.frame = toolbarFrame;
if (!self.toolbarIsTranslucent) {
CGRect photoAlbumFrame = self.photoAlbumView.frame;
photoAlbumFrame.size.height = self.view.bounds.size.height - toolbarFrame.size.height;
self.photoAlbumView.frame = photoAlbumFrame;
}
}
- (UIView *)rotatingFooterView {
return self.toolbar.hidden ? nil : self.toolbar;
}
- (void)didHideChrome {
_isAnimatingChrome = NO;
if (self.toolbarIsTranslucent) {
self.toolbar.hidden = YES;
}
[self.navigationController setNavigationBarHidden:YES animated:NO];
_isChromeHidden = YES;
}
- (void)didShowChrome {
_isAnimatingChrome = NO;
_isChromeHidden = NO;
}
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
return UIStatusBarAnimationSlide;
}
- (BOOL)prefersStatusBarHidden {
return _prefersStatusBarHidden;
}
- (void)setChromeVisibility:(BOOL)isVisible animated:(BOOL)animated {
if (_isAnimatingChrome
|| (!isVisible && _isChromeHidden)
|| (isVisible && !_isChromeHidden)
|| !self.chromeCanBeHidden) {
// Nothing to do here.
return;
}
CGRect toolbarFrame = self.toolbar.frame;
CGRect bounds = self.view.bounds;
if (self.toolbarIsTranslucent) {
// Reset the toolbar's initial position.
if (!isVisible) {
toolbarFrame.origin.y = bounds.size.height - toolbarFrame.size.height;
} else {
// Ensure that the toolbar is visible through the animation.
self.toolbar.hidden = NO;
toolbarFrame.origin.y = bounds.size.height;
}
self.toolbar.frame = toolbarFrame;
}
// Show/hide the system chrome.
BOOL isStatusBarAppearanceSupported = [self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)];
if (!isStatusBarAppearanceSupported) {
[[UIApplication sharedApplication] setStatusBarHidden:!isVisible
withAnimation:(animated
? UIStatusBarAnimationSlide
: UIStatusBarAnimationNone)];
}
if (self.toolbarIsTranslucent) {
// Place the toolbar at its final location.
if (isVisible) {
// Slide up.
toolbarFrame.origin.y = bounds.size.height - toolbarFrame.size.height;
} else {
// Slide down.
toolbarFrame.origin.y = bounds.size.height;
}
}
// If there is a navigation bar, place it at its final location.
CGRect navigationBarFrame = self.navigationController.navigationBar.frame;
if (animated) {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:(isVisible
? @selector(didShowChrome)
: @selector(didHideChrome))];
// Ensure that the animation matches the status bar's.
[UIView setAnimationDuration:NIStatusBarAnimationDuration()];
[UIView setAnimationCurve:NIStatusBarAnimationCurve()];
}
if (isStatusBarAppearanceSupported) {
_prefersStatusBarHidden = !isVisible;
[self setNeedsStatusBarAppearanceUpdate];
}
if (nil != self.navigationController.navigationBar) {
if (isVisible) {
[UIView setAnimationsEnabled:NO];
[self.navigationController setNavigationBarHidden:NO animated:NO];
navigationBarFrame.origin.y = 0;
self.navigationController.navigationBar.frame = navigationBarFrame;
self.navigationController.navigationBar.alpha = 0;
[UIView setAnimationsEnabled:YES];
navigationBarFrame.origin.y = NIStatusBarHeight();
} else {
navigationBarFrame.origin.y = 0;
}
}
if (self.toolbarIsTranslucent) {
self.toolbar.frame = toolbarFrame;
}
if (nil != self.navigationController.navigationBar) {
self.navigationController.navigationBar.frame = navigationBarFrame;
self.navigationController.navigationBar.alpha = (isVisible ? 1 : 0);
}
if (animated) {
_isAnimatingChrome = YES;
[UIView commitAnimations];
} else if (!isVisible) {
[self didHideChrome];
} else if (isVisible) {
[self didShowChrome];
}
}
- (void)toggleChromeVisibility {
[self setChromeVisibility:(_isChromeHidden || _isAnimatingChrome) animated:YES];
}
#pragma mark - UIGestureRecognizer
- (void)didTap {
SEL selector = @selector(toggleChromeVisibility);
if (self.photoAlbumView.zoomingIsEnabled) {
// Cancel any previous delayed performs so that we don't stack them.
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:selector object:nil];
// We need to delay taking action on the first tap in case a second tap comes in, causing
// a double-tap gesture to be recognized and the photo to be zoomed.
[self performSelector: selector
withObject: nil
afterDelay: 0.3];
} else {
// When zooming is disabled, double-tap-to-zoom is also disabled so we don't have to
// be as careful; just toggle the chrome immediately.
[self toggleChromeVisibility];
}
}
- (void)refreshChromeState {
self.previousButton.enabled = [self.photoAlbumView hasPrevious];
self.nextButton.enabled = [self.photoAlbumView hasNext];
[self setChromeTitle];
}
- (void)setChromeTitle {
self.title = [NSString stringWithFormat:@"%zd of %zd",
(self.photoAlbumView.centerPageIndex + 1),
self.photoAlbumView.numberOfPages];
}
#pragma mark - NIPhotoAlbumScrollViewDelegate
- (void)pagingScrollViewDidScroll:(NIPagingScrollView *)pagingScrollView {
if (self.hidesChromeWhenScrolling) {
[self setChromeVisibility:NO animated:YES];
}
}
- (void)photoAlbumScrollView: (NIPhotoAlbumScrollView *)photoAlbumScrollView
didZoomIn: (BOOL)didZoomIn {
// This delegate method is called after a double-tap gesture, so cancel any pending
// single-tap gestures.
[NSObject cancelPreviousPerformRequestsWithTarget: self
selector: @selector(toggleChromeVisibility)
object: nil];
}
- (void)pagingScrollViewDidChangePages:(NIPagingScrollView *)pagingScrollView {
// We animate the scrubber when the chrome won't disappear as a nice touch.
// We don't bother animating if the chrome disappears when scrolling because the user
// will barely see the animation happen.
[self.photoScrubberView setSelectedPhotoIndex: [pagingScrollView centerPageIndex]
animated: !self.hidesChromeWhenScrolling];
[self refreshChromeState];
}
#pragma mark - NIPhotoScrubberViewDelegate
- (void)photoScrubberViewDidChangeSelection:(NIPhotoScrubberView *)photoScrubberView {
[self.photoAlbumView moveToPageAtIndex:photoScrubberView.selectedPhotoIndex animated:NO];
[self refreshChromeState];
}
#pragma mark - Actions
- (void)didTapNextButton {
[self.photoAlbumView moveToNextAnimated:self.animateMovingToNextAndPreviousPhotos];
[self refreshChromeState];
}
- (void)didTapPreviousButton {
[self.photoAlbumView moveToPreviousAnimated:self.animateMovingToNextAndPreviousPhotos];
[self refreshChromeState];
}
#pragma mark - Public
- (void)settoolbarIsTranslucent:(BOOL)enabled {
_toolbarIsTranslucent = enabled;
self.toolbar.translucent = enabled;
}
- (void)setHidesChromeWhenScrolling:(BOOL)hidesToolbar {
_hidesChromeWhenScrolling = hidesToolbar;
if (hidesToolbar) {
[self addTapGestureToView];
} else {
[_tapGesture setEnabled:_chromeCanBeHidden];
}
}
- (void)setChromeCanBeHidden:(BOOL)canBeHidden {
_chromeCanBeHidden = canBeHidden;
if (canBeHidden) {
[self addTapGestureToView];
} else {
self.hidesChromeWhenScrolling = NO;
if ([self isViewLoaded]) {
// Ensure that the chrome is visible.
[self setChromeVisibility:YES animated:NO];
}
}
}
- (void)setScrubberIsEnabled:(BOOL)enabled {
if (_scrubberIsEnabled != enabled) {
_scrubberIsEnabled = enabled;
if ([self isViewLoaded]) {
[self updateToolbarItems];
}
}
}
@end

122
Pods/Nimbus/src/photos/src/NimbusPhotos.h

@ -1,122 +0,0 @@
//
// Copyright 2011-2014 NimbusKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
/**
* @defgroup NimbusPhotos Nimbus Photos
* @{
*
* <div id="github" feature="photos"></div>
*
* Photo viewers are a common, non-trivial feature in many types of iOS apps ranging from
* simple photo viewers to apps that fetch photos from an API. The Nimbus photo album viewer
* is designed to consume minimal amounts of memory and encourage the use of threads to provide
* a high quality user experience that doesn't include any blocking of the UI while images
* are loaded from disk or the network. The photo viewer pre-caches images in an album to either
* side of the current image so that the user will ideally always have a high-quality
* photo experience.
*
* <h2>Adding the Photos Feature to Your Application</h2>
*
* The Nimbus Photos feature uses a small number of custom photos that are stored in the
* NimbusPhotos bundle. You must add this bundle to your application, ensuring that you select
* the "Create Folder References" option and that the bundle is copied in the
* "Copy Bundle Resources" phase.
*
* The bundle can be found at <code>src/photos/resources/NimbusPhotos.bundle</code>.
*
*
* <h2>Feature Breakdown</h2>
*
* NIPhotoAlbumScrollView - A paged scroll view that implements a data source similar to that
* of UITableView. This scroll view consumes minimal amounts of memory and is built to be fast
* and responsive.
*
* NIPhotoScrollView - A single page within the NIPhotoAlbumScrollView. This view implements
* the zooming and rotation functionality for a photo.
*
* NIPhotoScrubberView - A scrubber view for skimming through a set of photos. This view
* made its debut by Apple on the iPad in Photos.app. Nimbus' implementation of this view
* is built to be responsive and consume little memory.
*
* NIToolbarPhotoViewController - A skeleton implementation of a view controller that includes
* multiple configurable properties. This controller will show a scrubber on the iPad and
* next/previous arrows on the iPhone. It also provides support for hiding and showing the
* app's chrome. If you wish to use this controller you must simply implement the data source
* for the photo album. NetworkPhotoAlbum in the examples/photos directory demos building
* a data source that fetches its information from the network.
*
*
* <h2>Architecture</h2>
*
* The architectural design of the photo album view takes inspiration from UITableView. Images
* are requested only when they might become visible and are released when they become
* inaccessible again. Each page of the photo album view is a recycled NIPhotoScrollView.
* These page views handle zooming and panning within a given photo. The photo album view
* NIPhotoAlbumScrollView contains a paging scroll view of these page views and provides
* interfaces for maintaining the orientation during rotations.
*
* The view controller NIToolbarPhotoViewController is provided as a basic implementation of
* functionality that is expected from a photo viewer. This includes: a toolbar with next and
* previous arrows; auto-rotation support; and toggling the chrome.
*
* <h3>NIPhotoAlbumScrollView</h3>
*
* NIPhotoAlbumScrollView is the meat of the Nimbus photo viewer's functionality. Contained
* within this view are pages of NIPhotoScrollView views. In your view controller you are
* expected to implement the NIPhotoAlbumScrollViewDataSource in order to provide the photo
* album view with the necessary information for presenting an album.
*
*
* <h2>Example Applications</h2>
*
* <h3>Network Photo Albums</h3>
*
* <a href="https://github.com/jverkoey/nimbus/tree/master/examples/photos/NetworkPhotoAlbums">View the README on GitHub</a>
*
* This sample application demos the use of the multiple photo APIs to fetch photos from public
* photo album and display them in high-definition on the iPad and iPhone.
*
* The following APIs are currently demoed:
*
* - Facebook Graph API
* - Dribbble Shots
*
* Sample location: <code>examples/photos/NetworkPhotoAlbums</code>
*
*
* <h2>Screenshots</h2>
*
* @image html photos-iphone-example1.png "Screenshot of a basic photo album on the iPhone."
*
* Image source: <a href="http://www.flickr.com/photos/janekm/360669001/">flickr.com/photos/janekm/360669001</a>
*/
/**@}*/
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "NIPhotoAlbumScrollView.h"
#import "NIPhotoAlbumScrollViewDataSource.h"
#import "NIPhotoAlbumScrollViewDelegate.h"
#import "NIPhotoScrollView.h"
#import "NIPhotoScrollViewDelegate.h"
#import "NIPhotoScrollViewPhotoSize.h"
#import "NIPhotoScrubberView.h"
#import "NIToolbarPhotoViewController.h"
#import "NimbusPagingScrollView.h"
#import "NimbusCore.h"

21
Pods/WebBrowser/LICENSE.md

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2016 Teambition
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

90
Pods/WebBrowser/README.md

@ -1,90 +0,0 @@
# WebBrowser
A web browser using WebKit and written in Swift for iOS apps.
![Example](Gif/WebBrowserExample.gif "WebBrowserExample")
## How To Get Started
### Carthage
Specify "WebBrowser" in your ```Cartfile```:
```ogdl
github "teambition/WebBrowser"
```
### CocoaPods
Specify "WebBrowser" in your ```Podfile```:
```ruby
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!
pod 'WebBrowser'
```
### Usage
#### Initialization
```swift
let webBrowserViewController = WebBrowserViewController()
// assign delegate
webBrowserViewController.delegate = self
webBrowserViewController.language = .english
webBrowserViewController.tintColor = ...
webBrowserViewController.barTintColor = ...
webBrowserViewController.isToolbarHidden = false
webBrowserViewController.isShowActionBarButton = true
webBrowserViewController.toolbarItemSpace = 50
webBrowserViewController.isShowURLInNavigationBarWhenLoading = true
webBrowserViewController.isShowPageTitleInNavigationBar = true
webBrowserViewController.customApplicationActivities = ...
webBrowserViewController.loadURLString("https://www.apple.com/cn/")
```
#### Pushing to the navigation stack
```swift
navigationController?.pushViewController(webBrowserViewController, animated: true)
```
#### Presenting modally
```swift
let navigationWebBrowser = WebBrowserViewController.rootNavigationWebBrowser(webBrowser: webBrowserViewController)
present(navigationWebBrowser, animated: true, completion: nil)
```
#### Implement the delegate
```swift
func webBrowser(_ webBrowser: WebBrowserViewController, didStartLoad url: URL?) {
// do something
}
func webBrowser(_ webBrowser: WebBrowserViewController, didFinishLoad url: URL?) {
// do something
}
func webBrowser(_ webBrowser: WebBrowserViewController, didFailLoad url: URL?, withError error: Error) {
// do something
}
func webBrowserWillDismiss(_ webBrowser: WebBrowserViewController) {
// do something
}
func webBrowserDidDismiss(_ webBrowser: WebBrowserViewController) {
// do something
}
```
## Minimum Requirement
iOS 8.0
## Localization
WebBrowser supports 5 languages: English, Simplified Chinese, Traditional Chinese, Korean, Japanese. You can set the language when initialization.
## Release Notes
* [Release Notes](https://github.com/teambition/WebBrowser/releases)
## License
WebBrowser is released under the MIT license. See [LICENSE](https://github.com/teambition/WebBrowser/blob/master/LICENSE.md) for details.
## More Info
Have a question? Please [open an issue](https://github.com/teambition/WebBrowser/issues/new)!

44
Pods/WebBrowser/WebBrowser/InternationalControl.swift

@ -1,44 +0,0 @@
//
// InternationalControl.swift
// WebBrowser
//
// Created by Xin Hong on 16/4/27.
// Copyright © 2016 Teambition. All rights reserved.
//
import Foundation
public enum WebBrowserLanguage {
case english
case simplifiedChinese
case traditionalChinese
case korean
case japanese
internal var identifier: String {
switch self {
case .english: return "en"
case .simplifiedChinese: return "zh-Hans"
case .traditionalChinese: return "zh-Hant"
case .korean: return "ko"
case .japanese: return "ja"
}
}
}
internal func LocalizedString(key: String, comment: String? = nil) -> String {
return InternationalControl.sharedControl.localizedString(key: key, comment: comment)
}
internal struct InternationalControl {
internal static var sharedControl = InternationalControl()
internal var language: WebBrowserLanguage = .english
internal func localizedString(key: String, comment: String? = nil) -> String {
guard let localizationPath = WebBrowser.localizationPath(forIdentifier: language.identifier) else {
return key
}
let bundle = Bundle(path: localizationPath)
return bundle?.localizedString(forKey: key, value: nil, table: "WebBrowser") ?? key
}
}

39
Pods/WebBrowser/WebBrowser/NavigationBarAppearance.swift

@ -1,39 +0,0 @@
//
// NavigationBarAppearance.swift
// WebBrowser
//
// Created by Xin Hong on 16/4/30.
// Copyright © 2016 Teambition. All rights reserved.
//
import UIKit
internal struct NavigationBarAppearance {
var isHidden = false
var tintColor = UIColor.blue
var barTintColor: UIColor?
var isTranslucent = true
var shadowImage: UIImage?
var backgroundImageForBarMetricsDefault: UIImage?
var backgroundImageForBarMetricsCompact: UIImage?
init() { }
init(navigationBar: UINavigationBar) {
tintColor = navigationBar.tintColor
barTintColor = navigationBar.barTintColor
isTranslucent = navigationBar.isTranslucent
shadowImage = navigationBar.shadowImage
backgroundImageForBarMetricsDefault = navigationBar.backgroundImage(for: .default)
backgroundImageForBarMetricsCompact = navigationBar.backgroundImage(for: .compact)
}
func apply(to navigationBar: UINavigationBar) {
navigationBar.tintColor = tintColor
navigationBar.barTintColor = barTintColor
navigationBar.isTranslucent = isTranslucent
navigationBar.shadowImage = shadowImage
navigationBar.setBackgroundImage(backgroundImageForBarMetricsDefault, for: .default)
navigationBar.setBackgroundImage(backgroundImageForBarMetricsCompact, for: .compact)
}
}

14
Pods/WebBrowser/WebBrowser/Resources/LocalizedStrings/en.lproj/WebBrowser.strings

@ -1,14 +0,0 @@
/*
WebBrowser.strings
WebBrowser
Created by Xin Hong on 16/4/27.
Copyright © 2016年 Teambition. All rights reserved.
*/
"Done" = "Done";
"Cancel" = "Cancel";
"Open" = "Open";
"OpenExternalAppAlert.title" = "Leave this app?";
"OpenExternalAppAlert.message" = "This web page is trying to open an outside app. Are you sure to open it?";
"Open in Safari" = "Open in Safari";

14
Pods/WebBrowser/WebBrowser/Resources/LocalizedStrings/ja.lproj/WebBrowser.strings

@ -1,14 +0,0 @@
/*
WebBrowser.strings
WebBrowser
Created by Xin Hong on 16/4/27.
Copyright © 2016年 Teambition. All rights reserved.
*/
"Done" = "完了";
"Cancel" = "キャンセル";
"Open" = "開く";
"OpenExternalAppAlert.title" = "離れるこの App?";
"OpenExternalAppAlert.message" = "このページこのページしようとして開くもうひとつApp, 確定開く?";
"Open in Safari" = "Safariで開く";

14
Pods/WebBrowser/WebBrowser/Resources/LocalizedStrings/ko.lproj/WebBrowser.strings

@ -1,14 +0,0 @@
/*
WebBrowser.strings
WebBrowser
Created by Xin Hong on 16/4/27.
Copyright © 2016年 Teambition. All rights reserved.
*/
"Done" = "완료";
"Cancel" = "취소";
"Open" = "열기";
"OpenExternalAppAlert.title" = "떠나다이 App?";
"OpenExternalAppAlert.message" = "이 페이지 애쓰고 있다 열기 다른 App, 확정 열기?";
"Open in Safari" = "Safari로 열기";

14
Pods/WebBrowser/WebBrowser/Resources/LocalizedStrings/zh-Hans.lproj/WebBrowser.strings

@ -1,14 +0,0 @@
/*
WebBrowser.strings
WebBrowser
Created by Xin Hong on 16/4/27.
Copyright © 2016年 Teambition. All rights reserved.
*/
"Done" = "完成";
"Cancel" = "取消";
"Open" = "打开";
"OpenExternalAppAlert.title" = "离开此应用?";
"OpenExternalAppAlert.message" = "此页面正试图打开另一个应用,确定要打开吗?";
"Open in Safari" = "在 Safari 中打开";

14
Pods/WebBrowser/WebBrowser/Resources/LocalizedStrings/zh-Hant.lproj/WebBrowser.strings

@ -1,14 +0,0 @@
/*
WebBrowser.strings
WebBrowser
Created by Xin Hong on 16/4/27.
Copyright © 2016年 Teambition. All rights reserved.
*/
"Done" = "完成";
"Cancel" = "取消";
"Open" = "打開";
"OpenExternalAppAlert.title" = "離開此App?";
"OpenExternalAppAlert.message" = "此頁面正試圖打開另一App,確定要打開嗎?";
"Open in Safari" = "在 Safari 中打開";

6
Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/Contents.json

@ -1,6 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

23
Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/backIcon.imageset/Contents.json

@ -1,23 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "backIcon.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "backIcon@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "backIcon@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/backIcon.imageset/backIcon.png

Before

Width: 22  |  Height: 22  |  Size: 1.2 KiB

BIN
Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/backIcon.imageset/backIcon@2x.png

Before

Width: 44  |  Height: 44  |  Size: 1.6 KiB

BIN
Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/backIcon.imageset/backIcon@3x.png

Before

Width: 66  |  Height: 66  |  Size: 1.9 KiB

23
Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/forwardIcon.imageset/Contents.json

@ -1,23 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "forwardIcon.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "forwardIcon@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "forwardIcon@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/forwardIcon.imageset/forwardIcon.png

Before

Width: 22  |  Height: 22  |  Size: 1.3 KiB

BIN
Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/forwardIcon.imageset/forwardIcon@2x.png

Before

Width: 44  |  Height: 44  |  Size: 1.6 KiB

BIN
Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/forwardIcon.imageset/forwardIcon@3x.png

Before

Width: 66  |  Height: 66  |  Size: 1.9 KiB

33
Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/safariIcon.imageset/Contents.json

@ -1,33 +0,0 @@
{
"images" : [
{
"idiom" : "iphone",
"filename" : "safariIcon.png",
"scale" : "1x"
},
{
"idiom" : "iphone",
"filename" : "safariIcon@2x.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"filename" : "safariIcon@3x.png",
"scale" : "3x"
},
{
"idiom" : "ipad",
"filename" : "safariIcon~iPad.png",
"scale" : "1x"
},
{
"idiom" : "ipad",
"filename" : "safariIcon@2x~iPad.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/safariIcon.imageset/safariIcon.png

Before

Width: 60  |  Height: 60  |  Size: 1.5 KiB

BIN
Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/safariIcon.imageset/safariIcon@2x.png

Before

Width: 120  |  Height: 120  |  Size: 3.6 KiB

BIN
Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/safariIcon.imageset/safariIcon@2x~iPad.png

Before

Width: 152  |  Height: 152  |  Size: 4.9 KiB

BIN
Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/safariIcon.imageset/safariIcon@3x.png

Before

Width: 180  |  Height: 180  |  Size: 6.0 KiB

BIN
Pods/WebBrowser/WebBrowser/Resources/WebBrowser.xcassets/safariIcon.imageset/safariIcon~iPad.png

Before

Width: 76  |  Height: 76  |  Size: 2.1 KiB

49
Pods/WebBrowser/WebBrowser/SafariActivity.swift

@ -1,49 +0,0 @@
//
// SafariActivity.swift
// WebBrowser
//
// Created by Xin Hong on 16/4/27.
// Copyright © 2016 Teambition. All rights reserved.
//
import UIKit
open class SafariActivity: UIActivity {
open var url: URL?
open override var activityType: UIActivity.ActivityType? {
return ActivityType(String(describing: self))
}
open override var activityTitle : String? {
return LocalizedString(key: "Open in Safari")
}
open override var activityImage : UIImage? {
return WebBrowser.image(named: "safariIcon")
}
open override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
for activityItem in activityItems {
if let activityURL = activityItem as? URL {
return UIApplication.shared.canOpenURL(activityURL)
}
}
return false
}
open override func prepare(withActivityItems activityItems: [Any]) {
for activityItem in activityItems {
if let activityURL = activityItem as? URL {
url = activityURL
}
}
}
open override func perform() {
if let url = url {
let completed = UIApplication.shared.openURL(url)
activityDidFinish(completed)
}
}
}

30
Pods/WebBrowser/WebBrowser/ToolbarAppearance.swift

@ -1,30 +0,0 @@
//
// ToolbarAppearance.swift
// WebBrowser
//
// Created by Xin Hong on 16/4/30.
// Copyright © 2016 Teambition. All rights reserved.
//
import UIKit
internal struct ToolbarAppearance {
var isHidden = true
var tintColor = UIColor.blue
var barTintColor: UIColor?
var isTranslucent = true
init() { }
init(toolbar: UIToolbar) {
tintColor = toolbar.tintColor
barTintColor = toolbar.barTintColor
isTranslucent = toolbar.isTranslucent
}
func apply(to toolbar: UIToolbar) {
toolbar.tintColor = tintColor
toolbar.barTintColor = barTintColor
toolbar.isTranslucent = isTranslucent
}
}

38
Pods/WebBrowser/WebBrowser/WebBrowser.swift

@ -1,38 +0,0 @@
//
// WebBrowser.swift
// WebBrowser
//
// Created by Xin Hong on 16/4/27.
// Copyright © 2016 Teambition. All rights reserved.
//
import UIKit
internal struct WebBrowser {
static let estimatedProgressKeyPath = "estimatedProgress"
static var estimatedProgressContext = 0
static let defaultToolbarItemSpace: CGFloat = 50
static var resourceBundleURL: URL? {
let resourceBundleURL = Bundle(for: WebBrowserViewController.self).url(forResource: "WebBrowser", withExtension: "bundle")
return resourceBundleURL
}
static func localizationPath(forIdentifier identifier: String) -> String? {
if let path = Bundle(identifier: "Teambition.WebBrowser")?.path(forResource: identifier, ofType: "lproj") {
return path
} else if let resourceBundleURL = resourceBundleURL, let resourceBundle = Bundle(url: resourceBundleURL) {
return resourceBundle.path(forResource: identifier, ofType: "lproj")
}
return nil
}
static func image(named name: String) -> UIImage? {
if let image = UIImage(named: name, in: Bundle(for: WebBrowserViewController.self), compatibleWith: nil) {
return image
} else if let resourceBundleURL = resourceBundleURL, let resourceBundle = Bundle(url: resourceBundleURL) {
return UIImage(named: name, in: resourceBundle, compatibleWith: nil)
}
return nil
}
}

46
Pods/WebBrowser/WebBrowser/WebBrowserDelegate.swift

@ -1,46 +0,0 @@
//
// WebBrowserDelegate.swift
// WebBrowser
//
// Created by Xin Hong on 16/4/26.
// Copyright © 2016 Teambition. All rights reserved.
//
import UIKit
import WebKit
public protocol WebBrowserDelegate: class {
func webBrowser(_ webBrowser: WebBrowserViewController, didStartLoad url: URL?)
func webBrowser(_ webBrowser: WebBrowserViewController, didFinishLoad url: URL?)
func webBrowser(_ webBrowser: WebBrowserViewController, didFailLoad url: URL?, withError error: Error)
func webBrowserWillDismiss(_ webBrowser: WebBrowserViewController)
func webBrowserDidDismiss(_ webBrowser: WebBrowserViewController)
func webBrowser(_ webBrowser: WebBrowserViewController, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) -> Bool
}
public extension WebBrowserDelegate {
func webBrowser(_ webBrowser: WebBrowserViewController, didStartLoad url: URL?) {
}
func webBrowser(_ webBrowser: WebBrowserViewController, didFinishLoad url: URL?) {
}
func webBrowser(_ webBrowser: WebBrowserViewController, didFailLoad url: URL?, withError error: Error) {
}
func webBrowserWillDismiss(_ webBrowser: WebBrowserViewController) {
}
func webBrowserDidDismiss(_ webBrowser: WebBrowserViewController) {
}
func webBrowser(_ webBrowser: WebBrowserViewController, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) -> Bool {
return false
}
}

412
Pods/WebBrowser/WebBrowser/WebBrowserViewController.swift

@ -1,412 +0,0 @@
//
// WebBrowserViewController.swift
// WebBrowser
//
// Created by Xin Hong on 16/4/26.
// Copyright © 2016 Teambition. All rights reserved.
//
import UIKit
import WebKit
open class WebBrowserViewController: UIViewController {
open weak var delegate: WebBrowserDelegate?
open var language: WebBrowserLanguage = .english {
didSet {
InternationalControl.sharedControl.language = language
}
}
open var tintColor = UIColor.blue {
didSet {
updateTintColor()
}
}
open var barTintColor: UIColor? {
didSet {
updateBarTintColor()
}
}
open var isToolbarHidden = false {
didSet {
navigationController?.setToolbarHidden(isToolbarHidden, animated: true)
}
}
open var toolbarItemSpace = WebBrowser.defaultToolbarItemSpace {
didSet {
itemFixedSeparator.width = toolbarItemSpace
}
}
open var isShowActionBarButton = true {
didSet {
updateToolBarState()
}
}
open var customApplicationActivities = [UIActivity]()
open var isShowURLInNavigationBarWhenLoading = true
open var isShowPageTitleInNavigationBar = true
fileprivate var webView = WKWebView(frame: CGRect.zero)
public func getWKWebView() -> WKWebView {
return webView
}
fileprivate lazy var progressView: UIProgressView = {
let progressView = UIProgressView(progressViewStyle: .default)
progressView.trackTintColor = .clear
progressView.tintColor = self.tintColor
return progressView
}()
fileprivate var previousNavigationControllerNavigationBarAppearance = NavigationBarAppearance()
fileprivate var previousNavigationControllerToolbarAppearance = ToolbarAppearance()
fileprivate lazy var refreshButton: UIBarButtonItem = {
let refreshButton = UIBarButtonItem(barButtonSystemItem: .refresh, target: self, action: #selector(WebBrowserViewController.refreshButtonTapped(_:)))
return refreshButton
}()
fileprivate lazy var stopButton: UIBarButtonItem = {
let stopButton = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(WebBrowserViewController.stopButtonTapped(_:)))
return stopButton
}()
fileprivate lazy var backButton: UIBarButtonItem = {
let backIcon = WebBrowser.image(named: "backIcon")
let backButton = UIBarButtonItem(image: backIcon, style: .plain, target: self, action: #selector(WebBrowserViewController.backButtonTapped(_:)))
return backButton
}()
fileprivate lazy var forwardButton: UIBarButtonItem = {
let forwardIcon = WebBrowser.image(named: "forwardIcon")
let forwardButton = UIBarButtonItem(image: forwardIcon, style: .plain, target: self, action: #selector(WebBrowserViewController.forwardButtonTapped(_:)))
return forwardButton
}()
fileprivate lazy var actionButton: UIBarButtonItem = {
let actionButton = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(WebBrowserViewController.actionButtonTapped(_:)))
return actionButton
}()
fileprivate lazy var itemFixedSeparator: UIBarButtonItem = {
let itemFixedSeparator = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
itemFixedSeparator.width = self.toolbarItemSpace
return itemFixedSeparator
}()
fileprivate lazy var itemFlexibleSeparator: UIBarButtonItem = {
let itemFlexibleSeparator = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
return itemFlexibleSeparator
}()
public var onOpenExternalAppHandler: ((_ isOpen: Bool) -> Void)?
// MARK: - Life cycle
open override func viewDidLoad() {
super.viewDidLoad()
savePreviousNavigationControllerState()
configureWebView()
configureProgressView()
}
open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(false, animated: true)
navigationController?.navigationBar.setBackgroundImage(nil, for: .default)
navigationController?.navigationBar.shadowImage = nil
navigationController?.navigationBar.isTranslucent = true
navigationController?.navigationBar.addSubview(progressView)
navigationController?.setToolbarHidden(isToolbarHidden, animated: true)
progressView.alpha = 0
updateTintColor()
updateBarTintColor()
updateToolBarState()
}
open override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
restorePreviousNavigationControllerState(animated: animated)
progressView.removeFromSuperview()
}
public convenience init(configuration: WKWebViewConfiguration) {
self.init()
webView = WKWebView(frame: CGRect.zero, configuration: configuration)
}
open class func rootNavigationWebBrowser(webBrowser: WebBrowserViewController) -> UINavigationController {
webBrowser.navigationItem.rightBarButtonItem = UIBarButtonItem(title: LocalizedString(key: "Done"), style: .done, target: webBrowser, action: #selector(WebBrowserViewController.doneButtonTapped(_:)))
let navigationController = UINavigationController(rootViewController: webBrowser)
return navigationController
}
deinit {
webView.uiDelegate = nil
webView.navigationDelegate = nil
if isViewLoaded {
webView.removeObserver(self, forKeyPath: WebBrowser.estimatedProgressKeyPath)
}
}
// MARK: - Public
open func loadRequest(_ request: URLRequest) {
webView.load(request)
}
open func loadURL(_ url: URL) {
webView.load(URLRequest(url: url))
}
open func loadURLString(_ urlString: String) {
guard let url = URL(string: urlString) else {
return
}
webView.load(URLRequest(url: url))
}
open func loadHTMLString(_ htmlString: String, baseURL: URL?) {
webView.loadHTMLString(htmlString, baseURL: baseURL)
}
}
extension WebBrowserViewController {
// MARK: - Helper
fileprivate func configureWebView() {
webView.frame = view.bounds
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
webView.autoresizesSubviews = true
webView.navigationDelegate = self
webView.uiDelegate = self
webView.isMultipleTouchEnabled = true
webView.scrollView.alwaysBounceVertical = true
view.addSubview(webView)
webView.addObserver(self, forKeyPath: WebBrowser.estimatedProgressKeyPath, options: .new, context: &WebBrowser.estimatedProgressContext)
}
fileprivate func configureProgressView() {
let yPosition: CGFloat = {
guard let navigationBar = self.navigationController?.navigationBar else {
return 0
}
return navigationBar.frame.height - self.progressView.frame.height
}()
progressView.frame = CGRect(x: 0, y: yPosition, width: view.frame.width, height: progressView.frame.width)
progressView.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
}
fileprivate func savePreviousNavigationControllerState() {
guard let navigationController = navigationController else {
return
}
var navigationBarAppearance = NavigationBarAppearance(navigationBar: navigationController.navigationBar)
navigationBarAppearance.isHidden = navigationController.isNavigationBarHidden
previousNavigationControllerNavigationBarAppearance = navigationBarAppearance
var toolbarAppearance = ToolbarAppearance(toolbar: navigationController.toolbar)
toolbarAppearance.isHidden = navigationController.isToolbarHidden
previousNavigationControllerToolbarAppearance = toolbarAppearance
}
fileprivate func restorePreviousNavigationControllerState(animated: Bool) {
guard let navigationController = navigationController else {
return
}
navigationController.setNavigationBarHidden(previousNavigationControllerNavigationBarAppearance.isHidden, animated: animated)
navigationController.setToolbarHidden(previousNavigationControllerToolbarAppearance.isHidden, animated: animated)
previousNavigationControllerNavigationBarAppearance.apply(to: navigationController.navigationBar)
previousNavigationControllerToolbarAppearance.apply(to: navigationController.toolbar)
}
fileprivate func updateTintColor() {
progressView.tintColor = tintColor
navigationController?.navigationBar.tintColor = tintColor
navigationController?.toolbar.tintColor = tintColor
}
fileprivate func updateBarTintColor() {
navigationController?.navigationBar.barTintColor = barTintColor
navigationController?.toolbar.barTintColor = barTintColor
}
}
extension WebBrowserViewController {
// MARK: - UIBarButtonItem actions
@objc func refreshButtonTapped(_ sender: UIBarButtonItem) {
webView.stopLoading()
webView.reload()
}
@objc func stopButtonTapped(_ sender: UIBarButtonItem) {
webView.stopLoading()
}
@objc func backButtonTapped(_ sender: UIBarButtonItem) {
webView.goBack()
updateToolBarState()
}
@objc func forwardButtonTapped(_ sender: UIBarButtonItem) {
webView.goForward()
updateToolBarState()
}
@objc func actionButtonTapped(_ sender: UIBarButtonItem) {
DispatchQueue.main.async {
var activityItems = [Any]()
if let url = self.webView.url {
activityItems.append(url)
}
var applicationActivities = [UIActivity]()
applicationActivities.append(SafariActivity())
applicationActivities.append(contentsOf: self.customApplicationActivities)
let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities)
activityViewController.view.tintColor = self.tintColor
if UIDevice.current.userInterfaceIdiom == .pad {
activityViewController.popoverPresentationController?.barButtonItem = sender
activityViewController.popoverPresentationController?.permittedArrowDirections = .any
self.present(activityViewController, animated: true, completion: nil)
} else {
self.present(activityViewController, animated: true, completion: nil)
}
}
}
@objc func doneButtonTapped(_ sender: UIBarButtonItem) {
delegate?.webBrowserWillDismiss(self)
dismiss(animated: true) {
self.delegate?.webBrowserDidDismiss(self)
}
}
}
extension WebBrowserViewController {
// MARK: - Tool bar
public func updateToolBarState() {
backButton.isEnabled = webView.canGoBack
forwardButton.isEnabled = webView.canGoForward
var barButtonItems = [UIBarButtonItem]()
if webView.isLoading {
barButtonItems = [backButton, itemFixedSeparator, forwardButton, itemFixedSeparator, stopButton, itemFlexibleSeparator]
if let urlString = webView.url?.absoluteString, isShowURLInNavigationBarWhenLoading {
var titleString = urlString.replacingOccurrences(of: "http://", with: "", options: .literal, range: nil)
titleString = titleString.replacingOccurrences(of: "https://", with: "", options: .literal, range: nil)
navigationItem.title = titleString
}
} else {
barButtonItems = [backButton, itemFixedSeparator, forwardButton, itemFixedSeparator, refreshButton, itemFlexibleSeparator]
if isShowPageTitleInNavigationBar {
navigationItem.title = webView.title
}
}
if isShowActionBarButton {
barButtonItems.append(actionButton)
}
setToolbarItems(barButtonItems, animated: true)
}
}
extension WebBrowserViewController {
// MARK: - External app support
fileprivate func externalAppRequiredToOpen(_ url: URL) -> Bool {
let validSchemes: Set<String> = ["http", "https"]
if let urlScheme = url.scheme {
return !validSchemes.contains(urlScheme)
} else {
return false
}
}
fileprivate func openExternalApp(with url: URL) {
let externalAppPermissionAlert = UIAlertController(title: LocalizedString(key: "OpenExternalAppAlert.title"), message: LocalizedString(key: "OpenExternalAppAlert.message"), preferredStyle: .alert)
let cancelAction = UIAlertAction(title: LocalizedString(key: "Cancel"), style: .cancel, handler: { [weak self] (action) in
self?.onOpenExternalAppHandler?(false)
})
let openAction = UIAlertAction(title: LocalizedString(key: "Open"), style: .default) { [weak self] (action) in
UIApplication.shared.openURL(url)
self?.onOpenExternalAppHandler?(true)
}
externalAppPermissionAlert.addAction(cancelAction)
externalAppPermissionAlert.addAction(openAction)
present(externalAppPermissionAlert, animated: true, completion: nil)
}
}
extension WebBrowserViewController {
// MARK: - Observer
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let keyPath = keyPath, (keyPath == WebBrowser.estimatedProgressKeyPath && context == &WebBrowser.estimatedProgressContext) {
progressView.alpha = 1
let animated = webView.estimatedProgress > Double(progressView.progress)
progressView.setProgress(Float(webView.estimatedProgress), animated: animated)
if webView.estimatedProgress >= 1 {
UIView.animate(withDuration: 0.3, delay: 0.3, options: .curveEaseOut, animations: {
self.progressView.alpha = 0
}, completion: { (finished) in
self.progressView.progress = 0
})
}
} else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
}
extension WebBrowserViewController: WKNavigationDelegate {
// MARK: - WKNavigationDelegate
public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
updateToolBarState()
delegate?.webBrowser(self, didStartLoad: webView.url)
}
public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
updateToolBarState()
delegate?.webBrowser(self, didFinishLoad: webView.url)
}
public func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
updateToolBarState()
delegate?.webBrowser(self, didFailLoad: webView.url, withError: error)
}
public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
updateToolBarState()
delegate?.webBrowser(self, didFailLoad: webView.url, withError: error)
}
public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if let oriDelegate = delegate, oriDelegate.webBrowser(self, decidePolicyFor: navigationAction, decisionHandler: decisionHandler) {
return
}
if let url = navigationAction.request.url {
if !externalAppRequiredToOpen(url) {
if navigationAction.targetFrame == nil {
loadURL(url)
decisionHandler(.cancel)
return
}
} else if UIApplication.shared.canOpenURL(url) {
openExternalApp(with: url)
decisionHandler(.cancel)
return
}
}
decisionHandler(.allow)
}
}
extension WebBrowserViewController: WKUIDelegate {
// MARK: - WKUIDelegate
public func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
if let mainFrame = navigationAction.targetFrame?.isMainFrame, mainFrame == false {
webView.load(navigationAction.request)
}
return nil
}
}

46
kplayer.xcodeproj/project.pbxproj

@ -9,8 +9,8 @@
/* Begin PBXBuildFile section */
1C73600CB93F16F4F28C116F /* KSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736A6E8396EE306B1AD3A8 /* KSettingsView.swift */; };
1C736048BFA120F5C7D36874 /* readme.md in Sources */ = {isa = PBXBuildFile; fileRef = 1C73685B4BBFDAFBF08C032C /* readme.md */; };
1C73604B4926AA24595D4FD7 /* DocPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7363CD8FF2C0A56DB63ADD /* DocPickerViewController.swift */; };
1C7360C0F2A4F0214FE353BD /* FileHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7367ECBD369A2A0C94C499 /* FileHelper.swift */; };
1C73613562EB375F53A0BD03 /* ServerDownloadDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736595533B56039C417E0D /* ServerDownloadDelegate.swift */; };
1C7361B3AF46CEB30D3F4FA0 /* KSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736AE5021E3D985FE3402D /* KSettings.swift */; };
1C73620BC7F5A1F35FFFF9FC /* SPhotoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736AD9F0A39AD543FC1176 /* SPhotoView.swift */; };
1C73631EACF56BABD3B2BCFB /* LayoutTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736BC4450890C45F8FBC63 /* LayoutTools.swift */; };
@ -22,11 +22,12 @@
1C736503B656C999E5E12081 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7365B06FA66294E99AC2D3 /* NetworkManager.swift */; };
1C73654C9EA6D255CFC039C5 /* NetworkHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73620D01687FB4F1811C5C /* NetworkHelper.swift */; };
1C736559059B6FBA1C661191 /* KToggleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73661C3F9F4E53645551AD /* KToggleButton.swift */; };
1C7365885FAF292F2221ED44 /* PhotoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73673DC671535E3A049F54 /* PhotoController.swift */; };
1C7365C59F72C29EA41C8717 /* SVideoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736EEC570C71AAC50F2E95 /* SVideoModel.swift */; };
1C7365C929045799553DF4BC /* MasterModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7363EDB190A6B76C5AAAF7 /* MasterModel.swift */; };
1C7365CE76693E7772585CA8 /* SVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73621E431C9BEC1440B936 /* SVideoPlayer.swift */; };
1C73666A07CF2416B1B8D3F0 /* KSettingsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736C94157754DE1C808173 /* KSettingsModel.swift */; };
1C736690D123BD4B24874394 /* pathfinder.scpt in Sources */ = {isa = PBXBuildFile; fileRef = 1C7369BED02028D8564E82D5 /* pathfinder.scpt */; };
1C736699A2992473AC2626AC /* MasterSplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736CDA5E3039EEE0CC39E4 /* MasterSplitView.swift */; };
1C7366A0CFD2B55BF8C3BAF0 /* NetworkDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7364F10BED5DA0F1C0423C /* NetworkDelegate.swift */; };
1C7366BEA68D6E4CEE43417E /* ImageLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736CC8D90375E86CD01964 /* ImageLoader.swift */; };
1C7366FE5C760C8D5117207F /* SVideoLoopPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360F9835128FC0A198ED0 /* SVideoLoopPlayer.swift */; };
@ -37,6 +38,7 @@
1C73675C34BE0990D44570BE /* ItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736253AB7A95EA41B605B7 /* ItemModel.swift */; };
1C736771C503FB0D52AEB8F7 /* kplayer.js in Sources */ = {isa = PBXBuildFile; fileRef = 1C73625012D50E457D18A785 /* kplayer.js */; };
1C736776CF759CA3DB136F33 /* KBrowserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736EF64DE56AD058A4F612 /* KBrowserView.swift */; };
1C736789A4B89024F6485608 /* PlayerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736F5826258B8B4BA6ECE6 /* PlayerApp.swift */; };
1C7367AF39961D2BA72480ED /* DataLoadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736F9338CE36708244D42A /* DataLoadOperation.swift */; };
1C736821D6DF2237A3EABCC1 /* ViewControllerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73648CEC974A2500172064 /* ViewControllerExtensions.swift */; };
1C7368242038C0FF6C9631E7 /* VideoHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7364709899FF62774B0199 /* VideoHelper.swift */; };
@ -44,7 +46,6 @@
1C7368DBC47F0CC152504141 /* SPhotoScrubber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7368EFFBCD688E0D425117 /* SPhotoScrubber.swift */; };
1C73691A9C7174E0C6B57267 /* stringutil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736B794396F2E50387B8F2 /* stringutil.swift */; };
1C73693A1334A7792856FC58 /* MasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73611D226B48C24DB37535 /* MasterViewController.swift */; };
1C736953BDBBAFC40884132A /* BrowserController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73602350ACE2436736F981 /* BrowserController.swift */; };
1C73696E4C0353053BF98031 /* links.html in Resources */ = {isa = PBXBuildFile; fileRef = 1C73615FFA2AA98BD1C56CD4 /* links.html */; };
1C736998044A9A7D89411892 /* AsyncImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360B6D0757D4FB6433E7B /* AsyncImage.swift */; };
1C7369ABC44CFB530EA71FB6 /* HeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736D9BB5498E7E8F11C754 /* HeaderCell.swift */; };
@ -53,6 +54,7 @@
1C736A5FA5BA53B2597F2ED7 /* Kirschkeks-256x256.png in Resources */ = {isa = PBXBuildFile; fileRef = 1C736059262A57AADE6AB761 /* Kirschkeks-256x256.png */; };
1C736A622876405F3EE2D043 /* EditItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7366C09381DC0052B52B69 /* EditItemView.swift */; };
1C736A7B6221A1D50FB3904C /* ItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73631C96E6C860833052CA /* ItemType.swift */; };
1C736A9DE203A7658D85B7B6 /* ItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73630B535C2358125793D4 /* ItemView.swift */; };
1C736B4B0889BD35DC566124 /* nspersistentcontainer-defaults-swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7361F01841F546FA7AFD58 /* nspersistentcontainer-defaults-swift.swift */; };
1C736BEC4C4263EF6A89E9E3 /* SPhotoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736F83C6190A3A0D5BD1F0 /* SPhotoModel.swift */; };
1C736C00693A05747578DF87 /* grabber.js in Sources */ = {isa = PBXBuildFile; fileRef = 1C73619BBFA9295A11C9ACBA /* grabber.js */; };
@ -66,6 +68,7 @@
1C736D5A7C7CB9B14AF0ECA6 /* DetailViewController+Show.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736B17A8E9FB352B90A903 /* DetailViewController+Show.swift */; };
1C736D89CF86841F4C98A1F7 /* KPersistentContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7362DE1D6BE634D7C2ACBF /* KPersistentContainer.swift */; };
1C736DAE7F2A530AACDA0D18 /* KFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7363743CD8120637AC1EDE /* KFrame.swift */; };
1C736DEE40337056C8D2A044 /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7363745D2F89492F2D79C1 /* DetailView.swift */; };
1C736DFA8544C773E3C22F10 /* VideoPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73675F8DDFA82DEADB542E /* VideoPlayerView.swift */; };
1C736DFD076D9CC30F0B9D58 /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736677D4EF2437358B2387 /* Utility.swift */; };
1C736E21B246C0BE7E123FD3 /* MediaModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736B41C6AC33F3FA592C63 /* MediaModel.swift */; };
@ -106,7 +109,6 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
1C73602350ACE2436736F981 /* BrowserController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrowserController.swift; sourceTree = "<group>"; };
1C7360295486647982CFEACF /* UIViewController+Alert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Alert.swift"; sourceTree = "<group>"; };
1C736059262A57AADE6AB761 /* Kirschkeks-256x256.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Kirschkeks-256x256.png"; path = "kplayer/Kirschkeks-256x256.png"; sourceTree = "<group>"; };
1C736069C214E9522BB1BD97 /* ItemCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemCell.swift; sourceTree = "<group>"; };
@ -126,21 +128,23 @@
1C736253AB7A95EA41B605B7 /* ItemModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemModel.swift; sourceTree = "<group>"; };
1C736260E748CF136FF37EA7 /* UploadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UploadOperation.swift; sourceTree = "<group>"; };
1C7362DE1D6BE634D7C2ACBF /* KPersistentContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KPersistentContainer.swift; sourceTree = "<group>"; };
1C73630B535C2358125793D4 /* ItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemView.swift; sourceTree = "<group>"; };
1C73631C96E6C860833052CA /* ItemType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemType.swift; sourceTree = "<group>"; };
1C7363743CD8120637AC1EDE /* KFrame.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KFrame.swift; sourceTree = "<group>"; };
1C7363745D2F89492F2D79C1 /* DetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailView.swift; sourceTree = "<group>"; };
1C7363CD8FF2C0A56DB63ADD /* DocPickerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocPickerViewController.swift; sourceTree = "<group>"; };
1C7363EDB190A6B76C5AAAF7 /* MasterModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MasterModel.swift; sourceTree = "<group>"; };
1C73645DBD6499A726D34973 /* links.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = links.html; sourceTree = "<group>"; };
1C73647019E6C2E822127BA3 /* DatabaseManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseManager.swift; sourceTree = "<group>"; };
1C7364709899FF62774B0199 /* VideoHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoHelper.swift; sourceTree = "<group>"; };
1C73648CEC974A2500172064 /* ViewControllerExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewControllerExtensions.swift; sourceTree = "<group>"; };
1C7364F10BED5DA0F1C0423C /* NetworkDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkDelegate.swift; sourceTree = "<group>"; };
1C73654AB95A2D629833BEC5 /* SearchItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchItemView.swift; sourceTree = "<group>"; };
1C736595533B56039C417E0D /* ServerDownloadDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerDownloadDelegate.swift; sourceTree = "<group>"; };
1C73659CC9B523B957E58DC6 /* LocalManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalManager.swift; sourceTree = "<group>"; };
1C7365B06FA66294E99AC2D3 /* NetworkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = "<group>"; };
1C73661C3F9F4E53645551AD /* KToggleButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KToggleButton.swift; sourceTree = "<group>"; };
1C736677D4EF2437358B2387 /* Utility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utility.swift; sourceTree = "<group>"; };
1C7366C09381DC0052B52B69 /* EditItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditItemView.swift; sourceTree = "<group>"; };
1C73673DC671535E3A049F54 /* PhotoController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoController.swift; sourceTree = "<group>"; };
1C73675F8DDFA82DEADB542E /* VideoPlayerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoPlayerView.swift; sourceTree = "<group>"; };
1C736777456388CA571DA17B /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; };
1C7367B39F09CCD1760A345A /* SEmbeddedVideo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SEmbeddedVideo.swift; sourceTree = "<group>"; };
@ -164,6 +168,7 @@
1C736C72CDF8902484856B3B /* SelfSizingHostingController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelfSizingHostingController.swift; sourceTree = "<group>"; };
1C736C94157754DE1C808173 /* KSettingsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KSettingsModel.swift; sourceTree = "<group>"; };
1C736CC8D90375E86CD01964 /* ImageLoader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageLoader.swift; sourceTree = "<group>"; };
1C736CDA5E3039EEE0CC39E4 /* MasterSplitView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MasterSplitView.swift; sourceTree = "<group>"; };
1C736D27EC608FAAFDB4A68C /* WebView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebView.swift; sourceTree = "<group>"; };
1C736D50A22FC4553165199D /* FlexibleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlexibleView.swift; sourceTree = "<group>"; };
1C736D9BB5498E7E8F11C754 /* HeaderCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderCell.swift; sourceTree = "<group>"; };
@ -175,6 +180,7 @@
1C736EA15A11AF7D57F85824 /* ThumbnailCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThumbnailCache.swift; sourceTree = "<group>"; };
1C736EEC570C71AAC50F2E95 /* SVideoModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SVideoModel.swift; sourceTree = "<group>"; };
1C736EF64DE56AD058A4F612 /* KBrowserView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KBrowserView.swift; sourceTree = "<group>"; };
1C736F5826258B8B4BA6ECE6 /* PlayerApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayerApp.swift; sourceTree = "<group>"; };
1C736F83C6190A3A0D5BD1F0 /* SPhotoModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPhotoModel.swift; sourceTree = "<group>"; };
1C736F9338CE36708244D42A /* DataLoadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataLoadOperation.swift; sourceTree = "<group>"; };
6D522F61736592330F481B4F /* Pods-kplayer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-kplayer.debug.xcconfig"; path = "Pods/Target Support Files/Pods-kplayer/Pods-kplayer.debug.xcconfig"; sourceTree = "<group>"; };
@ -228,9 +234,10 @@
1C736069C214E9522BB1BD97 /* ItemCell.swift */,
1C736D9BB5498E7E8F11C754 /* HeaderCell.swift */,
1C7369F53095B7A4D65679C2 /* DetailViewController.swift */,
1C73602350ACE2436736F981 /* BrowserController.swift */,
1C7366C09381DC0052B52B69 /* EditItemView.swift */,
1C736B17A8E9FB352B90A903 /* DetailViewController+Show.swift */,
1C7363745D2F89492F2D79C1 /* DetailView.swift */,
1C73630B535C2358125793D4 /* ItemView.swift */,
);
path = detail;
sourceTree = "<group>";
@ -250,7 +257,6 @@
1C7363B608460DED4F522D1C /* photo */ = {
isa = PBXGroup;
children = (
1C73673DC671535E3A049F54 /* PhotoController.swift */,
1C736AD9F0A39AD543FC1176 /* SPhotoView.swift */,
1C736F83C6190A3A0D5BD1F0 /* SPhotoModel.swift */,
1C7368EFFBCD688E0D425117 /* SPhotoScrubber.swift */,
@ -265,8 +271,10 @@
1C73611D226B48C24DB37535 /* MasterViewController.swift */,
1C7364F10BED5DA0F1C0423C /* NetworkDelegate.swift */,
1C736A6E8396EE306B1AD3A8 /* KSettingsView.swift */,
1C736595533B56039C417E0D /* ServerDownloadDelegate.swift */,
1C73654AB95A2D629833BEC5 /* SearchItemView.swift */,
1C736CDA5E3039EEE0CC39E4 /* MasterSplitView.swift */,
1C7363EDB190A6B76C5AAAF7 /* MasterModel.swift */,
1C7363CD8FF2C0A56DB63ADD /* DocPickerViewController.swift */,
);
path = master;
sourceTree = "<group>";
@ -411,6 +419,7 @@
1C736F3946A38499113D351A /* video */,
1C73633B55AC5378053BDCE2 /* server */,
1C736FEAC391DC13A5D6E61E /* web */,
1C736F5826258B8B4BA6ECE6 /* PlayerApp.swift */,
);
path = kplayer;
sourceTree = "<group>";
@ -559,9 +568,6 @@
"${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework",
"${BUILT_PRODUCTS_DIR}/FileBrowser/FileBrowser.framework",
"${BUILT_PRODUCTS_DIR}/HanekeSwift/Haneke.framework",
"${BUILT_PRODUCTS_DIR}/NVActivityIndicatorView/NVActivityIndicatorView.framework",
"${BUILT_PRODUCTS_DIR}/Nimbus/Nimbus.framework",
"${BUILT_PRODUCTS_DIR}/WebBrowser/WebBrowser.framework",
"${BUILT_PRODUCTS_DIR}/ZIPFoundation/ZIPFoundation.framework",
);
name = "[CP] Embed Pods Frameworks";
@ -569,9 +575,6 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FileBrowser.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Haneke.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/NVActivityIndicatorView.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimbus.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebBrowser.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ZIPFoundation.framework",
);
runOnlyForDeploymentPostprocessing = 0;
@ -617,7 +620,6 @@
1C73640D928DE56D35175D39 /* UploadOperation.swift in Sources */,
1C73646F87B495A47D7943C7 /* NetData.swift in Sources */,
1C73693A1334A7792856FC58 /* MasterViewController.swift in Sources */,
1C7365885FAF292F2221ED44 /* PhotoController.swift in Sources */,
1C736D16E81BA1FB325200E0 /* HanekeFetchOperation.swift in Sources */,
1C736D24891597F2728230EE /* ImageLoadOperation.swift in Sources */,
1C7367AF39961D2BA72480ED /* DataLoadOperation.swift in Sources */,
@ -625,7 +627,6 @@
1C73675C34BE0990D44570BE /* ItemModel.swift in Sources */,
1C73691A9C7174E0C6B57267 /* stringutil.swift in Sources */,
1C736821D6DF2237A3EABCC1 /* ViewControllerExtensions.swift in Sources */,
1C736953BDBBAFC40884132A /* BrowserController.swift in Sources */,
1C73671FC2CCCACAA2FFC153 /* ThumbnailCache.swift in Sources */,
C91E05892795AC5C0003AB79 /* KTag+CoreDataClass.swift in Sources */,
1C736E21B246C0BE7E123FD3 /* MediaModel.swift in Sources */,
@ -646,7 +647,6 @@
C91E058B2795AC5C0003AB79 /* KItem+CoreDataClass.swift in Sources */,
1C736A622876405F3EE2D043 /* EditItemView.swift in Sources */,
C9D67651291E73A00060179C /* Cached+CoreDataProperties.swift in Sources */,
1C73613562EB375F53A0BD03 /* ServerDownloadDelegate.swift in Sources */,
1C736EC45EE7DA5F7FCE63DA /* LocalManager.swift in Sources */,
1C736DFD076D9CC30F0B9D58 /* Utility.swift in Sources */,
1C736998044A9A7D89411892 /* AsyncImage.swift in Sources */,
@ -680,6 +680,12 @@
1C736EFF1E09988625FF770C /* hints.md in Sources */,
1C73670151CCBC714795807F /* SRangeSlider.swift in Sources */,
1C736E5C86EFC8E8C1ABA131 /* raspberrypi.js in Sources */,
1C736699A2992473AC2626AC /* MasterSplitView.swift in Sources */,
1C7365C929045799553DF4BC /* MasterModel.swift in Sources */,
1C736DEE40337056C8D2A044 /* DetailView.swift in Sources */,
1C736A9DE203A7658D85B7B6 /* ItemView.swift in Sources */,
1C73604B4926AA24595D4FD7 /* DocPickerViewController.swift in Sources */,
1C736789A4B89024F6485608 /* PlayerApp.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -768,7 +774,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -817,7 +823,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";

44
kplayer.xcodeproj/xcuserdata/marcoschmickler.xcuserdatad/xcschemes/kplayer.xcscheme

@ -1,10 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
version = "1.3">
<BuildAction>
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForRunning = "YES">
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C98AF5CE1B124D6A00D196CC"
@ -15,11 +21,25 @@
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C98AF5CE1B124D6A00D196CC"
@ -33,4 +53,18 @@
referenceType = "1">
</LocationScenarioReference>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

37
kplayer/AppDelegate.swift

@ -31,42 +31,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
NetworkManager.sharedInstance.wakeLinkstation()
let url = URL(string: NetworkManager.sharedInstance.vidurl)?.appendingPathComponent("ren").appendingPathComponent("kplayer.txt")
var roots = [MediaItem]()
do {
let remoteroots = try String(contentsOf: url!)
let components = remoteroots.split(separator: "\n")
for c in components {
let line = c.split(separator: " ")
let name = String(line[0])
let root = String(line[1])
let item = MediaItem(name: name, path:"", root: root, type: ItemType.REMOTEROOT)
roots.append(item)
}
} catch {
}
let web = MediaItem(name: "web", path:"", root: "/srv/samba/ren/web", type: ItemType.WEBROOT)
web.loaded = true
let google = MediaItem(name: "google", path:"www.google.de", root: "", type: ItemType.PICFOLDER)
google.loaded = true
web.children = [ google ]
let urlPath = Bundle.main.url(forResource: "links", withExtension: "html")
let parser = HtmlParser(url: urlPath)
parser.parse()
google.children = parser.items
roots.append(LocalManager.sharedInstance.favorites)
roots.append(MediaItem(name: "extern", path:"", root: "", type: ItemType.FAVROOT))
roots.append(MediaItem(name: "tags", path:"", root: "", type: ItemType.TAGROOT))
roots.append(MediaItem(name: "models", path:"models", root: "", type: ItemType.TAGROOT))
roots.append(web)
controller.model.items = roots
controller.model.items = LocalManager.sharedInstance.model!.items
NetworkManager.sharedInstance.alive()

25
kplayer/PlayerApp.swift

@ -0,0 +1,25 @@
//
// Created by Marco Schmickler on 20.08.23.
// Copyright (c) 2023 Marco Schmickler. All rights reserved.
//
import Foundation
import SwiftUI
//@main
struct PlayerApp: App {
let model: MasterModel
init() {
let del = NetworkDelegate()
model = MasterModel(itemModel: LocalManager.sharedInstance.model!, delegate: del, detailDelegate: del);
}
var body: some Scene {
WindowGroup {
MasterSplitView(completionHandler: {
}, model: model);
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save