diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..f80b18f Binary files /dev/null and b/.DS_Store differ diff --git a/Podfile b/Podfile new file mode 100644 index 0000000..a718647 --- /dev/null +++ b/Podfile @@ -0,0 +1,31 @@ +platform :ios, '9.0' + +target 'SDAutoLayoutDemo' do + pod 'AFNetworking', '~> 4.0' + pod 'MJRefresh' + pod 'MJExtension' + + target 'SDAutoLayoutDemoTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '9.0' + end + end + + # Apple SDK marks netinet6/in6.h as private when using modules; netinet/in.h is sufficient. + %w[ + AFNetworking/AFNetworking/AFHTTPSessionManager.m + AFNetworking/AFNetworking/AFNetworkReachabilityManager.m + ].each do |rel| + path = installer.sandbox.root + rel + next unless File.exist?(path) + contents = File.read(path) + patched = contents.gsub(/^#import \n/, '') + File.write(path, patched) if patched != contents + end +end diff --git a/Podfile.lock b/Podfile.lock new file mode 100644 index 0000000..d2d73bf --- /dev/null +++ b/Podfile.lock @@ -0,0 +1,38 @@ +PODS: + - AFNetworking (4.0.1): + - AFNetworking/NSURLSession (= 4.0.1) + - AFNetworking/Reachability (= 4.0.1) + - AFNetworking/Security (= 4.0.1) + - AFNetworking/Serialization (= 4.0.1) + - AFNetworking/UIKit (= 4.0.1) + - AFNetworking/NSURLSession (4.0.1): + - AFNetworking/Reachability + - AFNetworking/Security + - AFNetworking/Serialization + - AFNetworking/Reachability (4.0.1) + - AFNetworking/Security (4.0.1) + - AFNetworking/Serialization (4.0.1) + - AFNetworking/UIKit (4.0.1): + - AFNetworking/NSURLSession + - MJExtension (3.4.1) + - MJRefresh (3.7.6) + +DEPENDENCIES: + - AFNetworking (~> 4.0) + - MJExtension + - MJRefresh + +SPEC REPOS: + trunk: + - AFNetworking + - MJExtension + - MJRefresh + +SPEC CHECKSUMS: + AFNetworking: 3bd23d814e976cd148d7d44c3ab78017b744cd58 + MJExtension: 21c5f6f8c4d5d8844b7ae8fbae08fed0b501f961 + MJRefresh: 2fe7fb43a5167ceda20bb7e63f130c04fd1814a5 + +PODFILE CHECKSUM: a62c39409cf3d1c88cf89df3813bc94ce4fb7c02 + +COCOAPODS: 1.16.2 diff --git a/Pods/.DS_Store b/Pods/.DS_Store new file mode 100644 index 0000000..efd1fb1 Binary files /dev/null and b/Pods/.DS_Store differ diff --git a/Pods/AFNetworking/AFNetworking/AFCompatibilityMacros.h b/Pods/AFNetworking/AFNetworking/AFCompatibilityMacros.h new file mode 100644 index 0000000..1f0ab26 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFCompatibilityMacros.h @@ -0,0 +1,49 @@ +// AFCompatibilityMacros.h +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#ifndef AFCompatibilityMacros_h +#define AFCompatibilityMacros_h + +#ifdef API_AVAILABLE + #define AF_API_AVAILABLE(...) API_AVAILABLE(__VA_ARGS__) +#else + #define AF_API_AVAILABLE(...) +#endif // API_AVAILABLE + +#ifdef API_UNAVAILABLE + #define AF_API_UNAVAILABLE(...) API_UNAVAILABLE(__VA_ARGS__) +#else + #define AF_API_UNAVAILABLE(...) +#endif // API_UNAVAILABLE + +#if __has_warning("-Wunguarded-availability-new") + #define AF_CAN_USE_AT_AVAILABLE 1 +#else + #define AF_CAN_USE_AT_AVAILABLE 0 +#endif + +#if ((__IPHONE_OS_VERSION_MAX_ALLOWED && __IPHONE_OS_VERSION_MAX_ALLOWED < 100000) || (__MAC_OS_VERSION_MAX_ALLOWED && __MAC_OS_VERSION_MAX_ALLOWED < 101200) ||(__WATCH_OS_MAX_VERSION_ALLOWED && __WATCH_OS_MAX_VERSION_ALLOWED < 30000) ||(__TV_OS_MAX_VERSION_ALLOWED && __TV_OS_MAX_VERSION_ALLOWED < 100000)) + #define AF_CAN_INCLUDE_SESSION_TASK_METRICS 0 +#else + #define AF_CAN_INCLUDE_SESSION_TASK_METRICS 1 +#endif + +#endif /* AFCompatibilityMacros_h */ diff --git a/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h b/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h new file mode 100644 index 0000000..943fc22 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h @@ -0,0 +1,285 @@ +// AFHTTPSessionManager.h +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import +#if !TARGET_OS_WATCH +#import +#endif +#import + +#import "AFURLSessionManager.h" + +/** + `AFHTTPSessionManager` is a subclass of `AFURLSessionManager` with convenience methods for making HTTP requests. When a `baseURL` is provided, requests made with the `GET` / `POST` / et al. convenience methods can be made with relative paths. + + ## Subclassing Notes + + Developers targeting iOS 7 or Mac OS X 10.9 or later that deal extensively with a web service are encouraged to subclass `AFHTTPSessionManager`, providing a class method that returns a shared singleton object on which authentication and other configuration can be shared across the application. + + ## Methods to Override + + To change the behavior of all data task operation construction, which is also used in the `GET` / `POST` / et al. convenience methods, override `dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:`. + + ## Serialization + + Requests created by an HTTP client will contain default headers and encode parameters according to the `requestSerializer` property, which is an object conforming to ``. + + Responses received from the server are automatically validated and serialized by the `responseSerializers` property, which is an object conforming to `` + + ## URL Construction Using Relative Paths + + For HTTP convenience methods, the request serializer constructs URLs from the path relative to the `-baseURL`, using `NSURL +URLWithString:relativeToURL:`, when provided. If `baseURL` is `nil`, `path` needs to resolve to a valid `NSURL` object using `NSURL +URLWithString:`. + + Below are a few examples of how `baseURL` and relative paths interact: + + NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"]; + [NSURL URLWithString:@"foo" relativeToURL:baseURL]; // http://example.com/v1/foo + [NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL]; // http://example.com/v1/foo?bar=baz + [NSURL URLWithString:@"/foo" relativeToURL:baseURL]; // http://example.com/foo + [NSURL URLWithString:@"foo/" relativeToURL:baseURL]; // http://example.com/v1/foo + [NSURL URLWithString:@"/foo/" relativeToURL:baseURL]; // http://example.com/foo/ + [NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // http://example2.com/ + + Also important to note is that a trailing slash will be added to any `baseURL` without one. This would otherwise cause unexpected behavior when constructing URLs using paths without a leading slash. + + @warning Managers for background sessions must be owned for the duration of their use. This can be accomplished by creating an application-wide or shared singleton instance. + */ + +NS_ASSUME_NONNULL_BEGIN + +@interface AFHTTPSessionManager : AFURLSessionManager + +/** + The URL used to construct requests from relative paths in methods like `requestWithMethod:URLString:parameters:`, and the `GET` / `POST` / et al. convenience methods. + */ +@property (readonly, nonatomic, strong, nullable) NSURL *baseURL; + +/** + Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies. + + @warning `requestSerializer` must not be `nil`. + */ +@property (nonatomic, strong) AFHTTPRequestSerializer * requestSerializer; + +/** + Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`. + + @warning `responseSerializer` must not be `nil`. + */ +@property (nonatomic, strong) AFHTTPResponseSerializer * responseSerializer; + +///------------------------------- +/// @name Managing Security Policy +///------------------------------- + +/** + The security policy used by created session to evaluate server trust for secure connections. `AFURLSessionManager` uses the `defaultPolicy` unless otherwise specified. A security policy configured with `AFSSLPinningModePublicKey` or `AFSSLPinningModeCertificate` can only be applied on a session manager initialized with a secure base URL (i.e. https). Applying a security policy with pinning enabled on an insecure session manager throws an `Invalid Security Policy` exception. + */ +@property (nonatomic, strong) AFSecurityPolicy *securityPolicy; + +///--------------------- +/// @name Initialization +///--------------------- + +/** + Creates and returns an `AFHTTPSessionManager` object. + */ ++ (instancetype)manager; + +/** + Initializes an `AFHTTPSessionManager` object with the specified base URL. + + @param url The base URL for the HTTP client. + + @return The newly-initialized HTTP client + */ +- (instancetype)initWithBaseURL:(nullable NSURL *)url; + +/** + Initializes an `AFHTTPSessionManager` object with the specified base URL. + + This is the designated initializer. + + @param url The base URL for the HTTP client. + @param configuration The configuration used to create the managed session. + + @return The newly-initialized HTTP client + */ +- (instancetype)initWithBaseURL:(nullable NSURL *)url + sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER; + +///--------------------------- +/// @name Making HTTP Requests +///--------------------------- + +/** + Creates and runs an `NSURLSessionDataTask` with a `GET` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param headers The headers appended to the default headers for this request. + @param downloadProgress A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler: + */ +- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString + parameters:(nullable id)parameters + headers:(nullable NSDictionary *)headers + progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress + success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; + +/** + Creates and runs an `NSURLSessionDataTask` with a `HEAD` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param headers The headers appended to the default headers for this request. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes a single arguments: the data task. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:completionHandler: + */ +- (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString + parameters:(nullable id)parameters + headers:(nullable NSDictionary *)headers + success:(nullable void (^)(NSURLSessionDataTask *task))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; + +/** + Creates and runs an `NSURLSessionDataTask` with a `POST` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param headers The headers appended to the default headers for this request. + @param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler: + */ +- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString + parameters:(nullable id)parameters + headers:(nullable NSDictionary *)headers + progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress + success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; + +/** + Creates and runs an `NSURLSessionDataTask` with a multipart `POST` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param headers The headers appended to the default headers for this request. + @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol. + @param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler: + */ +- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString + parameters:(nullable id)parameters + headers:(nullable NSDictionary *)headers + constructingBodyWithBlock:(nullable void (^)(id formData))block + progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress + success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; + +/** + Creates and runs an `NSURLSessionDataTask` with a `PUT` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param headers The headers appended to the default headers for this request. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:completionHandler: + */ +- (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString + parameters:(nullable id)parameters + headers:(nullable NSDictionary *)headers + success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; + +/** + Creates and runs an `NSURLSessionDataTask` with a `PATCH` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param headers The headers appended to the default headers for this request. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:completionHandler: + */ +- (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString + parameters:(nullable id)parameters + headers:(nullable NSDictionary *)headers + success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; + +/** + Creates and runs an `NSURLSessionDataTask` with a `DELETE` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param headers The headers appended to the default headers for this request. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:completionHandler: + */ +- (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString + parameters:(nullable id)parameters + headers:(nullable NSDictionary *)headers + success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; + +/** + Creates an `NSURLSessionDataTask` with a custom `HTTPMethod` request. + + @param method The HTTPMethod string used to create the request. + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param headers The headers appended to the default headers for this request. + @param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. + @param downloadProgress A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler: + */ +- (nullable NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(nullable id)parameters + headers:(nullable NSDictionary *)headers + uploadProgress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress + downloadProgress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress + success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m b/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m new file mode 100644 index 0000000..2e25641 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m @@ -0,0 +1,356 @@ +// AFHTTPSessionManager.m +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import "AFHTTPSessionManager.h" + +#import "AFURLRequestSerialization.h" +#import "AFURLResponseSerialization.h" + +#import +#import +#import + +#import +#import +#import +#import + +#if TARGET_OS_IOS || TARGET_OS_TV +#import +#elif TARGET_OS_WATCH +#import +#endif + +@interface AFHTTPSessionManager () +@property (readwrite, nonatomic, strong) NSURL *baseURL; +@end + +@implementation AFHTTPSessionManager +@dynamic responseSerializer; + ++ (instancetype)manager { + return [[[self class] alloc] initWithBaseURL:nil]; +} + +- (instancetype)init { + return [self initWithBaseURL:nil]; +} + +- (instancetype)initWithBaseURL:(NSURL *)url { + return [self initWithBaseURL:url sessionConfiguration:nil]; +} + +- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration { + return [self initWithBaseURL:nil sessionConfiguration:configuration]; +} + +- (instancetype)initWithBaseURL:(NSURL *)url + sessionConfiguration:(NSURLSessionConfiguration *)configuration +{ + self = [super initWithSessionConfiguration:configuration]; + if (!self) { + return nil; + } + + // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected + if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) { + url = [url URLByAppendingPathComponent:@""]; + } + + self.baseURL = url; + + self.requestSerializer = [AFHTTPRequestSerializer serializer]; + self.responseSerializer = [AFJSONResponseSerializer serializer]; + + return self; +} + +#pragma mark - + +- (void)setRequestSerializer:(AFHTTPRequestSerializer *)requestSerializer { + NSParameterAssert(requestSerializer); + + _requestSerializer = requestSerializer; +} + +- (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { + NSParameterAssert(responseSerializer); + + [super setResponseSerializer:responseSerializer]; +} + +@dynamic securityPolicy; + +- (void)setSecurityPolicy:(AFSecurityPolicy *)securityPolicy { + if (securityPolicy.SSLPinningMode != AFSSLPinningModeNone && ![self.baseURL.scheme isEqualToString:@"https"]) { + NSString *pinningMode = @"Unknown Pinning Mode"; + switch (securityPolicy.SSLPinningMode) { + case AFSSLPinningModeNone: pinningMode = @"AFSSLPinningModeNone"; break; + case AFSSLPinningModeCertificate: pinningMode = @"AFSSLPinningModeCertificate"; break; + case AFSSLPinningModePublicKey: pinningMode = @"AFSSLPinningModePublicKey"; break; + } + NSString *reason = [NSString stringWithFormat:@"A security policy configured with `%@` can only be applied on a manager with a secure base URL (i.e. https)", pinningMode]; + @throw [NSException exceptionWithName:@"Invalid Security Policy" reason:reason userInfo:nil]; + } + + [super setSecurityPolicy:securityPolicy]; +} + +#pragma mark - + +- (NSURLSessionDataTask *)GET:(NSString *)URLString + parameters:(nullable id)parameters + headers:(nullable NSDictionary *)headers + progress:(nullable void (^)(NSProgress * _Nonnull))downloadProgress + success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure +{ + + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET" + URLString:URLString + parameters:parameters + headers:headers + uploadProgress:nil + downloadProgress:downloadProgress + success:success + failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (NSURLSessionDataTask *)HEAD:(NSString *)URLString + parameters:(nullable id)parameters + headers:(nullable NSDictionary *)headers + success:(nullable void (^)(NSURLSessionDataTask * _Nonnull))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure +{ + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"HEAD" URLString:URLString parameters:parameters headers:headers uploadProgress:nil downloadProgress:nil success:^(NSURLSessionDataTask *task, __unused id responseObject) { + if (success) { + success(task); + } + } failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString + parameters:(nullable id)parameters + headers:(nullable NSDictionary *)headers + progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress + success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure +{ + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters headers:headers uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (NSURLSessionDataTask *)POST:(NSString *)URLString + parameters:(nullable id)parameters + headers:(nullable NSDictionary *)headers + constructingBodyWithBlock:(nullable void (^)(id _Nonnull))block + progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress + success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure +{ + NSError *serializationError = nil; + NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError]; + for (NSString *headerField in headers.keyEnumerator) { + [request setValue:headers[headerField] forHTTPHeaderField:headerField]; + } + if (serializationError) { + if (failure) { + dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ + failure(nil, serializationError); + }); + } + + return nil; + } + + __block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:uploadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { + if (error) { + if (failure) { + failure(task, error); + } + } else { + if (success) { + success(task, responseObject); + } + } + }]; + + [task resume]; + + return task; +} + +- (NSURLSessionDataTask *)PUT:(NSString *)URLString + parameters:(nullable id)parameters + headers:(nullable NSDictionary *)headers + success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure +{ + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PUT" URLString:URLString parameters:parameters headers:headers uploadProgress:nil downloadProgress:nil success:success failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (NSURLSessionDataTask *)PATCH:(NSString *)URLString + parameters:(nullable id)parameters + headers:(nullable NSDictionary *)headers + success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure +{ + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PATCH" URLString:URLString parameters:parameters headers:headers uploadProgress:nil downloadProgress:nil success:success failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (NSURLSessionDataTask *)DELETE:(NSString *)URLString + parameters:(nullable id)parameters + headers:(nullable NSDictionary *)headers + success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure +{ + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"DELETE" URLString:URLString parameters:parameters headers:headers uploadProgress:nil downloadProgress:nil success:success failure:failure]; + + [dataTask resume]; + + return dataTask; +} + + +- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(nullable id)parameters + headers:(nullable NSDictionary *)headers + uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress + downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress + success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure +{ + NSError *serializationError = nil; + NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError]; + for (NSString *headerField in headers.keyEnumerator) { + [request setValue:headers[headerField] forHTTPHeaderField:headerField]; + } + if (serializationError) { + if (failure) { + dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ + failure(nil, serializationError); + }); + } + + return nil; + } + + __block NSURLSessionDataTask *dataTask = nil; + dataTask = [self dataTaskWithRequest:request + uploadProgress:uploadProgress + downloadProgress:downloadProgress + completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { + if (error) { + if (failure) { + failure(dataTask, error); + } + } else { + if (success) { + success(dataTask, responseObject); + } + } + }]; + + return dataTask; +} + +#pragma mark - NSObject + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.session, self.operationQueue]; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + NSURL *baseURL = [decoder decodeObjectOfClass:[NSURL class] forKey:NSStringFromSelector(@selector(baseURL))]; + NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"]; + if (!configuration) { + NSString *configurationIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"identifier"]; + if (configurationIdentifier) { + configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:configurationIdentifier]; + } + } + + self = [self initWithBaseURL:baseURL sessionConfiguration:configuration]; + if (!self) { + return nil; + } + + self.requestSerializer = [decoder decodeObjectOfClass:[AFHTTPRequestSerializer class] forKey:NSStringFromSelector(@selector(requestSerializer))]; + self.responseSerializer = [decoder decodeObjectOfClass:[AFHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))]; + AFSecurityPolicy *decodedPolicy = [decoder decodeObjectOfClass:[AFSecurityPolicy class] forKey:NSStringFromSelector(@selector(securityPolicy))]; + if (decodedPolicy) { + self.securityPolicy = decodedPolicy; + } + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:self.baseURL forKey:NSStringFromSelector(@selector(baseURL))]; + if ([self.session.configuration conformsToProtocol:@protocol(NSCoding)]) { + [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"]; + } else { + [coder encodeObject:self.session.configuration.identifier forKey:@"identifier"]; + } + [coder encodeObject:self.requestSerializer forKey:NSStringFromSelector(@selector(requestSerializer))]; + [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))]; + [coder encodeObject:self.securityPolicy forKey:NSStringFromSelector(@selector(securityPolicy))]; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + AFHTTPSessionManager *HTTPClient = [[[self class] allocWithZone:zone] initWithBaseURL:self.baseURL sessionConfiguration:self.session.configuration]; + + HTTPClient.requestSerializer = [self.requestSerializer copyWithZone:zone]; + HTTPClient.responseSerializer = [self.responseSerializer copyWithZone:zone]; + HTTPClient.securityPolicy = [self.securityPolicy copyWithZone:zone]; + return HTTPClient; +} + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h b/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h new file mode 100644 index 0000000..21982a0 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h @@ -0,0 +1,216 @@ +// AFNetworkReachabilityManager.h +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import + +#if !TARGET_OS_WATCH +#import + +typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) { + AFNetworkReachabilityStatusUnknown = -1, + AFNetworkReachabilityStatusNotReachable = 0, + AFNetworkReachabilityStatusReachableViaWWAN = 1, + AFNetworkReachabilityStatusReachableViaWiFi = 2, +}; + +NS_ASSUME_NONNULL_BEGIN + +/** + `AFNetworkReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces. + + Reachability can be used to determine background information about why a network operation failed, or to trigger a network operation retrying when a connection is established. It should not be used to prevent a user from initiating a network request, as it's possible that an initial request may be required to establish reachability. + + See Apple's Reachability Sample Code ( https://developer.apple.com/library/ios/samplecode/reachability/ ) + + @warning Instances of `AFNetworkReachabilityManager` must be started with `-startMonitoring` before reachability status can be determined. + */ +@interface AFNetworkReachabilityManager : NSObject + +/** + The current network reachability status. + */ +@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; + +/** + Whether or not the network is currently reachable. + */ +@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable; + +/** + Whether or not the network is currently reachable via WWAN. + */ +@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN; + +/** + Whether or not the network is currently reachable via WiFi. + */ +@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi; + +///--------------------- +/// @name Initialization +///--------------------- + +/** + Returns the shared network reachability manager. + */ ++ (instancetype)sharedManager; + +/** + Creates and returns a network reachability manager with the default socket address. + + @return An initialized network reachability manager, actively monitoring the default socket address. + */ ++ (instancetype)manager; + +/** + Creates and returns a network reachability manager for the specified domain. + + @param domain The domain used to evaluate network reachability. + + @return An initialized network reachability manager, actively monitoring the specified domain. + */ ++ (instancetype)managerForDomain:(NSString *)domain; + +/** + Creates and returns a network reachability manager for the socket address. + + @param address The socket address (`sockaddr_in6`) used to evaluate network reachability. + + @return An initialized network reachability manager, actively monitoring the specified socket address. + */ ++ (instancetype)managerForAddress:(const void *)address; + +/** + Initializes an instance of a network reachability manager from the specified reachability object. + + @param reachability The reachability object to monitor. + + @return An initialized network reachability manager, actively monitoring the specified reachability. + */ +- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER; + +/** + * Unavailable initializer + */ ++ (instancetype)new NS_UNAVAILABLE; + +/** + * Unavailable initializer + */ +- (instancetype)init NS_UNAVAILABLE; + +///-------------------------------------------------- +/// @name Starting & Stopping Reachability Monitoring +///-------------------------------------------------- + +/** + Starts monitoring for changes in network reachability status. + */ +- (void)startMonitoring; + +/** + Stops monitoring for changes in network reachability status. + */ +- (void)stopMonitoring; + +///------------------------------------------------- +/// @name Getting Localized Reachability Description +///------------------------------------------------- + +/** + Returns a localized string representation of the current network reachability status. + */ +- (NSString *)localizedNetworkReachabilityStatusString; + +///--------------------------------------------------- +/// @name Setting Network Reachability Change Callback +///--------------------------------------------------- + +/** + Sets a callback to be executed when the network availability of the `baseURL` host changes. + + @param block A block object to be executed when the network availability of the `baseURL` host changes.. This block has no return value and takes a single argument which represents the various reachability states from the device to the `baseURL`. + */ +- (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block; + +@end + +///---------------- +/// @name Constants +///---------------- + +/** + ## Network Reachability + + The following constants are provided by `AFNetworkReachabilityManager` as possible network reachability statuses. + + enum { + AFNetworkReachabilityStatusUnknown, + AFNetworkReachabilityStatusNotReachable, + AFNetworkReachabilityStatusReachableViaWWAN, + AFNetworkReachabilityStatusReachableViaWiFi, + } + + `AFNetworkReachabilityStatusUnknown` + The `baseURL` host reachability is not known. + + `AFNetworkReachabilityStatusNotReachable` + The `baseURL` host cannot be reached. + + `AFNetworkReachabilityStatusReachableViaWWAN` + The `baseURL` host can be reached via a cellular connection, such as EDGE or GPRS. + + `AFNetworkReachabilityStatusReachableViaWiFi` + The `baseURL` host can be reached via a Wi-Fi connection. + + ### Keys for Notification UserInfo Dictionary + + Strings that are used as keys in a `userInfo` dictionary in a network reachability status change notification. + + `AFNetworkingReachabilityNotificationStatusItem` + A key in the userInfo dictionary in a `AFNetworkingReachabilityDidChangeNotification` notification. + The corresponding value is an `NSNumber` object representing the `AFNetworkReachabilityStatus` value for the current reachability status. + */ + +///-------------------- +/// @name Notifications +///-------------------- + +/** + Posted when network reachability changes. + This notification assigns no notification object. The `userInfo` dictionary contains an `NSNumber` object under the `AFNetworkingReachabilityNotificationStatusItem` key, representing the `AFNetworkReachabilityStatus` value for the current network reachability. + + @warning In order for network reachability to be monitored, include the `SystemConfiguration` framework in the active target's "Link Binary With Library" build phase, and add `#import ` to the header prefix of the project (`Prefix.pch`). + */ +FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification; +FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityNotificationStatusItem; + +///-------------------- +/// @name Functions +///-------------------- + +/** + Returns a localized string representation of an `AFNetworkReachabilityStatus` value. + */ +FOUNDATION_EXPORT NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status); + +NS_ASSUME_NONNULL_END +#endif diff --git a/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m b/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m new file mode 100644 index 0000000..949dfef --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m @@ -0,0 +1,268 @@ +// AFNetworkReachabilityManager.m +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import "AFNetworkReachabilityManager.h" +#if !TARGET_OS_WATCH + +#import +#import +#import +#import + +NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change"; +NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem"; + +typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status); +typedef AFNetworkReachabilityManager * (^AFNetworkReachabilityStatusCallback)(AFNetworkReachabilityStatus status); + +NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) { + switch (status) { + case AFNetworkReachabilityStatusNotReachable: + return NSLocalizedStringFromTable(@"Not Reachable", @"AFNetworking", nil); + case AFNetworkReachabilityStatusReachableViaWWAN: + return NSLocalizedStringFromTable(@"Reachable via WWAN", @"AFNetworking", nil); + case AFNetworkReachabilityStatusReachableViaWiFi: + return NSLocalizedStringFromTable(@"Reachable via WiFi", @"AFNetworking", nil); + case AFNetworkReachabilityStatusUnknown: + default: + return NSLocalizedStringFromTable(@"Unknown", @"AFNetworking", nil); + } +} + +static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) { + BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0); + BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0); + BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)); + BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0); + BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction)); + + AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown; + if (isNetworkReachable == NO) { + status = AFNetworkReachabilityStatusNotReachable; + } +#if TARGET_OS_IPHONE + else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) { + status = AFNetworkReachabilityStatusReachableViaWWAN; + } +#endif + else { + status = AFNetworkReachabilityStatusReachableViaWiFi; + } + + return status; +} + +/** + * Queue a status change notification for the main thread. + * + * This is done to ensure that the notifications are received in the same order + * as they are sent. If notifications are sent directly, it is possible that + * a queued notification (for an earlier status condition) is processed after + * the later update, resulting in the listener being left in the wrong state. + */ +static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusCallback block) { + AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); + dispatch_async(dispatch_get_main_queue(), ^{ + AFNetworkReachabilityManager *manager = nil; + if (block) { + manager = block(status); + } + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) }; + [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:manager userInfo:userInfo]; + }); +} + +static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) { + AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusCallback)info); +} + + +static const void * AFNetworkReachabilityRetainCallback(const void *info) { + return Block_copy(info); +} + +static void AFNetworkReachabilityReleaseCallback(const void *info) { + if (info) { + Block_release(info); + } +} + +@interface AFNetworkReachabilityManager () +@property (readonly, nonatomic, assign) SCNetworkReachabilityRef networkReachability; +@property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; +@property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock; +@end + +@implementation AFNetworkReachabilityManager + ++ (instancetype)sharedManager { + static AFNetworkReachabilityManager *_sharedManager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _sharedManager = [self manager]; + }); + + return _sharedManager; +} + ++ (instancetype)managerForDomain:(NSString *)domain { + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]); + + AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; + + CFRelease(reachability); + + return manager; +} + ++ (instancetype)managerForAddress:(const void *)address { + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address); + AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; + + CFRelease(reachability); + + return manager; +} + ++ (instancetype)manager +{ +#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) + struct sockaddr_in6 address; + bzero(&address, sizeof(address)); + address.sin6_len = sizeof(address); + address.sin6_family = AF_INET6; +#else + struct sockaddr_in address; + bzero(&address, sizeof(address)); + address.sin_len = sizeof(address); + address.sin_family = AF_INET; +#endif + return [self managerForAddress:&address]; +} + +- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability { + self = [super init]; + if (!self) { + return nil; + } + + _networkReachability = CFRetain(reachability); + self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown; + + return self; +} + +- (instancetype)init +{ + @throw [NSException exceptionWithName:NSGenericException + reason:@"`-init` unavailable. Use `-initWithReachability:` instead" + userInfo:nil]; + return nil; +} + +- (void)dealloc { + [self stopMonitoring]; + + if (_networkReachability != NULL) { + CFRelease(_networkReachability); + } +} + +#pragma mark - + +- (BOOL)isReachable { + return [self isReachableViaWWAN] || [self isReachableViaWiFi]; +} + +- (BOOL)isReachableViaWWAN { + return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN; +} + +- (BOOL)isReachableViaWiFi { + return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi; +} + +#pragma mark - + +- (void)startMonitoring { + [self stopMonitoring]; + + if (!self.networkReachability) { + return; + } + + __weak __typeof(self)weakSelf = self; + AFNetworkReachabilityStatusCallback callback = ^(AFNetworkReachabilityStatus status) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + + strongSelf.networkReachabilityStatus = status; + if (strongSelf.networkReachabilityStatusBlock) { + strongSelf.networkReachabilityStatusBlock(status); + } + + return strongSelf; + }; + + SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL}; + SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context); + SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ + SCNetworkReachabilityFlags flags; + if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) { + AFPostReachabilityStatusChange(flags, callback); + } + }); +} + +- (void)stopMonitoring { + if (!self.networkReachability) { + return; + } + + SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); +} + +#pragma mark - + +- (NSString *)localizedNetworkReachabilityStatusString { + return AFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus); +} + +#pragma mark - + +- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block { + self.networkReachabilityStatusBlock = block; +} + +#pragma mark - NSKeyValueObserving + ++ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { + if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) { + return [NSSet setWithObject:@"networkReachabilityStatus"]; + } + + return [super keyPathsForValuesAffectingValueForKey:key]; +} + +@end +#endif diff --git a/Pods/AFNetworking/AFNetworking/AFNetworking.h b/Pods/AFNetworking/AFNetworking/AFNetworking.h new file mode 100644 index 0000000..e2fb2f4 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFNetworking.h @@ -0,0 +1,41 @@ +// AFNetworking.h +// +// Copyright (c) 2013 AFNetworking (http://afnetworking.com/) +// +// 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. + +#import +#import +#import + +#ifndef _AFNETWORKING_ + #define _AFNETWORKING_ + + #import "AFURLRequestSerialization.h" + #import "AFURLResponseSerialization.h" + #import "AFSecurityPolicy.h" + +#if !TARGET_OS_WATCH + #import "AFNetworkReachabilityManager.h" +#endif + + #import "AFURLSessionManager.h" + #import "AFHTTPSessionManager.h" + +#endif /* _AFNETWORKING_ */ diff --git a/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h b/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h new file mode 100644 index 0000000..9b966a5 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h @@ -0,0 +1,161 @@ +// AFSecurityPolicy.h +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import +#import + +typedef NS_ENUM(NSUInteger, AFSSLPinningMode) { + AFSSLPinningModeNone, + AFSSLPinningModePublicKey, + AFSSLPinningModeCertificate, +}; + +/** + `AFSecurityPolicy` evaluates server trust against pinned X.509 certificates and public keys over secure connections. + + Adding pinned SSL certificates to your app helps prevent man-in-the-middle attacks and other vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged to route all communication over an HTTPS connection with SSL pinning configured and enabled. + */ + +NS_ASSUME_NONNULL_BEGIN + +@interface AFSecurityPolicy : NSObject + +/** + The criteria by which server trust should be evaluated against the pinned SSL certificates. Defaults to `AFSSLPinningModeNone`. + */ +@property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode; + +/** + The certificates used to evaluate server trust according to the SSL pinning mode. + + Note that if pinning is enabled, `evaluateServerTrust:forDomain:` will return true if any pinned certificate matches. + + @see policyWithPinningMode:withPinnedCertificates: + */ +@property (nonatomic, strong, nullable) NSSet *pinnedCertificates; + +/** + Whether or not to trust servers with an invalid or expired SSL certificates. Defaults to `NO`. + */ +@property (nonatomic, assign) BOOL allowInvalidCertificates; + +/** + Whether or not to validate the domain name in the certificate's CN field. Defaults to `YES`. + */ +@property (nonatomic, assign) BOOL validatesDomainName; + +///----------------------------------------- +/// @name Getting Certificates from the Bundle +///----------------------------------------- + +/** + Returns any certificates included in the bundle. If you are using AFNetworking as an embedded framework, you must use this method to find the certificates you have included in your app bundle, and use them when creating your security policy by calling `policyWithPinningMode:withPinnedCertificates`. + + @return The certificates included in the given bundle. + */ ++ (NSSet *)certificatesInBundle:(NSBundle *)bundle; + +///----------------------------------------- +/// @name Getting Specific Security Policies +///----------------------------------------- + +/** + Returns the shared default security policy, which does not allow invalid certificates, validates domain name, and does not validate against pinned certificates or public keys. + + @return The default security policy. + */ ++ (instancetype)defaultPolicy; + +///--------------------- +/// @name Initialization +///--------------------- + +/** + Creates and returns a security policy with the specified pinning mode. + + Certificates with the `.cer` extension found in the main bundle will be pinned. If you want more control over which certificates are pinned, please use `policyWithPinningMode:withPinnedCertificates:` instead. + + @param pinningMode The SSL pinning mode. + + @return A new security policy. + + @see -policyWithPinningMode:withPinnedCertificates: + */ ++ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode; + +/** + Creates and returns a security policy with the specified pinning mode. + + @param pinningMode The SSL pinning mode. + @param pinnedCertificates The certificates to pin against. + + @return A new security policy. + + @see +certificatesInBundle: + @see -pinnedCertificates +*/ ++ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates; + +///------------------------------ +/// @name Evaluating Server Trust +///------------------------------ + +/** + Whether or not the specified server trust should be accepted, based on the security policy. + + This method should be used when responding to an authentication challenge from a server. + + @param serverTrust The X.509 certificate trust of the server. + @param domain The domain of serverTrust. If `nil`, the domain will not be validated. + + @return Whether or not to trust the server. + */ +- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust + forDomain:(nullable NSString *)domain; + +@end + +NS_ASSUME_NONNULL_END + +///---------------- +/// @name Constants +///---------------- + +/** + ## SSL Pinning Modes + + The following constants are provided by `AFSSLPinningMode` as possible SSL pinning modes. + + enum { + AFSSLPinningModeNone, + AFSSLPinningModePublicKey, + AFSSLPinningModeCertificate, + } + + `AFSSLPinningModeNone` + Do not used pinned certificates to validate servers. + + `AFSSLPinningModePublicKey` + Validate host certificates against public keys of pinned certificates. + + `AFSSLPinningModeCertificate` + Validate host certificates against pinned certificates. +*/ diff --git a/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m b/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m new file mode 100644 index 0000000..da199aa --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m @@ -0,0 +1,341 @@ +// AFSecurityPolicy.m +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import "AFSecurityPolicy.h" + +#import + +#if !TARGET_OS_IOS && !TARGET_OS_WATCH && !TARGET_OS_TV +static NSData * AFSecKeyGetData(SecKeyRef key) { + CFDataRef data = NULL; + + __Require_noErr_Quiet(SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data), _out); + + return (__bridge_transfer NSData *)data; + +_out: + if (data) { + CFRelease(data); + } + + return nil; +} +#endif + +static BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) { +#if TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV + return [(__bridge id)key1 isEqual:(__bridge id)key2]; +#else + return [AFSecKeyGetData(key1) isEqual:AFSecKeyGetData(key2)]; +#endif +} + +static id AFPublicKeyForCertificate(NSData *certificate) { + id allowedPublicKey = nil; + SecCertificateRef allowedCertificate; + SecPolicyRef policy = nil; + SecTrustRef allowedTrust = nil; + SecTrustResultType result; + + allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate); + __Require_Quiet(allowedCertificate != NULL, _out); + + policy = SecPolicyCreateBasicX509(); + __Require_noErr_Quiet(SecTrustCreateWithCertificates(allowedCertificate, policy, &allowedTrust), _out); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out); +#pragma clang diagnostic pop + + allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust); + +_out: + if (allowedTrust) { + CFRelease(allowedTrust); + } + + if (policy) { + CFRelease(policy); + } + + if (allowedCertificate) { + CFRelease(allowedCertificate); + } + + return allowedPublicKey; +} + +static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) { + BOOL isValid = NO; + SecTrustResultType result; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out); +#pragma clang diagnostic pop + + isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed); + +_out: + return isValid; +} + +static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) { + CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); + NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; + + for (CFIndex i = 0; i < certificateCount; i++) { + SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); + [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)]; + } + + return [NSArray arrayWithArray:trustChain]; +} + +static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) { + SecPolicyRef policy = SecPolicyCreateBasicX509(); + CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); + NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; + for (CFIndex i = 0; i < certificateCount; i++) { + SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); + + SecCertificateRef someCertificates[] = {certificate}; + CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL); + + SecTrustRef trust; + __Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out); + SecTrustResultType result; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + __Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out); +#pragma clang diagnostic pop + [trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)]; + + _out: + if (trust) { + CFRelease(trust); + } + + if (certificates) { + CFRelease(certificates); + } + + continue; + } + CFRelease(policy); + + return [NSArray arrayWithArray:trustChain]; +} + +#pragma mark - + +@interface AFSecurityPolicy() +@property (readwrite, nonatomic, assign) AFSSLPinningMode SSLPinningMode; +@property (readwrite, nonatomic, strong) NSSet *pinnedPublicKeys; +@end + +@implementation AFSecurityPolicy + ++ (NSSet *)certificatesInBundle:(NSBundle *)bundle { + NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."]; + + NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]]; + for (NSString *path in paths) { + NSData *certificateData = [NSData dataWithContentsOfFile:path]; + [certificates addObject:certificateData]; + } + + return [NSSet setWithSet:certificates]; +} + ++ (instancetype)defaultPolicy { + AFSecurityPolicy *securityPolicy = [[self alloc] init]; + securityPolicy.SSLPinningMode = AFSSLPinningModeNone; + + return securityPolicy; +} + ++ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode { + NSSet *defaultPinnedCertificates = [self certificatesInBundle:[NSBundle mainBundle]]; + return [self policyWithPinningMode:pinningMode withPinnedCertificates:defaultPinnedCertificates]; +} + ++ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates { + AFSecurityPolicy *securityPolicy = [[self alloc] init]; + securityPolicy.SSLPinningMode = pinningMode; + + [securityPolicy setPinnedCertificates:pinnedCertificates]; + + return securityPolicy; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.validatesDomainName = YES; + + return self; +} + +- (void)setPinnedCertificates:(NSSet *)pinnedCertificates { + _pinnedCertificates = pinnedCertificates; + + if (self.pinnedCertificates) { + NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]]; + for (NSData *certificate in self.pinnedCertificates) { + id publicKey = AFPublicKeyForCertificate(certificate); + if (!publicKey) { + continue; + } + [mutablePinnedPublicKeys addObject:publicKey]; + } + self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys]; + } else { + self.pinnedPublicKeys = nil; + } +} + +#pragma mark - + +- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust + forDomain:(NSString *)domain +{ + if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) { + // https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html + // According to the docs, you should only trust your provided certs for evaluation. + // Pinned certificates are added to the trust. Without pinned certificates, + // there is nothing to evaluate against. + // + // From Apple Docs: + // "Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors). + // Instead, add your own (self-signed) CA certificate to the list of trusted anchors." + NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning."); + return NO; + } + + NSMutableArray *policies = [NSMutableArray array]; + if (self.validatesDomainName) { + [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)]; + } else { + [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()]; + } + + SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies); + + if (self.SSLPinningMode == AFSSLPinningModeNone) { + return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust); + } else if (!self.allowInvalidCertificates && !AFServerTrustIsValid(serverTrust)) { + return NO; + } + + switch (self.SSLPinningMode) { + case AFSSLPinningModeCertificate: { + NSMutableArray *pinnedCertificates = [NSMutableArray array]; + for (NSData *certificateData in self.pinnedCertificates) { + [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)]; + } + SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates); + + if (!AFServerTrustIsValid(serverTrust)) { + return NO; + } + + // obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA) + NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust); + + for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) { + if ([self.pinnedCertificates containsObject:trustChainCertificate]) { + return YES; + } + } + + return NO; + } + case AFSSLPinningModePublicKey: { + NSUInteger trustedPublicKeyCount = 0; + NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust); + + for (id trustChainPublicKey in publicKeys) { + for (id pinnedPublicKey in self.pinnedPublicKeys) { + if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) { + trustedPublicKeyCount += 1; + } + } + } + return trustedPublicKeyCount > 0; + } + + default: + return NO; + } + + return NO; +} + +#pragma mark - NSKeyValueObserving + ++ (NSSet *)keyPathsForValuesAffectingPinnedPublicKeys { + return [NSSet setWithObject:@"pinnedCertificates"]; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + + self = [self init]; + if (!self) { + return nil; + } + + self.SSLPinningMode = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(SSLPinningMode))] unsignedIntegerValue]; + self.allowInvalidCertificates = [decoder decodeBoolForKey:NSStringFromSelector(@selector(allowInvalidCertificates))]; + self.validatesDomainName = [decoder decodeBoolForKey:NSStringFromSelector(@selector(validatesDomainName))]; + self.pinnedCertificates = [decoder decodeObjectOfClass:[NSSet class] forKey:NSStringFromSelector(@selector(pinnedCertificates))]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [coder encodeObject:[NSNumber numberWithUnsignedInteger:self.SSLPinningMode] forKey:NSStringFromSelector(@selector(SSLPinningMode))]; + [coder encodeBool:self.allowInvalidCertificates forKey:NSStringFromSelector(@selector(allowInvalidCertificates))]; + [coder encodeBool:self.validatesDomainName forKey:NSStringFromSelector(@selector(validatesDomainName))]; + [coder encodeObject:self.pinnedCertificates forKey:NSStringFromSelector(@selector(pinnedCertificates))]; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + AFSecurityPolicy *securityPolicy = [[[self class] allocWithZone:zone] init]; + securityPolicy.SSLPinningMode = self.SSLPinningMode; + securityPolicy.allowInvalidCertificates = self.allowInvalidCertificates; + securityPolicy.validatesDomainName = self.validatesDomainName; + securityPolicy.pinnedCertificates = [self.pinnedCertificates copyWithZone:zone]; + + return securityPolicy; +} + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h b/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h new file mode 100644 index 0000000..b17e871 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h @@ -0,0 +1,479 @@ +// AFURLRequestSerialization.h +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import +#import + +#if TARGET_OS_IOS || TARGET_OS_TV +#import +#elif TARGET_OS_WATCH +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + Returns a percent-escaped string following RFC 3986 for a query string key or value. + RFC 3986 states that the following characters are "reserved" characters. + - General Delimiters: ":", "#", "[", "]", "@", "?", "/" + - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" + + In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow + query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/" + should be percent-escaped in the query string. + + @param string The string to be percent-escaped. + + @return The percent-escaped string. + */ +FOUNDATION_EXPORT NSString * AFPercentEscapedStringFromString(NSString *string); + +/** + A helper method to generate encoded url query parameters for appending to the end of a URL. + + @param parameters A dictionary of key/values to be encoded. + + @return A url encoded query string + */ +FOUNDATION_EXPORT NSString * AFQueryStringFromParameters(NSDictionary *parameters); + +/** + The `AFURLRequestSerialization` protocol is adopted by an object that encodes parameters for a specified HTTP requests. Request serializers may encode parameters as query strings, HTTP bodies, setting the appropriate HTTP header fields as necessary. + + For example, a JSON request serializer may set the HTTP body of the request to a JSON representation, and set the `Content-Type` HTTP header field value to `application/json`. + */ +@protocol AFURLRequestSerialization + +/** + Returns a request with the specified parameters encoded into a copy of the original request. + + @param request The original request. + @param parameters The parameters to be encoded. + @param error The error that occurred while attempting to encode the request parameters. + + @return A serialized request. + */ +- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request + withParameters:(nullable id)parameters + error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW; + +@end + +#pragma mark - + +/** + + */ +typedef NS_ENUM(NSUInteger, AFHTTPRequestQueryStringSerializationStyle) { + AFHTTPRequestQueryStringDefaultStyle = 0, +}; + +@protocol AFMultipartFormData; + +/** + `AFHTTPRequestSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation. + + Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPRequestSerializer` in order to ensure consistent default behavior. + */ +@interface AFHTTPRequestSerializer : NSObject + +/** + The string encoding used to serialize parameters. `NSUTF8StringEncoding` by default. + */ +@property (nonatomic, assign) NSStringEncoding stringEncoding; + +/** + Whether created requests can use the device’s cellular radio (if present). `YES` by default. + + @see NSMutableURLRequest -setAllowsCellularAccess: + */ +@property (nonatomic, assign) BOOL allowsCellularAccess; + +/** + The cache policy of created requests. `NSURLRequestUseProtocolCachePolicy` by default. + + @see NSMutableURLRequest -setCachePolicy: + */ +@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy; + +/** + Whether created requests should use the default cookie handling. `YES` by default. + + @see NSMutableURLRequest -setHTTPShouldHandleCookies: + */ +@property (nonatomic, assign) BOOL HTTPShouldHandleCookies; + +/** + Whether created requests can continue transmitting data before receiving a response from an earlier transmission. `NO` by default + + @see NSMutableURLRequest -setHTTPShouldUsePipelining: + */ +@property (nonatomic, assign) BOOL HTTPShouldUsePipelining; + +/** + The network service type for created requests. `NSURLNetworkServiceTypeDefault` by default. + + @see NSMutableURLRequest -setNetworkServiceType: + */ +@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType; + +/** + The timeout interval, in seconds, for created requests. The default timeout interval is 60 seconds. + + @see NSMutableURLRequest -setTimeoutInterval: + */ +@property (nonatomic, assign) NSTimeInterval timeoutInterval; + +///--------------------------------------- +/// @name Configuring HTTP Request Headers +///--------------------------------------- + +/** + Default HTTP header field values to be applied to serialized requests. By default, these include the following: + + - `Accept-Language` with the contents of `NSLocale +preferredLanguages` + - `User-Agent` with the contents of various bundle identifiers and OS designations + + @discussion To add or remove default request headers, use `setValue:forHTTPHeaderField:`. + */ +@property (readonly, nonatomic, strong) NSDictionary *HTTPRequestHeaders; + +/** + Creates and returns a serializer with default configuration. + */ ++ (instancetype)serializer; + +/** + Sets the value for the HTTP headers set in request objects made by the HTTP client. If `nil`, removes the existing value for that header. + + @param field The HTTP header to set a default value for + @param value The value set as default for the specified header, or `nil` + */ +- (void)setValue:(nullable NSString *)value +forHTTPHeaderField:(NSString *)field; + +/** + Returns the value for the HTTP headers set in the request serializer. + + @param field The HTTP header to retrieve the default value for + + @return The value set as default for the specified header, or `nil` + */ +- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field; + +/** + Sets the "Authorization" HTTP header set in request objects made by the HTTP client to a basic authentication value with Base64-encoded username and password. This overwrites any existing value for this header. + + @param username The HTTP basic auth username + @param password The HTTP basic auth password + */ +- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username + password:(NSString *)password; + +/** + Clears any existing value for the "Authorization" HTTP header. + */ +- (void)clearAuthorizationHeader; + +///------------------------------------------------------- +/// @name Configuring Query String Parameter Serialization +///------------------------------------------------------- + +/** + HTTP methods for which serialized requests will encode parameters as a query string. `GET`, `HEAD`, and `DELETE` by default. + */ +@property (nonatomic, strong) NSSet *HTTPMethodsEncodingParametersInURI; + +/** + Set the method of query string serialization according to one of the pre-defined styles. + + @param style The serialization style. + + @see AFHTTPRequestQueryStringSerializationStyle + */ +- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style; + +/** + Set the a custom method of query string serialization according to the specified block. + + @param block A block that defines a process of encoding parameters into a query string. This block returns the query string and takes three arguments: the request, the parameters to encode, and the error that occurred when attempting to encode parameters for the given request. + */ +- (void)setQueryStringSerializationWithBlock:(nullable NSString * _Nullable (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block; + +///------------------------------- +/// @name Creating Request Objects +///------------------------------- + +/** + Creates an `NSMutableURLRequest` object with the specified HTTP method and URL string. + + If the HTTP method is `GET`, `HEAD`, or `DELETE`, the parameters will be used to construct a url-encoded query string that is appended to the request's URL. Otherwise, the parameters will be encoded according to the value of the `parameterEncoding` property, and set as the request body. + + @param method The HTTP method for the request, such as `GET`, `POST`, `PUT`, or `DELETE`. This parameter must not be `nil`. + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be either set as a query string for `GET` requests, or the request HTTP body. + @param error The error that occurred while constructing the request. + + @return An `NSMutableURLRequest` object. + */ +- (nullable NSMutableURLRequest *)requestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(nullable id)parameters + error:(NSError * _Nullable __autoreleasing *)error; + +/** + Creates an `NSMutableURLRequest` object with the specified HTTP method and URLString, and constructs a `multipart/form-data` HTTP body, using the specified parameters and multipart form data block. See http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.2 + + Multipart form requests are automatically streamed, reading files directly from disk along with in-memory data in a single HTTP body. The resulting `NSMutableURLRequest` object has an `HTTPBodyStream` property, so refrain from setting `HTTPBodyStream` or `HTTPBody` on this request object, as it will clear out the multipart form body stream. + + @param method The HTTP method for the request. This parameter must not be `GET` or `HEAD`, or `nil`. + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded and set in the request HTTP body. + @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol. + @param error The error that occurred while constructing the request. + + @return An `NSMutableURLRequest` object + */ +- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(nullable NSDictionary *)parameters + constructingBodyWithBlock:(nullable void (^)(id formData))block + error:(NSError * _Nullable __autoreleasing *)error; + +/** + Creates an `NSMutableURLRequest` by removing the `HTTPBodyStream` from a request, and asynchronously writing its contents into the specified file, invoking the completion handler when finished. + + @param request The multipart form request. The `HTTPBodyStream` property of `request` must not be `nil`. + @param fileURL The file URL to write multipart form contents to. + @param handler A handler block to execute. + + @discussion There is a bug in `NSURLSessionTask` that causes requests to not send a `Content-Length` header when streaming contents from an HTTP body, which is notably problematic when interacting with the Amazon S3 webservice. As a workaround, this method takes a request constructed with `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error:`, or any other request with an `HTTPBodyStream`, writes the contents to the specified file and returns a copy of the original request with the `HTTPBodyStream` property set to `nil`. From here, the file can either be passed to `AFURLSessionManager -uploadTaskWithRequest:fromFile:progress:completionHandler:`, or have its contents read into an `NSData` that's assigned to the `HTTPBody` property of the request. + + @see https://github.com/AFNetworking/AFNetworking/issues/1398 + */ +- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request + writingStreamContentsToFile:(NSURL *)fileURL + completionHandler:(nullable void (^)(NSError * _Nullable error))handler; + +@end + +#pragma mark - + +/** + The `AFMultipartFormData` protocol defines the methods supported by the parameter in the block argument of `AFHTTPRequestSerializer -multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:`. + */ +@protocol AFMultipartFormData + +/** + Appends the HTTP header `Content-Disposition: file; filename=#{generated filename}; name=#{name}"` and `Content-Type: #{generated mimeType}`, followed by the encoded file data and the multipart form boundary. + + The filename and MIME type for this data in the form will be automatically generated, using the last path component of the `fileURL` and system associated MIME type for the `fileURL` extension, respectively. + + @param fileURL The URL corresponding to the file whose content will be appended to the form. This parameter must not be `nil`. + @param name The name to be associated with the specified data. This parameter must not be `nil`. + @param error If an error occurs, upon return contains an `NSError` object that describes the problem. + + @return `YES` if the file data was successfully appended, otherwise `NO`. + */ +- (BOOL)appendPartWithFileURL:(NSURL *)fileURL + name:(NSString *)name + error:(NSError * _Nullable __autoreleasing *)error; + +/** + Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary. + + @param fileURL The URL corresponding to the file whose content will be appended to the form. This parameter must not be `nil`. + @param name The name to be associated with the specified data. This parameter must not be `nil`. + @param fileName The file name to be used in the `Content-Disposition` header. This parameter must not be `nil`. + @param mimeType The declared MIME type of the file data. This parameter must not be `nil`. + @param error If an error occurs, upon return contains an `NSError` object that describes the problem. + + @return `YES` if the file data was successfully appended otherwise `NO`. + */ +- (BOOL)appendPartWithFileURL:(NSURL *)fileURL + name:(NSString *)name + fileName:(NSString *)fileName + mimeType:(NSString *)mimeType + error:(NSError * _Nullable __autoreleasing *)error; + +/** + Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the data from the input stream and the multipart form boundary. + + @param inputStream The input stream to be appended to the form data + @param name The name to be associated with the specified input stream. This parameter must not be `nil`. + @param fileName The filename to be associated with the specified input stream. This parameter must not be `nil`. + @param length The length of the specified input stream in bytes. + @param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`. + */ +- (void)appendPartWithInputStream:(nullable NSInputStream *)inputStream + name:(NSString *)name + fileName:(NSString *)fileName + length:(int64_t)length + mimeType:(NSString *)mimeType; + +/** + Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary. + + @param data The data to be encoded and appended to the form data. + @param name The name to be associated with the specified data. This parameter must not be `nil`. + @param fileName The filename to be associated with the specified data. This parameter must not be `nil`. + @param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`. + */ +- (void)appendPartWithFileData:(NSData *)data + name:(NSString *)name + fileName:(NSString *)fileName + mimeType:(NSString *)mimeType; + +/** + Appends the HTTP headers `Content-Disposition: form-data; name=#{name}"`, followed by the encoded data and the multipart form boundary. + + @param data The data to be encoded and appended to the form data. + @param name The name to be associated with the specified data. This parameter must not be `nil`. + */ + +- (void)appendPartWithFormData:(NSData *)data + name:(NSString *)name; + + +/** + Appends HTTP headers, followed by the encoded data and the multipart form boundary. + + @param headers The HTTP headers to be appended to the form data. + @param body The data to be encoded and appended to the form data. This parameter must not be `nil`. + */ +- (void)appendPartWithHeaders:(nullable NSDictionary *)headers + body:(NSData *)body; + +/** + Throttles request bandwidth by limiting the packet size and adding a delay for each chunk read from the upload stream. + + When uploading over a 3G or EDGE connection, requests may fail with "request body stream exhausted". Setting a maximum packet size and delay according to the recommended values (`kAFUploadStream3GSuggestedPacketSize` and `kAFUploadStream3GSuggestedDelay`) lowers the risk of the input stream exceeding its allocated bandwidth. Unfortunately, there is no definite way to distinguish between a 3G, EDGE, or LTE connection over `NSURLConnection`. As such, it is not recommended that you throttle bandwidth based solely on network reachability. Instead, you should consider checking for the "request body stream exhausted" in a failure block, and then retrying the request with throttled bandwidth. + + @param numberOfBytes Maximum packet size, in number of bytes. The default packet size for an input stream is 16kb. + @param delay Duration of delay each time a packet is read. By default, no delay is set. + */ +- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes + delay:(NSTimeInterval)delay; + +@end + +#pragma mark - + +/** + `AFJSONRequestSerializer` is a subclass of `AFHTTPRequestSerializer` that encodes parameters as JSON using `NSJSONSerialization`, setting the `Content-Type` of the encoded request to `application/json`. + */ +@interface AFJSONRequestSerializer : AFHTTPRequestSerializer + +/** + Options for writing the request JSON data from Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONWritingOptions". `0` by default. + */ +@property (nonatomic, assign) NSJSONWritingOptions writingOptions; + +/** + Creates and returns a JSON serializer with specified reading and writing options. + + @param writingOptions The specified JSON writing options. + */ ++ (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions; + +@end + +#pragma mark - + +/** + `AFPropertyListRequestSerializer` is a subclass of `AFHTTPRequestSerializer` that encodes parameters as JSON using `NSPropertyListSerializer`, setting the `Content-Type` of the encoded request to `application/x-plist`. + */ +@interface AFPropertyListRequestSerializer : AFHTTPRequestSerializer + +/** + The property list format. Possible values are described in "NSPropertyListFormat". + */ +@property (nonatomic, assign) NSPropertyListFormat format; + +/** + @warning The `writeOptions` property is currently unused. + */ +@property (nonatomic, assign) NSPropertyListWriteOptions writeOptions; + +/** + Creates and returns a property list serializer with a specified format, read options, and write options. + + @param format The property list format. + @param writeOptions The property list write options. + + @warning The `writeOptions` property is currently unused. + */ ++ (instancetype)serializerWithFormat:(NSPropertyListFormat)format + writeOptions:(NSPropertyListWriteOptions)writeOptions; + +@end + +#pragma mark - + +///---------------- +/// @name Constants +///---------------- + +/** + ## Error Domains + + The following error domain is predefined. + + - `NSString * const AFURLRequestSerializationErrorDomain` + + ### Constants + + `AFURLRequestSerializationErrorDomain` + AFURLRequestSerializer errors. Error codes for `AFURLRequestSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`. + */ +FOUNDATION_EXPORT NSString * const AFURLRequestSerializationErrorDomain; + +/** + ## User info dictionary keys + + These keys may exist in the user info dictionary, in addition to those defined for NSError. + + - `NSString * const AFNetworkingOperationFailingURLRequestErrorKey` + + ### Constants + + `AFNetworkingOperationFailingURLRequestErrorKey` + The corresponding value is an `NSURLRequest` containing the request of the operation associated with an error. This key is only present in the `AFURLRequestSerializationErrorDomain`. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLRequestErrorKey; + +/** + ## Throttling Bandwidth for HTTP Request Input Streams + + @see -throttleBandwidthWithPacketSize:delay: + + ### Constants + + `kAFUploadStream3GSuggestedPacketSize` + Maximum packet size, in number of bytes. Equal to 16kb. + + `kAFUploadStream3GSuggestedDelay` + Duration of delay each time a packet is read. Equal to 0.2 seconds. + */ +FOUNDATION_EXPORT NSUInteger const kAFUploadStream3GSuggestedPacketSize; +FOUNDATION_EXPORT NSTimeInterval const kAFUploadStream3GSuggestedDelay; + +NS_ASSUME_NONNULL_END diff --git a/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m b/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m new file mode 100644 index 0000000..f60b6f9 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m @@ -0,0 +1,1399 @@ +// AFURLRequestSerialization.m +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import "AFURLRequestSerialization.h" + +#if TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV +#import +#else +#import +#endif + +NSString * const AFURLRequestSerializationErrorDomain = @"com.alamofire.error.serialization.request"; +NSString * const AFNetworkingOperationFailingURLRequestErrorKey = @"com.alamofire.serialization.request.error.response"; + +typedef NSString * (^AFQueryStringSerializationBlock)(NSURLRequest *request, id parameters, NSError *__autoreleasing *error); + +/** + Returns a percent-escaped string following RFC 3986 for a query string key or value. + RFC 3986 states that the following characters are "reserved" characters. + - General Delimiters: ":", "#", "[", "]", "@", "?", "/" + - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" + + In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow + query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/" + should be percent-escaped in the query string. + - parameter string: The string to be percent-escaped. + - returns: The percent-escaped string. + */ +NSString * AFPercentEscapedStringFromString(NSString *string) { + static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4 + static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;="; + + NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy]; + [allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]]; + + // FIXME: https://github.com/AFNetworking/AFNetworking/pull/3028 + // return [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; + + static NSUInteger const batchSize = 50; + + NSUInteger index = 0; + NSMutableString *escaped = @"".mutableCopy; + + while (index < string.length) { + NSUInteger length = MIN(string.length - index, batchSize); + NSRange range = NSMakeRange(index, length); + + // To avoid breaking up character sequences such as 👴🏻👮🏽 + range = [string rangeOfComposedCharacterSequencesForRange:range]; + + NSString *substring = [string substringWithRange:range]; + NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; + [escaped appendString:encoded]; + + index += range.length; + } + + return escaped; +} + +#pragma mark - + +@interface AFQueryStringPair : NSObject +@property (readwrite, nonatomic, strong) id field; +@property (readwrite, nonatomic, strong) id value; + +- (instancetype)initWithField:(id)field value:(id)value; + +- (NSString *)URLEncodedStringValue; +@end + +@implementation AFQueryStringPair + +- (instancetype)initWithField:(id)field value:(id)value { + self = [super init]; + if (!self) { + return nil; + } + + self.field = field; + self.value = value; + + return self; +} + +- (NSString *)URLEncodedStringValue { + if (!self.value || [self.value isEqual:[NSNull null]]) { + return AFPercentEscapedStringFromString([self.field description]); + } else { + return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])]; + } +} + +@end + +#pragma mark - + +FOUNDATION_EXPORT NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary); +FOUNDATION_EXPORT NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value); + +NSString * AFQueryStringFromParameters(NSDictionary *parameters) { + NSMutableArray *mutablePairs = [NSMutableArray array]; + for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) { + [mutablePairs addObject:[pair URLEncodedStringValue]]; + } + + return [mutablePairs componentsJoinedByString:@"&"]; +} + +NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) { + return AFQueryStringPairsFromKeyAndValue(nil, dictionary); +} + +NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) { + NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; + + NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)]; + + if ([value isKindOfClass:[NSDictionary class]]) { + NSDictionary *dictionary = value; + // Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries + for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) { + id nestedValue = dictionary[nestedKey]; + if (nestedValue) { + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)]; + } + } + } else if ([value isKindOfClass:[NSArray class]]) { + NSArray *array = value; + for (id nestedValue in array) { + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)]; + } + } else if ([value isKindOfClass:[NSSet class]]) { + NSSet *set = value; + for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) { + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)]; + } + } else { + [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]]; + } + + return mutableQueryStringComponents; +} + +#pragma mark - + +@interface AFStreamingMultipartFormData : NSObject +- (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest + stringEncoding:(NSStringEncoding)encoding; + +- (NSMutableURLRequest *)requestByFinalizingMultipartFormData; +@end + +#pragma mark - + +static NSArray * AFHTTPRequestSerializerObservedKeyPaths() { + static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))]; + }); + + return _AFHTTPRequestSerializerObservedKeyPaths; +} + +static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerObserverContext; + +@interface AFHTTPRequestSerializer () +@property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths; +@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders; +@property (readwrite, nonatomic, strong) dispatch_queue_t requestHeaderModificationQueue; +@property (readwrite, nonatomic, assign) AFHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle; +@property (readwrite, nonatomic, copy) AFQueryStringSerializationBlock queryStringSerialization; +@end + +@implementation AFHTTPRequestSerializer + ++ (instancetype)serializer { + return [[self alloc] init]; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.stringEncoding = NSUTF8StringEncoding; + + self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary]; + self.requestHeaderModificationQueue = dispatch_queue_create("requestHeaderModificationQueue", DISPATCH_QUEUE_CONCURRENT); + + // Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 + NSMutableArray *acceptLanguagesComponents = [NSMutableArray array]; + [[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + float q = 1.0f - (idx * 0.1f); + [acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]]; + *stop = q <= 0.5f; + }]; + [self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"]; + + // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43 + NSString *userAgent = nil; +#if TARGET_OS_IOS + userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]]; +#elif TARGET_OS_TV + userAgent = [NSString stringWithFormat:@"%@/%@ (%@; tvOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]]; +#elif TARGET_OS_WATCH + userAgent = [NSString stringWithFormat:@"%@/%@ (%@; watchOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[WKInterfaceDevice currentDevice] model], [[WKInterfaceDevice currentDevice] systemVersion], [[WKInterfaceDevice currentDevice] screenScale]]; +#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) + userAgent = [NSString stringWithFormat:@"%@/%@ (Mac OS X %@)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[NSProcessInfo processInfo] operatingSystemVersionString]]; +#endif + if (userAgent) { + if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) { + NSMutableString *mutableUserAgent = [userAgent mutableCopy]; + if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)) { + userAgent = mutableUserAgent; + } + } + [self setValue:userAgent forHTTPHeaderField:@"User-Agent"]; + } + + // HTTP Method Definitions; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html + self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil]; + + self.mutableObservedChangedKeyPaths = [NSMutableSet set]; + for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { + if ([self respondsToSelector:NSSelectorFromString(keyPath)]) { + [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext]; + } + } + + return self; +} + +- (void)dealloc { + for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { + if ([self respondsToSelector:NSSelectorFromString(keyPath)]) { + [self removeObserver:self forKeyPath:keyPath context:AFHTTPRequestSerializerObserverContext]; + } + } +} + +#pragma mark - + +// Workarounds for crashing behavior using Key-Value Observing with XCTest +// See https://github.com/AFNetworking/AFNetworking/issues/2523 + +- (void)setAllowsCellularAccess:(BOOL)allowsCellularAccess { + [self willChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))]; + _allowsCellularAccess = allowsCellularAccess; + [self didChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))]; +} + +- (void)setCachePolicy:(NSURLRequestCachePolicy)cachePolicy { + [self willChangeValueForKey:NSStringFromSelector(@selector(cachePolicy))]; + _cachePolicy = cachePolicy; + [self didChangeValueForKey:NSStringFromSelector(@selector(cachePolicy))]; +} + +- (void)setHTTPShouldHandleCookies:(BOOL)HTTPShouldHandleCookies { + [self willChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldHandleCookies))]; + _HTTPShouldHandleCookies = HTTPShouldHandleCookies; + [self didChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldHandleCookies))]; +} + +- (void)setHTTPShouldUsePipelining:(BOOL)HTTPShouldUsePipelining { + [self willChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldUsePipelining))]; + _HTTPShouldUsePipelining = HTTPShouldUsePipelining; + [self didChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldUsePipelining))]; +} + +- (void)setNetworkServiceType:(NSURLRequestNetworkServiceType)networkServiceType { + [self willChangeValueForKey:NSStringFromSelector(@selector(networkServiceType))]; + _networkServiceType = networkServiceType; + [self didChangeValueForKey:NSStringFromSelector(@selector(networkServiceType))]; +} + +- (void)setTimeoutInterval:(NSTimeInterval)timeoutInterval { + [self willChangeValueForKey:NSStringFromSelector(@selector(timeoutInterval))]; + _timeoutInterval = timeoutInterval; + [self didChangeValueForKey:NSStringFromSelector(@selector(timeoutInterval))]; +} + +#pragma mark - + +- (NSDictionary *)HTTPRequestHeaders { + NSDictionary __block *value; + dispatch_sync(self.requestHeaderModificationQueue, ^{ + value = [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders]; + }); + return value; +} + +- (void)setValue:(NSString *)value +forHTTPHeaderField:(NSString *)field +{ + dispatch_barrier_sync(self.requestHeaderModificationQueue, ^{ + [self.mutableHTTPRequestHeaders setValue:value forKey:field]; + }); +} + +- (NSString *)valueForHTTPHeaderField:(NSString *)field { + NSString __block *value; + dispatch_sync(self.requestHeaderModificationQueue, ^{ + value = [self.mutableHTTPRequestHeaders valueForKey:field]; + }); + return value; +} + +- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username + password:(NSString *)password +{ + NSData *basicAuthCredentials = [[NSString stringWithFormat:@"%@:%@", username, password] dataUsingEncoding:NSUTF8StringEncoding]; + NSString *base64AuthCredentials = [basicAuthCredentials base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0]; + [self setValue:[NSString stringWithFormat:@"Basic %@", base64AuthCredentials] forHTTPHeaderField:@"Authorization"]; +} + +- (void)clearAuthorizationHeader { + dispatch_barrier_sync(self.requestHeaderModificationQueue, ^{ + [self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"]; + }); +} + +#pragma mark - + +- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style { + self.queryStringSerializationStyle = style; + self.queryStringSerialization = nil; +} + +- (void)setQueryStringSerializationWithBlock:(NSString *(^)(NSURLRequest *, id, NSError *__autoreleasing *))block { + self.queryStringSerialization = block; +} + +#pragma mark - + +- (NSMutableURLRequest *)requestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(id)parameters + error:(NSError *__autoreleasing *)error +{ + NSParameterAssert(method); + NSParameterAssert(URLString); + + NSURL *url = [NSURL URLWithString:URLString]; + + NSParameterAssert(url); + + NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url]; + mutableRequest.HTTPMethod = method; + + for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { + if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) { + [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath]; + } + } + + mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy]; + + return mutableRequest; +} + +- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(NSDictionary *)parameters + constructingBodyWithBlock:(void (^)(id formData))block + error:(NSError *__autoreleasing *)error +{ + NSParameterAssert(method); + NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]); + + NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error]; + + __block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding]; + + if (parameters) { + for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) { + NSData *data = nil; + if ([pair.value isKindOfClass:[NSData class]]) { + data = pair.value; + } else if ([pair.value isEqual:[NSNull null]]) { + data = [NSData data]; + } else { + data = [[pair.value description] dataUsingEncoding:self.stringEncoding]; + } + + if (data) { + [formData appendPartWithFormData:data name:[pair.field description]]; + } + } + } + + if (block) { + block(formData); + } + + return [formData requestByFinalizingMultipartFormData]; +} + +- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request + writingStreamContentsToFile:(NSURL *)fileURL + completionHandler:(void (^)(NSError *error))handler +{ + NSParameterAssert(request.HTTPBodyStream); + NSParameterAssert([fileURL isFileURL]); + + NSInputStream *inputStream = request.HTTPBodyStream; + NSOutputStream *outputStream = [[NSOutputStream alloc] initWithURL:fileURL append:NO]; + __block NSError *error = nil; + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + + [inputStream open]; + [outputStream open]; + + while ([inputStream hasBytesAvailable] && [outputStream hasSpaceAvailable]) { + uint8_t buffer[1024]; + + NSInteger bytesRead = [inputStream read:buffer maxLength:1024]; + if (inputStream.streamError || bytesRead < 0) { + error = inputStream.streamError; + break; + } + + NSInteger bytesWritten = [outputStream write:buffer maxLength:(NSUInteger)bytesRead]; + if (outputStream.streamError || bytesWritten < 0) { + error = outputStream.streamError; + break; + } + + if (bytesRead == 0 && bytesWritten == 0) { + break; + } + } + + [outputStream close]; + [inputStream close]; + + if (handler) { + dispatch_async(dispatch_get_main_queue(), ^{ + handler(error); + }); + } + }); + + NSMutableURLRequest *mutableRequest = [request mutableCopy]; + mutableRequest.HTTPBodyStream = nil; + + return mutableRequest; +} + +#pragma mark - AFURLRequestSerialization + +- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request + withParameters:(id)parameters + error:(NSError *__autoreleasing *)error +{ + NSParameterAssert(request); + + NSMutableURLRequest *mutableRequest = [request mutableCopy]; + + [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { + if (![request valueForHTTPHeaderField:field]) { + [mutableRequest setValue:value forHTTPHeaderField:field]; + } + }]; + + NSString *query = nil; + if (parameters) { + if (self.queryStringSerialization) { + NSError *serializationError; + query = self.queryStringSerialization(request, parameters, &serializationError); + + if (serializationError) { + if (error) { + *error = serializationError; + } + + return nil; + } + } else { + switch (self.queryStringSerializationStyle) { + case AFHTTPRequestQueryStringDefaultStyle: + query = AFQueryStringFromParameters(parameters); + break; + } + } + } + + if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) { + if (query && query.length > 0) { + mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]]; + } + } else { + // #2864: an empty string is a valid x-www-form-urlencoded payload + if (!query) { + query = @""; + } + if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { + [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; + } + [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]]; + } + + return mutableRequest; +} + +#pragma mark - NSKeyValueObserving + ++ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { + if ([AFHTTPRequestSerializerObservedKeyPaths() containsObject:key]) { + return NO; + } + + return [super automaticallyNotifiesObserversForKey:key]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(__unused id)object + change:(NSDictionary *)change + context:(void *)context +{ + if (context == AFHTTPRequestSerializerObserverContext) { + if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) { + [self.mutableObservedChangedKeyPaths removeObject:keyPath]; + } else { + [self.mutableObservedChangedKeyPaths addObject:keyPath]; + } + } +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + self = [self init]; + if (!self) { + return nil; + } + + self.mutableHTTPRequestHeaders = [[decoder decodeObjectOfClass:[NSDictionary class] forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))] mutableCopy]; + self.queryStringSerializationStyle = (AFHTTPRequestQueryStringSerializationStyle)[[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))] unsignedIntegerValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + dispatch_sync(self.requestHeaderModificationQueue, ^{ + [coder encodeObject:self.mutableHTTPRequestHeaders forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))]; + }); + [coder encodeObject:@(self.queryStringSerializationStyle) forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))]; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + AFHTTPRequestSerializer *serializer = [[[self class] allocWithZone:zone] init]; + dispatch_sync(self.requestHeaderModificationQueue, ^{ + serializer.mutableHTTPRequestHeaders = [self.mutableHTTPRequestHeaders mutableCopyWithZone:zone]; + }); + serializer.queryStringSerializationStyle = self.queryStringSerializationStyle; + serializer.queryStringSerialization = self.queryStringSerialization; + + return serializer; +} + +@end + +#pragma mark - + +static NSString * AFCreateMultipartFormBoundary() { + return [NSString stringWithFormat:@"Boundary+%08X%08X", arc4random(), arc4random()]; +} + +static NSString * const kAFMultipartFormCRLF = @"\r\n"; + +static inline NSString * AFMultipartFormInitialBoundary(NSString *boundary) { + return [NSString stringWithFormat:@"--%@%@", boundary, kAFMultipartFormCRLF]; +} + +static inline NSString * AFMultipartFormEncapsulationBoundary(NSString *boundary) { + return [NSString stringWithFormat:@"%@--%@%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF]; +} + +static inline NSString * AFMultipartFormFinalBoundary(NSString *boundary) { + return [NSString stringWithFormat:@"%@--%@--%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF]; +} + +static inline NSString * AFContentTypeForPathExtension(NSString *extension) { + NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL); + NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType); + if (!contentType) { + return @"application/octet-stream"; + } else { + return contentType; + } +} + +NSUInteger const kAFUploadStream3GSuggestedPacketSize = 1024 * 16; +NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2; + +@interface AFHTTPBodyPart : NSObject +@property (nonatomic, assign) NSStringEncoding stringEncoding; +@property (nonatomic, strong) NSDictionary *headers; +@property (nonatomic, copy) NSString *boundary; +@property (nonatomic, strong) id body; +@property (nonatomic, assign) unsigned long long bodyContentLength; +@property (nonatomic, strong) NSInputStream *inputStream; + +@property (nonatomic, assign) BOOL hasInitialBoundary; +@property (nonatomic, assign) BOOL hasFinalBoundary; + +@property (readonly, nonatomic, assign, getter = hasBytesAvailable) BOOL bytesAvailable; +@property (readonly, nonatomic, assign) unsigned long long contentLength; + +- (NSInteger)read:(uint8_t *)buffer + maxLength:(NSUInteger)length; +@end + +@interface AFMultipartBodyStream : NSInputStream +@property (nonatomic, assign) NSUInteger numberOfBytesInPacket; +@property (nonatomic, assign) NSTimeInterval delay; +@property (nonatomic, strong) NSInputStream *inputStream; +@property (readonly, nonatomic, assign) unsigned long long contentLength; +@property (readonly, nonatomic, assign, getter = isEmpty) BOOL empty; + +- (instancetype)initWithStringEncoding:(NSStringEncoding)encoding; +- (void)setInitialAndFinalBoundaries; +- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart; +@end + +#pragma mark - + +@interface AFStreamingMultipartFormData () +@property (readwrite, nonatomic, copy) NSMutableURLRequest *request; +@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding; +@property (readwrite, nonatomic, copy) NSString *boundary; +@property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream; +@end + +@implementation AFStreamingMultipartFormData + +- (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest + stringEncoding:(NSStringEncoding)encoding +{ + self = [super init]; + if (!self) { + return nil; + } + + self.request = urlRequest; + self.stringEncoding = encoding; + self.boundary = AFCreateMultipartFormBoundary(); + self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding]; + + return self; +} + +- (void)setRequest:(NSMutableURLRequest *)request +{ + _request = [request mutableCopy]; +} + +- (BOOL)appendPartWithFileURL:(NSURL *)fileURL + name:(NSString *)name + error:(NSError * __autoreleasing *)error +{ + NSParameterAssert(fileURL); + NSParameterAssert(name); + + NSString *fileName = [fileURL lastPathComponent]; + NSString *mimeType = AFContentTypeForPathExtension([fileURL pathExtension]); + + return [self appendPartWithFileURL:fileURL name:name fileName:fileName mimeType:mimeType error:error]; +} + +- (BOOL)appendPartWithFileURL:(NSURL *)fileURL + name:(NSString *)name + fileName:(NSString *)fileName + mimeType:(NSString *)mimeType + error:(NSError * __autoreleasing *)error +{ + NSParameterAssert(fileURL); + NSParameterAssert(name); + NSParameterAssert(fileName); + NSParameterAssert(mimeType); + + if (![fileURL isFileURL]) { + NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"Expected URL to be a file URL", @"AFNetworking", nil)}; + if (error) { + *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo]; + } + + return NO; + } else if ([fileURL checkResourceIsReachableAndReturnError:error] == NO) { + NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"File URL not reachable.", @"AFNetworking", nil)}; + if (error) { + *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo]; + } + + return NO; + } + + NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:error]; + if (!fileAttributes) { + return NO; + } + + NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; + [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"]; + [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; + + AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; + bodyPart.stringEncoding = self.stringEncoding; + bodyPart.headers = mutableHeaders; + bodyPart.boundary = self.boundary; + bodyPart.body = fileURL; + bodyPart.bodyContentLength = [fileAttributes[NSFileSize] unsignedLongLongValue]; + [self.bodyStream appendHTTPBodyPart:bodyPart]; + + return YES; +} + +- (void)appendPartWithInputStream:(NSInputStream *)inputStream + name:(NSString *)name + fileName:(NSString *)fileName + length:(int64_t)length + mimeType:(NSString *)mimeType +{ + NSParameterAssert(name); + NSParameterAssert(fileName); + NSParameterAssert(mimeType); + + NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; + [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"]; + [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; + + AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; + bodyPart.stringEncoding = self.stringEncoding; + bodyPart.headers = mutableHeaders; + bodyPart.boundary = self.boundary; + bodyPart.body = inputStream; + + bodyPart.bodyContentLength = (unsigned long long)length; + + [self.bodyStream appendHTTPBodyPart:bodyPart]; +} + +- (void)appendPartWithFileData:(NSData *)data + name:(NSString *)name + fileName:(NSString *)fileName + mimeType:(NSString *)mimeType +{ + NSParameterAssert(name); + NSParameterAssert(fileName); + NSParameterAssert(mimeType); + + NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; + [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"]; + [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; + + [self appendPartWithHeaders:mutableHeaders body:data]; +} + +- (void)appendPartWithFormData:(NSData *)data + name:(NSString *)name +{ + NSParameterAssert(name); + + NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; + [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"", name] forKey:@"Content-Disposition"]; + + [self appendPartWithHeaders:mutableHeaders body:data]; +} + +- (void)appendPartWithHeaders:(NSDictionary *)headers + body:(NSData *)body +{ + NSParameterAssert(body); + + AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; + bodyPart.stringEncoding = self.stringEncoding; + bodyPart.headers = headers; + bodyPart.boundary = self.boundary; + bodyPart.bodyContentLength = [body length]; + bodyPart.body = body; + + [self.bodyStream appendHTTPBodyPart:bodyPart]; +} + +- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes + delay:(NSTimeInterval)delay +{ + self.bodyStream.numberOfBytesInPacket = numberOfBytes; + self.bodyStream.delay = delay; +} + +- (NSMutableURLRequest *)requestByFinalizingMultipartFormData { + if ([self.bodyStream isEmpty]) { + return self.request; + } + + // Reset the initial and final boundaries to ensure correct Content-Length + [self.bodyStream setInitialAndFinalBoundaries]; + [self.request setHTTPBodyStream:self.bodyStream]; + + [self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary] forHTTPHeaderField:@"Content-Type"]; + [self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"]; + + return self.request; +} + +@end + +#pragma mark - + +@interface NSStream () +@property (readwrite) NSStreamStatus streamStatus; +@property (readwrite, copy) NSError *streamError; +@end + +@interface AFMultipartBodyStream () +@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding; +@property (readwrite, nonatomic, strong) NSMutableArray *HTTPBodyParts; +@property (readwrite, nonatomic, strong) NSEnumerator *HTTPBodyPartEnumerator; +@property (readwrite, nonatomic, strong) AFHTTPBodyPart *currentHTTPBodyPart; +@property (readwrite, nonatomic, strong) NSOutputStream *outputStream; +@property (readwrite, nonatomic, strong) NSMutableData *buffer; +@end + +@implementation AFMultipartBodyStream +#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1100) +@synthesize delegate; +#endif +@synthesize streamStatus; +@synthesize streamError; + +- (instancetype)initWithStringEncoding:(NSStringEncoding)encoding { + self = [super init]; + if (!self) { + return nil; + } + + self.stringEncoding = encoding; + self.HTTPBodyParts = [NSMutableArray array]; + self.numberOfBytesInPacket = NSIntegerMax; + + return self; +} + +- (void)setInitialAndFinalBoundaries { + if ([self.HTTPBodyParts count] > 0) { + for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) { + bodyPart.hasInitialBoundary = NO; + bodyPart.hasFinalBoundary = NO; + } + + [[self.HTTPBodyParts firstObject] setHasInitialBoundary:YES]; + [[self.HTTPBodyParts lastObject] setHasFinalBoundary:YES]; + } +} + +- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart { + [self.HTTPBodyParts addObject:bodyPart]; +} + +- (BOOL)isEmpty { + return [self.HTTPBodyParts count] == 0; +} + +#pragma mark - NSInputStream + +- (NSInteger)read:(uint8_t *)buffer + maxLength:(NSUInteger)length +{ + if ([self streamStatus] == NSStreamStatusClosed) { + return 0; + } + + NSInteger totalNumberOfBytesRead = 0; + + while ((NSUInteger)totalNumberOfBytesRead < MIN(length, self.numberOfBytesInPacket)) { + if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) { + if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) { + break; + } + } else { + NSUInteger maxLength = MIN(length, self.numberOfBytesInPacket) - (NSUInteger)totalNumberOfBytesRead; + NSInteger numberOfBytesRead = [self.currentHTTPBodyPart read:&buffer[totalNumberOfBytesRead] maxLength:maxLength]; + if (numberOfBytesRead == -1) { + self.streamError = self.currentHTTPBodyPart.inputStream.streamError; + break; + } else { + totalNumberOfBytesRead += numberOfBytesRead; + + if (self.delay > 0.0f) { + [NSThread sleepForTimeInterval:self.delay]; + } + } + } + } + + return totalNumberOfBytesRead; +} + +- (BOOL)getBuffer:(__unused uint8_t **)buffer + length:(__unused NSUInteger *)len +{ + return NO; +} + +- (BOOL)hasBytesAvailable { + return [self streamStatus] == NSStreamStatusOpen; +} + +#pragma mark - NSStream + +- (void)open { + if (self.streamStatus == NSStreamStatusOpen) { + return; + } + + self.streamStatus = NSStreamStatusOpen; + + [self setInitialAndFinalBoundaries]; + self.HTTPBodyPartEnumerator = [self.HTTPBodyParts objectEnumerator]; +} + +- (void)close { + self.streamStatus = NSStreamStatusClosed; +} + +- (id)propertyForKey:(__unused NSString *)key { + return nil; +} + +- (BOOL)setProperty:(__unused id)property + forKey:(__unused NSString *)key +{ + return NO; +} + +- (void)scheduleInRunLoop:(__unused NSRunLoop *)aRunLoop + forMode:(__unused NSString *)mode +{} + +- (void)removeFromRunLoop:(__unused NSRunLoop *)aRunLoop + forMode:(__unused NSString *)mode +{} + +- (unsigned long long)contentLength { + unsigned long long length = 0; + for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) { + length += [bodyPart contentLength]; + } + + return length; +} + +#pragma mark - Undocumented CFReadStream Bridged Methods + +- (void)_scheduleInCFRunLoop:(__unused CFRunLoopRef)aRunLoop + forMode:(__unused CFStringRef)aMode +{} + +- (void)_unscheduleFromCFRunLoop:(__unused CFRunLoopRef)aRunLoop + forMode:(__unused CFStringRef)aMode +{} + +- (BOOL)_setCFClientFlags:(__unused CFOptionFlags)inFlags + callback:(__unused CFReadStreamClientCallBack)inCallback + context:(__unused CFStreamClientContext *)inContext { + return NO; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + AFMultipartBodyStream *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding]; + + for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) { + [bodyStreamCopy appendHTTPBodyPart:[bodyPart copy]]; + } + + [bodyStreamCopy setInitialAndFinalBoundaries]; + + return bodyStreamCopy; +} + +@end + +#pragma mark - + +typedef enum { + AFEncapsulationBoundaryPhase = 1, + AFHeaderPhase = 2, + AFBodyPhase = 3, + AFFinalBoundaryPhase = 4, +} AFHTTPBodyPartReadPhase; + +@interface AFHTTPBodyPart () { + AFHTTPBodyPartReadPhase _phase; + NSInputStream *_inputStream; + unsigned long long _phaseReadOffset; +} + +- (BOOL)transitionToNextPhase; +- (NSInteger)readData:(NSData *)data + intoBuffer:(uint8_t *)buffer + maxLength:(NSUInteger)length; +@end + +@implementation AFHTTPBodyPart + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + [self transitionToNextPhase]; + + return self; +} + +- (void)dealloc { + if (_inputStream) { + [_inputStream close]; + _inputStream = nil; + } +} + +- (NSInputStream *)inputStream { + if (!_inputStream) { + if ([self.body isKindOfClass:[NSData class]]) { + _inputStream = [NSInputStream inputStreamWithData:self.body]; + } else if ([self.body isKindOfClass:[NSURL class]]) { + _inputStream = [NSInputStream inputStreamWithURL:self.body]; + } else if ([self.body isKindOfClass:[NSInputStream class]]) { + _inputStream = self.body; + } else { + _inputStream = [NSInputStream inputStreamWithData:[NSData data]]; + } + } + + return _inputStream; +} + +- (NSString *)stringForHeaders { + NSMutableString *headerString = [NSMutableString string]; + for (NSString *field in [self.headers allKeys]) { + [headerString appendString:[NSString stringWithFormat:@"%@: %@%@", field, [self.headers valueForKey:field], kAFMultipartFormCRLF]]; + } + [headerString appendString:kAFMultipartFormCRLF]; + + return [NSString stringWithString:headerString]; +} + +- (unsigned long long)contentLength { + unsigned long long length = 0; + + NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding]; + length += [encapsulationBoundaryData length]; + + NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding]; + length += [headersData length]; + + length += _bodyContentLength; + + NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]); + length += [closingBoundaryData length]; + + return length; +} + +- (BOOL)hasBytesAvailable { + // Allows `read:maxLength:` to be called again if `AFMultipartFormFinalBoundary` doesn't fit into the available buffer + if (_phase == AFFinalBoundaryPhase) { + return YES; + } + + switch (self.inputStream.streamStatus) { + case NSStreamStatusNotOpen: + case NSStreamStatusOpening: + case NSStreamStatusOpen: + case NSStreamStatusReading: + case NSStreamStatusWriting: + return YES; + case NSStreamStatusAtEnd: + case NSStreamStatusClosed: + case NSStreamStatusError: + default: + return NO; + } +} + +- (NSInteger)read:(uint8_t *)buffer + maxLength:(NSUInteger)length +{ + NSInteger totalNumberOfBytesRead = 0; + + if (_phase == AFEncapsulationBoundaryPhase) { + NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding]; + totalNumberOfBytesRead += [self readData:encapsulationBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; + } + + if (_phase == AFHeaderPhase) { + NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding]; + totalNumberOfBytesRead += [self readData:headersData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; + } + + if (_phase == AFBodyPhase) { + NSInteger numberOfBytesRead = 0; + + numberOfBytesRead = [self.inputStream read:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; + if (numberOfBytesRead == -1) { + return -1; + } else { + totalNumberOfBytesRead += numberOfBytesRead; + + if ([self.inputStream streamStatus] >= NSStreamStatusAtEnd) { + [self transitionToNextPhase]; + } + } + } + + if (_phase == AFFinalBoundaryPhase) { + NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]); + totalNumberOfBytesRead += [self readData:closingBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; + } + + return totalNumberOfBytesRead; +} + +- (NSInteger)readData:(NSData *)data + intoBuffer:(uint8_t *)buffer + maxLength:(NSUInteger)length +{ + NSRange range = NSMakeRange((NSUInteger)_phaseReadOffset, MIN([data length] - ((NSUInteger)_phaseReadOffset), length)); + [data getBytes:buffer range:range]; + + _phaseReadOffset += range.length; + + if (((NSUInteger)_phaseReadOffset) >= [data length]) { + [self transitionToNextPhase]; + } + + return (NSInteger)range.length; +} + +- (BOOL)transitionToNextPhase { + if (![[NSThread currentThread] isMainThread]) { + dispatch_sync(dispatch_get_main_queue(), ^{ + [self transitionToNextPhase]; + }); + return YES; + } + + switch (_phase) { + case AFEncapsulationBoundaryPhase: + _phase = AFHeaderPhase; + break; + case AFHeaderPhase: + [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; + [self.inputStream open]; + _phase = AFBodyPhase; + break; + case AFBodyPhase: + [self.inputStream close]; + _phase = AFFinalBoundaryPhase; + break; + case AFFinalBoundaryPhase: + default: + _phase = AFEncapsulationBoundaryPhase; + break; + } + _phaseReadOffset = 0; + + return YES; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + AFHTTPBodyPart *bodyPart = [[[self class] allocWithZone:zone] init]; + + bodyPart.stringEncoding = self.stringEncoding; + bodyPart.headers = self.headers; + bodyPart.bodyContentLength = self.bodyContentLength; + bodyPart.body = self.body; + bodyPart.boundary = self.boundary; + + return bodyPart; +} + +@end + +#pragma mark - + +@implementation AFJSONRequestSerializer + ++ (instancetype)serializer { + return [self serializerWithWritingOptions:(NSJSONWritingOptions)0]; +} + ++ (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions +{ + AFJSONRequestSerializer *serializer = [[self alloc] init]; + serializer.writingOptions = writingOptions; + + return serializer; +} + +#pragma mark - AFURLRequestSerialization + +- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request + withParameters:(id)parameters + error:(NSError *__autoreleasing *)error +{ + NSParameterAssert(request); + + if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) { + return [super requestBySerializingRequest:request withParameters:parameters error:error]; + } + + NSMutableURLRequest *mutableRequest = [request mutableCopy]; + + [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { + if (![request valueForHTTPHeaderField:field]) { + [mutableRequest setValue:value forHTTPHeaderField:field]; + } + }]; + + if (parameters) { + if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { + [mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + } + + if (![NSJSONSerialization isValidJSONObject:parameters]) { + if (error) { + NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"The `parameters` argument is not valid JSON.", @"AFNetworking", nil)}; + *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo]; + } + return nil; + } + + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]; + + if (!jsonData) { + return nil; + } + + [mutableRequest setHTTPBody:jsonData]; + } + + return mutableRequest; +} + +#pragma mark - NSSecureCoding + +- (instancetype)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.writingOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(writingOptions))] unsignedIntegerValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:@(self.writingOptions) forKey:NSStringFromSelector(@selector(writingOptions))]; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + AFJSONRequestSerializer *serializer = [super copyWithZone:zone]; + serializer.writingOptions = self.writingOptions; + + return serializer; +} + +@end + +#pragma mark - + +@implementation AFPropertyListRequestSerializer + ++ (instancetype)serializer { + return [self serializerWithFormat:NSPropertyListXMLFormat_v1_0 writeOptions:0]; +} + ++ (instancetype)serializerWithFormat:(NSPropertyListFormat)format + writeOptions:(NSPropertyListWriteOptions)writeOptions +{ + AFPropertyListRequestSerializer *serializer = [[self alloc] init]; + serializer.format = format; + serializer.writeOptions = writeOptions; + + return serializer; +} + +#pragma mark - AFURLRequestSerializer + +- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request + withParameters:(id)parameters + error:(NSError *__autoreleasing *)error +{ + NSParameterAssert(request); + + if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) { + return [super requestBySerializingRequest:request withParameters:parameters error:error]; + } + + NSMutableURLRequest *mutableRequest = [request mutableCopy]; + + [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { + if (![request valueForHTTPHeaderField:field]) { + [mutableRequest setValue:value forHTTPHeaderField:field]; + } + }]; + + if (parameters) { + if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { + [mutableRequest setValue:@"application/x-plist" forHTTPHeaderField:@"Content-Type"]; + } + + NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error]; + + if (!plistData) { + return nil; + } + + [mutableRequest setHTTPBody:plistData]; + } + + return mutableRequest; +} + +#pragma mark - NSSecureCoding + +- (instancetype)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.format = (NSPropertyListFormat)[[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(format))] unsignedIntegerValue]; + self.writeOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(writeOptions))] unsignedIntegerValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:@(self.format) forKey:NSStringFromSelector(@selector(format))]; + [coder encodeObject:@(self.writeOptions) forKey:NSStringFromSelector(@selector(writeOptions))]; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + AFPropertyListRequestSerializer *serializer = [super copyWithZone:zone]; + serializer.format = self.format; + serializer.writeOptions = self.writeOptions; + + return serializer; +} + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h b/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h new file mode 100644 index 0000000..56a4d28 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h @@ -0,0 +1,313 @@ +// AFURLResponseSerialization.h +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + Recursively removes `NSNull` values from a JSON object. +*/ +FOUNDATION_EXPORT id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions); + +/** + The `AFURLResponseSerialization` protocol is adopted by an object that decodes data into a more useful object representation, according to details in the server response. Response serializers may additionally perform validation on the incoming response and data. + + For example, a JSON response serializer may check for an acceptable status code (`2XX` range) and content type (`application/json`), decoding a valid JSON response into an object. + */ +@protocol AFURLResponseSerialization + +/** + The response object decoded from the data associated with a specified response. + + @param response The response to be processed. + @param data The response data to be decoded. + @param error The error that occurred while attempting to decode the response data. + + @return The object decoded from the specified response data. + */ +- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response + data:(nullable NSData *)data + error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW; + +@end + +#pragma mark - + +/** + `AFHTTPResponseSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation. + + Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPResponseSerializer` in order to ensure consistent default behavior. + */ +@interface AFHTTPResponseSerializer : NSObject + +- (instancetype)init; + +/** + Creates and returns a serializer with default configuration. + */ ++ (instancetype)serializer; + +///----------------------------------------- +/// @name Configuring Response Serialization +///----------------------------------------- + +/** + The acceptable HTTP status codes for responses. When non-`nil`, responses with status codes not contained by the set will result in an error during validation. + + See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html + */ +@property (nonatomic, copy, nullable) NSIndexSet *acceptableStatusCodes; + +/** + The acceptable MIME types for responses. When non-`nil`, responses with a `Content-Type` with MIME types that do not intersect with the set will result in an error during validation. + */ +@property (nonatomic, copy, nullable) NSSet *acceptableContentTypes; + +/** + Validates the specified response and data. + + In its base implementation, this method checks for an acceptable status code and content type. Subclasses may wish to add other domain-specific checks. + + @param response The response to be validated. + @param data The data associated with the response. + @param error The error that occurred while attempting to validate the response. + + @return `YES` if the response is valid, otherwise `NO`. + */ +- (BOOL)validateResponse:(nullable NSHTTPURLResponse *)response + data:(nullable NSData *)data + error:(NSError * _Nullable __autoreleasing *)error; + +@end + +#pragma mark - + + +/** + `AFJSONResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes JSON responses. + + By default, `AFJSONResponseSerializer` accepts the following MIME types, which includes the official standard, `application/json`, as well as other commonly-used types: + + - `application/json` + - `text/json` + - `text/javascript` + + In RFC 7159 - Section 8.1, it states that JSON text is required to be encoded in UTF-8, UTF-16, or UTF-32, and the default encoding is UTF-8. NSJSONSerialization provides support for all the encodings listed in the specification, and recommends UTF-8 for efficiency. Using an unsupported encoding will result in serialization error. See the `NSJSONSerialization` documentation for more details. + */ +@interface AFJSONResponseSerializer : AFHTTPResponseSerializer + +- (instancetype)init; + +/** + Options for reading the response JSON data and creating the Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default. + */ +@property (nonatomic, assign) NSJSONReadingOptions readingOptions; + +/** + Whether to remove keys with `NSNull` values from response JSON. Defaults to `NO`. + */ +@property (nonatomic, assign) BOOL removesKeysWithNullValues; + +/** + Creates and returns a JSON serializer with specified reading and writing options. + + @param readingOptions The specified JSON reading options. + */ ++ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions; + +@end + +#pragma mark - + +/** + `AFXMLParserResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLParser` objects. + + By default, `AFXMLParserResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: + + - `application/xml` + - `text/xml` + */ +@interface AFXMLParserResponseSerializer : AFHTTPResponseSerializer + +@end + +#pragma mark - + +#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED + +/** + `AFXMLDocumentResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. + + By default, `AFXMLDocumentResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: + + - `application/xml` + - `text/xml` + */ +@interface AFXMLDocumentResponseSerializer : AFHTTPResponseSerializer + +- (instancetype)init; + +/** + Input and output options specifically intended for `NSXMLDocument` objects. For possible values, see the `NSXMLDocument` documentation section "Input and Output Options". `0` by default. + */ +@property (nonatomic, assign) NSUInteger options; + +/** + Creates and returns an XML document serializer with the specified options. + + @param mask The XML document options. + */ ++ (instancetype)serializerWithXMLDocumentOptions:(NSUInteger)mask; + +@end + +#endif + +#pragma mark - + +/** + `AFPropertyListResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. + + By default, `AFPropertyListResponseSerializer` accepts the following MIME types: + + - `application/x-plist` + */ +@interface AFPropertyListResponseSerializer : AFHTTPResponseSerializer + +- (instancetype)init; + +/** + The property list format. Possible values are described in "NSPropertyListFormat". + */ +@property (nonatomic, assign) NSPropertyListFormat format; + +/** + The property list reading options. Possible values are described in "NSPropertyListMutabilityOptions." + */ +@property (nonatomic, assign) NSPropertyListReadOptions readOptions; + +/** + Creates and returns a property list serializer with a specified format, read options, and write options. + + @param format The property list format. + @param readOptions The property list reading options. + */ ++ (instancetype)serializerWithFormat:(NSPropertyListFormat)format + readOptions:(NSPropertyListReadOptions)readOptions; + +@end + +#pragma mark - + +/** + `AFImageResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes image responses. + + By default, `AFImageResponseSerializer` accepts the following MIME types, which correspond to the image formats supported by UIImage or NSImage: + + - `image/tiff` + - `image/jpeg` + - `image/gif` + - `image/png` + - `image/ico` + - `image/x-icon` + - `image/bmp` + - `image/x-bmp` + - `image/x-xbitmap` + - `image/x-win-bitmap` + */ +@interface AFImageResponseSerializer : AFHTTPResponseSerializer + +#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH +/** + The scale factor used when interpreting the image data to construct `responseImage`. Specifying a scale factor of 1.0 results in an image whose size matches the pixel-based dimensions of the image. Applying a different scale factor changes the size of the image as reported by the size property. This is set to the value of scale of the main screen by default, which automatically scales images for retina displays, for instance. + */ +@property (nonatomic, assign) CGFloat imageScale; + +/** + Whether to automatically inflate response image data for compressed formats (such as PNG or JPEG). Enabling this can significantly improve drawing performance on iOS when used with `setCompletionBlockWithSuccess:failure:`, as it allows a bitmap representation to be constructed in the background rather than on the main thread. `YES` by default. + */ +@property (nonatomic, assign) BOOL automaticallyInflatesResponseImage; +#endif + +@end + +#pragma mark - + +/** + `AFCompoundSerializer` is a subclass of `AFHTTPResponseSerializer` that delegates the response serialization to the first `AFHTTPResponseSerializer` object that returns an object for `responseObjectForResponse:data:error:`, falling back on the default behavior of `AFHTTPResponseSerializer`. This is useful for supporting multiple potential types and structures of server responses with a single serializer. + */ +@interface AFCompoundResponseSerializer : AFHTTPResponseSerializer + +/** + The component response serializers. + */ +@property (readonly, nonatomic, copy) NSArray > *responseSerializers; + +/** + Creates and returns a compound serializer comprised of the specified response serializers. + + @warning Each response serializer specified must be a subclass of `AFHTTPResponseSerializer`, and response to `-validateResponse:data:error:`. + */ ++ (instancetype)compoundSerializerWithResponseSerializers:(NSArray > *)responseSerializers; + +@end + +///---------------- +/// @name Constants +///---------------- + +/** + ## Error Domains + + The following error domain is predefined. + + - `NSString * const AFURLResponseSerializationErrorDomain` + + ### Constants + + `AFURLResponseSerializationErrorDomain` + AFURLResponseSerializer errors. Error codes for `AFURLResponseSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`. + */ +FOUNDATION_EXPORT NSString * const AFURLResponseSerializationErrorDomain; + +/** + ## User info dictionary keys + + These keys may exist in the user info dictionary, in addition to those defined for NSError. + + - `NSString * const AFNetworkingOperationFailingURLResponseErrorKey` + - `NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey` + + ### Constants + + `AFNetworkingOperationFailingURLResponseErrorKey` + The corresponding value is an `NSURLResponse` containing the response of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`. + + `AFNetworkingOperationFailingURLResponseDataErrorKey` + The corresponding value is an `NSData` containing the original data of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseErrorKey; + +FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey; + +NS_ASSUME_NONNULL_END diff --git a/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m b/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m new file mode 100755 index 0000000..2715a1b --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m @@ -0,0 +1,836 @@ +// AFURLResponseSerialization.m +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import "AFURLResponseSerialization.h" + +#import + +#if TARGET_OS_IOS +#import +#elif TARGET_OS_WATCH +#import +#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +#import +#endif + +NSString * const AFURLResponseSerializationErrorDomain = @"com.alamofire.error.serialization.response"; +NSString * const AFNetworkingOperationFailingURLResponseErrorKey = @"com.alamofire.serialization.response.error.response"; +NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey = @"com.alamofire.serialization.response.error.data"; + +static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) { + if (!error) { + return underlyingError; + } + + if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) { + return error; + } + + NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy]; + mutableUserInfo[NSUnderlyingErrorKey] = underlyingError; + + return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo]; +} + +static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) { + if ([error.domain isEqualToString:domain] && error.code == code) { + return YES; + } else if (error.userInfo[NSUnderlyingErrorKey]) { + return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain); + } + + return NO; +} + +id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) { + if ([JSONObject isKindOfClass:[NSArray class]]) { + NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]]; + for (id value in (NSArray *)JSONObject) { + if (![value isEqual:[NSNull null]]) { + [mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)]; + } + } + + return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray]; + } else if ([JSONObject isKindOfClass:[NSDictionary class]]) { + NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject]; + for (id key in [(NSDictionary *)JSONObject allKeys]) { + id value = (NSDictionary *)JSONObject[key]; + if (!value || [value isEqual:[NSNull null]]) { + [mutableDictionary removeObjectForKey:key]; + } else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) { + mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions); + } + } + + return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary]; + } + + return JSONObject; +} + +@implementation AFHTTPResponseSerializer + ++ (instancetype)serializer { + return [[self alloc] init]; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)]; + self.acceptableContentTypes = nil; + + return self; +} + +#pragma mark - + +- (BOOL)validateResponse:(NSHTTPURLResponse *)response + data:(NSData *)data + error:(NSError * __autoreleasing *)error +{ + BOOL responseIsValid = YES; + NSError *validationError = nil; + + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { + if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] && + !([response MIMEType] == nil && [data length] == 0)) { + + if ([data length] > 0 && [response URL]) { + NSMutableDictionary *mutableUserInfo = [@{ + NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]], + NSURLErrorFailingURLErrorKey:[response URL], + AFNetworkingOperationFailingURLResponseErrorKey: response, + } mutableCopy]; + if (data) { + mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data; + } + + validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError); + } + + responseIsValid = NO; + } + + if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) { + NSMutableDictionary *mutableUserInfo = [@{ + NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode], + NSURLErrorFailingURLErrorKey:[response URL], + AFNetworkingOperationFailingURLResponseErrorKey: response, + } mutableCopy]; + + if (data) { + mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data; + } + + validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError); + + responseIsValid = NO; + } + } + + if (error && !responseIsValid) { + *error = validationError; + } + + return responseIsValid; +} + +#pragma mark - AFURLResponseSerialization + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + [self validateResponse:(NSHTTPURLResponse *)response data:data error:error]; + + return data; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + self = [self init]; + if (!self) { + return nil; + } + + self.acceptableStatusCodes = [decoder decodeObjectOfClass:[NSIndexSet class] forKey:NSStringFromSelector(@selector(acceptableStatusCodes))]; + self.acceptableContentTypes = [decoder decodeObjectOfClass:[NSSet class] forKey:NSStringFromSelector(@selector(acceptableContentTypes))]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [coder encodeObject:self.acceptableStatusCodes forKey:NSStringFromSelector(@selector(acceptableStatusCodes))]; + [coder encodeObject:self.acceptableContentTypes forKey:NSStringFromSelector(@selector(acceptableContentTypes))]; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + AFHTTPResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + serializer.acceptableStatusCodes = [self.acceptableStatusCodes copyWithZone:zone]; + serializer.acceptableContentTypes = [self.acceptableContentTypes copyWithZone:zone]; + + return serializer; +} + +@end + +#pragma mark - + +@implementation AFJSONResponseSerializer + ++ (instancetype)serializer { + return [self serializerWithReadingOptions:(NSJSONReadingOptions)0]; +} + ++ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions { + AFJSONResponseSerializer *serializer = [[self alloc] init]; + serializer.readingOptions = readingOptions; + + return serializer; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil]; + + return self; +} + +#pragma mark - AFURLResponseSerialization + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { + if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { + return nil; + } + } + + // Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization. + // See https://github.com/rails/rails/issues/1742 + BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]]; + + if (data.length == 0 || isSpace) { + return nil; + } + + NSError *serializationError = nil; + + id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError]; + + if (!responseObject) + { + if (error) { + *error = AFErrorWithUnderlyingError(serializationError, *error); + } + return nil; + } + + if (self.removesKeysWithNullValues) { + return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions); + } + + return responseObject; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.readingOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(readingOptions))] unsignedIntegerValue]; + self.removesKeysWithNullValues = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(removesKeysWithNullValues))] boolValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:@(self.readingOptions) forKey:NSStringFromSelector(@selector(readingOptions))]; + [coder encodeObject:@(self.removesKeysWithNullValues) forKey:NSStringFromSelector(@selector(removesKeysWithNullValues))]; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + AFJSONResponseSerializer *serializer = [super copyWithZone:zone]; + serializer.readingOptions = self.readingOptions; + serializer.removesKeysWithNullValues = self.removesKeysWithNullValues; + + return serializer; +} + +@end + +#pragma mark - + +@implementation AFXMLParserResponseSerializer + ++ (instancetype)serializer { + AFXMLParserResponseSerializer *serializer = [[self alloc] init]; + + return serializer; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml", nil]; + + return self; +} + +#pragma mark - AFURLResponseSerialization + +- (id)responseObjectForResponse:(NSHTTPURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { + if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { + return nil; + } + } + + return [[NSXMLParser alloc] initWithData:data]; +} + +@end + +#pragma mark - + +#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED + +@implementation AFXMLDocumentResponseSerializer + ++ (instancetype)serializer { + return [self serializerWithXMLDocumentOptions:0]; +} + ++ (instancetype)serializerWithXMLDocumentOptions:(NSUInteger)mask { + AFXMLDocumentResponseSerializer *serializer = [[self alloc] init]; + serializer.options = mask; + + return serializer; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml", nil]; + + return self; +} + +#pragma mark - AFURLResponseSerialization + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { + if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { + return nil; + } + } + + NSError *serializationError = nil; + NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:data options:self.options error:&serializationError]; + + if (!document) + { + if (error) { + *error = AFErrorWithUnderlyingError(serializationError, *error); + } + return nil; + } + + return document; +} + +#pragma mark - NSSecureCoding + +- (instancetype)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.options = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(options))] unsignedIntegerValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:@(self.options) forKey:NSStringFromSelector(@selector(options))]; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + AFXMLDocumentResponseSerializer *serializer = [super copyWithZone:zone]; + serializer.options = self.options; + + return serializer; +} + +@end + +#endif + +#pragma mark - + +@implementation AFPropertyListResponseSerializer + ++ (instancetype)serializer { + return [self serializerWithFormat:NSPropertyListXMLFormat_v1_0 readOptions:0]; +} + ++ (instancetype)serializerWithFormat:(NSPropertyListFormat)format + readOptions:(NSPropertyListReadOptions)readOptions +{ + AFPropertyListResponseSerializer *serializer = [[self alloc] init]; + serializer.format = format; + serializer.readOptions = readOptions; + + return serializer; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/x-plist", nil]; + + return self; +} + +#pragma mark - AFURLResponseSerialization + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { + if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { + return nil; + } + } + + if (!data) { + return nil; + } + + NSError *serializationError = nil; + + id responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError]; + + if (!responseObject) + { + if (error) { + *error = AFErrorWithUnderlyingError(serializationError, *error); + } + return nil; + } + + return responseObject; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.format = (NSPropertyListFormat)[[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(format))] unsignedIntegerValue]; + self.readOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(readOptions))] unsignedIntegerValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:@(self.format) forKey:NSStringFromSelector(@selector(format))]; + [coder encodeObject:@(self.readOptions) forKey:NSStringFromSelector(@selector(readOptions))]; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + AFPropertyListResponseSerializer *serializer = [super copyWithZone:zone]; + serializer.format = self.format; + serializer.readOptions = self.readOptions; + + return serializer; +} + +@end + +#pragma mark - + +#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH +#import +#import + +@interface UIImage (AFNetworkingSafeImageLoading) ++ (UIImage *)af_safeImageWithData:(NSData *)data; +@end + +static NSLock* imageLock = nil; + +@implementation UIImage (AFNetworkingSafeImageLoading) + ++ (UIImage *)af_safeImageWithData:(NSData *)data { + UIImage* image = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + imageLock = [[NSLock alloc] init]; + }); + + [imageLock lock]; + image = [UIImage imageWithData:data]; + [imageLock unlock]; + return image; +} + +@end + +static UIImage * AFImageWithDataAtScale(NSData *data, CGFloat scale) { + UIImage *image = [UIImage af_safeImageWithData:data]; + if (image.images) { + return image; + } + + return [[UIImage alloc] initWithCGImage:[image CGImage] scale:scale orientation:image.imageOrientation]; +} + +static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response, NSData *data, CGFloat scale) { + if (!data || [data length] == 0) { + return nil; + } + + CGImageRef imageRef = NULL; + CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); + + if ([response.MIMEType isEqualToString:@"image/png"]) { + imageRef = CGImageCreateWithPNGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault); + } else if ([response.MIMEType isEqualToString:@"image/jpeg"]) { + imageRef = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault); + + if (imageRef) { + CGColorSpaceRef imageColorSpace = CGImageGetColorSpace(imageRef); + CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(imageColorSpace); + + // CGImageCreateWithJPEGDataProvider does not properly handle CMKY, so fall back to AFImageWithDataAtScale + if (imageColorSpaceModel == kCGColorSpaceModelCMYK) { + CGImageRelease(imageRef); + imageRef = NULL; + } + } + } + + CGDataProviderRelease(dataProvider); + + UIImage *image = AFImageWithDataAtScale(data, scale); + if (!imageRef) { + if (image.images || !image) { + return image; + } + + imageRef = CGImageCreateCopy([image CGImage]); + if (!imageRef) { + return nil; + } + } + + size_t width = CGImageGetWidth(imageRef); + size_t height = CGImageGetHeight(imageRef); + size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef); + + if (width * height > 1024 * 1024 || bitsPerComponent > 8) { + CGImageRelease(imageRef); + + return image; + } + + // CGImageGetBytesPerRow() calculates incorrectly in iOS 5.0, so defer to CGBitmapContextCreate + size_t bytesPerRow = 0; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace); + CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); + + if (colorSpaceModel == kCGColorSpaceModelRGB) { + uint32_t alpha = (bitmapInfo & kCGBitmapAlphaInfoMask); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wassign-enum" + if (alpha == kCGImageAlphaNone) { + bitmapInfo &= ~kCGBitmapAlphaInfoMask; + bitmapInfo |= kCGImageAlphaNoneSkipFirst; + } else if (!(alpha == kCGImageAlphaNoneSkipFirst || alpha == kCGImageAlphaNoneSkipLast)) { + bitmapInfo &= ~kCGBitmapAlphaInfoMask; + bitmapInfo |= kCGImageAlphaPremultipliedFirst; + } +#pragma clang diagnostic pop + } + + CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo); + + CGColorSpaceRelease(colorSpace); + + if (!context) { + CGImageRelease(imageRef); + + return image; + } + + CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), imageRef); + CGImageRef inflatedImageRef = CGBitmapContextCreateImage(context); + + CGContextRelease(context); + + UIImage *inflatedImage = [[UIImage alloc] initWithCGImage:inflatedImageRef scale:scale orientation:image.imageOrientation]; + + CGImageRelease(inflatedImageRef); + CGImageRelease(imageRef); + + return inflatedImage; +} +#endif + + +@implementation AFImageResponseSerializer + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"image/tiff", @"image/jpeg", @"image/gif", @"image/png", @"image/ico", @"image/x-icon", @"image/bmp", @"image/x-bmp", @"image/x-xbitmap", @"image/x-win-bitmap", nil]; + +#if TARGET_OS_IOS || TARGET_OS_TV + self.imageScale = [[UIScreen mainScreen] scale]; + self.automaticallyInflatesResponseImage = YES; +#elif TARGET_OS_WATCH + self.imageScale = [[WKInterfaceDevice currentDevice] screenScale]; + self.automaticallyInflatesResponseImage = YES; +#endif + + return self; +} + +#pragma mark - AFURLResponseSerializer + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { + if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { + return nil; + } + } + +#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH + if (self.automaticallyInflatesResponseImage) { + return AFInflatedImageFromResponseWithDataAtScale((NSHTTPURLResponse *)response, data, self.imageScale); + } else { + return AFImageWithDataAtScale(data, self.imageScale); + } +#else + // Ensure that the image is set to it's correct pixel width and height + NSBitmapImageRep *bitimage = [[NSBitmapImageRep alloc] initWithData:data]; + NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize([bitimage pixelsWide], [bitimage pixelsHigh])]; + [image addRepresentation:bitimage]; + + return image; +#endif + + return nil; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + +#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH + NSNumber *imageScale = [decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(imageScale))]; +#if CGFLOAT_IS_DOUBLE + self.imageScale = [imageScale doubleValue]; +#else + self.imageScale = [imageScale floatValue]; +#endif + + self.automaticallyInflatesResponseImage = [decoder decodeBoolForKey:NSStringFromSelector(@selector(automaticallyInflatesResponseImage))]; +#endif + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + +#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH + [coder encodeObject:@(self.imageScale) forKey:NSStringFromSelector(@selector(imageScale))]; + [coder encodeBool:self.automaticallyInflatesResponseImage forKey:NSStringFromSelector(@selector(automaticallyInflatesResponseImage))]; +#endif +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + AFImageResponseSerializer *serializer = [super copyWithZone:zone]; + +#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH + serializer.imageScale = self.imageScale; + serializer.automaticallyInflatesResponseImage = self.automaticallyInflatesResponseImage; +#endif + + return serializer; +} + +@end + +#pragma mark - + +@interface AFCompoundResponseSerializer () +@property (readwrite, nonatomic, copy) NSArray *responseSerializers; +@end + +@implementation AFCompoundResponseSerializer + ++ (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSerializers { + AFCompoundResponseSerializer *serializer = [[self alloc] init]; + serializer.responseSerializers = responseSerializers; + + return serializer; +} + +#pragma mark - AFURLResponseSerialization + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + for (id serializer in self.responseSerializers) { + if (![serializer isKindOfClass:[AFHTTPResponseSerializer class]]) { + continue; + } + + NSError *serializerError = nil; + id responseObject = [serializer responseObjectForResponse:response data:data error:&serializerError]; + if (responseObject) { + if (error) { + *error = AFErrorWithUnderlyingError(serializerError, *error); + } + + return responseObject; + } + } + + return [super responseObjectForResponse:response data:data error:error]; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + NSSet *classes = [NSSet setWithArray:@[[NSArray class], [AFHTTPResponseSerializer class]]]; + self.responseSerializers = [decoder decodeObjectOfClasses:classes forKey:NSStringFromSelector(@selector(responseSerializers))]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:self.responseSerializers forKey:NSStringFromSelector(@selector(responseSerializers))]; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + AFCompoundResponseSerializer *serializer = [super copyWithZone:zone]; + serializer.responseSerializers = self.responseSerializers; + + return serializer; +} + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h b/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h new file mode 100644 index 0000000..88700c3 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h @@ -0,0 +1,516 @@ +// AFURLSessionManager.h +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + + +#import + +#import "AFURLResponseSerialization.h" +#import "AFURLRequestSerialization.h" +#import "AFSecurityPolicy.h" +#import "AFCompatibilityMacros.h" +#if !TARGET_OS_WATCH +#import "AFNetworkReachabilityManager.h" +#endif + +/** + `AFURLSessionManager` creates and manages an `NSURLSession` object based on a specified `NSURLSessionConfiguration` object, which conforms to ``, ``, ``, and ``. + + ## Subclassing Notes + + This is the base class for `AFHTTPSessionManager`, which adds functionality specific to making HTTP requests. If you are looking to extend `AFURLSessionManager` specifically for HTTP, consider subclassing `AFHTTPSessionManager` instead. + + ## NSURLSession & NSURLSessionTask Delegate Methods + + `AFURLSessionManager` implements the following delegate methods: + + ### `NSURLSessionDelegate` + + - `URLSession:didBecomeInvalidWithError:` + - `URLSession:didReceiveChallenge:completionHandler:` + - `URLSessionDidFinishEventsForBackgroundURLSession:` + + ### `NSURLSessionTaskDelegate` + + - `URLSession:willPerformHTTPRedirection:newRequest:completionHandler:` + - `URLSession:task:didReceiveChallenge:completionHandler:` + - `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:` + - `URLSession:task:needNewBodyStream:` + - `URLSession:task:didCompleteWithError:` + + ### `NSURLSessionDataDelegate` + + - `URLSession:dataTask:didReceiveResponse:completionHandler:` + - `URLSession:dataTask:didBecomeDownloadTask:` + - `URLSession:dataTask:didReceiveData:` + - `URLSession:dataTask:willCacheResponse:completionHandler:` + + ### `NSURLSessionDownloadDelegate` + + - `URLSession:downloadTask:didFinishDownloadingToURL:` + - `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:` + - `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:` + + If any of these methods are overridden in a subclass, they _must_ call the `super` implementation first. + + ## Network Reachability Monitoring + + Network reachability status and change monitoring is available through the `reachabilityManager` property. Applications may choose to monitor network reachability conditions in order to prevent or suspend any outbound requests. See `AFNetworkReachabilityManager` for more details. + + ## NSCoding Caveats + + - Encoded managers do not include any block properties. Be sure to set delegate callback blocks when using `-initWithCoder:` or `NSKeyedUnarchiver`. + + ## NSCopying Caveats + + - `-copy` and `-copyWithZone:` return a new manager with a new `NSURLSession` created from the configuration of the original. + - Operation copies do not include any delegate callback blocks, as they often strongly captures a reference to `self`, which would otherwise have the unintuitive side-effect of pointing to the _original_ session manager when copied. + + @warning Managers for background sessions must be owned for the duration of their use. This can be accomplished by creating an application-wide or shared singleton instance. + */ + +NS_ASSUME_NONNULL_BEGIN + +@interface AFURLSessionManager : NSObject + +/** + The managed session. + */ +@property (readonly, nonatomic, strong) NSURLSession *session; + +/** + The operation queue on which delegate callbacks are run. + */ +@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue; + +/** + Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`. + + @warning `responseSerializer` must not be `nil`. + */ +@property (nonatomic, strong) id responseSerializer; + +///------------------------------- +/// @name Managing Security Policy +///------------------------------- + +/** + The security policy used by created session to evaluate server trust for secure connections. `AFURLSessionManager` uses the `defaultPolicy` unless otherwise specified. + */ +@property (nonatomic, strong) AFSecurityPolicy *securityPolicy; + +#if !TARGET_OS_WATCH +///-------------------------------------- +/// @name Monitoring Network Reachability +///-------------------------------------- + +/** + The network reachability manager. `AFURLSessionManager` uses the `sharedManager` by default. + */ +@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager; +#endif + +///---------------------------- +/// @name Getting Session Tasks +///---------------------------- + +/** + The data, upload, and download tasks currently run by the managed session. + */ +@property (readonly, nonatomic, strong) NSArray *tasks; + +/** + The data tasks currently run by the managed session. + */ +@property (readonly, nonatomic, strong) NSArray *dataTasks; + +/** + The upload tasks currently run by the managed session. + */ +@property (readonly, nonatomic, strong) NSArray *uploadTasks; + +/** + The download tasks currently run by the managed session. + */ +@property (readonly, nonatomic, strong) NSArray *downloadTasks; + +///------------------------------- +/// @name Managing Callback Queues +///------------------------------- + +/** + The dispatch queue for `completionBlock`. If `NULL` (default), the main queue is used. + */ +@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue; + +/** + The dispatch group for `completionBlock`. If `NULL` (default), a private dispatch group is used. + */ +@property (nonatomic, strong, nullable) dispatch_group_t completionGroup; + +///--------------------- +/// @name Initialization +///--------------------- + +/** + Creates and returns a manager for a session created with the specified configuration. This is the designated initializer. + + @param configuration The configuration used to create the managed session. + + @return A manager for a newly-created session. + */ +- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER; + +/** + Invalidates the managed session, optionally canceling pending tasks and optionally resets given session. + + @param cancelPendingTasks Whether or not to cancel pending tasks. + @param resetSession Whether or not to reset the session of the manager. + */ +- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks resetSession:(BOOL)resetSession; + +///------------------------- +/// @name Running Data Tasks +///------------------------- + +/** + Creates an `NSURLSessionDataTask` with the specified request. + + @param request The HTTP request for the request. + @param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. + @param downloadProgressBlock A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue. + @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. + */ +- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request + uploadProgress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock + downloadProgress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock + completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler; + +///--------------------------- +/// @name Running Upload Tasks +///--------------------------- + +/** + Creates an `NSURLSessionUploadTask` with the specified request for a local file. + + @param request The HTTP request for the request. + @param fileURL A URL to the local file to be uploaded. + @param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. + @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. + + @see `attemptsToRecreateUploadTasksForBackgroundSessions` + */ +- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request + fromFile:(NSURL *)fileURL + progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock + completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler; + +/** + Creates an `NSURLSessionUploadTask` with the specified request for an HTTP body. + + @param request The HTTP request for the request. + @param bodyData A data object containing the HTTP body to be uploaded. + @param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. + @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. + */ +- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request + fromData:(nullable NSData *)bodyData + progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock + completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler; + +/** + Creates an `NSURLSessionUploadTask` with the specified streaming request. + + @param request The HTTP request for the request. + @param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. + @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. + */ +- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request + progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock + completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler; + +///----------------------------- +/// @name Running Download Tasks +///----------------------------- + +/** + Creates an `NSURLSessionDownloadTask` with the specified request. + + @param request The HTTP request for the request. + @param downloadProgressBlock A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue. + @param destination A block object to be executed in order to determine the destination of the downloaded file. This block takes two arguments, the target path & the server response, and returns the desired file URL of the resulting download. The temporary file used during the download will be automatically deleted after being moved to the returned URL. + @param completionHandler A block to be executed when a task finishes. This block has no return value and takes three arguments: the server response, the path of the downloaded file, and the error describing the network or parsing error that occurred, if any. + + @warning If using a background `NSURLSessionConfiguration` on iOS, these blocks will be lost when the app is terminated. Background sessions may prefer to use `-setDownloadTaskDidFinishDownloadingBlock:` to specify the URL for saving the downloaded file, rather than the destination block of this method. + */ +- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request + progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock + destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination + completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler; + +/** + Creates an `NSURLSessionDownloadTask` with the specified resume data. + + @param resumeData The data used to resume downloading. + @param downloadProgressBlock A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue. + @param destination A block object to be executed in order to determine the destination of the downloaded file. This block takes two arguments, the target path & the server response, and returns the desired file URL of the resulting download. The temporary file used during the download will be automatically deleted after being moved to the returned URL. + @param completionHandler A block to be executed when a task finishes. This block has no return value and takes three arguments: the server response, the path of the downloaded file, and the error describing the network or parsing error that occurred, if any. + */ +- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData + progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock + destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination + completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler; + +///--------------------------------- +/// @name Getting Progress for Tasks +///--------------------------------- + +/** + Returns the upload progress of the specified task. + + @param task The session task. Must not be `nil`. + + @return An `NSProgress` object reporting the upload progress of a task, or `nil` if the progress is unavailable. + */ +- (nullable NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task; + +/** + Returns the download progress of the specified task. + + @param task The session task. Must not be `nil`. + + @return An `NSProgress` object reporting the download progress of a task, or `nil` if the progress is unavailable. + */ +- (nullable NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task; + +///----------------------------------------- +/// @name Setting Session Delegate Callbacks +///----------------------------------------- + +/** + Sets a block to be executed when the managed session becomes invalid, as handled by the `NSURLSessionDelegate` method `URLSession:didBecomeInvalidWithError:`. + + @param block A block object to be executed when the managed session becomes invalid. The block has no return value, and takes two arguments: the session, and the error related to the cause of invalidation. + */ +- (void)setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block; + +/** + Sets a block to be executed when a connection level authentication challenge has occurred, as handled by the `NSURLSessionDelegate` method `URLSession:didReceiveChallenge:completionHandler:`. + + @param block A block object to be executed when a connection level authentication challenge has occurred. The block returns the disposition of the authentication challenge, and takes three arguments: the session, the authentication challenge, and a pointer to the credential that should be used to resolve the challenge. + + @warning Implementing a session authentication challenge handler yourself totally bypasses AFNetworking's security policy defined in `AFSecurityPolicy`. Make sure you fully understand the implications before implementing a custom session authentication challenge handler. If you do not want to bypass AFNetworking's security policy, use `setTaskDidReceiveAuthenticationChallengeBlock:` instead. + + @see -securityPolicy + @see -setTaskDidReceiveAuthenticationChallengeBlock: + */ +- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block; + +///-------------------------------------- +/// @name Setting Task Delegate Callbacks +///-------------------------------------- + +/** + Sets a block to be executed when a task requires a new request body stream to send to the remote server, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:needNewBodyStream:`. + + @param block A block object to be executed when a task requires a new request body stream. + */ +- (void)setTaskNeedNewBodyStreamBlock:(nullable NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block; + +/** + Sets a block to be executed when an HTTP request is attempting to perform a redirection to a different URL, as handled by the `NSURLSessionTaskDelegate` method `URLSession:willPerformHTTPRedirection:newRequest:completionHandler:`. + + @param block A block object to be executed when an HTTP request is attempting to perform a redirection to a different URL. The block returns the request to be made for the redirection, and takes four arguments: the session, the task, the redirection response, and the request corresponding to the redirection response. + */ +- (void)setTaskWillPerformHTTPRedirectionBlock:(nullable NSURLRequest * _Nullable (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block; + +/** + Sets a block to be executed when a session task has received a request specific authentication challenge, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didReceiveChallenge:completionHandler:`. + + @param authenticationChallengeHandler A block object to be executed when a session task has received a request specific authentication challenge. + + When implementing an authentication challenge handler, you should check the authentication method first (`challenge.protectionSpace.authenticationMethod `) to decide if you want to handle the authentication challenge yourself or if you want AFNetworking to handle it. If you want AFNetworking to handle the authentication challenge, just return `@(NSURLSessionAuthChallengePerformDefaultHandling)`. For example, you certainly want AFNetworking to handle certificate validation (i.e. authentication method == `NSURLAuthenticationMethodServerTrust`) as defined by the security policy. If you want to handle the challenge yourself, you have four options: + + 1. Return `nil` from the authentication challenge handler. You **MUST** call the completion handler with a disposition and credentials yourself. Use this if you need to present a user interface to let the user enter their credentials. + 2. Return an `NSError` object from the authentication challenge handler. You **MUST NOT** call the completion handler when returning an `NSError `. The returned error will be reported in the completion handler of the task. Use this if you need to abort an authentication challenge with a specific error. + 3. Return an `NSURLCredential` object from the authentication challenge handler. You **MUST NOT** call the completion handler when returning an `NSURLCredential`. The returned credentials will be used to fulfil the challenge. Use this when you can get credentials without presenting a user interface. + 4. Return an `NSNumber` object wrapping an `NSURLSessionAuthChallengeDisposition`. Supported values are `@(NSURLSessionAuthChallengePerformDefaultHandling)`, `@(NSURLSessionAuthChallengeCancelAuthenticationChallenge)` and `@(NSURLSessionAuthChallengeRejectProtectionSpace)`. You **MUST NOT** call the completion handler when returning an `NSNumber`. + + If you return anything else from the authentication challenge handler, an exception will be thrown. + + For more information about how URL sessions handle the different types of authentication challenges, see [NSURLSession](https://developer.apple.com/reference/foundation/nsurlsession?language=objc) and [URL Session Programming Guide](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html). + + @see -securityPolicy + */ +- (void)setAuthenticationChallengeHandler:(id (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, void (^completionHandler)(NSURLSessionAuthChallengeDisposition , NSURLCredential * _Nullable)))authenticationChallengeHandler; + +/** + Sets a block to be executed periodically to track upload progress, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`. + + @param block A block object to be called when an undetermined number of bytes have been uploaded to the server. This block has no return value and takes five arguments: the session, the task, the number of bytes written since the last time the upload progress block was called, the total bytes written, and the total bytes expected to be written during the request, as initially determined by the length of the HTTP body. This block may be called multiple times, and will execute on the main thread. + */ +- (void)setTaskDidSendBodyDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block; + +/** + Sets a block to be executed as the last message related to a specific task, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didCompleteWithError:`. + + @param block A block object to be executed when a session task is completed. The block has no return value, and takes three arguments: the session, the task, and any error that occurred in the process of executing the task. + */ +- (void)setTaskDidCompleteBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, NSError * _Nullable error))block; + +/** + Sets a block to be executed when metrics are finalized related to a specific task, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didFinishCollectingMetrics:`. + + @param block A block object to be executed when a session task is completed. The block has no return value, and takes three arguments: the session, the task, and any metrics that were collected in the process of executing the task. + */ +#if AF_CAN_INCLUDE_SESSION_TASK_METRICS +- (void)setTaskDidFinishCollectingMetricsBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, NSURLSessionTaskMetrics * _Nullable metrics))block AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10)); +#endif +///------------------------------------------- +/// @name Setting Data Task Delegate Callbacks +///------------------------------------------- + +/** + Sets a block to be executed when a data task has received a response, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didReceiveResponse:completionHandler:`. + + @param block A block object to be executed when a data task has received a response. The block returns the disposition of the session response, and takes three arguments: the session, the data task, and the received response. + */ +- (void)setDataTaskDidReceiveResponseBlock:(nullable NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block; + +/** + Sets a block to be executed when a data task has become a download task, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didBecomeDownloadTask:`. + + @param block A block object to be executed when a data task has become a download task. The block has no return value, and takes three arguments: the session, the data task, and the download task it has become. + */ +- (void)setDataTaskDidBecomeDownloadTaskBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block; + +/** + Sets a block to be executed when a data task receives data, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didReceiveData:`. + + @param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the session, the data task, and the data received. This block may be called multiple times, and will execute on the session manager operation queue. + */ +- (void)setDataTaskDidReceiveDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block; + +/** + Sets a block to be executed to determine the caching behavior of a data task, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:willCacheResponse:completionHandler:`. + + @param block A block object to be executed to determine the caching behavior of a data task. The block returns the response to cache, and takes three arguments: the session, the data task, and the proposed cached URL response. + */ +- (void)setDataTaskWillCacheResponseBlock:(nullable NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block; + +/** + Sets a block to be executed once all messages enqueued for a session have been delivered, as handled by the `NSURLSessionDataDelegate` method `URLSessionDidFinishEventsForBackgroundURLSession:`. + + @param block A block object to be executed once all messages enqueued for a session have been delivered. The block has no return value and takes a single argument: the session. + */ +- (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullable void (^)(NSURLSession *session))block AF_API_UNAVAILABLE(macos); + +///----------------------------------------------- +/// @name Setting Download Task Delegate Callbacks +///----------------------------------------------- + +/** + Sets a block to be executed when a download task has completed a download, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didFinishDownloadingToURL:`. + + @param block A block object to be executed when a download task has completed. The block returns the URL the download should be moved to, and takes three arguments: the session, the download task, and the temporary location of the downloaded file. If the file manager encounters an error while attempting to move the temporary file to the destination, an `AFURLSessionDownloadTaskDidFailToMoveFileNotification` will be posted, with the download task as its object, and the user info of the error. + */ +- (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block; + +/** + Sets a block to be executed periodically to track download progress, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:`. + + @param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes five arguments: the session, the download task, the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the session manager operation queue. + */ +- (void)setDownloadTaskDidWriteDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block; + +/** + Sets a block to be executed when a download task has been resumed, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`. + + @param block A block object to be executed when a download task has been resumed. The block has no return value and takes four arguments: the session, the download task, the file offset of the resumed download, and the total number of bytes expected to be downloaded. + */ +- (void)setDownloadTaskDidResumeBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block; + +@end + +///-------------------- +/// @name Notifications +///-------------------- + +/** + Posted when a task resumes. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidResumeNotification; + +/** + Posted when a task finishes executing. Includes a userInfo dictionary with additional information about the task. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteNotification; + +/** + Posted when a task suspends its execution. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidSuspendNotification; + +/** + Posted when a session is invalidated. + */ +FOUNDATION_EXPORT NSString * const AFURLSessionDidInvalidateNotification; + +/** + Posted when a session download task finished moving the temporary download file to a specified destination successfully. + */ +FOUNDATION_EXPORT NSString * const AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification; + +/** + Posted when a session download task encountered an error when moving the temporary download file to a specified destination. + */ +FOUNDATION_EXPORT NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification; + +/** + The raw response data of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if response data exists for the task. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteResponseDataKey; + +/** + The serialized response object of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if the response was serialized. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey; + +/** + The response serializer used to serialize the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if the task has an associated response serializer. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey; + +/** + The file path associated with the download task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if an the response data has been stored directly to disk. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteAssetPathKey; + +/** + Any error associated with the task, or the serialization of the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if an error exists. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteErrorKey; + +/** + The session task metrics taken from the download task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteSessionTaskMetrics` + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteSessionTaskMetrics; + +NS_ASSUME_NONNULL_END diff --git a/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m b/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m new file mode 100644 index 0000000..c8b6810 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m @@ -0,0 +1,1274 @@ +// AFURLSessionManager.m +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import "AFURLSessionManager.h" +#import + +static dispatch_queue_t url_session_manager_processing_queue() { + static dispatch_queue_t af_url_session_manager_processing_queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT); + }); + + return af_url_session_manager_processing_queue; +} + +static dispatch_group_t url_session_manager_completion_group() { + static dispatch_group_t af_url_session_manager_completion_group; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + af_url_session_manager_completion_group = dispatch_group_create(); + }); + + return af_url_session_manager_completion_group; +} + +NSString * const AFNetworkingTaskDidResumeNotification = @"com.alamofire.networking.task.resume"; +NSString * const AFNetworkingTaskDidCompleteNotification = @"com.alamofire.networking.task.complete"; +NSString * const AFNetworkingTaskDidSuspendNotification = @"com.alamofire.networking.task.suspend"; +NSString * const AFURLSessionDidInvalidateNotification = @"com.alamofire.networking.session.invalidate"; +NSString * const AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification = @"com.alamofire.networking.session.download.file-manager-succeed"; +NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification = @"com.alamofire.networking.session.download.file-manager-error"; + +NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse"; +NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey = @"com.alamofire.networking.task.complete.responseserializer"; +NSString * const AFNetworkingTaskDidCompleteResponseDataKey = @"com.alamofire.networking.complete.finish.responsedata"; +NSString * const AFNetworkingTaskDidCompleteErrorKey = @"com.alamofire.networking.task.complete.error"; +NSString * const AFNetworkingTaskDidCompleteAssetPathKey = @"com.alamofire.networking.task.complete.assetpath"; +NSString * const AFNetworkingTaskDidCompleteSessionTaskMetrics = @"com.alamofire.networking.complete.sessiontaskmetrics"; + +static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock"; + +typedef void (^AFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error); +typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential); + +typedef NSURLRequest * (^AFURLSessionTaskWillPerformHTTPRedirectionBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request); +typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionTaskDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential); +typedef id (^AFURLSessionTaskAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, void (^completionHandler)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential)); +typedef void (^AFURLSessionDidFinishEventsForBackgroundURLSessionBlock)(NSURLSession *session); + +typedef NSInputStream * (^AFURLSessionTaskNeedNewBodyStreamBlock)(NSURLSession *session, NSURLSessionTask *task); +typedef void (^AFURLSessionTaskDidSendBodyDataBlock)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend); +typedef void (^AFURLSessionTaskDidCompleteBlock)(NSURLSession *session, NSURLSessionTask *task, NSError *error); +#if AF_CAN_INCLUDE_SESSION_TASK_METRICS +typedef void (^AFURLSessionTaskDidFinishCollectingMetricsBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLSessionTaskMetrics * metrics) AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10)); +#endif + +typedef NSURLSessionResponseDisposition (^AFURLSessionDataTaskDidReceiveResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response); +typedef void (^AFURLSessionDataTaskDidBecomeDownloadTaskBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask); +typedef void (^AFURLSessionDataTaskDidReceiveDataBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data); +typedef NSCachedURLResponse * (^AFURLSessionDataTaskWillCacheResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse); + +typedef NSURL * (^AFURLSessionDownloadTaskDidFinishDownloadingBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location); +typedef void (^AFURLSessionDownloadTaskDidWriteDataBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite); +typedef void (^AFURLSessionDownloadTaskDidResumeBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes); +typedef void (^AFURLSessionTaskProgressBlock)(NSProgress *); + +typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error); + +#pragma mark - + +@interface AFURLSessionManagerTaskDelegate : NSObject +- (instancetype)initWithTask:(NSURLSessionTask *)task; +@property (nonatomic, weak) AFURLSessionManager *manager; +@property (nonatomic, strong) NSMutableData *mutableData; +@property (nonatomic, strong) NSProgress *uploadProgress; +@property (nonatomic, strong) NSProgress *downloadProgress; +@property (nonatomic, copy) NSURL *downloadFileURL; +#if AF_CAN_INCLUDE_SESSION_TASK_METRICS +@property (nonatomic, strong) NSURLSessionTaskMetrics *sessionTaskMetrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10)); +#endif +@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; +@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock; +@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock; +@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler; +@end + +@implementation AFURLSessionManagerTaskDelegate + +- (instancetype)initWithTask:(NSURLSessionTask *)task { + self = [super init]; + if (!self) { + return nil; + } + + _mutableData = [NSMutableData data]; + _uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil]; + _downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil]; + + __weak __typeof__(task) weakTask = task; + for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ]) + { + progress.totalUnitCount = NSURLSessionTransferSizeUnknown; + progress.cancellable = YES; + progress.cancellationHandler = ^{ + [weakTask cancel]; + }; + progress.pausable = YES; + progress.pausingHandler = ^{ + [weakTask suspend]; + }; +#if AF_CAN_USE_AT_AVAILABLE + if (@available(macOS 10.11, *)) +#else + if ([progress respondsToSelector:@selector(setResumingHandler:)]) +#endif + { + progress.resumingHandler = ^{ + [weakTask resume]; + }; + } + + [progress addObserver:self + forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) + options:NSKeyValueObservingOptionNew + context:NULL]; + } + return self; +} + +- (void)dealloc { + [self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))]; + [self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))]; +} + +#pragma mark - NSProgress Tracking + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + if ([object isEqual:self.downloadProgress]) { + if (self.downloadProgressBlock) { + self.downloadProgressBlock(object); + } + } + else if ([object isEqual:self.uploadProgress]) { + if (self.uploadProgressBlock) { + self.uploadProgressBlock(object); + } + } +} + +static const void * const AuthenticationChallengeErrorKey = &AuthenticationChallengeErrorKey; + +#pragma mark - NSURLSessionTaskDelegate + +- (void)URLSession:(__unused NSURLSession *)session + task:(NSURLSessionTask *)task +didCompleteWithError:(NSError *)error +{ + error = objc_getAssociatedObject(task, AuthenticationChallengeErrorKey) ?: error; + __strong AFURLSessionManager *manager = self.manager; + + __block id responseObject = nil; + + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer; + + //Performance Improvement from #2672 + NSData *data = nil; + if (self.mutableData) { + data = [self.mutableData copy]; + //We no longer need the reference, so nil it out to gain back some memory. + self.mutableData = nil; + } + +#if AF_CAN_USE_AT_AVAILABLE && AF_CAN_INCLUDE_SESSION_TASK_METRICS + if (@available(iOS 10, macOS 10.12, watchOS 3, tvOS 10, *)) { + if (self.sessionTaskMetrics) { + userInfo[AFNetworkingTaskDidCompleteSessionTaskMetrics] = self.sessionTaskMetrics; + } + } +#endif + + if (self.downloadFileURL) { + userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL; + } else if (data) { + userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data; + } + + if (error) { + userInfo[AFNetworkingTaskDidCompleteErrorKey] = error; + + dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{ + if (self.completionHandler) { + self.completionHandler(task.response, responseObject, error); + } + + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; + }); + }); + } else { + dispatch_async(url_session_manager_processing_queue(), ^{ + NSError *serializationError = nil; + responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError]; + + if (self.downloadFileURL) { + responseObject = self.downloadFileURL; + } + + if (responseObject) { + userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject; + } + + if (serializationError) { + userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError; + } + + dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{ + if (self.completionHandler) { + self.completionHandler(task.response, responseObject, serializationError); + } + + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; + }); + }); + }); + } +} + +#if AF_CAN_INCLUDE_SESSION_TASK_METRICS +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10)) { + self.sessionTaskMetrics = metrics; +} +#endif + +#pragma mark - NSURLSessionDataDelegate + +- (void)URLSession:(__unused NSURLSession *)session + dataTask:(__unused NSURLSessionDataTask *)dataTask + didReceiveData:(NSData *)data +{ + self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive; + self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived; + + [self.mutableData appendData:data]; +} + +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task + didSendBodyData:(int64_t)bytesSent + totalBytesSent:(int64_t)totalBytesSent +totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{ + + self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend; + self.uploadProgress.completedUnitCount = task.countOfBytesSent; +} + +#pragma mark - NSURLSessionDownloadDelegate + +- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask + didWriteData:(int64_t)bytesWritten + totalBytesWritten:(int64_t)totalBytesWritten +totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{ + + self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite; + self.downloadProgress.completedUnitCount = totalBytesWritten; +} + +- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask + didResumeAtOffset:(int64_t)fileOffset +expectedTotalBytes:(int64_t)expectedTotalBytes{ + + self.downloadProgress.totalUnitCount = expectedTotalBytes; + self.downloadProgress.completedUnitCount = fileOffset; +} + +- (void)URLSession:(NSURLSession *)session + downloadTask:(NSURLSessionDownloadTask *)downloadTask +didFinishDownloadingToURL:(NSURL *)location +{ + self.downloadFileURL = nil; + + if (self.downloadTaskDidFinishDownloading) { + self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); + if (self.downloadFileURL) { + NSError *fileManagerError = nil; + + if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]) { + [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo]; + } else { + [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification object:downloadTask userInfo:nil]; + } + } + } +} + +@end + +#pragma mark - + +/** + * A workaround for issues related to key-value observing the `state` of an `NSURLSessionTask`. + * + * See: + * - https://github.com/AFNetworking/AFNetworking/issues/1477 + * - https://github.com/AFNetworking/AFNetworking/issues/2638 + * - https://github.com/AFNetworking/AFNetworking/pull/2702 + */ + +static inline void af_swizzleSelector(Class theClass, SEL originalSelector, SEL swizzledSelector) { + Method originalMethod = class_getInstanceMethod(theClass, originalSelector); + Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector); + method_exchangeImplementations(originalMethod, swizzledMethod); +} + +static inline BOOL af_addMethod(Class theClass, SEL selector, Method method) { + return class_addMethod(theClass, selector, method_getImplementation(method), method_getTypeEncoding(method)); +} + +static NSString * const AFNSURLSessionTaskDidResumeNotification = @"com.alamofire.networking.nsurlsessiontask.resume"; +static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofire.networking.nsurlsessiontask.suspend"; + +@interface _AFURLSessionTaskSwizzling : NSObject + +@end + +@implementation _AFURLSessionTaskSwizzling + ++ (void)load { + /** + WARNING: Trouble Ahead + https://github.com/AFNetworking/AFNetworking/pull/2702 + */ + + if (NSClassFromString(@"NSURLSessionTask")) { + /** + iOS 7 and iOS 8 differ in NSURLSessionTask implementation, which makes the next bit of code a bit tricky. + Many Unit Tests have been built to validate as much of this behavior has possible. + Here is what we know: + - NSURLSessionTasks are implemented with class clusters, meaning the class you request from the API isn't actually the type of class you will get back. + - Simply referencing `[NSURLSessionTask class]` will not work. You need to ask an `NSURLSession` to actually create an object, and grab the class from there. + - On iOS 7, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `__NSCFURLSessionTask`. + - On iOS 8, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `NSURLSessionTask`. + - On iOS 7, `__NSCFLocalSessionTask` and `__NSCFURLSessionTask` are the only two classes that have their own implementations of `resume` and `suspend`, and `__NSCFLocalSessionTask` DOES NOT CALL SUPER. This means both classes need to be swizzled. + - On iOS 8, `NSURLSessionTask` is the only class that implements `resume` and `suspend`. This means this is the only class that needs to be swizzled. + - Because `NSURLSessionTask` is not involved in the class hierarchy for every version of iOS, its easier to add the swizzled methods to a dummy class and manage them there. + + Some Assumptions: + - No implementations of `resume` or `suspend` call super. If this were to change in a future version of iOS, we'd need to handle it. + - No background task classes override `resume` or `suspend` + + The current solution: + 1) Grab an instance of `__NSCFLocalDataTask` by asking an instance of `NSURLSession` for a data task. + 2) Grab a pointer to the original implementation of `af_resume` + 3) Check to see if the current class has an implementation of resume. If so, continue to step 4. + 4) Grab the super class of the current class. + 5) Grab a pointer for the current class to the current implementation of `resume`. + 6) Grab a pointer for the super class to the current implementation of `resume`. + 7) If the current class implementation of `resume` is not equal to the super class implementation of `resume` AND the current implementation of `resume` is not equal to the original implementation of `af_resume`, THEN swizzle the methods + 8) Set the current class to the super class, and repeat steps 3-8 + */ + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration]; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull" + NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil]; +#pragma clang diagnostic pop + IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume))); + Class currentClass = [localDataTask class]; + + while (class_getInstanceMethod(currentClass, @selector(resume))) { + Class superClass = [currentClass superclass]; + IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume))); + IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume))); + if (classResumeIMP != superclassResumeIMP && + originalAFResumeIMP != classResumeIMP) { + [self swizzleResumeAndSuspendMethodForClass:currentClass]; + } + currentClass = [currentClass superclass]; + } + + [localDataTask cancel]; + [session finishTasksAndInvalidate]; + } +} + ++ (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass { + Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume)); + Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend)); + + if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) { + af_swizzleSelector(theClass, @selector(resume), @selector(af_resume)); + } + + if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) { + af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend)); + } +} + +- (NSURLSessionTaskState)state { + NSAssert(NO, @"State method should never be called in the actual dummy class"); + return NSURLSessionTaskStateCanceling; +} + +- (void)af_resume { + NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); + NSURLSessionTaskState state = [self state]; + [self af_resume]; + + if (state != NSURLSessionTaskStateRunning) { + [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self]; + } +} + +- (void)af_suspend { + NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); + NSURLSessionTaskState state = [self state]; + [self af_suspend]; + + if (state != NSURLSessionTaskStateSuspended) { + [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self]; + } +} +@end + +#pragma mark - + +@interface AFURLSessionManager () +@property (readwrite, nonatomic, strong) NSURLSessionConfiguration *sessionConfiguration; +@property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue; +@property (readwrite, nonatomic, strong) NSURLSession *session; +@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier; +@property (readonly, nonatomic, copy) NSString *taskDescriptionForSessionTasks; +@property (readwrite, nonatomic, strong) NSLock *lock; +@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid; +@property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge; +@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession AF_API_UNAVAILABLE(macos); +@property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection; +@property (readwrite, nonatomic, copy) AFURLSessionTaskAuthenticationChallengeBlock authenticationChallengeHandler; +@property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream; +@property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData; +@property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete; +#if AF_CAN_INCLUDE_SESSION_TASK_METRICS +@property (readwrite, nonatomic, copy) AFURLSessionTaskDidFinishCollectingMetricsBlock taskDidFinishCollectingMetrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10)); +#endif +@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse; +@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask; +@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData; +@property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse; +@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; +@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData; +@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume; +@end + +@implementation AFURLSessionManager + +- (instancetype)init { + return [self initWithSessionConfiguration:nil]; +} + +- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration { + self = [super init]; + if (!self) { + return nil; + } + + if (!configuration) { + configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; + } + + self.sessionConfiguration = configuration; + + self.operationQueue = [[NSOperationQueue alloc] init]; + self.operationQueue.maxConcurrentOperationCount = 1; + + self.responseSerializer = [AFJSONResponseSerializer serializer]; + + self.securityPolicy = [AFSecurityPolicy defaultPolicy]; + +#if !TARGET_OS_WATCH + self.reachabilityManager = [AFNetworkReachabilityManager sharedManager]; +#endif + + self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init]; + + self.lock = [[NSLock alloc] init]; + self.lock.name = AFURLSessionManagerLockName; + + [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { + for (NSURLSessionDataTask *task in dataTasks) { + [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil]; + } + + for (NSURLSessionUploadTask *uploadTask in uploadTasks) { + [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil]; + } + + for (NSURLSessionDownloadTask *downloadTask in downloadTasks) { + [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil]; + } + }]; + + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +#pragma mark - + +- (NSURLSession *)session { + + @synchronized (self) { + if (!_session) { + _session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]; + } + } + return _session; +} + +#pragma mark - + + +- (NSString *)taskDescriptionForSessionTasks { + return [NSString stringWithFormat:@"%p", self]; +} + +- (void)taskDidResume:(NSNotification *)notification { + NSURLSessionTask *task = notification.object; + if ([task respondsToSelector:@selector(taskDescription)]) { + if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task]; + }); + } + } +} + +- (void)taskDidSuspend:(NSNotification *)notification { + NSURLSessionTask *task = notification.object; + if ([task respondsToSelector:@selector(taskDescription)]) { + if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task]; + }); + } + } +} + +#pragma mark - + +- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task { + NSParameterAssert(task); + + AFURLSessionManagerTaskDelegate *delegate = nil; + [self.lock lock]; + delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)]; + [self.lock unlock]; + + return delegate; +} + +- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate + forTask:(NSURLSessionTask *)task +{ + NSParameterAssert(task); + NSParameterAssert(delegate); + + [self.lock lock]; + self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate; + [self addNotificationObserverForTask:task]; + [self.lock unlock]; +} + +- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask + uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock + downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask]; + delegate.manager = self; + delegate.completionHandler = completionHandler; + + dataTask.taskDescription = self.taskDescriptionForSessionTasks; + [self setDelegate:delegate forTask:dataTask]; + + delegate.uploadProgressBlock = uploadProgressBlock; + delegate.downloadProgressBlock = downloadProgressBlock; +} + +- (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask + progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:uploadTask]; + delegate.manager = self; + delegate.completionHandler = completionHandler; + + uploadTask.taskDescription = self.taskDescriptionForSessionTasks; + + [self setDelegate:delegate forTask:uploadTask]; + + delegate.uploadProgressBlock = uploadProgressBlock; +} + +- (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask + progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock + destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination + completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler +{ + AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask]; + delegate.manager = self; + delegate.completionHandler = completionHandler; + + if (destination) { + delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) { + return destination(location, task.response); + }; + } + + downloadTask.taskDescription = self.taskDescriptionForSessionTasks; + + [self setDelegate:delegate forTask:downloadTask]; + + delegate.downloadProgressBlock = downloadProgressBlock; +} + +- (void)removeDelegateForTask:(NSURLSessionTask *)task { + NSParameterAssert(task); + + [self.lock lock]; + [self removeNotificationObserverForTask:task]; + [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)]; + [self.lock unlock]; +} + +#pragma mark - + +- (NSArray *)tasksForKeyPath:(NSString *)keyPath { + __block NSArray *tasks = nil; + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { + if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) { + tasks = dataTasks; + } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) { + tasks = uploadTasks; + } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) { + tasks = downloadTasks; + } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) { + tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"]; + } + + dispatch_semaphore_signal(semaphore); + }]; + + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + + return tasks; +} + +- (NSArray *)tasks { + return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; +} + +- (NSArray *)dataTasks { + return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; +} + +- (NSArray *)uploadTasks { + return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; +} + +- (NSArray *)downloadTasks { + return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; +} + +#pragma mark - + +- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks resetSession:(BOOL)resetSession { + if (cancelPendingTasks) { + [self.session invalidateAndCancel]; + } else { + [self.session finishTasksAndInvalidate]; + } + if (resetSession) { + self.session = nil; + } +} + +#pragma mark - + +- (void)setResponseSerializer:(id )responseSerializer { + NSParameterAssert(responseSerializer); + + _responseSerializer = responseSerializer; +} + +#pragma mark - +- (void)addNotificationObserverForTask:(NSURLSessionTask *)task { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task]; +} + +- (void)removeNotificationObserverForTask:(NSURLSessionTask *)task { + [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidSuspendNotification object:task]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidResumeNotification object:task]; +} + +#pragma mark - + +- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request + uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock + downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock + completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler { + + NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request]; + + [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler]; + + return dataTask; +} + +#pragma mark - + +- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request + fromFile:(NSURL *)fileURL + progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; + + if (uploadTask) { + [self addDelegateForUploadTask:uploadTask + progress:uploadProgressBlock + completionHandler:completionHandler]; + } + + return uploadTask; +} + +- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request + fromData:(NSData *)bodyData + progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData]; + + [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler]; + + return uploadTask; +} + +- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request + progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithStreamedRequest:request]; + + [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler]; + + return uploadTask; +} + +#pragma mark - + +- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request + progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock + destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination + completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler +{ + NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request]; + + [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler]; + + return downloadTask; +} + +- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData + progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock + destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination + completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler +{ + NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithResumeData:resumeData]; + + [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler]; + + return downloadTask; +} + +#pragma mark - +- (NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task { + return [[self delegateForTask:task] uploadProgress]; +} + +- (NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task { + return [[self delegateForTask:task] downloadProgress]; +} + +#pragma mark - + +- (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block { + self.sessionDidBecomeInvalid = block; +} + +- (void)setSessionDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block { + self.sessionDidReceiveAuthenticationChallenge = block; +} + +#if !TARGET_OS_OSX +- (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block { + self.didFinishEventsForBackgroundURLSession = block; +} +#endif + +#pragma mark - + +- (void)setTaskNeedNewBodyStreamBlock:(NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block { + self.taskNeedNewBodyStream = block; +} + +- (void)setTaskWillPerformHTTPRedirectionBlock:(NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block { + self.taskWillPerformHTTPRedirection = block; +} + +- (void)setTaskDidSendBodyDataBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block { + self.taskDidSendBodyData = block; +} + +- (void)setTaskDidCompleteBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, NSError *error))block { + self.taskDidComplete = block; +} + +#if AF_CAN_INCLUDE_SESSION_TASK_METRICS +- (void)setTaskDidFinishCollectingMetricsBlock:(void (^)(NSURLSession * _Nonnull, NSURLSessionTask * _Nonnull, NSURLSessionTaskMetrics * _Nullable))block AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10)) { + self.taskDidFinishCollectingMetrics = block; +} +#endif + +#pragma mark - + +- (void)setDataTaskDidReceiveResponseBlock:(NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block { + self.dataTaskDidReceiveResponse = block; +} + +- (void)setDataTaskDidBecomeDownloadTaskBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block { + self.dataTaskDidBecomeDownloadTask = block; +} + +- (void)setDataTaskDidReceiveDataBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block { + self.dataTaskDidReceiveData = block; +} + +- (void)setDataTaskWillCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block { + self.dataTaskWillCacheResponse = block; +} + +#pragma mark - + +- (void)setDownloadTaskDidFinishDownloadingBlock:(NSURL * (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block { + self.downloadTaskDidFinishDownloading = block; +} + +- (void)setDownloadTaskDidWriteDataBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block { + self.downloadTaskDidWriteData = block; +} + +- (void)setDownloadTaskDidResumeBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block { + self.downloadTaskDidResume = block; +} + +#pragma mark - NSObject + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, self.session, self.operationQueue]; +} + +- (BOOL)respondsToSelector:(SEL)selector { + if (selector == @selector(URLSession:didReceiveChallenge:completionHandler:)) { + return self.sessionDidReceiveAuthenticationChallenge != nil; + } else if (selector == @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)) { + return self.taskWillPerformHTTPRedirection != nil; + } else if (selector == @selector(URLSession:dataTask:didReceiveResponse:completionHandler:)) { + return self.dataTaskDidReceiveResponse != nil; + } else if (selector == @selector(URLSession:dataTask:willCacheResponse:completionHandler:)) { + return self.dataTaskWillCacheResponse != nil; + } +#if !TARGET_OS_OSX + else if (selector == @selector(URLSessionDidFinishEventsForBackgroundURLSession:)) { + return self.didFinishEventsForBackgroundURLSession != nil; + } +#endif + + return [[self class] instancesRespondToSelector:selector]; +} + +#pragma mark - NSURLSessionDelegate + +- (void)URLSession:(NSURLSession *)session +didBecomeInvalidWithError:(NSError *)error +{ + if (self.sessionDidBecomeInvalid) { + self.sessionDidBecomeInvalid(session, error); + } + + [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session]; +} + +- (void)URLSession:(NSURLSession *)session +didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler +{ + NSAssert(self.sessionDidReceiveAuthenticationChallenge != nil, @"`respondsToSelector:` implementation forces `URLSession:didReceiveChallenge:completionHandler:` to be called only if `self.sessionDidReceiveAuthenticationChallenge` is not nil"); + + NSURLCredential *credential = nil; + NSURLSessionAuthChallengeDisposition disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential); + + if (completionHandler) { + completionHandler(disposition, credential); + } +} + +#pragma mark - NSURLSessionTaskDelegate + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +willPerformHTTPRedirection:(NSHTTPURLResponse *)response + newRequest:(NSURLRequest *)request + completionHandler:(void (^)(NSURLRequest *))completionHandler +{ + NSURLRequest *redirectRequest = request; + + if (self.taskWillPerformHTTPRedirection) { + redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request); + } + + if (completionHandler) { + completionHandler(redirectRequest); + } +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler +{ + BOOL evaluateServerTrust = NO; + NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; + NSURLCredential *credential = nil; + + if (self.authenticationChallengeHandler) { + id result = self.authenticationChallengeHandler(session, task, challenge, completionHandler); + if (result == nil) { + return; + } else if ([result isKindOfClass:NSError.class]) { + objc_setAssociatedObject(task, AuthenticationChallengeErrorKey, result, OBJC_ASSOCIATION_RETAIN); + disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; + } else if ([result isKindOfClass:NSURLCredential.class]) { + credential = result; + disposition = NSURLSessionAuthChallengeUseCredential; + } else if ([result isKindOfClass:NSNumber.class]) { + disposition = [result integerValue]; + NSAssert(disposition == NSURLSessionAuthChallengePerformDefaultHandling || disposition == NSURLSessionAuthChallengeCancelAuthenticationChallenge || disposition == NSURLSessionAuthChallengeRejectProtectionSpace, @""); + evaluateServerTrust = disposition == NSURLSessionAuthChallengePerformDefaultHandling && [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]; + } else { + @throw [NSException exceptionWithName:@"Invalid Return Value" reason:@"The return value from the authentication challenge handler must be nil, an NSError, an NSURLCredential or an NSNumber." userInfo:nil]; + } + } else { + evaluateServerTrust = [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]; + } + + if (evaluateServerTrust) { + if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { + disposition = NSURLSessionAuthChallengeUseCredential; + credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; + } else { + objc_setAssociatedObject(task, AuthenticationChallengeErrorKey, + [self serverTrustErrorForServerTrust:challenge.protectionSpace.serverTrust url:task.currentRequest.URL], + OBJC_ASSOCIATION_RETAIN); + disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; + } + } + + if (completionHandler) { + completionHandler(disposition, credential); + } +} + +- (nonnull NSError *)serverTrustErrorForServerTrust:(nullable SecTrustRef)serverTrust url:(nullable NSURL *)url +{ + NSBundle *CFNetworkBundle = [NSBundle bundleWithIdentifier:@"com.apple.CFNetwork"]; + NSString *defaultValue = @"The certificate for this server is invalid. You might be connecting to a server that is pretending to be “%@” which could put your confidential information at risk."; + NSString *descriptionFormat = NSLocalizedStringWithDefaultValue(@"Err-1202.w", nil, CFNetworkBundle, defaultValue, @"") ?: defaultValue; + NSString *localizedDescription = [descriptionFormat componentsSeparatedByString:@"%@"].count <= 2 ? [NSString localizedStringWithFormat:descriptionFormat, url.host] : descriptionFormat; + NSMutableDictionary *userInfo = [@{ + NSLocalizedDescriptionKey: localizedDescription + } mutableCopy]; + + if (serverTrust) { + userInfo[NSURLErrorFailingURLPeerTrustErrorKey] = (__bridge id)serverTrust; + } + + if (url) { + userInfo[NSURLErrorFailingURLErrorKey] = url; + + if (url.absoluteString) { + userInfo[NSURLErrorFailingURLStringErrorKey] = url.absoluteString; + } + } + + return [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorServerCertificateUntrusted userInfo:userInfo]; +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task + needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler +{ + NSInputStream *inputStream = nil; + + if (self.taskNeedNewBodyStream) { + inputStream = self.taskNeedNewBodyStream(session, task); + } else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) { + inputStream = [task.originalRequest.HTTPBodyStream copy]; + } + + if (completionHandler) { + completionHandler(inputStream); + } +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task + didSendBodyData:(int64_t)bytesSent + totalBytesSent:(int64_t)totalBytesSent +totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend +{ + + int64_t totalUnitCount = totalBytesExpectedToSend; + if (totalUnitCount == NSURLSessionTransferSizeUnknown) { + NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"]; + if (contentLength) { + totalUnitCount = (int64_t) [contentLength longLongValue]; + } + } + + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; + + if (delegate) { + [delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend]; + } + + if (self.taskDidSendBodyData) { + self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount); + } +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +didCompleteWithError:(NSError *)error +{ + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; + + // delegate may be nil when completing a task in the background + if (delegate) { + [delegate URLSession:session task:task didCompleteWithError:error]; + + [self removeDelegateForTask:task]; + } + + if (self.taskDidComplete) { + self.taskDidComplete(session, task, error); + } +} + +#if AF_CAN_INCLUDE_SESSION_TASK_METRICS +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10)) +{ + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; + // Metrics may fire after URLSession:task:didCompleteWithError: is called, delegate may be nil + if (delegate) { + [delegate URLSession:session task:task didFinishCollectingMetrics:metrics]; + } + + if (self.taskDidFinishCollectingMetrics) { + self.taskDidFinishCollectingMetrics(session, task, metrics); + } +} +#endif + +#pragma mark - NSURLSessionDataDelegate + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask +didReceiveResponse:(NSURLResponse *)response + completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler +{ + NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow; + + if (self.dataTaskDidReceiveResponse) { + disposition = self.dataTaskDidReceiveResponse(session, dataTask, response); + } + + if (completionHandler) { + completionHandler(disposition); + } +} + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask +didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask +{ + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask]; + if (delegate) { + [self removeDelegateForTask:dataTask]; + [self setDelegate:delegate forTask:downloadTask]; + } + + if (self.dataTaskDidBecomeDownloadTask) { + self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask); + } +} + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask + didReceiveData:(NSData *)data +{ + + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask]; + [delegate URLSession:session dataTask:dataTask didReceiveData:data]; + + if (self.dataTaskDidReceiveData) { + self.dataTaskDidReceiveData(session, dataTask, data); + } +} + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask + willCacheResponse:(NSCachedURLResponse *)proposedResponse + completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler +{ + NSCachedURLResponse *cachedResponse = proposedResponse; + + if (self.dataTaskWillCacheResponse) { + cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse); + } + + if (completionHandler) { + completionHandler(cachedResponse); + } +} + +#if !TARGET_OS_OSX +- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session { + if (self.didFinishEventsForBackgroundURLSession) { + dispatch_async(dispatch_get_main_queue(), ^{ + self.didFinishEventsForBackgroundURLSession(session); + }); + } +} +#endif + +#pragma mark - NSURLSessionDownloadDelegate + +- (void)URLSession:(NSURLSession *)session + downloadTask:(NSURLSessionDownloadTask *)downloadTask +didFinishDownloadingToURL:(NSURL *)location +{ + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; + if (self.downloadTaskDidFinishDownloading) { + NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); + if (fileURL) { + delegate.downloadFileURL = fileURL; + NSError *error = nil; + + if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) { + [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo]; + } else { + [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification object:downloadTask userInfo:nil]; + } + + return; + } + } + + if (delegate) { + [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location]; + } +} + +- (void)URLSession:(NSURLSession *)session + downloadTask:(NSURLSessionDownloadTask *)downloadTask + didWriteData:(int64_t)bytesWritten + totalBytesWritten:(int64_t)totalBytesWritten +totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite +{ + + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; + + if (delegate) { + [delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite]; + } + + if (self.downloadTaskDidWriteData) { + self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); + } +} + +- (void)URLSession:(NSURLSession *)session + downloadTask:(NSURLSessionDownloadTask *)downloadTask + didResumeAtOffset:(int64_t)fileOffset +expectedTotalBytes:(int64_t)expectedTotalBytes +{ + + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; + + if (delegate) { + [delegate URLSession:session downloadTask:downloadTask didResumeAtOffset:fileOffset expectedTotalBytes:expectedTotalBytes]; + } + + if (self.downloadTaskDidResume) { + self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes); + } +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"]; + + self = [self initWithSessionConfiguration:configuration]; + if (!self) { + return nil; + } + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"]; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + return [[[self class] allocWithZone:zone] initWithSessionConfiguration:self.session.configuration]; +} + +@end diff --git a/Pods/AFNetworking/LICENSE b/Pods/AFNetworking/LICENSE new file mode 100644 index 0000000..f611f42 --- /dev/null +++ b/Pods/AFNetworking/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011-2020 Alamofire Software Foundation (http://alamofire.org/) + +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. diff --git a/Pods/AFNetworking/README.md b/Pods/AFNetworking/README.md new file mode 100644 index 0000000..d193dfe --- /dev/null +++ b/Pods/AFNetworking/README.md @@ -0,0 +1,298 @@ +

+ AFNetworking +

+ +[![Build Status](https://github.com/AFNetworking/AFNetworking/workflows/AFNetworking%20CI/badge.svg?branch=master)](https://github.com/AFNetworking/AFNetworking/actions) +[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/AFNetworking.svg)](https://img.shields.io/cocoapods/v/AFNetworking.svg) +[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![Platform](https://img.shields.io/cocoapods/p/AFNetworking.svg?style=flat)](http://cocoadocs.org/docsets/AFNetworking) +[![Twitter](https://img.shields.io/badge/twitter-@AFNetworking-blue.svg?style=flat)](http://twitter.com/AFNetworking) + +AFNetworking is a delightful networking library for iOS, macOS, watchOS, and tvOS. It's built on top of the [Foundation URL Loading System](https://developer.apple.com/documentation/foundation/url_loading_system), extending the powerful high-level networking abstractions built into Cocoa. It has a modular architecture with well-designed, feature-rich APIs that are a joy to use. + +Perhaps the most important feature of all, however, is the amazing community of developers who use and contribute to AFNetworking every day. AFNetworking powers some of the most popular and critically-acclaimed apps on the iPhone, iPad, and Mac. + +## How To Get Started + +- [Download AFNetworking](https://github.com/AFNetworking/AFNetworking/archive/master.zip) and try out the included Mac and iPhone example apps +- Read the ["Getting Started" guide](https://github.com/AFNetworking/AFNetworking/wiki/Getting-Started-with-AFNetworking), [FAQ](https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-FAQ), or [other articles on the Wiki](https://github.com/AFNetworking/AFNetworking/wiki) + +## Communication + +- If you **need help**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/afnetworking). (Tag 'afnetworking') +- If you'd like to **ask a general question**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/afnetworking). +- If you **found a bug**, _and can provide steps to reliably reproduce it_, open an issue. +- If you **have a feature request**, open an issue. +- If you **want to contribute**, submit a pull request. + +## Installation +AFNetworking supports multiple methods for installing the library in a project. + +## Installation with CocoaPods + +To integrate AFNetworking into your Xcode project using CocoaPods, specify it in your `Podfile`: + +```ruby +pod 'AFNetworking', '~> 4.0' +``` + +### Installation with Swift Package Manager + +Once you have your Swift package set up, adding AFNetworking as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`. + +```swift +dependencies: [ + .package(url: "https://github.com/AFNetworking/AFNetworking.git", .upToNextMajor(from: "4.0.0")) +] +``` + +> Note: AFNetworking's Swift package does not include it's UIKit extensions. + +### Installation with Carthage + +[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. To integrate AFNetworking, add the following to your `Cartfile`. + +```ogdl +github "AFNetworking/AFNetworking" ~> 4.0 +``` + +## Requirements + +| AFNetworking Version | Minimum iOS Target | Minimum macOS Target | Minimum watchOS Target | Minimum tvOS Target | Notes | +|:--------------------:|:---------------------------:|:----------------------------:|:----------------------------:|:----------------------------:|:-------------------------------------------------------------------------:| +| 4.x | iOS 9 | macOS 10.10 | watchOS 2.0 | tvOS 9.0 | Xcode 11+ is required. | +| 3.x | iOS 7 | OS X 10.9 | watchOS 2.0 | tvOS 9.0 | Xcode 7+ is required. `NSURLConnectionOperation` support has been removed. | +| 2.6 -> 2.6.3 | iOS 7 | OS X 10.9 | watchOS 2.0 | n/a | Xcode 7+ is required. | +| 2.0 -> 2.5.4 | iOS 6 | OS X 10.8 | n/a | n/a | Xcode 5+ is required. `NSURLSession` subspec requires iOS 7 or OS X 10.9. | +| 1.x | iOS 5 | Mac OS X 10.7 | n/a | n/a | +| 0.10.x | iOS 4 | Mac OS X 10.6 | n/a | n/a | + +(macOS projects must support [64-bit with modern Cocoa runtime](https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtVersionsPlatforms.html)). + +> Programming in Swift? Try [Alamofire](https://github.com/Alamofire/Alamofire) for a more conventional set of APIs. + +## Architecture + +### NSURLSession + +- `AFURLSessionManager` +- `AFHTTPSessionManager` + +### Serialization + +* `` + - `AFHTTPRequestSerializer` + - `AFJSONRequestSerializer` + - `AFPropertyListRequestSerializer` +* `` + - `AFHTTPResponseSerializer` + - `AFJSONResponseSerializer` + - `AFXMLParserResponseSerializer` + - `AFXMLDocumentResponseSerializer` _(macOS)_ + - `AFPropertyListResponseSerializer` + - `AFImageResponseSerializer` + - `AFCompoundResponseSerializer` + +### Additional Functionality + +- `AFSecurityPolicy` +- `AFNetworkReachabilityManager` + +## Usage + +### AFURLSessionManager + +`AFURLSessionManager` creates and manages an `NSURLSession` object based on a specified `NSURLSessionConfiguration` object, which conforms to ``, ``, ``, and ``. + +#### Creating a Download Task + +```objective-c +NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; +AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; + +NSURL *URL = [NSURL URLWithString:@"http://example.com/download.zip"]; +NSURLRequest *request = [NSURLRequest requestWithURL:URL]; + +NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) { + NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil]; + return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]]; +} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) { + NSLog(@"File downloaded to: %@", filePath); +}]; +[downloadTask resume]; +``` + +#### Creating an Upload Task + +```objective-c +NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; +AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; + +NSURL *URL = [NSURL URLWithString:@"http://example.com/upload"]; +NSURLRequest *request = [NSURLRequest requestWithURL:URL]; + +NSURL *filePath = [NSURL fileURLWithPath:@"file://path/to/image.png"]; +NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:request fromFile:filePath progress:nil completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) { + if (error) { + NSLog(@"Error: %@", error); + } else { + NSLog(@"Success: %@ %@", response, responseObject); + } +}]; +[uploadTask resume]; +``` + +#### Creating an Upload Task for a Multi-Part Request, with Progress + +```objective-c +NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id formData) { + [formData appendPartWithFileURL:[NSURL fileURLWithPath:@"file://path/to/image.jpg"] name:@"file" fileName:@"filename.jpg" mimeType:@"image/jpeg" error:nil]; + } error:nil]; + +AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; + +NSURLSessionUploadTask *uploadTask; +uploadTask = [manager + uploadTaskWithStreamedRequest:request + progress:^(NSProgress * _Nonnull uploadProgress) { + // This is not called back on the main queue. + // You are responsible for dispatching to the main queue for UI updates + dispatch_async(dispatch_get_main_queue(), ^{ + //Update the progress view + [progressView setProgress:uploadProgress.fractionCompleted]; + }); + } + completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) { + if (error) { + NSLog(@"Error: %@", error); + } else { + NSLog(@"%@ %@", response, responseObject); + } + }]; + +[uploadTask resume]; +``` + +#### Creating a Data Task + +```objective-c +NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; +AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; + +NSURL *URL = [NSURL URLWithString:@"http://httpbin.org/get"]; +NSURLRequest *request = [NSURLRequest requestWithURL:URL]; + +NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) { + if (error) { + NSLog(@"Error: %@", error); + } else { + NSLog(@"%@ %@", response, responseObject); + } +}]; +[dataTask resume]; +``` + +--- + +### Request Serialization + +Request serializers create requests from URL strings, encoding parameters as either a query string or HTTP body. + +```objective-c +NSString *URLString = @"http://example.com"; +NSDictionary *parameters = @{@"foo": @"bar", @"baz": @[@1, @2, @3]}; +``` + +#### Query String Parameter Encoding + +```objective-c +[[AFHTTPRequestSerializer serializer] requestWithMethod:@"GET" URLString:URLString parameters:parameters error:nil]; +``` + + GET http://example.com?foo=bar&baz[]=1&baz[]=2&baz[]=3 + +#### URL Form Parameter Encoding + +```objective-c +[[AFHTTPRequestSerializer serializer] requestWithMethod:@"POST" URLString:URLString parameters:parameters error:nil]; +``` + + POST http://example.com/ + Content-Type: application/x-www-form-urlencoded + + foo=bar&baz[]=1&baz[]=2&baz[]=3 + +#### JSON Parameter Encoding + +```objective-c +[[AFJSONRequestSerializer serializer] requestWithMethod:@"POST" URLString:URLString parameters:parameters error:nil]; +``` + + POST http://example.com/ + Content-Type: application/json + + {"foo": "bar", "baz": [1,2,3]} + +--- + +### Network Reachability Manager + +`AFNetworkReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces. + +* Do not use Reachability to determine if the original request should be sent. + * You should try to send it. +* You can use Reachability to determine when a request should be automatically retried. + * Although it may still fail, a Reachability notification that the connectivity is available is a good time to retry something. +* Network reachability is a useful tool for determining why a request might have failed. + * After a network request has failed, telling the user they're offline is better than giving them a more technical but accurate error, such as "request timed out." + +See also [WWDC 2012 session 706, "Networking Best Practices."](https://developer.apple.com/videos/play/wwdc2012-706/). + +#### Shared Network Reachability + +```objective-c +[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) { + NSLog(@"Reachability: %@", AFStringFromNetworkReachabilityStatus(status)); +}]; + +[[AFNetworkReachabilityManager sharedManager] startMonitoring]; +``` + +--- + +### Security Policy + +`AFSecurityPolicy` evaluates server trust against pinned X.509 certificates and public keys over secure connections. + +Adding pinned SSL certificates to your app helps prevent man-in-the-middle attacks and other vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged to route all communication over an HTTPS connection with SSL pinning configured and enabled. + +#### Allowing Invalid SSL Certificates + +```objective-c +AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; +manager.securityPolicy.allowInvalidCertificates = YES; // not recommended for production +``` + +--- + +## Unit Tests + +AFNetworking includes a suite of unit tests within the Tests subdirectory. These tests can be run simply be executed the test action on the platform framework you would like to test. + +## Credits + +AFNetworking is owned and maintained by the [Alamofire Software Foundation](http://alamofire.org). + +AFNetworking was originally created by [Scott Raymond](https://github.com/sco/) and [Mattt Thompson](https://github.com/mattt/) in the development of [Gowalla for iPhone](http://en.wikipedia.org/wiki/Gowalla). + +AFNetworking's logo was designed by [Alan Defibaugh](http://www.alandefibaugh.com/). + +And most of all, thanks to AFNetworking's [growing list of contributors](https://github.com/AFNetworking/AFNetworking/contributors). + +### Security Disclosure + +If you believe you have identified a security vulnerability with AFNetworking, you should report it as soon as possible via email to security@alamofire.org. Please do not post it to a public issue tracker. + +## License + +AFNetworking is released under the MIT license. See [LICENSE](https://github.com/AFNetworking/AFNetworking/blob/master/LICENSE) for details. diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.h b/Pods/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.h new file mode 100644 index 0000000..1a27bb4 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.h @@ -0,0 +1,160 @@ +// AFAutoPurgingImageCache.h +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import +#import + +#if TARGET_OS_IOS || TARGET_OS_TV +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + The `AFImageCache` protocol defines a set of APIs for adding, removing and fetching images from a cache synchronously. + */ +@protocol AFImageCache + +/** + Adds the image to the cache with the given identifier. + + @param image The image to cache. + @param identifier The unique identifier for the image in the cache. + */ +- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier; + +/** + Removes the image from the cache matching the given identifier. + + @param identifier The unique identifier for the image in the cache. + + @return A BOOL indicating whether or not the image was removed from the cache. + */ +- (BOOL)removeImageWithIdentifier:(NSString *)identifier; + +/** + Removes all images from the cache. + + @return A BOOL indicating whether or not all images were removed from the cache. + */ +- (BOOL)removeAllImages; + +/** + Returns the image in the cache associated with the given identifier. + + @param identifier The unique identifier for the image in the cache. + + @return An image for the matching identifier, or nil. + */ +- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier; +@end + + +/** + The `ImageRequestCache` protocol extends the `ImageCache` protocol by adding methods for adding, removing and fetching images from a cache given an `NSURLRequest` and additional identifier. + */ +@protocol AFImageRequestCache + +/** + Asks if the image should be cached using an identifier created from the request and additional identifier. + + @param image The image to be cached. + @param request The unique URL request identifing the image asset. + @param identifier The additional identifier to apply to the URL request to identify the image. + + @return A BOOL indicating whether or not the image should be added to the cache. YES will cache, NO will prevent caching. + */ +- (BOOL)shouldCacheImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier; + +/** + Adds the image to the cache using an identifier created from the request and additional identifier. + + @param image The image to cache. + @param request The unique URL request identifing the image asset. + @param identifier The additional identifier to apply to the URL request to identify the image. + */ +- (void)addImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier; + +/** + Removes the image from the cache using an identifier created from the request and additional identifier. + + @param request The unique URL request identifing the image asset. + @param identifier The additional identifier to apply to the URL request to identify the image. + + @return A BOOL indicating whether or not all images were removed from the cache. + */ +- (BOOL)removeImageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier; + +/** + Returns the image from the cache associated with an identifier created from the request and additional identifier. + + @param request The unique URL request identifing the image asset. + @param identifier The additional identifier to apply to the URL request to identify the image. + + @return An image for the matching request and identifier, or nil. + */ +- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier; + +@end + +/** + The `AutoPurgingImageCache` in an in-memory image cache used to store images up to a given memory capacity. When the memory capacity is reached, the image cache is sorted by last access date, then the oldest image is continuously purged until the preferred memory usage after purge is met. Each time an image is accessed through the cache, the internal access date of the image is updated. + */ +@interface AFAutoPurgingImageCache : NSObject + +/** + The total memory capacity of the cache in bytes. + */ +@property (nonatomic, assign) UInt64 memoryCapacity; + +/** + The preferred memory usage after purge in bytes. During a purge, images will be purged until the memory capacity drops below this limit. + */ +@property (nonatomic, assign) UInt64 preferredMemoryUsageAfterPurge; + +/** + The current total memory usage in bytes of all images stored within the cache. + */ +@property (nonatomic, assign, readonly) UInt64 memoryUsage; + +/** + Initialies the `AutoPurgingImageCache` instance with default values for memory capacity and preferred memory usage after purge limit. `memoryCapcity` defaults to `100 MB`. `preferredMemoryUsageAfterPurge` defaults to `60 MB`. + + @return The new `AutoPurgingImageCache` instance. + */ +- (instancetype)init; + +/** + Initialies the `AutoPurgingImageCache` instance with the given memory capacity and preferred memory usage + after purge limit. + + @param memoryCapacity The total memory capacity of the cache in bytes. + @param preferredMemoryCapacity The preferred memory usage after purge in bytes. + + @return The new `AutoPurgingImageCache` instance. + */ +- (instancetype)initWithMemoryCapacity:(UInt64)memoryCapacity preferredMemoryCapacity:(UInt64)preferredMemoryCapacity; + +@end + +NS_ASSUME_NONNULL_END + +#endif + diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.m b/Pods/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.m new file mode 100644 index 0000000..a09e87c --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.m @@ -0,0 +1,205 @@ +// AFAutoPurgingImageCache.m +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import + +#if TARGET_OS_IOS || TARGET_OS_TV + +#import "AFAutoPurgingImageCache.h" + +@interface AFCachedImage : NSObject + +@property (nonatomic, strong) UIImage *image; +@property (nonatomic, copy) NSString *identifier; +@property (nonatomic, assign) UInt64 totalBytes; +@property (nonatomic, strong) NSDate *lastAccessDate; +@property (nonatomic, assign) UInt64 currentMemoryUsage; + +@end + +@implementation AFCachedImage + +- (instancetype)initWithImage:(UIImage *)image identifier:(NSString *)identifier { + if (self = [self init]) { + self.image = image; + self.identifier = identifier; + + CGSize imageSize = CGSizeMake(image.size.width * image.scale, image.size.height * image.scale); + CGFloat bytesPerPixel = 4.0; + CGFloat bytesPerSize = imageSize.width * imageSize.height; + self.totalBytes = (UInt64)bytesPerPixel * (UInt64)bytesPerSize; + self.lastAccessDate = [NSDate date]; + } + return self; +} + +- (UIImage *)accessImage { + self.lastAccessDate = [NSDate date]; + return self.image; +} + +- (NSString *)description { + NSString *descriptionString = [NSString stringWithFormat:@"Idenfitier: %@ lastAccessDate: %@ ", self.identifier, self.lastAccessDate]; + return descriptionString; + +} + +@end + +@interface AFAutoPurgingImageCache () +@property (nonatomic, strong) NSMutableDictionary *cachedImages; +@property (nonatomic, assign) UInt64 currentMemoryUsage; +@property (nonatomic, strong) dispatch_queue_t synchronizationQueue; +@end + +@implementation AFAutoPurgingImageCache + +- (instancetype)init { + return [self initWithMemoryCapacity:100 * 1024 * 1024 preferredMemoryCapacity:60 * 1024 * 1024]; +} + +- (instancetype)initWithMemoryCapacity:(UInt64)memoryCapacity preferredMemoryCapacity:(UInt64)preferredMemoryCapacity { + if (self = [super init]) { + self.memoryCapacity = memoryCapacity; + self.preferredMemoryUsageAfterPurge = preferredMemoryCapacity; + self.cachedImages = [[NSMutableDictionary alloc] init]; + + NSString *queueName = [NSString stringWithFormat:@"com.alamofire.autopurgingimagecache-%@", [[NSUUID UUID] UUIDString]]; + self.synchronizationQueue = dispatch_queue_create([queueName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT); + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(removeAllImages) + name:UIApplicationDidReceiveMemoryWarningNotification + object:nil]; + + } + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (UInt64)memoryUsage { + __block UInt64 result = 0; + dispatch_sync(self.synchronizationQueue, ^{ + result = self.currentMemoryUsage; + }); + return result; +} + +- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier { + dispatch_barrier_async(self.synchronizationQueue, ^{ + AFCachedImage *cacheImage = [[AFCachedImage alloc] initWithImage:image identifier:identifier]; + + AFCachedImage *previousCachedImage = self.cachedImages[identifier]; + if (previousCachedImage != nil) { + self.currentMemoryUsage -= previousCachedImage.totalBytes; + } + + self.cachedImages[identifier] = cacheImage; + self.currentMemoryUsage += cacheImage.totalBytes; + }); + + dispatch_barrier_async(self.synchronizationQueue, ^{ + if (self.currentMemoryUsage > self.memoryCapacity) { + UInt64 bytesToPurge = self.currentMemoryUsage - self.preferredMemoryUsageAfterPurge; + NSMutableArray *sortedImages = [NSMutableArray arrayWithArray:self.cachedImages.allValues]; + NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastAccessDate" + ascending:YES]; + [sortedImages sortUsingDescriptors:@[sortDescriptor]]; + + UInt64 bytesPurged = 0; + + for (AFCachedImage *cachedImage in sortedImages) { + [self.cachedImages removeObjectForKey:cachedImage.identifier]; + bytesPurged += cachedImage.totalBytes; + if (bytesPurged >= bytesToPurge) { + break; + } + } + self.currentMemoryUsage -= bytesPurged; + } + }); +} + +- (BOOL)removeImageWithIdentifier:(NSString *)identifier { + __block BOOL removed = NO; + dispatch_barrier_sync(self.synchronizationQueue, ^{ + AFCachedImage *cachedImage = self.cachedImages[identifier]; + if (cachedImage != nil) { + [self.cachedImages removeObjectForKey:identifier]; + self.currentMemoryUsage -= cachedImage.totalBytes; + removed = YES; + } + }); + return removed; +} + +- (BOOL)removeAllImages { + __block BOOL removed = NO; + dispatch_barrier_sync(self.synchronizationQueue, ^{ + if (self.cachedImages.count > 0) { + [self.cachedImages removeAllObjects]; + self.currentMemoryUsage = 0; + removed = YES; + } + }); + return removed; +} + +- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier { + __block UIImage *image = nil; + dispatch_sync(self.synchronizationQueue, ^{ + AFCachedImage *cachedImage = self.cachedImages[identifier]; + image = [cachedImage accessImage]; + }); + return image; +} + +- (void)addImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier { + [self addImage:image withIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]]; +} + +- (BOOL)removeImageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier { + return [self removeImageWithIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]]; +} + +- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier { + return [self imageWithIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]]; +} + +- (NSString *)imageCacheKeyFromURLRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)additionalIdentifier { + NSString *key = request.URL.absoluteString; + if (additionalIdentifier != nil) { + key = [key stringByAppendingString:additionalIdentifier]; + } + return key; +} + +- (BOOL)shouldCacheImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier { + return YES; +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.h b/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.h new file mode 100644 index 0000000..3bf5a32 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.h @@ -0,0 +1,171 @@ +// AFImageDownloader.h +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import + +#if TARGET_OS_IOS || TARGET_OS_TV + +#import +#import "AFAutoPurgingImageCache.h" +#import "AFHTTPSessionManager.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, AFImageDownloadPrioritization) { + AFImageDownloadPrioritizationFIFO, + AFImageDownloadPrioritizationLIFO +}; + +/** + The `AFImageDownloadReceipt` is an object vended by the `AFImageDownloader` when starting a data task. It can be used to cancel active tasks running on the `AFImageDownloader` session. As a general rule, image data tasks should be cancelled using the `AFImageDownloadReceipt` instead of calling `cancel` directly on the `task` itself. The `AFImageDownloader` is optimized to handle duplicate task scenarios as well as pending versus active downloads. + */ +@interface AFImageDownloadReceipt : NSObject + +/** + The data task created by the `AFImageDownloader`. +*/ +@property (nonatomic, strong) NSURLSessionDataTask *task; + +/** + The unique identifier for the success and failure blocks when duplicate requests are made. + */ +@property (nonatomic, strong) NSUUID *receiptID; +@end + +/** The `AFImageDownloader` class is responsible for downloading images in parallel on a prioritized queue. Incoming downloads are added to the front or back of the queue depending on the download prioritization. Each downloaded image is cached in the underlying `NSURLCache` as well as the in-memory image cache. By default, any download request with a cached image equivalent in the image cache will automatically be served the cached image representation. + */ +@interface AFImageDownloader : NSObject + +/** + The image cache used to store all downloaded images in. `AFAutoPurgingImageCache` by default. + */ +@property (nonatomic, strong, nullable) id imageCache; + +/** + The `AFHTTPSessionManager` used to download images. By default, this is configured with an `AFImageResponseSerializer`, and a shared `NSURLCache` for all image downloads. + */ +@property (nonatomic, strong) AFHTTPSessionManager *sessionManager; + +/** + Defines the order prioritization of incoming download requests being inserted into the queue. `AFImageDownloadPrioritizationFIFO` by default. + */ +@property (nonatomic, assign) AFImageDownloadPrioritization downloadPrioritization; + +/** + The shared default instance of `AFImageDownloader` initialized with default values. + */ ++ (instancetype)defaultInstance; + +/** + Creates a default `NSURLCache` with common usage parameter values. + + @returns The default `NSURLCache` instance. + */ ++ (NSURLCache *)defaultURLCache; + +/** + The default `NSURLSessionConfiguration` with common usage parameter values. + */ ++ (NSURLSessionConfiguration *)defaultURLSessionConfiguration; + +/** + Default initializer + + @return An instance of `AFImageDownloader` initialized with default values. + */ +- (instancetype)init; + +/** + Initializer with specific `URLSessionConfiguration` + + @param configuration The `NSURLSessionConfiguration` to be be used + + @return An instance of `AFImageDownloader` initialized with default values and custom `NSURLSessionConfiguration` + */ +- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration; + +/** + Initializes the `AFImageDownloader` instance with the given session manager, download prioritization, maximum active download count and image cache. + + @param sessionManager The session manager to use to download images. + @param downloadPrioritization The download prioritization of the download queue. + @param maximumActiveDownloads The maximum number of active downloads allowed at any given time. Recommend `4`. + @param imageCache The image cache used to store all downloaded images in. + + @return The new `AFImageDownloader` instance. + */ +- (instancetype)initWithSessionManager:(AFHTTPSessionManager *)sessionManager + downloadPrioritization:(AFImageDownloadPrioritization)downloadPrioritization + maximumActiveDownloads:(NSInteger)maximumActiveDownloads + imageCache:(nullable id )imageCache; + +/** + Creates a data task using the `sessionManager` instance for the specified URL request. + + If the same data task is already in the queue or currently being downloaded, the success and failure blocks are + appended to the already existing task. Once the task completes, all success or failure blocks attached to the + task are executed in the order they were added. + + @param request The URL request. + @param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`. + @param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred. + + @return The image download receipt for the data task if available. `nil` if the image is stored in the cache. + cache and the URL request cache policy allows the cache to be used. + */ +- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request + success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success + failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure; + +/** + Creates a data task using the `sessionManager` instance for the specified URL request. + + If the same data task is already in the queue or currently being downloaded, the success and failure blocks are + appended to the already existing task. Once the task completes, all success or failure blocks attached to the + task are executed in the order they were added. + + @param request The URL request. + @param receiptID The identifier to use for the download receipt that will be created for this request. This must be a unique identifier that does not represent any other request. + @param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`. + @param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred. + + @return The image download receipt for the data task if available. `nil` if the image is stored in the cache. + cache and the URL request cache policy allows the cache to be used. + */ +- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request + withReceiptID:(NSUUID *)receiptID + success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success + failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure; + +/** + Cancels the data task in the receipt by removing the corresponding success and failure blocks and cancelling the data task if necessary. + + If the data task is pending in the queue, it will be cancelled if no other success and failure blocks are registered with the data task. If the data task is currently executing or is already completed, the success and failure blocks are removed and will not be called when the task finishes. + + @param imageDownloadReceipt The image download receipt to cancel. + */ +- (void)cancelTaskForImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt; + +@end + +#endif + +NS_ASSUME_NONNULL_END diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.m b/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.m new file mode 100644 index 0000000..008a782 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.m @@ -0,0 +1,421 @@ +// AFImageDownloader.m +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import + +#if TARGET_OS_IOS || TARGET_OS_TV + +#import "AFImageDownloader.h" +#import "AFHTTPSessionManager.h" + +@interface AFImageDownloaderResponseHandler : NSObject +@property (nonatomic, strong) NSUUID *uuid; +@property (nonatomic, copy) void (^successBlock)(NSURLRequest *, NSHTTPURLResponse *, UIImage *); +@property (nonatomic, copy) void (^failureBlock)(NSURLRequest *, NSHTTPURLResponse *, NSError *); +@end + +@implementation AFImageDownloaderResponseHandler + +- (instancetype)initWithUUID:(NSUUID *)uuid + success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success + failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure { + if (self = [self init]) { + self.uuid = uuid; + self.successBlock = success; + self.failureBlock = failure; + } + return self; +} + +- (NSString *)description { + return [NSString stringWithFormat: @"UUID: %@", [self.uuid UUIDString]]; +} + +@end + +@interface AFImageDownloaderMergedTask : NSObject +@property (nonatomic, strong) NSString *URLIdentifier; +@property (nonatomic, strong) NSUUID *identifier; +@property (nonatomic, strong) NSURLSessionDataTask *task; +@property (nonatomic, strong) NSMutableArray *responseHandlers; + +@end + +@implementation AFImageDownloaderMergedTask + +- (instancetype)initWithURLIdentifier:(NSString *)URLIdentifier identifier:(NSUUID *)identifier task:(NSURLSessionDataTask *)task { + if (self = [self init]) { + self.URLIdentifier = URLIdentifier; + self.task = task; + self.identifier = identifier; + self.responseHandlers = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)addResponseHandler:(AFImageDownloaderResponseHandler *)handler { + [self.responseHandlers addObject:handler]; +} + +- (void)removeResponseHandler:(AFImageDownloaderResponseHandler *)handler { + [self.responseHandlers removeObject:handler]; +} + +@end + +@implementation AFImageDownloadReceipt + +- (instancetype)initWithReceiptID:(NSUUID *)receiptID task:(NSURLSessionDataTask *)task { + if (self = [self init]) { + self.receiptID = receiptID; + self.task = task; + } + return self; +} + +@end + +@interface AFImageDownloader () + +@property (nonatomic, strong) dispatch_queue_t synchronizationQueue; +@property (nonatomic, strong) dispatch_queue_t responseQueue; + +@property (nonatomic, assign) NSInteger maximumActiveDownloads; +@property (nonatomic, assign) NSInteger activeRequestCount; + +@property (nonatomic, strong) NSMutableArray *queuedMergedTasks; +@property (nonatomic, strong) NSMutableDictionary *mergedTasks; + +@end + +@implementation AFImageDownloader + ++ (NSURLCache *)defaultURLCache { + NSUInteger memoryCapacity = 20 * 1024 * 1024; // 20MB + NSUInteger diskCapacity = 150 * 1024 * 1024; // 150MB + NSURL *cacheURL = [[[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory + inDomain:NSUserDomainMask + appropriateForURL:nil + create:YES + error:nil] + URLByAppendingPathComponent:@"com.alamofire.imagedownloader"]; + +#if TARGET_OS_MACCATALYST + return [[NSURLCache alloc] initWithMemoryCapacity:memoryCapacity + diskCapacity:diskCapacity + directoryURL:cacheURL]; +#else + return [[NSURLCache alloc] initWithMemoryCapacity:memoryCapacity + diskCapacity:diskCapacity + diskPath:[cacheURL path]]; +#endif +} + ++ (NSURLSessionConfiguration *)defaultURLSessionConfiguration { + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; + + //TODO set the default HTTP headers + + configuration.HTTPShouldSetCookies = YES; + configuration.HTTPShouldUsePipelining = NO; + + configuration.requestCachePolicy = NSURLRequestUseProtocolCachePolicy; + configuration.allowsCellularAccess = YES; + configuration.timeoutIntervalForRequest = 60.0; + configuration.URLCache = [AFImageDownloader defaultURLCache]; + + return configuration; +} + +- (instancetype)init { + NSURLSessionConfiguration *defaultConfiguration = [self.class defaultURLSessionConfiguration]; + return [self initWithSessionConfiguration:defaultConfiguration]; +} + +- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration { + AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:configuration]; + sessionManager.responseSerializer = [AFImageResponseSerializer serializer]; + + return [self initWithSessionManager:sessionManager + downloadPrioritization:AFImageDownloadPrioritizationFIFO + maximumActiveDownloads:4 + imageCache:[[AFAutoPurgingImageCache alloc] init]]; +} + +- (instancetype)initWithSessionManager:(AFHTTPSessionManager *)sessionManager + downloadPrioritization:(AFImageDownloadPrioritization)downloadPrioritization + maximumActiveDownloads:(NSInteger)maximumActiveDownloads + imageCache:(id )imageCache { + if (self = [super init]) { + self.sessionManager = sessionManager; + + self.downloadPrioritization = downloadPrioritization; + self.maximumActiveDownloads = maximumActiveDownloads; + self.imageCache = imageCache; + + self.queuedMergedTasks = [[NSMutableArray alloc] init]; + self.mergedTasks = [[NSMutableDictionary alloc] init]; + self.activeRequestCount = 0; + + NSString *name = [NSString stringWithFormat:@"com.alamofire.imagedownloader.synchronizationqueue-%@", [[NSUUID UUID] UUIDString]]; + self.synchronizationQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_SERIAL); + + name = [NSString stringWithFormat:@"com.alamofire.imagedownloader.responsequeue-%@", [[NSUUID UUID] UUIDString]]; + self.responseQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT); + } + + return self; +} + ++ (instancetype)defaultInstance { + static AFImageDownloader *sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[self alloc] init]; + }); + return sharedInstance; +} + +- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request + success:(void (^)(NSURLRequest * _Nonnull, NSHTTPURLResponse * _Nullable, UIImage * _Nonnull))success + failure:(void (^)(NSURLRequest * _Nonnull, NSHTTPURLResponse * _Nullable, NSError * _Nonnull))failure { + return [self downloadImageForURLRequest:request withReceiptID:[NSUUID UUID] success:success failure:failure]; +} + +- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request + withReceiptID:(nonnull NSUUID *)receiptID + success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success + failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure { + __block NSURLSessionDataTask *task = nil; + dispatch_sync(self.synchronizationQueue, ^{ + NSString *URLIdentifier = request.URL.absoluteString; + if (URLIdentifier == nil) { + if (failure) { + NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorBadURL userInfo:nil]; + dispatch_async(dispatch_get_main_queue(), ^{ + failure(request, nil, error); + }); + } + return; + } + + // 1) Append the success and failure blocks to a pre-existing request if it already exists + AFImageDownloaderMergedTask *existingMergedTask = self.mergedTasks[URLIdentifier]; + if (existingMergedTask != nil) { + AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID success:success failure:failure]; + [existingMergedTask addResponseHandler:handler]; + task = existingMergedTask.task; + return; + } + + // 2) Attempt to load the image from the image cache if the cache policy allows it + switch (request.cachePolicy) { + case NSURLRequestUseProtocolCachePolicy: + case NSURLRequestReturnCacheDataElseLoad: + case NSURLRequestReturnCacheDataDontLoad: { + UIImage *cachedImage = [self.imageCache imageforRequest:request withAdditionalIdentifier:nil]; + if (cachedImage != nil) { + if (success) { + dispatch_async(dispatch_get_main_queue(), ^{ + success(request, nil, cachedImage); + }); + } + return; + } + break; + } + default: + break; + } + + // 3) Create the request and set up authentication, validation and response serialization + NSUUID *mergedTaskIdentifier = [NSUUID UUID]; + NSURLSessionDataTask *createdTask; + __weak __typeof__(self) weakSelf = self; + + createdTask = [self.sessionManager + dataTaskWithRequest:request + uploadProgress:nil + downloadProgress:nil + completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) { + dispatch_async(self.responseQueue, ^{ + __strong __typeof__(weakSelf) strongSelf = weakSelf; + AFImageDownloaderMergedTask *mergedTask = [strongSelf safelyGetMergedTask:URLIdentifier]; + if ([mergedTask.identifier isEqual:mergedTaskIdentifier]) { + mergedTask = [strongSelf safelyRemoveMergedTaskWithURLIdentifier:URLIdentifier]; + if (error) { + for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) { + if (handler.failureBlock) { + dispatch_async(dispatch_get_main_queue(), ^{ + handler.failureBlock(request, (NSHTTPURLResponse *)response, error); + }); + } + } + } else { + if ([strongSelf.imageCache shouldCacheImage:responseObject forRequest:request withAdditionalIdentifier:nil]) { + [strongSelf.imageCache addImage:responseObject forRequest:request withAdditionalIdentifier:nil]; + } + + for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) { + if (handler.successBlock) { + dispatch_async(dispatch_get_main_queue(), ^{ + handler.successBlock(request, (NSHTTPURLResponse *)response, responseObject); + }); + } + } + + } + } + [strongSelf safelyDecrementActiveTaskCount]; + [strongSelf safelyStartNextTaskIfNecessary]; + }); + }]; + + // 4) Store the response handler for use when the request completes + AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID + success:success + failure:failure]; + AFImageDownloaderMergedTask *mergedTask = [[AFImageDownloaderMergedTask alloc] + initWithURLIdentifier:URLIdentifier + identifier:mergedTaskIdentifier + task:createdTask]; + [mergedTask addResponseHandler:handler]; + self.mergedTasks[URLIdentifier] = mergedTask; + + // 5) Either start the request or enqueue it depending on the current active request count + if ([self isActiveRequestCountBelowMaximumLimit]) { + [self startMergedTask:mergedTask]; + } else { + [self enqueueMergedTask:mergedTask]; + } + + task = mergedTask.task; + }); + if (task) { + return [[AFImageDownloadReceipt alloc] initWithReceiptID:receiptID task:task]; + } else { + return nil; + } +} + +- (void)cancelTaskForImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt { + dispatch_sync(self.synchronizationQueue, ^{ + NSString *URLIdentifier = imageDownloadReceipt.task.originalRequest.URL.absoluteString; + AFImageDownloaderMergedTask *mergedTask = self.mergedTasks[URLIdentifier]; + NSUInteger index = [mergedTask.responseHandlers indexOfObjectPassingTest:^BOOL(AFImageDownloaderResponseHandler * _Nonnull handler, __unused NSUInteger idx, __unused BOOL * _Nonnull stop) { + return handler.uuid == imageDownloadReceipt.receiptID; + }]; + + if (index != NSNotFound) { + AFImageDownloaderResponseHandler *handler = mergedTask.responseHandlers[index]; + [mergedTask removeResponseHandler:handler]; + NSString *failureReason = [NSString stringWithFormat:@"ImageDownloader cancelled URL request: %@",imageDownloadReceipt.task.originalRequest.URL.absoluteString]; + NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey:failureReason}; + NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:userInfo]; + if (handler.failureBlock) { + dispatch_async(dispatch_get_main_queue(), ^{ + handler.failureBlock(imageDownloadReceipt.task.originalRequest, nil, error); + }); + } + } + + if (mergedTask.responseHandlers.count == 0) { + [mergedTask.task cancel]; + [self removeMergedTaskWithURLIdentifier:URLIdentifier]; + } + }); +} + +- (AFImageDownloaderMergedTask *)safelyRemoveMergedTaskWithURLIdentifier:(NSString *)URLIdentifier { + __block AFImageDownloaderMergedTask *mergedTask = nil; + dispatch_sync(self.synchronizationQueue, ^{ + mergedTask = [self removeMergedTaskWithURLIdentifier:URLIdentifier]; + }); + return mergedTask; +} + +//This method should only be called from safely within the synchronizationQueue +- (AFImageDownloaderMergedTask *)removeMergedTaskWithURLIdentifier:(NSString *)URLIdentifier { + AFImageDownloaderMergedTask *mergedTask = self.mergedTasks[URLIdentifier]; + [self.mergedTasks removeObjectForKey:URLIdentifier]; + return mergedTask; +} + +- (void)safelyDecrementActiveTaskCount { + dispatch_sync(self.synchronizationQueue, ^{ + if (self.activeRequestCount > 0) { + self.activeRequestCount -= 1; + } + }); +} + +- (void)safelyStartNextTaskIfNecessary { + dispatch_sync(self.synchronizationQueue, ^{ + if ([self isActiveRequestCountBelowMaximumLimit]) { + while (self.queuedMergedTasks.count > 0) { + AFImageDownloaderMergedTask *mergedTask = [self dequeueMergedTask]; + if (mergedTask.task.state == NSURLSessionTaskStateSuspended) { + [self startMergedTask:mergedTask]; + break; + } + } + } + }); +} + +- (void)startMergedTask:(AFImageDownloaderMergedTask *)mergedTask { + [mergedTask.task resume]; + ++self.activeRequestCount; +} + +- (void)enqueueMergedTask:(AFImageDownloaderMergedTask *)mergedTask { + switch (self.downloadPrioritization) { + case AFImageDownloadPrioritizationFIFO: + [self.queuedMergedTasks addObject:mergedTask]; + break; + case AFImageDownloadPrioritizationLIFO: + [self.queuedMergedTasks insertObject:mergedTask atIndex:0]; + break; + } +} + +- (AFImageDownloaderMergedTask *)dequeueMergedTask { + AFImageDownloaderMergedTask *mergedTask = nil; + mergedTask = [self.queuedMergedTasks firstObject]; + [self.queuedMergedTasks removeObject:mergedTask]; + return mergedTask; +} + +- (BOOL)isActiveRequestCountBelowMaximumLimit { + return self.activeRequestCount < self.maximumActiveDownloads; +} + +- (AFImageDownloaderMergedTask *)safelyGetMergedTask:(NSString *)URLIdentifier { + __block AFImageDownloaderMergedTask *mergedTask; + dispatch_sync(self.synchronizationQueue, ^(){ + mergedTask = self.mergedTasks[URLIdentifier]; + }); + return mergedTask; +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h b/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h new file mode 100644 index 0000000..3bcf289 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h @@ -0,0 +1,103 @@ +// AFNetworkActivityIndicatorManager.h +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import + +#import + +#if TARGET_OS_IOS + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + `AFNetworkActivityIndicatorManager` manages the state of the network activity indicator in the status bar. When enabled, it will listen for notifications indicating that a session task has started or finished, and start or stop animating the indicator accordingly. The number of active requests is incremented and decremented much like a stack or a semaphore, and the activity indicator will animate so long as that number is greater than zero. + + You should enable the shared instance of `AFNetworkActivityIndicatorManager` when your application finishes launching. In `AppDelegate application:didFinishLaunchingWithOptions:` you can do so with the following code: + + [[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES]; + + By setting `enabled` to `YES` for `sharedManager`, the network activity indicator will show and hide automatically as requests start and finish. You should not ever need to call `incrementActivityCount` or `decrementActivityCount` yourself. + + See the Apple Human Interface Guidelines section about the Network Activity Indicator for more information: + http://developer.apple.com/library/iOS/#documentation/UserExperience/Conceptual/MobileHIG/UIElementGuidelines/UIElementGuidelines.html#//apple_ref/doc/uid/TP40006556-CH13-SW44 + */ +NS_EXTENSION_UNAVAILABLE_IOS("Use view controller based solutions where appropriate instead.") +@interface AFNetworkActivityIndicatorManager : NSObject + +/** + A Boolean value indicating whether the manager is enabled. + + If YES, the manager will change status bar network activity indicator according to network operation notifications it receives. The default value is NO. + */ +@property (nonatomic, assign, getter = isEnabled) BOOL enabled; + +/** + A Boolean value indicating whether the network activity indicator manager is currently active. +*/ +@property (readonly, nonatomic, assign, getter=isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible; + +/** + A time interval indicating the minimum duration of networking activity that should occur before the activity indicator is displayed. The default value 1 second. If the network activity indicator should be displayed immediately when network activity occurs, this value should be set to 0 seconds. + + Apple's HIG describes the following: + + > Display the network activity indicator to provide feedback when your app accesses the network for more than a couple of seconds. If the operation finishes sooner than that, you don’t have to show the network activity indicator, because the indicator is likely to disappear before users notice its presence. + + */ +@property (nonatomic, assign) NSTimeInterval activationDelay; + +/** + A time interval indicating the duration of time of no networking activity required before the activity indicator is disabled. This allows for continuous display of the network activity indicator across multiple requests. The default value is 0.17 seconds. + */ + +@property (nonatomic, assign) NSTimeInterval completionDelay; + +/** + Returns the shared network activity indicator manager object for the system. + + @return The systemwide network activity indicator manager. + */ ++ (instancetype)sharedManager; + +/** + Increments the number of active network requests. If this number was zero before incrementing, this will start animating the status bar network activity indicator. + */ +- (void)incrementActivityCount; + +/** + Decrements the number of active network requests. If this number becomes zero after decrementing, this will stop animating the status bar network activity indicator. + */ +- (void)decrementActivityCount; + +/** + Set the a custom method to be executed when the network activity indicator manager should be hidden/shown. By default, this is null, and the UIApplication Network Activity Indicator will be managed automatically. If this block is set, it is the responsiblity of the caller to manager the network activity indicator going forward. + + @param block A block to be executed when the network activity indicator status changes. + */ +- (void)setNetworkingActivityActionWithBlock:(nullable void (^)(BOOL networkActivityIndicatorVisible))block; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m b/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m new file mode 100644 index 0000000..8cb5677 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m @@ -0,0 +1,239 @@ +// AFNetworkActivityIndicatorManager.m +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import "AFNetworkActivityIndicatorManager.h" + +#if TARGET_OS_IOS +#import "AFURLSessionManager.h" + +typedef NS_ENUM(NSInteger, AFNetworkActivityManagerState) { + AFNetworkActivityManagerStateNotActive, + AFNetworkActivityManagerStateDelayingStart, + AFNetworkActivityManagerStateActive, + AFNetworkActivityManagerStateDelayingEnd +}; + +static NSTimeInterval const kDefaultAFNetworkActivityManagerActivationDelay = 1.0; +static NSTimeInterval const kDefaultAFNetworkActivityManagerCompletionDelay = 0.17; + +static NSURLRequest * AFNetworkRequestFromNotification(NSNotification *notification) { + if ([[notification object] respondsToSelector:@selector(originalRequest)]) { + return [(NSURLSessionTask *)[notification object] originalRequest]; + } else { + return nil; + } +} + +typedef void (^AFNetworkActivityActionBlock)(BOOL networkActivityIndicatorVisible); + +@interface AFNetworkActivityIndicatorManager () +@property (readwrite, nonatomic, assign) NSInteger activityCount; +@property (readwrite, nonatomic, strong) NSTimer *activationDelayTimer; +@property (readwrite, nonatomic, strong) NSTimer *completionDelayTimer; +@property (readonly, nonatomic, getter = isNetworkActivityOccurring) BOOL networkActivityOccurring; +@property (nonatomic, copy) AFNetworkActivityActionBlock networkActivityActionBlock; +@property (nonatomic, assign) AFNetworkActivityManagerState currentState; +@property (nonatomic, assign, getter=isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible; + +- (void)updateCurrentStateForNetworkActivityChange; +@end + +@implementation AFNetworkActivityIndicatorManager + ++ (instancetype)sharedManager { + static AFNetworkActivityIndicatorManager *_sharedManager = nil; + static dispatch_once_t oncePredicate; + dispatch_once(&oncePredicate, ^{ + _sharedManager = [[self alloc] init]; + }); + + return _sharedManager; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + self.currentState = AFNetworkActivityManagerStateNotActive; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidStart:) name:AFNetworkingTaskDidResumeNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidFinish:) name:AFNetworkingTaskDidSuspendNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidFinish:) name:AFNetworkingTaskDidCompleteNotification object:nil]; + self.activationDelay = kDefaultAFNetworkActivityManagerActivationDelay; + self.completionDelay = kDefaultAFNetworkActivityManagerCompletionDelay; + + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [_activationDelayTimer invalidate]; + [_completionDelayTimer invalidate]; +} + +- (void)setEnabled:(BOOL)enabled { + _enabled = enabled; + if (enabled == NO) { + [self setCurrentState:AFNetworkActivityManagerStateNotActive]; + } +} + +- (void)setNetworkingActivityActionWithBlock:(void (^)(BOOL networkActivityIndicatorVisible))block { + self.networkActivityActionBlock = block; +} + +- (BOOL)isNetworkActivityOccurring { + @synchronized(self) { + return self.activityCount > 0; + } +} + +- (void)setNetworkActivityIndicatorVisible:(BOOL)networkActivityIndicatorVisible { + if (_networkActivityIndicatorVisible != networkActivityIndicatorVisible) { + @synchronized(self) { + _networkActivityIndicatorVisible = networkActivityIndicatorVisible; + } + if (self.networkActivityActionBlock) { + self.networkActivityActionBlock(networkActivityIndicatorVisible); + } else { + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:networkActivityIndicatorVisible]; + } + } +} + + +- (void)incrementActivityCount { + @synchronized(self) { + self.activityCount++; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [self updateCurrentStateForNetworkActivityChange]; + }); +} + +- (void)decrementActivityCount { + @synchronized(self) { + self.activityCount = MAX(_activityCount - 1, 0); + } + dispatch_async(dispatch_get_main_queue(), ^{ + [self updateCurrentStateForNetworkActivityChange]; + }); +} + +- (void)networkRequestDidStart:(NSNotification *)notification { + if ([AFNetworkRequestFromNotification(notification) URL]) { + [self incrementActivityCount]; + } +} + +- (void)networkRequestDidFinish:(NSNotification *)notification { + if ([AFNetworkRequestFromNotification(notification) URL]) { + [self decrementActivityCount]; + } +} + +#pragma mark - Internal State Management +- (void)setCurrentState:(AFNetworkActivityManagerState)currentState { + @synchronized(self) { + if (_currentState != currentState) { + _currentState = currentState; + switch (currentState) { + case AFNetworkActivityManagerStateNotActive: + [self cancelActivationDelayTimer]; + [self cancelCompletionDelayTimer]; + [self setNetworkActivityIndicatorVisible:NO]; + break; + case AFNetworkActivityManagerStateDelayingStart: + [self startActivationDelayTimer]; + break; + case AFNetworkActivityManagerStateActive: + [self cancelCompletionDelayTimer]; + [self setNetworkActivityIndicatorVisible:YES]; + break; + case AFNetworkActivityManagerStateDelayingEnd: + [self startCompletionDelayTimer]; + break; + } + } + } +} + +- (void)updateCurrentStateForNetworkActivityChange { + if (self.enabled) { + switch (self.currentState) { + case AFNetworkActivityManagerStateNotActive: + if (self.isNetworkActivityOccurring) { + [self setCurrentState:AFNetworkActivityManagerStateDelayingStart]; + } + break; + case AFNetworkActivityManagerStateDelayingStart: + //No op. Let the delay timer finish out. + break; + case AFNetworkActivityManagerStateActive: + if (!self.isNetworkActivityOccurring) { + [self setCurrentState:AFNetworkActivityManagerStateDelayingEnd]; + } + break; + case AFNetworkActivityManagerStateDelayingEnd: + if (self.isNetworkActivityOccurring) { + [self setCurrentState:AFNetworkActivityManagerStateActive]; + } + break; + } + } +} + +- (void)startActivationDelayTimer { + self.activationDelayTimer = [NSTimer + timerWithTimeInterval:self.activationDelay target:self selector:@selector(activationDelayTimerFired) userInfo:nil repeats:NO]; + [[NSRunLoop mainRunLoop] addTimer:self.activationDelayTimer forMode:NSRunLoopCommonModes]; +} + +- (void)activationDelayTimerFired { + if (self.networkActivityOccurring) { + [self setCurrentState:AFNetworkActivityManagerStateActive]; + } else { + [self setCurrentState:AFNetworkActivityManagerStateNotActive]; + } +} + +- (void)startCompletionDelayTimer { + [self.completionDelayTimer invalidate]; + self.completionDelayTimer = [NSTimer timerWithTimeInterval:self.completionDelay target:self selector:@selector(completionDelayTimerFired) userInfo:nil repeats:NO]; + [[NSRunLoop mainRunLoop] addTimer:self.completionDelayTimer forMode:NSRunLoopCommonModes]; +} + +- (void)completionDelayTimerFired { + [self setCurrentState:AFNetworkActivityManagerStateNotActive]; +} + +- (void)cancelActivationDelayTimer { + [self.activationDelayTimer invalidate]; +} + +- (void)cancelCompletionDelayTimer { + [self.completionDelayTimer invalidate]; +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h new file mode 100644 index 0000000..d424c9b --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h @@ -0,0 +1,48 @@ +// UIActivityIndicatorView+AFNetworking.h +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import + +#import + +#if TARGET_OS_IOS || TARGET_OS_TV + +#import + +/** + This category adds methods to the UIKit framework's `UIActivityIndicatorView` class. The methods in this category provide support for automatically starting and stopping animation depending on the loading state of a session task. + */ +@interface UIActivityIndicatorView (AFNetworking) + +///---------------------------------- +/// @name Animating for Session Tasks +///---------------------------------- + +/** + Binds the animating state to the state of the specified task. + + @param task The task. If `nil`, automatic updating from any previously specified operation will be disabled. + */ +- (void)setAnimatingWithStateOfTask:(nullable NSURLSessionTask *)task; + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m new file mode 100644 index 0000000..602a72d --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m @@ -0,0 +1,114 @@ +// UIActivityIndicatorView+AFNetworking.m +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import "UIActivityIndicatorView+AFNetworking.h" +#import + +#if TARGET_OS_IOS || TARGET_OS_TV + +#import "AFURLSessionManager.h" + +@interface AFActivityIndicatorViewNotificationObserver : NSObject +@property (readonly, nonatomic, weak) UIActivityIndicatorView *activityIndicatorView; +- (instancetype)initWithActivityIndicatorView:(UIActivityIndicatorView *)activityIndicatorView; + +- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task; + +@end + +@implementation UIActivityIndicatorView (AFNetworking) + +- (AFActivityIndicatorViewNotificationObserver *)af_notificationObserver { + AFActivityIndicatorViewNotificationObserver *notificationObserver = objc_getAssociatedObject(self, @selector(af_notificationObserver)); + if (notificationObserver == nil) { + notificationObserver = [[AFActivityIndicatorViewNotificationObserver alloc] initWithActivityIndicatorView:self]; + objc_setAssociatedObject(self, @selector(af_notificationObserver), notificationObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return notificationObserver; +} + +- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task { + [[self af_notificationObserver] setAnimatingWithStateOfTask:task]; +} + +@end + +@implementation AFActivityIndicatorViewNotificationObserver + +- (instancetype)initWithActivityIndicatorView:(UIActivityIndicatorView *)activityIndicatorView +{ + self = [super init]; + if (self) { + _activityIndicatorView = activityIndicatorView; + } + return self; +} + +- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil]; + + if (task) { + if (task.state != NSURLSessionTaskStateCompleted) { + UIActivityIndicatorView *activityIndicatorView = self.activityIndicatorView; + if (task.state == NSURLSessionTaskStateRunning) { + [activityIndicatorView startAnimating]; + } else { + [activityIndicatorView stopAnimating]; + } + + [notificationCenter addObserver:self selector:@selector(af_startAnimating) name:AFNetworkingTaskDidResumeNotification object:task]; + [notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingTaskDidCompleteNotification object:task]; + [notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingTaskDidSuspendNotification object:task]; + } + } +} + +#pragma mark - + +- (void)af_startAnimating { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.activityIndicatorView startAnimating]; + }); +} + +- (void)af_stopAnimating { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.activityIndicatorView stopAnimating]; + }); +} + +#pragma mark - + +- (void)dealloc { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil]; +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h new file mode 100644 index 0000000..d33e0d4 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h @@ -0,0 +1,175 @@ +// UIButton+AFNetworking.h +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import + +#import + +#if TARGET_OS_IOS || TARGET_OS_TV + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class AFImageDownloader; + +/** + This category adds methods to the UIKit framework's `UIButton` class. The methods in this category provide support for loading remote images and background images asynchronously from a URL. + + @warning Compound values for control `state` (such as `UIControlStateHighlighted | UIControlStateDisabled`) are unsupported. + */ +@interface UIButton (AFNetworking) + +///------------------------------------ +/// @name Accessing the Image Downloader +///------------------------------------ + +/** + Set the shared image downloader used to download images. + + @param imageDownloader The shared image downloader used to download images. +*/ ++ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader; + +/** + The shared image downloader used to download images. + */ ++ (AFImageDownloader *)sharedImageDownloader; + +///-------------------- +/// @name Setting Image +///-------------------- + +/** + Asynchronously downloads an image from the specified URL, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + @param state The control state. + @param url The URL used for the image request. + */ +- (void)setImageForState:(UIControlState)state + withURL:(NSURL *)url; + +/** + Asynchronously downloads an image from the specified URL, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + @param state The control state. + @param url The URL used for the image request. + @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the button will not change its image until the image request finishes. + */ +- (void)setImageForState:(UIControlState)state + withURL:(NSURL *)url + placeholderImage:(nullable UIImage *)placeholderImage; + +/** + Asynchronously downloads an image from the specified URL request, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + If a success block is specified, it is the responsibility of the block to set the image of the button before returning. If no success block is specified, the default behavior of setting the image with `setImage:forState:` is applied. + + @param state The control state. + @param urlRequest The URL request used for the image request. + @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the button will not change its image until the image request finishes. + @param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`. + @param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred. + */ +- (void)setImageForState:(UIControlState)state + withURLRequest:(NSURLRequest *)urlRequest + placeholderImage:(nullable UIImage *)placeholderImage + success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success + failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure; + + +///------------------------------- +/// @name Setting Background Image +///------------------------------- + +/** + Asynchronously downloads an image from the specified URL, and sets it as the background image for the specified state once the request is finished. Any previous background image request for the receiver will be cancelled. + + If the background image is cached locally, the background image is set immediately, otherwise the specified placeholder background image will be set immediately, and then the remote background image will be set once the request is finished. + + @param state The control state. + @param url The URL used for the background image request. + */ +- (void)setBackgroundImageForState:(UIControlState)state + withURL:(NSURL *)url; + +/** + Asynchronously downloads an image from the specified URL, and sets it as the background image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + @param state The control state. + @param url The URL used for the background image request. + @param placeholderImage The background image to be set initially, until the background image request finishes. If `nil`, the button will not change its background image until the background image request finishes. + */ +- (void)setBackgroundImageForState:(UIControlState)state + withURL:(NSURL *)url + placeholderImage:(nullable UIImage *)placeholderImage; + +/** + Asynchronously downloads an image from the specified URL request, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + If a success block is specified, it is the responsibility of the block to set the image of the button before returning. If no success block is specified, the default behavior of setting the image with `setBackgroundImage:forState:` is applied. + + @param state The control state. + @param urlRequest The URL request used for the image request. + @param placeholderImage The background image to be set initially, until the background image request finishes. If `nil`, the button will not change its background image until the background image request finishes. + @param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`. + @param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred. + */ +- (void)setBackgroundImageForState:(UIControlState)state + withURLRequest:(NSURLRequest *)urlRequest + placeholderImage:(nullable UIImage *)placeholderImage + success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success + failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure; + + +///------------------------------ +/// @name Canceling Image Loading +///------------------------------ + +/** + Cancels any executing image task for the specified control state of the receiver, if one exists. + + @param state The control state. + */ +- (void)cancelImageDownloadTaskForState:(UIControlState)state; + +/** + Cancels any executing background image task for the specified control state of the receiver, if one exists. + + @param state The control state. + */ +- (void)cancelBackgroundImageDownloadTaskForState:(UIControlState)state; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m new file mode 100644 index 0000000..03aaf2a --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m @@ -0,0 +1,302 @@ +// UIButton+AFNetworking.m +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import "UIButton+AFNetworking.h" + +#import + +#if TARGET_OS_IOS || TARGET_OS_TV + +#import "UIImageView+AFNetworking.h" +#import "AFImageDownloader.h" + +@interface UIButton (_AFNetworking) +@end + +@implementation UIButton (_AFNetworking) + +#pragma mark - + +static char AFImageDownloadReceiptNormal; +static char AFImageDownloadReceiptHighlighted; +static char AFImageDownloadReceiptSelected; +static char AFImageDownloadReceiptDisabled; + +static const char * af_imageDownloadReceiptKeyForState(UIControlState state) { + switch (state) { + case UIControlStateHighlighted: + return &AFImageDownloadReceiptHighlighted; + case UIControlStateSelected: + return &AFImageDownloadReceiptSelected; + case UIControlStateDisabled: + return &AFImageDownloadReceiptDisabled; + case UIControlStateNormal: + default: + return &AFImageDownloadReceiptNormal; + } +} + +- (AFImageDownloadReceipt *)af_imageDownloadReceiptForState:(UIControlState)state { + return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, af_imageDownloadReceiptKeyForState(state)); +} + +- (void)af_setImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt + forState:(UIControlState)state +{ + objc_setAssociatedObject(self, af_imageDownloadReceiptKeyForState(state), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +static char AFBackgroundImageDownloadReceiptNormal; +static char AFBackgroundImageDownloadReceiptHighlighted; +static char AFBackgroundImageDownloadReceiptSelected; +static char AFBackgroundImageDownloadReceiptDisabled; + +static const char * af_backgroundImageDownloadReceiptKeyForState(UIControlState state) { + switch (state) { + case UIControlStateHighlighted: + return &AFBackgroundImageDownloadReceiptHighlighted; + case UIControlStateSelected: + return &AFBackgroundImageDownloadReceiptSelected; + case UIControlStateDisabled: + return &AFBackgroundImageDownloadReceiptDisabled; + case UIControlStateNormal: + default: + return &AFBackgroundImageDownloadReceiptNormal; + } +} + +- (AFImageDownloadReceipt *)af_backgroundImageDownloadReceiptForState:(UIControlState)state { + return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, af_backgroundImageDownloadReceiptKeyForState(state)); +} + +- (void)af_setBackgroundImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt + forState:(UIControlState)state +{ + objc_setAssociatedObject(self, af_backgroundImageDownloadReceiptKeyForState(state), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end + +#pragma mark - + +@implementation UIButton (AFNetworking) + ++ (AFImageDownloader *)sharedImageDownloader { + + return objc_getAssociatedObject([UIButton class], @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance]; +} + ++ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader { + objc_setAssociatedObject([UIButton class], @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +- (void)setImageForState:(UIControlState)state + withURL:(NSURL *)url +{ + [self setImageForState:state withURL:url placeholderImage:nil]; +} + +- (void)setImageForState:(UIControlState)state + withURL:(NSURL *)url + placeholderImage:(UIImage *)placeholderImage +{ + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + [request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; + + [self setImageForState:state withURLRequest:request placeholderImage:placeholderImage success:nil failure:nil]; +} + +- (void)setImageForState:(UIControlState)state + withURLRequest:(NSURLRequest *)urlRequest + placeholderImage:(nullable UIImage *)placeholderImage + success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success + failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure +{ + if ([self isActiveTaskURLEqualToURLRequest:urlRequest forState:state]) { + return; + } + + [self cancelImageDownloadTaskForState:state]; + + AFImageDownloader *downloader = [[self class] sharedImageDownloader]; + id imageCache = downloader.imageCache; + + //Use the image from the image cache if it exists + UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil]; + if (cachedImage) { + if (success) { + success(urlRequest, nil, cachedImage); + } else { + [self setImage:cachedImage forState:state]; + } + [self af_setImageDownloadReceipt:nil forState:state]; + } else { + if (placeholderImage) { + [self setImage:placeholderImage forState:state]; + } + + __weak __typeof(self)weakSelf = self; + NSUUID *downloadID = [NSUUID UUID]; + AFImageDownloadReceipt *receipt; + receipt = [downloader + downloadImageForURLRequest:urlRequest + withReceiptID:downloadID + success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([[strongSelf af_imageDownloadReceiptForState:state].receiptID isEqual:downloadID]) { + if (success) { + success(request, response, responseObject); + } else if (responseObject) { + [strongSelf setImage:responseObject forState:state]; + } + [strongSelf af_setImageDownloadReceipt:nil forState:state]; + } + + } + failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([[strongSelf af_imageDownloadReceiptForState:state].receiptID isEqual:downloadID]) { + if (failure) { + failure(request, response, error); + } + [strongSelf af_setImageDownloadReceipt:nil forState:state]; + } + }]; + + [self af_setImageDownloadReceipt:receipt forState:state]; + } +} + +#pragma mark - + +- (void)setBackgroundImageForState:(UIControlState)state + withURL:(NSURL *)url +{ + [self setBackgroundImageForState:state withURL:url placeholderImage:nil]; +} + +- (void)setBackgroundImageForState:(UIControlState)state + withURL:(NSURL *)url + placeholderImage:(nullable UIImage *)placeholderImage +{ + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + [request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; + + [self setBackgroundImageForState:state withURLRequest:request placeholderImage:placeholderImage success:nil failure:nil]; +} + +- (void)setBackgroundImageForState:(UIControlState)state + withURLRequest:(NSURLRequest *)urlRequest + placeholderImage:(nullable UIImage *)placeholderImage + success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success + failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure +{ + if ([self isActiveBackgroundTaskURLEqualToURLRequest:urlRequest forState:state]) { + return; + } + + [self cancelBackgroundImageDownloadTaskForState:state]; + + AFImageDownloader *downloader = [[self class] sharedImageDownloader]; + id imageCache = downloader.imageCache; + + //Use the image from the image cache if it exists + UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil]; + if (cachedImage) { + if (success) { + success(urlRequest, nil, cachedImage); + } else { + [self setBackgroundImage:cachedImage forState:state]; + } + [self af_setBackgroundImageDownloadReceipt:nil forState:state]; + } else { + if (placeholderImage) { + [self setBackgroundImage:placeholderImage forState:state]; + } + + __weak __typeof(self)weakSelf = self; + NSUUID *downloadID = [NSUUID UUID]; + AFImageDownloadReceipt *receipt; + receipt = [downloader + downloadImageForURLRequest:urlRequest + withReceiptID:downloadID + success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([[strongSelf af_backgroundImageDownloadReceiptForState:state].receiptID isEqual:downloadID]) { + if (success) { + success(request, response, responseObject); + } else if (responseObject) { + [strongSelf setBackgroundImage:responseObject forState:state]; + } + [strongSelf af_setBackgroundImageDownloadReceipt:nil forState:state]; + } + + } + failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([[strongSelf af_backgroundImageDownloadReceiptForState:state].receiptID isEqual:downloadID]) { + if (failure) { + failure(request, response, error); + } + [strongSelf af_setBackgroundImageDownloadReceipt:nil forState:state]; + } + }]; + + [self af_setBackgroundImageDownloadReceipt:receipt forState:state]; + } +} + +#pragma mark - + +- (void)cancelImageDownloadTaskForState:(UIControlState)state { + AFImageDownloadReceipt *receipt = [self af_imageDownloadReceiptForState:state]; + if (receipt != nil) { + [[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:receipt]; + [self af_setImageDownloadReceipt:nil forState:state]; + } +} + +- (void)cancelBackgroundImageDownloadTaskForState:(UIControlState)state { + AFImageDownloadReceipt *receipt = [self af_backgroundImageDownloadReceiptForState:state]; + if (receipt != nil) { + [[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:receipt]; + [self af_setBackgroundImageDownloadReceipt:nil forState:state]; + } +} + +- (BOOL)isActiveTaskURLEqualToURLRequest:(NSURLRequest *)urlRequest forState:(UIControlState)state { + AFImageDownloadReceipt *receipt = [self af_imageDownloadReceiptForState:state]; + return [receipt.task.originalRequest.URL.absoluteString isEqualToString:urlRequest.URL.absoluteString]; +} + +- (BOOL)isActiveBackgroundTaskURLEqualToURLRequest:(NSURLRequest *)urlRequest forState:(UIControlState)state { + AFImageDownloadReceipt *receipt = [self af_backgroundImageDownloadReceiptForState:state]; + return [receipt.task.originalRequest.URL.absoluteString isEqualToString:urlRequest.URL.absoluteString]; +} + + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h new file mode 100644 index 0000000..8929252 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h @@ -0,0 +1,109 @@ +// UIImageView+AFNetworking.h +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import + +#import + +#if TARGET_OS_IOS || TARGET_OS_TV + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class AFImageDownloader; + +/** + This category adds methods to the UIKit framework's `UIImageView` class. The methods in this category provide support for loading remote images asynchronously from a URL. + */ +@interface UIImageView (AFNetworking) + +///------------------------------------ +/// @name Accessing the Image Downloader +///------------------------------------ + +/** + Set the shared image downloader used to download images. + + @param imageDownloader The shared image downloader used to download images. + */ ++ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader; + +/** + The shared image downloader used to download images. + */ ++ (AFImageDownloader *)sharedImageDownloader; + +///-------------------- +/// @name Setting Image +///-------------------- + +/** + Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + By default, URL requests have a `Accept` header field value of "image / *", a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:` + + @param url The URL used for the image request. + */ +- (void)setImageWithURL:(NSURL *)url; + +/** + Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + By default, URL requests have a `Accept` header field value of "image / *", a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:` + + @param url The URL used for the image request. + @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the image view will not change its image until the image request finishes. + */ +- (void)setImageWithURL:(NSURL *)url + placeholderImage:(nullable UIImage *)placeholderImage; + +/** + Asynchronously downloads an image from the specified URL request, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + If a success block is specified, it is the responsibility of the block to set the image of the image view before returning. If no success block is specified, the default behavior of setting the image with `self.image = image` is applied. + + @param urlRequest The URL request used for the image request. + @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the image view will not change its image until the image request finishes. + @param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`. + @param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred. + */ +- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest + placeholderImage:(nullable UIImage *)placeholderImage + success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success + failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure; + +/** + Cancels any executing image operation for the receiver, if one exists. + */ +- (void)cancelImageDownloadTask; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m new file mode 100644 index 0000000..8ae4950 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m @@ -0,0 +1,159 @@ +// UIImageView+AFNetworking.m +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import "UIImageView+AFNetworking.h" + +#import + +#if TARGET_OS_IOS || TARGET_OS_TV + +#import "AFImageDownloader.h" + +@interface UIImageView (_AFNetworking) +@property (readwrite, nonatomic, strong, setter = af_setActiveImageDownloadReceipt:) AFImageDownloadReceipt *af_activeImageDownloadReceipt; +@end + +@implementation UIImageView (_AFNetworking) + +- (AFImageDownloadReceipt *)af_activeImageDownloadReceipt { + return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, @selector(af_activeImageDownloadReceipt)); +} + +- (void)af_setActiveImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt { + objc_setAssociatedObject(self, @selector(af_activeImageDownloadReceipt), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end + +#pragma mark - + +@implementation UIImageView (AFNetworking) + ++ (AFImageDownloader *)sharedImageDownloader { + return objc_getAssociatedObject([UIImageView class], @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance]; +} + ++ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader { + objc_setAssociatedObject([UIImageView class], @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +- (void)setImageWithURL:(NSURL *)url { + [self setImageWithURL:url placeholderImage:nil]; +} + +- (void)setImageWithURL:(NSURL *)url + placeholderImage:(UIImage *)placeholderImage +{ + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + [request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; + + [self setImageWithURLRequest:request placeholderImage:placeholderImage success:nil failure:nil]; +} + +- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest + placeholderImage:(UIImage *)placeholderImage + success:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success + failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure +{ + if ([urlRequest URL] == nil) { + self.image = placeholderImage; + if (failure) { + NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorBadURL userInfo:nil]; + failure(urlRequest, nil, error); + } + return; + } + + if ([self isActiveTaskURLEqualToURLRequest:urlRequest]) { + return; + } + + [self cancelImageDownloadTask]; + + AFImageDownloader *downloader = [[self class] sharedImageDownloader]; + id imageCache = downloader.imageCache; + + //Use the image from the image cache if it exists + UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil]; + if (cachedImage) { + if (success) { + success(urlRequest, nil, cachedImage); + } else { + self.image = cachedImage; + } + [self clearActiveDownloadInformation]; + } else { + if (placeholderImage) { + self.image = placeholderImage; + } + + __weak __typeof(self)weakSelf = self; + NSUUID *downloadID = [NSUUID UUID]; + AFImageDownloadReceipt *receipt; + receipt = [downloader + downloadImageForURLRequest:urlRequest + withReceiptID:downloadID + success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) { + if (success) { + success(request, response, responseObject); + } else if (responseObject) { + strongSelf.image = responseObject; + } + [strongSelf clearActiveDownloadInformation]; + } + + } + failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) { + if (failure) { + failure(request, response, error); + } + [strongSelf clearActiveDownloadInformation]; + } + }]; + + self.af_activeImageDownloadReceipt = receipt; + } +} + +- (void)cancelImageDownloadTask { + if (self.af_activeImageDownloadReceipt != nil) { + [[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:self.af_activeImageDownloadReceipt]; + [self clearActiveDownloadInformation]; + } +} + +- (void)clearActiveDownloadInformation { + self.af_activeImageDownloadReceipt = nil; +} + +- (BOOL)isActiveTaskURLEqualToURLRequest:(NSURLRequest *)urlRequest { + return [self.af_activeImageDownloadReceipt.task.originalRequest.URL.absoluteString isEqualToString:urlRequest.URL.absoluteString]; +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h new file mode 100644 index 0000000..aa9c0b0 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h @@ -0,0 +1,43 @@ +// UIKit+AFNetworking.h +// +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import + +#ifndef _UIKIT_AFNETWORKING_ + #define _UIKIT_AFNETWORKING_ + +#if TARGET_OS_IOS || TARGET_OS_TV + #import "AFAutoPurgingImageCache.h" + #import "AFImageDownloader.h" + #import "UIActivityIndicatorView+AFNetworking.h" + #import "UIButton+AFNetworking.h" + #import "UIImageView+AFNetworking.h" + #import "UIProgressView+AFNetworking.h" +#endif + +#if TARGET_OS_IOS + #import "AFNetworkActivityIndicatorManager.h" + #import "UIRefreshControl+AFNetworking.h" + #import "WKWebView+AFNetworking.h" +#endif + +#endif /* _UIKIT_AFNETWORKING_ */ diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h new file mode 100644 index 0000000..8ea0a73 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h @@ -0,0 +1,64 @@ +// UIProgressView+AFNetworking.h +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import + +#import + +#if TARGET_OS_IOS || TARGET_OS_TV + +#import + +NS_ASSUME_NONNULL_BEGIN + + +/** + This category adds methods to the UIKit framework's `UIProgressView` class. The methods in this category provide support for binding the progress to the upload and download progress of a session task. + */ +@interface UIProgressView (AFNetworking) + +///------------------------------------ +/// @name Setting Session Task Progress +///------------------------------------ + +/** + Binds the progress to the upload progress of the specified session task. + + @param task The session task. + @param animated `YES` if the change should be animated, `NO` if the change should happen immediately. + */ +- (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task + animated:(BOOL)animated; + +/** + Binds the progress to the download progress of the specified session task. + + @param task The session task. + @param animated `YES` if the change should be animated, `NO` if the change should happen immediately. + */ +- (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task + animated:(BOOL)animated; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m new file mode 100644 index 0000000..2ae753e --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m @@ -0,0 +1,126 @@ +// UIProgressView+AFNetworking.m +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import "UIProgressView+AFNetworking.h" + +#import + +#if TARGET_OS_IOS || TARGET_OS_TV + +#import "AFURLSessionManager.h" + +static void * AFTaskCountOfBytesSentContext = &AFTaskCountOfBytesSentContext; +static void * AFTaskCountOfBytesReceivedContext = &AFTaskCountOfBytesReceivedContext; + +#pragma mark - + +@implementation UIProgressView (AFNetworking) + +- (BOOL)af_uploadProgressAnimated { + return [(NSNumber *)objc_getAssociatedObject(self, @selector(af_uploadProgressAnimated)) boolValue]; +} + +- (void)af_setUploadProgressAnimated:(BOOL)animated { + objc_setAssociatedObject(self, @selector(af_uploadProgressAnimated), @(animated), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (BOOL)af_downloadProgressAnimated { + return [(NSNumber *)objc_getAssociatedObject(self, @selector(af_downloadProgressAnimated)) boolValue]; +} + +- (void)af_setDownloadProgressAnimated:(BOOL)animated { + objc_setAssociatedObject(self, @selector(af_downloadProgressAnimated), @(animated), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +- (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task + animated:(BOOL)animated +{ + if (task.state == NSURLSessionTaskStateCompleted) { + return; + } + + [task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext]; + [task addObserver:self forKeyPath:@"countOfBytesSent" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext]; + + [self af_setUploadProgressAnimated:animated]; +} + +- (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task + animated:(BOOL)animated +{ + if (task.state == NSURLSessionTaskStateCompleted) { + return; + } + + [task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext]; + [task addObserver:self forKeyPath:@"countOfBytesReceived" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext]; + + [self af_setDownloadProgressAnimated:animated]; +} + +#pragma mark - NSKeyValueObserving + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(__unused NSDictionary *)change + context:(void *)context +{ + if (context == AFTaskCountOfBytesSentContext || context == AFTaskCountOfBytesReceivedContext) { + if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) { + if ([object countOfBytesExpectedToSend] > 0) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self setProgress:[object countOfBytesSent] / ([object countOfBytesExpectedToSend] * 1.0f) animated:self.af_uploadProgressAnimated]; + }); + } + } + + if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) { + if ([object countOfBytesExpectedToReceive] > 0) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self setProgress:[object countOfBytesReceived] / ([object countOfBytesExpectedToReceive] * 1.0f) animated:self.af_downloadProgressAnimated]; + }); + } + } + + if ([keyPath isEqualToString:NSStringFromSelector(@selector(state))]) { + if ([(NSURLSessionTask *)object state] == NSURLSessionTaskStateCompleted) { + @try { + [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(state))]; + + if (context == AFTaskCountOfBytesSentContext) { + [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))]; + } + + if (context == AFTaskCountOfBytesReceivedContext) { + [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))]; + } + } + @catch (NSException * __unused exception) {} + } + } + } +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h new file mode 100644 index 0000000..215eafc --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h @@ -0,0 +1,53 @@ +// UIRefreshControl+AFNetworking.m +// +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import + +#import + +#if TARGET_OS_IOS + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + This category adds methods to the UIKit framework's `UIRefreshControl` class. The methods in this category provide support for automatically beginning and ending refreshing depending on the loading state of a session task. + */ +@interface UIRefreshControl (AFNetworking) + +///----------------------------------- +/// @name Refreshing for Session Tasks +///----------------------------------- + +/** + Binds the refreshing state to the state of the specified task. + + @param task The task. If `nil`, automatic updating from any previously specified operation will be disabled. + */ +- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m new file mode 100644 index 0000000..cd46916 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m @@ -0,0 +1,113 @@ +// UIRefreshControl+AFNetworking.m +// +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import "UIRefreshControl+AFNetworking.h" +#import + +#if TARGET_OS_IOS + +#import "AFURLSessionManager.h" + +@interface AFRefreshControlNotificationObserver : NSObject +@property (readonly, nonatomic, weak) UIRefreshControl *refreshControl; +- (instancetype)initWithActivityRefreshControl:(UIRefreshControl *)refreshControl; + +- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task; + +@end + +@implementation UIRefreshControl (AFNetworking) + +- (AFRefreshControlNotificationObserver *)af_notificationObserver { + AFRefreshControlNotificationObserver *notificationObserver = objc_getAssociatedObject(self, @selector(af_notificationObserver)); + if (notificationObserver == nil) { + notificationObserver = [[AFRefreshControlNotificationObserver alloc] initWithActivityRefreshControl:self]; + objc_setAssociatedObject(self, @selector(af_notificationObserver), notificationObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return notificationObserver; +} + +- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task { + [[self af_notificationObserver] setRefreshingWithStateOfTask:task]; +} + +@end + +@implementation AFRefreshControlNotificationObserver + +- (instancetype)initWithActivityRefreshControl:(UIRefreshControl *)refreshControl +{ + self = [super init]; + if (self) { + _refreshControl = refreshControl; + } + return self; +} + +- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil]; + + if (task) { + UIRefreshControl *refreshControl = self.refreshControl; + if (task.state == NSURLSessionTaskStateRunning) { + [refreshControl beginRefreshing]; + + [notificationCenter addObserver:self selector:@selector(af_beginRefreshing) name:AFNetworkingTaskDidResumeNotification object:task]; + [notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingTaskDidCompleteNotification object:task]; + [notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingTaskDidSuspendNotification object:task]; + } else { + [refreshControl endRefreshing]; + } + } +} + +#pragma mark - + +- (void)af_beginRefreshing { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.refreshControl beginRefreshing]; + }); +} + +- (void)af_endRefreshing { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.refreshControl endRefreshing]; + }); +} + +#pragma mark - + +- (void)dealloc { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil]; +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/WKWebView+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/WKWebView+AFNetworking.h new file mode 100644 index 0000000..680fedf --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/WKWebView+AFNetworking.h @@ -0,0 +1,80 @@ +// WkWebView+AFNetworking.h +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import + +#import + +#if TARGET_OS_IOS + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class AFHTTPSessionManager; + +@interface WKWebView (AFNetworking) + +/** + The session manager used to download all request + */ +@property (nonatomic, strong) AFHTTPSessionManager *sessionManager; + +/** + Asynchronously loads the specified request. + + @param request A URL request identifying the location of the content to load. This must not be `nil`. + @param navigation The WKNavigation object that containts information for tracking the loading progress of a webpage. This must not be `nil`. + @param progress A progress object monitoring the current download progress. + @param success A block object to be executed when the request finishes loading successfully. This block returns the HTML string to be loaded by the web view, and takes two arguments: the response, and the response string. + @param failure A block object to be executed when the data task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error that occurred. + */ +- (void)loadRequest:(NSURLRequest *)request + navigation:(WKNavigation * _Nonnull)navigation + progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress + success:(nullable NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success + failure:(nullable void (^)(NSError *error))failure; + +/** + Asynchronously loads the data associated with a particular request with a specified MIME type and text encoding. + + @param request A URL request identifying the location of the content to load. This must not be `nil`. + @param navigation The WKNavigation object that containts information for tracking the loading progress of a webpage. This must not be `nil`. + @param MIMEType The MIME type of the content. Defaults to the content type of the response if not specified. + @param textEncodingName The IANA encoding name, as in `utf-8` or `utf-16`. Defaults to the response text encoding if not specified. + @param progress A progress object monitoring the current download progress. + @param success A block object to be executed when the request finishes loading successfully. This block returns the data to be loaded by the web view and takes two arguments: the response, and the downloaded data. + @param failure A block object to be executed when the data task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error that occurred. + */ +- (void)loadRequest:(NSURLRequest *)request + navigation:(WKNavigation * _Nonnull)navigation + MIMEType:(nullable NSString *)MIMEType + textEncodingName:(nullable NSString *)textEncodingName + progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress + success:(nullable NSData * (^)(NSHTTPURLResponse *response, NSData *data))success + failure:(nullable void (^)(NSError *error))failure; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/WKWebView+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/WKWebView+AFNetworking.m new file mode 100644 index 0000000..6eca3c3 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/WKWebView+AFNetworking.m @@ -0,0 +1,154 @@ +// WkWebView+AFNetworking.m +// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) +// +// 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. + +#import "WKWebView+AFNetworking.h" + +#import + +#if TARGET_OS_IOS + +#import "AFHTTPSessionManager.h" +#import "AFURLResponseSerialization.h" +#import "AFURLRequestSerialization.h" + +@interface WKWebView (_AFNetworking) +@property (readwrite, nonatomic, strong, setter = af_setURLSessionTask:) NSURLSessionDataTask *af_URLSessionTask; +@end + +@implementation WKWebView (_AFNetworking) + +- (NSURLSessionDataTask *)af_URLSessionTask { + return (NSURLSessionDataTask *)objc_getAssociatedObject(self, @selector(af_URLSessionTask)); +} + +- (void)af_setURLSessionTask:(NSURLSessionDataTask *)af_URLSessionTask { + objc_setAssociatedObject(self, @selector(af_URLSessionTask), af_URLSessionTask, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end + +#pragma mark - + +@implementation WKWebView (AFNetworking) + +- (AFHTTPSessionManager *)sessionManager { + static AFHTTPSessionManager *_af_defaultHTTPSessionManager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _af_defaultHTTPSessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; + _af_defaultHTTPSessionManager.requestSerializer = [AFHTTPRequestSerializer serializer]; + _af_defaultHTTPSessionManager.responseSerializer = [AFHTTPResponseSerializer serializer]; + }); + + return objc_getAssociatedObject(self, @selector(sessionManager)) ?: _af_defaultHTTPSessionManager; +} + +- (void)setSessionManager:(AFHTTPSessionManager *)sessionManager { + objc_setAssociatedObject(self, @selector(sessionManager), sessionManager, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (AFHTTPResponseSerializer *)responseSerializer { + static AFHTTPResponseSerializer *_af_defaultResponseSerializer = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _af_defaultResponseSerializer = [AFHTTPResponseSerializer serializer]; + }); + + return objc_getAssociatedObject(self, @selector(responseSerializer)) ?: _af_defaultResponseSerializer; +} + +- (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { + objc_setAssociatedObject(self, @selector(responseSerializer), responseSerializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +- (void)loadRequest:(NSURLRequest *)request + navigation:(WKNavigation * _Nonnull)navigation + progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress + success:(nullable NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success + failure:(nullable void (^)(NSError *error))failure { + [self loadRequest:request navigation:navigation MIMEType:nil textEncodingName:nil progress:progress success:^NSData * _Nonnull(NSHTTPURLResponse * _Nonnull response, NSData * _Nonnull data) { + NSStringEncoding stringEncoding = NSUTF8StringEncoding; + if (response.textEncodingName) { + CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName); + if (encoding != kCFStringEncodingInvalidId) { + stringEncoding = CFStringConvertEncodingToNSStringEncoding(encoding); + } + } + + NSString *string = [[NSString alloc] initWithData:data encoding:stringEncoding]; + if (success) { + string = success(response, string); + } + + return [string dataUsingEncoding:stringEncoding]; + } failure:failure]; +} + +- (void)loadRequest:(NSURLRequest *)request + navigation:(WKNavigation * _Nonnull)navigation + MIMEType:(nullable NSString *)MIMEType + textEncodingName:(nullable NSString *)textEncodingName + progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress + success:(nullable NSData * (^)(NSHTTPURLResponse *response, NSData *data))success + failure:(nullable void (^)(NSError *error))failure { + NSParameterAssert(request); + + if (self.af_URLSessionTask.state == NSURLSessionTaskStateRunning || self.af_URLSessionTask.state == NSURLSessionTaskStateSuspended) { + [self.af_URLSessionTask cancel]; + } + self.af_URLSessionTask = nil; + + __weak __typeof(self)weakSelf = self; + __block NSURLSessionDataTask *dataTask; + __strong __typeof(weakSelf) strongSelf = weakSelf; + __strong __typeof(weakSelf.navigationDelegate) strongSelfDelegate = strongSelf.navigationDelegate; + dataTask = [self.sessionManager dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) { + if (error) { + if (failure) { + failure(error); + } + } else { + if (success) { + success((NSHTTPURLResponse *)response, responseObject); + } + [strongSelf loadData:responseObject MIMEType:MIMEType characterEncodingName:textEncodingName baseURL:[dataTask.currentRequest URL]]; + + if ([strongSelfDelegate respondsToSelector:@selector(webView:didFinishNavigation:)]) { + [strongSelfDelegate webView:strongSelf didFinishNavigation:navigation]; + } + } + }]; + self.af_URLSessionTask = dataTask; + if (progress != nil) { + *progress = [self.sessionManager downloadProgressForTask:dataTask]; + } + [self.af_URLSessionTask resume]; + + if ([strongSelfDelegate respondsToSelector:@selector(webView:didStartProvisionalNavigation:)]) { + [strongSelfDelegate webView:self didStartProvisionalNavigation:navigation]; + } +} + +@end + +#endif diff --git a/Pods/Headers/Private/AFNetworking/AFAutoPurgingImageCache.h b/Pods/Headers/Private/AFNetworking/AFAutoPurgingImageCache.h new file mode 120000 index 0000000..f9dc7db --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFAutoPurgingImageCache.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFCompatibilityMacros.h b/Pods/Headers/Private/AFNetworking/AFCompatibilityMacros.h new file mode 120000 index 0000000..487b3fe --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFCompatibilityMacros.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFCompatibilityMacros.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFHTTPSessionManager.h b/Pods/Headers/Private/AFNetworking/AFHTTPSessionManager.h new file mode 120000 index 0000000..56feb9f --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFHTTPSessionManager.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFHTTPSessionManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFImageDownloader.h b/Pods/Headers/Private/AFNetworking/AFImageDownloader.h new file mode 120000 index 0000000..ce47c92 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFImageDownloader.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/AFImageDownloader.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFNetworkActivityIndicatorManager.h b/Pods/Headers/Private/AFNetworking/AFNetworkActivityIndicatorManager.h new file mode 120000 index 0000000..67519d9 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFNetworkActivityIndicatorManager.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFNetworkReachabilityManager.h b/Pods/Headers/Private/AFNetworking/AFNetworkReachabilityManager.h new file mode 120000 index 0000000..68fc774 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFNetworkReachabilityManager.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFNetworkReachabilityManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFNetworking.h b/Pods/Headers/Private/AFNetworking/AFNetworking.h new file mode 120000 index 0000000..a5a38da --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFSecurityPolicy.h b/Pods/Headers/Private/AFNetworking/AFSecurityPolicy.h new file mode 120000 index 0000000..fd1322d --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFSecurityPolicy.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFSecurityPolicy.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFURLRequestSerialization.h b/Pods/Headers/Private/AFNetworking/AFURLRequestSerialization.h new file mode 120000 index 0000000..ca8209b --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFURLRequestSerialization.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFURLRequestSerialization.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFURLResponseSerialization.h b/Pods/Headers/Private/AFNetworking/AFURLResponseSerialization.h new file mode 120000 index 0000000..e36a765 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFURLResponseSerialization.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFURLResponseSerialization.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFURLSessionManager.h b/Pods/Headers/Private/AFNetworking/AFURLSessionManager.h new file mode 120000 index 0000000..835101d --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFURLSessionManager.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFURLSessionManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIActivityIndicatorView+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIActivityIndicatorView+AFNetworking.h new file mode 120000 index 0000000..c534ebf --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/UIActivityIndicatorView+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIButton+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIButton+AFNetworking.h new file mode 120000 index 0000000..8f2e221 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/UIButton+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIImageView+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIImageView+AFNetworking.h new file mode 120000 index 0000000..a95d673 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/UIImageView+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIKit+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIKit+AFNetworking.h new file mode 120000 index 0000000..95017cc --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/UIKit+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIProgressView+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIProgressView+AFNetworking.h new file mode 120000 index 0000000..730b167 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/UIProgressView+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIRefreshControl+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIRefreshControl+AFNetworking.h new file mode 120000 index 0000000..8efd826 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/UIRefreshControl+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/WKWebView+AFNetworking.h b/Pods/Headers/Private/AFNetworking/WKWebView+AFNetworking.h new file mode 120000 index 0000000..163607c --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/WKWebView+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/WKWebView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/MJExtension.h b/Pods/Headers/Private/MJExtension/MJExtension.h new file mode 120000 index 0000000..1df1880 --- /dev/null +++ b/Pods/Headers/Private/MJExtension/MJExtension.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/MJExtension.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/MJExtensionConst.h b/Pods/Headers/Private/MJExtension/MJExtensionConst.h new file mode 120000 index 0000000..f1a01cd --- /dev/null +++ b/Pods/Headers/Private/MJExtension/MJExtensionConst.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/MJExtensionConst.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/MJFoundation.h b/Pods/Headers/Private/MJExtension/MJFoundation.h new file mode 120000 index 0000000..7c66d2f --- /dev/null +++ b/Pods/Headers/Private/MJExtension/MJFoundation.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/MJFoundation.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/MJProperty.h b/Pods/Headers/Private/MJExtension/MJProperty.h new file mode 120000 index 0000000..5361eff --- /dev/null +++ b/Pods/Headers/Private/MJExtension/MJProperty.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/MJProperty.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/MJPropertyKey.h b/Pods/Headers/Private/MJExtension/MJPropertyKey.h new file mode 120000 index 0000000..d740b30 --- /dev/null +++ b/Pods/Headers/Private/MJExtension/MJPropertyKey.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/MJPropertyKey.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/MJPropertyType.h b/Pods/Headers/Private/MJExtension/MJPropertyType.h new file mode 120000 index 0000000..3ab7735 --- /dev/null +++ b/Pods/Headers/Private/MJExtension/MJPropertyType.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/MJPropertyType.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/NSObject+MJClass.h b/Pods/Headers/Private/MJExtension/NSObject+MJClass.h new file mode 120000 index 0000000..9dde422 --- /dev/null +++ b/Pods/Headers/Private/MJExtension/NSObject+MJClass.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/NSObject+MJClass.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/NSObject+MJCoding.h b/Pods/Headers/Private/MJExtension/NSObject+MJCoding.h new file mode 120000 index 0000000..8f4f799 --- /dev/null +++ b/Pods/Headers/Private/MJExtension/NSObject+MJCoding.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/NSObject+MJCoding.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/NSObject+MJKeyValue.h b/Pods/Headers/Private/MJExtension/NSObject+MJKeyValue.h new file mode 120000 index 0000000..9d5b17d --- /dev/null +++ b/Pods/Headers/Private/MJExtension/NSObject+MJKeyValue.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/NSObject+MJKeyValue.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/NSObject+MJProperty.h b/Pods/Headers/Private/MJExtension/NSObject+MJProperty.h new file mode 120000 index 0000000..2ee634e --- /dev/null +++ b/Pods/Headers/Private/MJExtension/NSObject+MJProperty.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/NSObject+MJProperty.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJExtension/NSString+MJExtension.h b/Pods/Headers/Private/MJExtension/NSString+MJExtension.h new file mode 120000 index 0000000..583ce69 --- /dev/null +++ b/Pods/Headers/Private/MJExtension/NSString+MJExtension.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/NSString+MJExtension.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefresh.h b/Pods/Headers/Private/MJRefresh/MJRefresh.h new file mode 120000 index 0000000..d83fdd6 --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefresh.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/MJRefresh.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshAutoFooter.h b/Pods/Headers/Private/MJRefresh/MJRefreshAutoFooter.h new file mode 120000 index 0000000..5bb1fbc --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefreshAutoFooter.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshAutoGifFooter.h b/Pods/Headers/Private/MJRefresh/MJRefreshAutoGifFooter.h new file mode 120000 index 0000000..1435437 --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefreshAutoGifFooter.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshAutoNormalFooter.h b/Pods/Headers/Private/MJRefresh/MJRefreshAutoNormalFooter.h new file mode 120000 index 0000000..6b87abe --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefreshAutoNormalFooter.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshAutoStateFooter.h b/Pods/Headers/Private/MJRefresh/MJRefreshAutoStateFooter.h new file mode 120000 index 0000000..19b2053 --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefreshAutoStateFooter.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshBackFooter.h b/Pods/Headers/Private/MJRefresh/MJRefreshBackFooter.h new file mode 120000 index 0000000..3df4973 --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefreshBackFooter.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshBackGifFooter.h b/Pods/Headers/Private/MJRefresh/MJRefreshBackGifFooter.h new file mode 120000 index 0000000..e5d5f96 --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefreshBackGifFooter.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshBackNormalFooter.h b/Pods/Headers/Private/MJRefresh/MJRefreshBackNormalFooter.h new file mode 120000 index 0000000..c578c7f --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefreshBackNormalFooter.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshBackStateFooter.h b/Pods/Headers/Private/MJRefresh/MJRefreshBackStateFooter.h new file mode 120000 index 0000000..3317188 --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefreshBackStateFooter.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshComponent.h b/Pods/Headers/Private/MJRefresh/MJRefreshComponent.h new file mode 120000 index 0000000..35cb1b1 --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefreshComponent.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Base/MJRefreshComponent.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshConfig.h b/Pods/Headers/Private/MJRefresh/MJRefreshConfig.h new file mode 120000 index 0000000..f18429b --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefreshConfig.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/MJRefreshConfig.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshConst.h b/Pods/Headers/Private/MJRefresh/MJRefreshConst.h new file mode 120000 index 0000000..035954b --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefreshConst.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/MJRefreshConst.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshFooter.h b/Pods/Headers/Private/MJRefresh/MJRefreshFooter.h new file mode 120000 index 0000000..743dda2 --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefreshFooter.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Base/MJRefreshFooter.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshGifHeader.h b/Pods/Headers/Private/MJRefresh/MJRefreshGifHeader.h new file mode 120000 index 0000000..aa6afbd --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefreshGifHeader.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshHeader.h b/Pods/Headers/Private/MJRefresh/MJRefreshHeader.h new file mode 120000 index 0000000..3a38b26 --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefreshHeader.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Base/MJRefreshHeader.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshNormalHeader.h b/Pods/Headers/Private/MJRefresh/MJRefreshNormalHeader.h new file mode 120000 index 0000000..b5cc164 --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefreshNormalHeader.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshNormalTrailer.h b/Pods/Headers/Private/MJRefresh/MJRefreshNormalTrailer.h new file mode 120000 index 0000000..f561996 --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefreshNormalTrailer.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Trailer/MJRefreshNormalTrailer.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshStateHeader.h b/Pods/Headers/Private/MJRefresh/MJRefreshStateHeader.h new file mode 120000 index 0000000..7bd25cf --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefreshStateHeader.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshStateTrailer.h b/Pods/Headers/Private/MJRefresh/MJRefreshStateTrailer.h new file mode 120000 index 0000000..7b54429 --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefreshStateTrailer.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Trailer/MJRefreshStateTrailer.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/MJRefreshTrailer.h b/Pods/Headers/Private/MJRefresh/MJRefreshTrailer.h new file mode 120000 index 0000000..a25342e --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/MJRefreshTrailer.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Base/MJRefreshTrailer.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/NSBundle+MJRefresh.h b/Pods/Headers/Private/MJRefresh/NSBundle+MJRefresh.h new file mode 120000 index 0000000..2a628b4 --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/NSBundle+MJRefresh.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/NSBundle+MJRefresh.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/UICollectionViewLayout+MJRefresh.h b/Pods/Headers/Private/MJRefresh/UICollectionViewLayout+MJRefresh.h new file mode 120000 index 0000000..151df84 --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/UICollectionViewLayout+MJRefresh.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/UICollectionViewLayout+MJRefresh.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/UIScrollView+MJExtension.h b/Pods/Headers/Private/MJRefresh/UIScrollView+MJExtension.h new file mode 120000 index 0000000..1919784 --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/UIScrollView+MJExtension.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/UIScrollView+MJExtension.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/UIScrollView+MJRefresh.h b/Pods/Headers/Private/MJRefresh/UIScrollView+MJRefresh.h new file mode 120000 index 0000000..47b703d --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/UIScrollView+MJRefresh.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/UIScrollView+MJRefresh.h \ No newline at end of file diff --git a/Pods/Headers/Private/MJRefresh/UIView+MJExtension.h b/Pods/Headers/Private/MJRefresh/UIView+MJExtension.h new file mode 120000 index 0000000..5f19813 --- /dev/null +++ b/Pods/Headers/Private/MJRefresh/UIView+MJExtension.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/UIView+MJExtension.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFAutoPurgingImageCache.h b/Pods/Headers/Public/AFNetworking/AFAutoPurgingImageCache.h new file mode 120000 index 0000000..f9dc7db --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFAutoPurgingImageCache.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFCompatibilityMacros.h b/Pods/Headers/Public/AFNetworking/AFCompatibilityMacros.h new file mode 120000 index 0000000..487b3fe --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFCompatibilityMacros.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFCompatibilityMacros.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFHTTPSessionManager.h b/Pods/Headers/Public/AFNetworking/AFHTTPSessionManager.h new file mode 120000 index 0000000..56feb9f --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFHTTPSessionManager.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFHTTPSessionManager.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFImageDownloader.h b/Pods/Headers/Public/AFNetworking/AFImageDownloader.h new file mode 120000 index 0000000..ce47c92 --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFImageDownloader.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/AFImageDownloader.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFNetworkActivityIndicatorManager.h b/Pods/Headers/Public/AFNetworking/AFNetworkActivityIndicatorManager.h new file mode 120000 index 0000000..67519d9 --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFNetworkActivityIndicatorManager.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFNetworkReachabilityManager.h b/Pods/Headers/Public/AFNetworking/AFNetworkReachabilityManager.h new file mode 120000 index 0000000..68fc774 --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFNetworkReachabilityManager.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFNetworkReachabilityManager.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFNetworking.h b/Pods/Headers/Public/AFNetworking/AFNetworking.h new file mode 120000 index 0000000..a5a38da --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFSecurityPolicy.h b/Pods/Headers/Public/AFNetworking/AFSecurityPolicy.h new file mode 120000 index 0000000..fd1322d --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFSecurityPolicy.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFSecurityPolicy.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFURLRequestSerialization.h b/Pods/Headers/Public/AFNetworking/AFURLRequestSerialization.h new file mode 120000 index 0000000..ca8209b --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFURLRequestSerialization.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFURLRequestSerialization.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFURLResponseSerialization.h b/Pods/Headers/Public/AFNetworking/AFURLResponseSerialization.h new file mode 120000 index 0000000..e36a765 --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFURLResponseSerialization.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFURLResponseSerialization.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFURLSessionManager.h b/Pods/Headers/Public/AFNetworking/AFURLSessionManager.h new file mode 120000 index 0000000..835101d --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFURLSessionManager.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFURLSessionManager.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIActivityIndicatorView+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIActivityIndicatorView+AFNetworking.h new file mode 120000 index 0000000..c534ebf --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/UIActivityIndicatorView+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIButton+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIButton+AFNetworking.h new file mode 120000 index 0000000..8f2e221 --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/UIButton+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIImageView+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIImageView+AFNetworking.h new file mode 120000 index 0000000..a95d673 --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/UIImageView+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIKit+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIKit+AFNetworking.h new file mode 120000 index 0000000..95017cc --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/UIKit+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIProgressView+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIProgressView+AFNetworking.h new file mode 120000 index 0000000..730b167 --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/UIProgressView+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIRefreshControl+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIRefreshControl+AFNetworking.h new file mode 120000 index 0000000..8efd826 --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/UIRefreshControl+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/WKWebView+AFNetworking.h b/Pods/Headers/Public/AFNetworking/WKWebView+AFNetworking.h new file mode 120000 index 0000000..163607c --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/WKWebView+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/WKWebView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/MJExtension.h b/Pods/Headers/Public/MJExtension/MJExtension.h new file mode 120000 index 0000000..1df1880 --- /dev/null +++ b/Pods/Headers/Public/MJExtension/MJExtension.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/MJExtension.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/MJExtensionConst.h b/Pods/Headers/Public/MJExtension/MJExtensionConst.h new file mode 120000 index 0000000..f1a01cd --- /dev/null +++ b/Pods/Headers/Public/MJExtension/MJExtensionConst.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/MJExtensionConst.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/MJFoundation.h b/Pods/Headers/Public/MJExtension/MJFoundation.h new file mode 120000 index 0000000..7c66d2f --- /dev/null +++ b/Pods/Headers/Public/MJExtension/MJFoundation.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/MJFoundation.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/MJProperty.h b/Pods/Headers/Public/MJExtension/MJProperty.h new file mode 120000 index 0000000..5361eff --- /dev/null +++ b/Pods/Headers/Public/MJExtension/MJProperty.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/MJProperty.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/MJPropertyKey.h b/Pods/Headers/Public/MJExtension/MJPropertyKey.h new file mode 120000 index 0000000..d740b30 --- /dev/null +++ b/Pods/Headers/Public/MJExtension/MJPropertyKey.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/MJPropertyKey.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/MJPropertyType.h b/Pods/Headers/Public/MJExtension/MJPropertyType.h new file mode 120000 index 0000000..3ab7735 --- /dev/null +++ b/Pods/Headers/Public/MJExtension/MJPropertyType.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/MJPropertyType.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/NSObject+MJClass.h b/Pods/Headers/Public/MJExtension/NSObject+MJClass.h new file mode 120000 index 0000000..9dde422 --- /dev/null +++ b/Pods/Headers/Public/MJExtension/NSObject+MJClass.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/NSObject+MJClass.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/NSObject+MJCoding.h b/Pods/Headers/Public/MJExtension/NSObject+MJCoding.h new file mode 120000 index 0000000..8f4f799 --- /dev/null +++ b/Pods/Headers/Public/MJExtension/NSObject+MJCoding.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/NSObject+MJCoding.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/NSObject+MJKeyValue.h b/Pods/Headers/Public/MJExtension/NSObject+MJKeyValue.h new file mode 120000 index 0000000..9d5b17d --- /dev/null +++ b/Pods/Headers/Public/MJExtension/NSObject+MJKeyValue.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/NSObject+MJKeyValue.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/NSObject+MJProperty.h b/Pods/Headers/Public/MJExtension/NSObject+MJProperty.h new file mode 120000 index 0000000..2ee634e --- /dev/null +++ b/Pods/Headers/Public/MJExtension/NSObject+MJProperty.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/NSObject+MJProperty.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJExtension/NSString+MJExtension.h b/Pods/Headers/Public/MJExtension/NSString+MJExtension.h new file mode 120000 index 0000000..583ce69 --- /dev/null +++ b/Pods/Headers/Public/MJExtension/NSString+MJExtension.h @@ -0,0 +1 @@ +../../../MJExtension/MJExtension/NSString+MJExtension.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefresh.h b/Pods/Headers/Public/MJRefresh/MJRefresh.h new file mode 120000 index 0000000..d83fdd6 --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefresh.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/MJRefresh.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshAutoFooter.h b/Pods/Headers/Public/MJRefresh/MJRefreshAutoFooter.h new file mode 120000 index 0000000..5bb1fbc --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefreshAutoFooter.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshAutoGifFooter.h b/Pods/Headers/Public/MJRefresh/MJRefreshAutoGifFooter.h new file mode 120000 index 0000000..1435437 --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefreshAutoGifFooter.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshAutoNormalFooter.h b/Pods/Headers/Public/MJRefresh/MJRefreshAutoNormalFooter.h new file mode 120000 index 0000000..6b87abe --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefreshAutoNormalFooter.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshAutoStateFooter.h b/Pods/Headers/Public/MJRefresh/MJRefreshAutoStateFooter.h new file mode 120000 index 0000000..19b2053 --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefreshAutoStateFooter.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshBackFooter.h b/Pods/Headers/Public/MJRefresh/MJRefreshBackFooter.h new file mode 120000 index 0000000..3df4973 --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefreshBackFooter.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshBackGifFooter.h b/Pods/Headers/Public/MJRefresh/MJRefreshBackGifFooter.h new file mode 120000 index 0000000..e5d5f96 --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefreshBackGifFooter.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshBackNormalFooter.h b/Pods/Headers/Public/MJRefresh/MJRefreshBackNormalFooter.h new file mode 120000 index 0000000..c578c7f --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefreshBackNormalFooter.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshBackStateFooter.h b/Pods/Headers/Public/MJRefresh/MJRefreshBackStateFooter.h new file mode 120000 index 0000000..3317188 --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefreshBackStateFooter.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshComponent.h b/Pods/Headers/Public/MJRefresh/MJRefreshComponent.h new file mode 120000 index 0000000..35cb1b1 --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefreshComponent.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Base/MJRefreshComponent.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshConfig.h b/Pods/Headers/Public/MJRefresh/MJRefreshConfig.h new file mode 120000 index 0000000..f18429b --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefreshConfig.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/MJRefreshConfig.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshConst.h b/Pods/Headers/Public/MJRefresh/MJRefreshConst.h new file mode 120000 index 0000000..035954b --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefreshConst.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/MJRefreshConst.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshFooter.h b/Pods/Headers/Public/MJRefresh/MJRefreshFooter.h new file mode 120000 index 0000000..743dda2 --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefreshFooter.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Base/MJRefreshFooter.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshGifHeader.h b/Pods/Headers/Public/MJRefresh/MJRefreshGifHeader.h new file mode 120000 index 0000000..aa6afbd --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefreshGifHeader.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshHeader.h b/Pods/Headers/Public/MJRefresh/MJRefreshHeader.h new file mode 120000 index 0000000..3a38b26 --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefreshHeader.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Base/MJRefreshHeader.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshNormalHeader.h b/Pods/Headers/Public/MJRefresh/MJRefreshNormalHeader.h new file mode 120000 index 0000000..b5cc164 --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefreshNormalHeader.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshNormalTrailer.h b/Pods/Headers/Public/MJRefresh/MJRefreshNormalTrailer.h new file mode 120000 index 0000000..f561996 --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefreshNormalTrailer.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Trailer/MJRefreshNormalTrailer.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshStateHeader.h b/Pods/Headers/Public/MJRefresh/MJRefreshStateHeader.h new file mode 120000 index 0000000..7bd25cf --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefreshStateHeader.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshStateTrailer.h b/Pods/Headers/Public/MJRefresh/MJRefreshStateTrailer.h new file mode 120000 index 0000000..7b54429 --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefreshStateTrailer.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Custom/Trailer/MJRefreshStateTrailer.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/MJRefreshTrailer.h b/Pods/Headers/Public/MJRefresh/MJRefreshTrailer.h new file mode 120000 index 0000000..a25342e --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/MJRefreshTrailer.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/Base/MJRefreshTrailer.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/NSBundle+MJRefresh.h b/Pods/Headers/Public/MJRefresh/NSBundle+MJRefresh.h new file mode 120000 index 0000000..2a628b4 --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/NSBundle+MJRefresh.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/NSBundle+MJRefresh.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/UICollectionViewLayout+MJRefresh.h b/Pods/Headers/Public/MJRefresh/UICollectionViewLayout+MJRefresh.h new file mode 120000 index 0000000..151df84 --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/UICollectionViewLayout+MJRefresh.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/UICollectionViewLayout+MJRefresh.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/UIScrollView+MJExtension.h b/Pods/Headers/Public/MJRefresh/UIScrollView+MJExtension.h new file mode 120000 index 0000000..1919784 --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/UIScrollView+MJExtension.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/UIScrollView+MJExtension.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/UIScrollView+MJRefresh.h b/Pods/Headers/Public/MJRefresh/UIScrollView+MJRefresh.h new file mode 120000 index 0000000..47b703d --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/UIScrollView+MJRefresh.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/UIScrollView+MJRefresh.h \ No newline at end of file diff --git a/Pods/Headers/Public/MJRefresh/UIView+MJExtension.h b/Pods/Headers/Public/MJRefresh/UIView+MJExtension.h new file mode 120000 index 0000000..5f19813 --- /dev/null +++ b/Pods/Headers/Public/MJRefresh/UIView+MJExtension.h @@ -0,0 +1 @@ +../../../MJRefresh/MJRefresh/UIView+MJExtension.h \ No newline at end of file diff --git a/Pods/MJExtension/LICENSE b/Pods/MJExtension/LICENSE new file mode 100644 index 0000000..9c294c6 --- /dev/null +++ b/Pods/MJExtension/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2013-2019 MJExtension (https://github.com/CoderMJLee/MJExtension) + +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. diff --git a/Pods/MJExtension/MJExtension/MJExtension.h b/Pods/MJExtension/MJExtension/MJExtension.h new file mode 100644 index 0000000..f2ede55 --- /dev/null +++ b/Pods/MJExtension/MJExtension/MJExtension.h @@ -0,0 +1,27 @@ +// +// MJExtension.h +// MJExtension +// +// Created by mj on 14-1-15. +// Copyright (c) 2014年 小码哥. All rights reserved. +// + +#import +#import "NSObject+MJCoding.h" +#import "NSObject+MJProperty.h" +#import "NSObject+MJClass.h" +#import "NSObject+MJKeyValue.h" +#import "NSString+MJExtension.h" +#import "MJExtensionConst.h" + +#import "MJFoundation.h" + +//! Project version number for MJExtension. +FOUNDATION_EXPORT double MJExtensionVersionNumber; + +//! Project version string for MJExtension. +FOUNDATION_EXPORT const unsigned char MJExtensionVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/Pods/MJExtension/MJExtension/MJExtensionConst.h b/Pods/MJExtension/MJExtension/MJExtensionConst.h new file mode 100644 index 0000000..5120431 --- /dev/null +++ b/Pods/MJExtension/MJExtension/MJExtensionConst.h @@ -0,0 +1,111 @@ + +#ifndef __MJExtensionConst__H__ +#define __MJExtensionConst__H__ + +#import + +#ifndef MJ_LOCK +#define MJ_LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); +#endif + +#ifndef MJ_UNLOCK +#define MJ_UNLOCK(lock) dispatch_semaphore_signal(lock); +#endif + +// 信号量 +#define MJExtensionSemaphoreCreate \ +extern dispatch_semaphore_t mje_signalSemaphore; \ +extern dispatch_once_t mje_onceTokenSemaphore; \ +dispatch_once(&mje_onceTokenSemaphore, ^{ \ + mje_signalSemaphore = dispatch_semaphore_create(1); \ +}); + +// 过期 +#define MJExtensionDeprecated(instead) NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, instead) + +// 构建错误 +#define MJExtensionBuildError(clazz, msg) \ +NSError *error = [NSError errorWithDomain:msg code:250 userInfo:nil]; \ +[clazz setMj_error:error]; + +// 日志输出 +#ifdef DEBUG +#define MJExtensionLog(...) NSLog(__VA_ARGS__) +#else +#define MJExtensionLog(...) +#endif + +/** + * 断言 + * @param condition 条件 + * @param returnValue 返回值 + */ +#define MJExtensionAssertError(condition, returnValue, clazz, msg) \ +[clazz setMj_error:nil]; \ +if ((condition) == NO) { \ + MJExtensionBuildError(clazz, msg); \ + return returnValue;\ +} + +#define MJExtensionAssert2(condition, returnValue) \ +if ((condition) == NO) return returnValue; + +/** + * 断言 + * @param condition 条件 + */ +#define MJExtensionAssert(condition) MJExtensionAssert2(condition, ) + +/** + * 断言 + * @param param 参数 + * @param returnValue 返回值 + */ +#define MJExtensionAssertParamNotNil2(param, returnValue) \ +MJExtensionAssert2((param) != nil, returnValue) + +/** + * 断言 + * @param param 参数 + */ +#define MJExtensionAssertParamNotNil(param) MJExtensionAssertParamNotNil2(param, ) + +/** + * 打印所有的属性 + */ +#define MJLogAllIvars \ +- (NSString *)description \ +{ \ + return [self mj_keyValues].description; \ +} +#define MJExtensionLogAllProperties MJLogAllIvars + +/** 仅在 Debugger 展示所有的属性 */ +#define MJImplementDebugDescription \ +- (NSString *)debugDescription \ +{ \ +return [self mj_keyValues].debugDescription; \ +} + +/** + * 类型(属性类型) + */ +FOUNDATION_EXPORT NSString *const MJPropertyTypeInt; +FOUNDATION_EXPORT NSString *const MJPropertyTypeShort; +FOUNDATION_EXPORT NSString *const MJPropertyTypeFloat; +FOUNDATION_EXPORT NSString *const MJPropertyTypeDouble; +FOUNDATION_EXPORT NSString *const MJPropertyTypeLong; +FOUNDATION_EXPORT NSString *const MJPropertyTypeLongLong; +FOUNDATION_EXPORT NSString *const MJPropertyTypeChar; +FOUNDATION_EXPORT NSString *const MJPropertyTypeBOOL1; +FOUNDATION_EXPORT NSString *const MJPropertyTypeBOOL2; +FOUNDATION_EXPORT NSString *const MJPropertyTypePointer; + +FOUNDATION_EXPORT NSString *const MJPropertyTypeIvar; +FOUNDATION_EXPORT NSString *const MJPropertyTypeMethod; +FOUNDATION_EXPORT NSString *const MJPropertyTypeBlock; +FOUNDATION_EXPORT NSString *const MJPropertyTypeClass; +FOUNDATION_EXPORT NSString *const MJPropertyTypeSEL; +FOUNDATION_EXPORT NSString *const MJPropertyTypeId; + +#endif diff --git a/Pods/MJExtension/MJExtension/MJExtensionConst.m b/Pods/MJExtension/MJExtension/MJExtensionConst.m new file mode 100644 index 0000000..24bcca5 --- /dev/null +++ b/Pods/MJExtension/MJExtension/MJExtensionConst.m @@ -0,0 +1,27 @@ +#ifndef __MJExtensionConst__M__ +#define __MJExtensionConst__M__ + +#import + +/** + * 成员变量类型(属性类型) + */ +NSString *const MJPropertyTypeInt = @"i"; +NSString *const MJPropertyTypeShort = @"s"; +NSString *const MJPropertyTypeFloat = @"f"; +NSString *const MJPropertyTypeDouble = @"d"; +NSString *const MJPropertyTypeLong = @"l"; +NSString *const MJPropertyTypeLongLong = @"q"; +NSString *const MJPropertyTypeChar = @"c"; +NSString *const MJPropertyTypeBOOL1 = @"c"; +NSString *const MJPropertyTypeBOOL2 = @"b"; +NSString *const MJPropertyTypePointer = @"*"; + +NSString *const MJPropertyTypeIvar = @"^{objc_ivar=}"; +NSString *const MJPropertyTypeMethod = @"^{objc_method=}"; +NSString *const MJPropertyTypeBlock = @"@?"; +NSString *const MJPropertyTypeClass = @"#"; +NSString *const MJPropertyTypeSEL = @":"; +NSString *const MJPropertyTypeId = @"@"; + +#endif \ No newline at end of file diff --git a/Pods/MJExtension/MJExtension/MJFoundation.h b/Pods/MJExtension/MJExtension/MJFoundation.h new file mode 100644 index 0000000..f2c1967 --- /dev/null +++ b/Pods/MJExtension/MJExtension/MJFoundation.h @@ -0,0 +1,16 @@ +// +// MJFoundation.h +// MJExtensionExample +// +// Created by MJ Lee on 14/7/16. +// Copyright (c) 2014年 小码哥. All rights reserved. +// + +#import + +@interface MJFoundation : NSObject + ++ (BOOL)isClassFromFoundation:(Class)c; ++ (BOOL)isFromNSObjectProtocolProperty:(NSString *)propertyName; + +@end diff --git a/Pods/MJExtension/MJExtension/MJFoundation.m b/Pods/MJExtension/MJExtension/MJFoundation.m new file mode 100644 index 0000000..31e107d --- /dev/null +++ b/Pods/MJExtension/MJExtension/MJFoundation.m @@ -0,0 +1,70 @@ +// +// MJFoundation.m +// MJExtensionExample +// +// Created by MJ Lee on 14/7/16. +// Copyright (c) 2014年 小码哥. All rights reserved. +// + +#import "MJFoundation.h" +#import "MJExtensionConst.h" +#import +#import "objc/runtime.h" + +@implementation MJFoundation + ++ (BOOL)isClassFromFoundation:(Class)c +{ + if (c == [NSObject class] || c == [NSManagedObject class]) return YES; + + static NSSet *foundationClasses; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + // 集合中没有NSObject,因为几乎所有的类都是继承自NSObject,具体是不是NSObject需要特殊判断 + foundationClasses = [NSSet setWithObjects: + [NSURL class], + [NSDate class], + [NSValue class], + [NSData class], + [NSError class], + [NSArray class], + [NSDictionary class], + [NSString class], + [NSAttributedString class], nil]; + }); + + __block BOOL result = NO; + [foundationClasses enumerateObjectsUsingBlock:^(Class foundationClass, BOOL *stop) { + if ([c isSubclassOfClass:foundationClass]) { + result = YES; + *stop = YES; + } + }]; + return result; +} + ++ (BOOL)isFromNSObjectProtocolProperty:(NSString *)propertyName +{ + if (!propertyName) return NO; + + static NSSet *objectProtocolPropertyNames; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + unsigned int count = 0; + objc_property_t *propertyList = protocol_copyPropertyList(@protocol(NSObject), &count); + NSMutableSet *propertyNames = [NSMutableSet setWithCapacity:count]; + for (int i = 0; i < count; i++) { + objc_property_t property = propertyList[i]; + NSString *propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding]; + if (propertyName) { + [propertyNames addObject:propertyName]; + } + } + objectProtocolPropertyNames = [propertyNames copy]; + free(propertyList); + }); + + return [objectProtocolPropertyNames containsObject:propertyName]; +} + +@end diff --git a/Pods/MJExtension/MJExtension/MJProperty.h b/Pods/MJExtension/MJExtension/MJProperty.h new file mode 100644 index 0000000..90ac6bc --- /dev/null +++ b/Pods/MJExtension/MJExtension/MJProperty.h @@ -0,0 +1,53 @@ +// +// MJProperty.h +// MJExtensionExample +// +// Created by MJ Lee on 15/4/17. +// Copyright (c) 2015年 小码哥. All rights reserved. +// 包装一个成员属性 + +#import +#import +#import "MJPropertyType.h" +#import "MJPropertyKey.h" + +/** + * 包装一个成员 + */ +@interface MJProperty : NSObject +/** 成员属性 */ +@property (nonatomic, assign) objc_property_t property; +/** 成员属性的名字 */ +@property (nonatomic, readonly) NSString *name; + +/** 成员属性的类型 */ +@property (nonatomic, readonly) MJPropertyType *type; +/** 成员属性来源于哪个类(可能是父类) */ +@property (nonatomic, assign) Class srcClass; + +/**** 同一个成员属性 - 父类和子类的行为可能不一致(originKey、propertyKeys、objectClassInArray) ****/ +/** 设置最原始的key */ +- (void)setOriginKey:(id)originKey forClass:(Class)c; +/** 对应着字典中的多级key(里面存放的数组,数组里面都是MJPropertyKey对象) */ +- (NSArray *)propertyKeysForClass:(Class)c; + +/** 模型数组中的模型类型 */ +- (void)setObjectClassInArray:(Class)objectClass forClass:(Class)c; +- (Class)objectClassInArrayForClass:(Class)c; +/**** 同一个成员变量 - 父类和子类的行为可能不一致(key、keys、objectClassInArray) ****/ + +/** + * 设置object的成员变量值 + */ +- (void)setValue:(id)value forObject:(id)object; +/** + * 得到object的成员属性值 + */ +- (id)valueForObject:(id)object; + +/** + * 初始化 + */ ++ (instancetype)cachedPropertyWithProperty:(objc_property_t)property; + +@end diff --git a/Pods/MJExtension/MJExtension/MJProperty.m b/Pods/MJExtension/MJExtension/MJProperty.m new file mode 100644 index 0000000..dcda032 --- /dev/null +++ b/Pods/MJExtension/MJExtension/MJProperty.m @@ -0,0 +1,211 @@ +// +// MJProperty.m +// MJExtensionExample +// +// Created by MJ Lee on 15/4/17. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import "MJProperty.h" +#import "MJFoundation.h" +#import "MJExtensionConst.h" +#import +#include "TargetConditionals.h" + +@interface MJProperty() +@property (strong, nonatomic) NSMutableDictionary *propertyKeysDict; +@property (strong, nonatomic) NSMutableDictionary *objectClassInArrayDict; +@property (strong, nonatomic) dispatch_semaphore_t propertyKeysLock; +@property (strong, nonatomic) dispatch_semaphore_t objectClassInArrayLock; +@end + +@implementation MJProperty + +#pragma mark - 初始化 +- (instancetype)init +{ + if (self = [super init]) { + _propertyKeysDict = [NSMutableDictionary dictionary]; + _objectClassInArrayDict = [NSMutableDictionary dictionary]; + _propertyKeysLock = dispatch_semaphore_create(1); + _objectClassInArrayLock = dispatch_semaphore_create(1); + } + return self; +} + +#pragma mark - 缓存 ++ (instancetype)cachedPropertyWithProperty:(objc_property_t)property +{ + MJProperty *propertyObj = objc_getAssociatedObject(self, property); + if (propertyObj == nil) { + propertyObj = [[self alloc] init]; + propertyObj.property = property; + objc_setAssociatedObject(self, property, propertyObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return propertyObj; +} + +#pragma mark - 公共方法 +- (void)setProperty:(objc_property_t)property +{ + _property = property; + + MJExtensionAssertParamNotNil(property); + + // 1.属性名 + _name = @(property_getName(property)); + + // 2.成员类型 + NSString *attrs = @(property_getAttributes(property)); + NSUInteger dotLoc = [attrs rangeOfString:@","].location; + NSString *code = nil; + NSUInteger loc = 1; + if (dotLoc == NSNotFound) { // 没有, + code = [attrs substringFromIndex:loc]; + } else { + code = [attrs substringWithRange:NSMakeRange(loc, dotLoc - loc)]; + } + _type = [MJPropertyType cachedTypeWithCode:code]; +} + +/** + * 获得成员变量的值 + */ +- (id)valueForObject:(id)object +{ + if (self.type.KVCDisabled) return [NSNull null]; + + id value = [object valueForKey:self.name]; + + // 32位BOOL类型转换json后成Int类型 + /** https://github.com/CoderMJLee/MJExtension/issues/545 */ + // 32 bit device OR 32 bit Simulator +#if defined(__arm__) || (TARGET_OS_SIMULATOR && !__LP64__) + if (self.type.isBoolType) { + value = @([(NSNumber *)value boolValue]); + } +#endif + + return value; +} + +/** + * 设置成员变量的值 + */ +- (void)setValue:(id)value forObject:(id)object +{ + if (self.type.KVCDisabled || value == nil) return; + [object setValue:value forKey:self.name]; +} + +/** + * 通过字符串key创建对应的keys + */ +- (NSArray *)propertyKeysWithStringKey:(NSString *)stringKey +{ + if (stringKey.length == 0) return nil; + + NSMutableArray *propertyKeys = [NSMutableArray array]; + // 如果有多级映射 + NSArray *oldKeys = [stringKey componentsSeparatedByString:@"."]; + + for (NSString *oldKey in oldKeys) { + NSUInteger start = [oldKey rangeOfString:@"["].location; + if (start != NSNotFound) { // 有索引的key + NSString *prefixKey = [oldKey substringToIndex:start]; + NSString *indexKey = prefixKey; + if (prefixKey.length) { + MJPropertyKey *propertyKey = [[MJPropertyKey alloc] init]; + propertyKey.name = prefixKey; + [propertyKeys addObject:propertyKey]; + + indexKey = [oldKey stringByReplacingOccurrencesOfString:prefixKey withString:@""]; + } + + /** 解析索引 **/ + // 元素 + NSArray *cmps = [[indexKey stringByReplacingOccurrencesOfString:@"[" withString:@""] componentsSeparatedByString:@"]"]; + for (NSInteger i = 0; i + +typedef enum { + MJPropertyKeyTypeDictionary = 0, // 字典的key + MJPropertyKeyTypeArray // 数组的key +} MJPropertyKeyType; + +/** + * 属性的key + */ +@interface MJPropertyKey : NSObject +/** key的名字 */ +@property (copy, nonatomic) NSString *name; +/** key的种类,可能是@"10",可能是@"age" */ +@property (assign, nonatomic) MJPropertyKeyType type; + +/** + * 根据当前的key,也就是name,从object(字典或者数组)中取值 + */ +- (id)valueInObject:(id)object; + +@end diff --git a/Pods/MJExtension/MJExtension/MJPropertyKey.m b/Pods/MJExtension/MJExtension/MJPropertyKey.m new file mode 100644 index 0000000..438d019 --- /dev/null +++ b/Pods/MJExtension/MJExtension/MJPropertyKey.m @@ -0,0 +1,25 @@ +// +// MJPropertyKey.m +// MJExtensionExample +// +// Created by MJ Lee on 15/8/11. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import "MJPropertyKey.h" + +@implementation MJPropertyKey + +- (id)valueInObject:(id)object +{ + if ([object isKindOfClass:[NSDictionary class]] && self.type == MJPropertyKeyTypeDictionary) { + return object[self.name]; + } else if ([object isKindOfClass:[NSArray class]] && self.type == MJPropertyKeyTypeArray) { + NSArray *array = object; + NSUInteger index = self.name.intValue; + if (index < array.count) return array[index]; + return nil; + } + return nil; +} +@end diff --git a/Pods/MJExtension/MJExtension/MJPropertyType.h b/Pods/MJExtension/MJExtension/MJPropertyType.h new file mode 100755 index 0000000..8c53f27 --- /dev/null +++ b/Pods/MJExtension/MJExtension/MJPropertyType.h @@ -0,0 +1,39 @@ +// +// MJPropertyType.h +// MJExtension +// +// Created by mj on 14-1-15. +// Copyright (c) 2014年 小码哥. All rights reserved. +// 包装一种类型 + +#import + +/** + * 包装一种类型 + */ +@interface MJPropertyType : NSObject +/** 类型标识符 */ +@property (nonatomic, copy) NSString *code; + +/** 是否为id类型 */ +@property (nonatomic, readonly, getter=isIdType) BOOL idType; + +/** 是否为基本数字类型:int、float等 */ +@property (nonatomic, readonly, getter=isNumberType) BOOL numberType; + +/** 是否为BOOL类型 */ +@property (nonatomic, readonly, getter=isBoolType) BOOL boolType; + +/** 对象类型(如果是基本数据类型,此值为nil) */ +@property (nonatomic, readonly) Class typeClass; + +/** 类型是否来自于Foundation框架,比如NSString、NSArray */ +@property (nonatomic, readonly, getter = isFromFoundation) BOOL fromFoundation; +/** 类型是否不支持KVC */ +@property (nonatomic, readonly, getter = isKVCDisabled) BOOL KVCDisabled; + +/** + * 获得缓存的类型对象 + */ ++ (instancetype)cachedTypeWithCode:(NSString *)code; +@end \ No newline at end of file diff --git a/Pods/MJExtension/MJExtension/MJPropertyType.m b/Pods/MJExtension/MJExtension/MJPropertyType.m new file mode 100755 index 0000000..77d6b30 --- /dev/null +++ b/Pods/MJExtension/MJExtension/MJPropertyType.m @@ -0,0 +1,71 @@ +// +// MJPropertyType.m +// MJExtension +// +// Created by mj on 14-1-15. +// Copyright (c) 2014年 小码哥. All rights reserved. +// + +#import "MJPropertyType.h" +#import "MJExtension.h" +#import "MJFoundation.h" +#import "MJExtensionConst.h" + +@implementation MJPropertyType + ++ (instancetype)cachedTypeWithCode:(NSString *)code +{ + MJExtensionAssertParamNotNil2(code, nil); + + static NSMutableDictionary *types; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + types = [NSMutableDictionary dictionary]; + }); + + MJPropertyType *type = types[code]; + if (type == nil) { + type = [[self alloc] init]; + type.code = code; + types[code] = type; + } + return type; +} + +#pragma mark - 公共方法 +- (void)setCode:(NSString *)code +{ + _code = code; + + MJExtensionAssertParamNotNil(code); + + if ([code isEqualToString:MJPropertyTypeId]) { + _idType = YES; + } else if (code.length == 0) { + _KVCDisabled = YES; + } else if (code.length > 3 && [code hasPrefix:@"@\""]) { + // 去掉@"和",截取中间的类型名称 + _code = [code substringWithRange:NSMakeRange(2, code.length - 3)]; + _typeClass = NSClassFromString(_code); + _fromFoundation = [MJFoundation isClassFromFoundation:_typeClass]; + _numberType = [_typeClass isSubclassOfClass:[NSNumber class]]; + + } else if ([code isEqualToString:MJPropertyTypeSEL] || + [code isEqualToString:MJPropertyTypeIvar] || + [code isEqualToString:MJPropertyTypeMethod]) { + _KVCDisabled = YES; + } + + // 是否为数字类型 + NSString *lowerCode = _code.lowercaseString; + NSArray *numberTypes = @[MJPropertyTypeInt, MJPropertyTypeShort, MJPropertyTypeBOOL1, MJPropertyTypeBOOL2, MJPropertyTypeFloat, MJPropertyTypeDouble, MJPropertyTypeLong, MJPropertyTypeLongLong, MJPropertyTypeChar]; + if ([numberTypes containsObject:lowerCode]) { + _numberType = YES; + + if ([lowerCode isEqualToString:MJPropertyTypeBOOL1] + || [lowerCode isEqualToString:MJPropertyTypeBOOL2]) { + _boolType = YES; + } + } +} +@end diff --git a/Pods/MJExtension/MJExtension/NSObject+MJClass.h b/Pods/MJExtension/MJExtension/NSObject+MJClass.h new file mode 100644 index 0000000..260c8fc --- /dev/null +++ b/Pods/MJExtension/MJExtension/NSObject+MJClass.h @@ -0,0 +1,90 @@ +// +// NSObject+MJClass.h +// MJExtensionExample +// +// Created by MJ Lee on 15/8/11. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import + +/** + * 遍历所有类的block(父类) + */ +typedef void (^MJClassesEnumeration)(Class c, BOOL *stop); + +/** 这个数组中的属性名才会进行字典和模型的转换 */ +typedef NSArray * (^MJAllowedPropertyNames)(void); +/** 这个数组中的属性名才会进行归档 */ +typedef NSArray * (^MJAllowedCodingPropertyNames)(void); + +/** 这个数组中的属性名将会被忽略:不进行字典和模型的转换 */ +typedef NSArray * (^MJIgnoredPropertyNames)(void); +/** 这个数组中的属性名将会被忽略:不进行归档 */ +typedef NSArray * (^MJIgnoredCodingPropertyNames)(void); + +/** + * 类相关的扩展 + */ +@interface NSObject (MJClass) +/** + * 遍历所有的类 + */ ++ (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration; ++ (void)mj_enumerateAllClasses:(MJClassesEnumeration)enumeration; + +#pragma mark - 属性白名单配置 +/** + * 这个数组中的属性名才会进行字典和模型的转换 + * + * @param allowedPropertyNames 这个数组中的属性名才会进行字典和模型的转换 + */ ++ (void)mj_setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames; + +/** + * 这个数组中的属性名才会进行字典和模型的转换 + */ ++ (NSMutableArray *)mj_totalAllowedPropertyNames; + +#pragma mark - 属性黑名单配置 +/** + * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 + * + * @param ignoredPropertyNames 这个数组中的属性名将会被忽略:不进行字典和模型的转换 + */ ++ (void)mj_setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames; + +/** + * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 + */ ++ (NSMutableArray *)mj_totalIgnoredPropertyNames; + +#pragma mark - 归档属性白名单配置 +/** + * 这个数组中的属性名才会进行归档 + * + * @param allowedCodingPropertyNames 这个数组中的属性名才会进行归档 + */ ++ (void)mj_setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames; + +/** + * 这个数组中的属性名才会进行字典和模型的转换 + */ ++ (NSMutableArray *)mj_totalAllowedCodingPropertyNames; + +#pragma mark - 归档属性黑名单配置 +/** + * 这个数组中的属性名将会被忽略:不进行归档 + * + * @param ignoredCodingPropertyNames 这个数组中的属性名将会被忽略:不进行归档 + */ ++ (void)mj_setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames; + +/** + * 这个数组中的属性名将会被忽略:不进行归档 + */ ++ (NSMutableArray *)mj_totalIgnoredCodingPropertyNames; + +#pragma mark - 内部使用 ++ (void)mj_setupBlockReturnValue:(id (^)(void))block key:(const char *)key; +@end diff --git a/Pods/MJExtension/MJExtension/NSObject+MJClass.m b/Pods/MJExtension/MJExtension/NSObject+MJClass.m new file mode 100644 index 0000000..c476751 --- /dev/null +++ b/Pods/MJExtension/MJExtension/NSObject+MJClass.m @@ -0,0 +1,174 @@ +// +// NSObject+MJClass.m +// MJExtensionExample +// +// Created by MJ Lee on 15/8/11. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import "NSObject+MJClass.h" +#import "NSObject+MJCoding.h" +#import "NSObject+MJKeyValue.h" +#import "MJFoundation.h" +#import + +static const char MJAllowedPropertyNamesKey = '\0'; +static const char MJIgnoredPropertyNamesKey = '\0'; +static const char MJAllowedCodingPropertyNamesKey = '\0'; +static const char MJIgnoredCodingPropertyNamesKey = '\0'; + +@implementation NSObject (MJClass) + ++ (NSMutableDictionary *)mj_classDictForKey:(const void *)key +{ + static NSMutableDictionary *allowedPropertyNamesDict; + static NSMutableDictionary *ignoredPropertyNamesDict; + static NSMutableDictionary *allowedCodingPropertyNamesDict; + static NSMutableDictionary *ignoredCodingPropertyNamesDict; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + allowedPropertyNamesDict = [NSMutableDictionary dictionary]; + ignoredPropertyNamesDict = [NSMutableDictionary dictionary]; + allowedCodingPropertyNamesDict = [NSMutableDictionary dictionary]; + ignoredCodingPropertyNamesDict = [NSMutableDictionary dictionary]; + }); + + if (key == &MJAllowedPropertyNamesKey) return allowedPropertyNamesDict; + if (key == &MJIgnoredPropertyNamesKey) return ignoredPropertyNamesDict; + if (key == &MJAllowedCodingPropertyNamesKey) return allowedCodingPropertyNamesDict; + if (key == &MJIgnoredCodingPropertyNamesKey) return ignoredCodingPropertyNamesDict; + return nil; +} + ++ (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration +{ + // 1.没有block就直接返回 + if (enumeration == nil) return; + + // 2.停止遍历的标记 + BOOL stop = NO; + + // 3.当前正在遍历的类 + Class c = self; + + // 4.开始遍历每一个类 + while (c && !stop) { + // 4.1.执行操作 + enumeration(c, &stop); + + // 4.2.获得父类 + c = class_getSuperclass(c); + + if ([MJFoundation isClassFromFoundation:c]) break; + } +} + ++ (void)mj_enumerateAllClasses:(MJClassesEnumeration)enumeration +{ + // 1.没有block就直接返回 + if (enumeration == nil) return; + + // 2.停止遍历的标记 + BOOL stop = NO; + + // 3.当前正在遍历的类 + Class c = self; + + // 4.开始遍历每一个类 + while (c && !stop) { + // 4.1.执行操作 + enumeration(c, &stop); + + // 4.2.获得父类 + c = class_getSuperclass(c); + } +} + +#pragma mark - 属性黑名单配置 ++ (void)mj_setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames +{ + [self mj_setupBlockReturnValue:ignoredPropertyNames key:&MJIgnoredPropertyNamesKey]; +} + ++ (NSMutableArray *)mj_totalIgnoredPropertyNames +{ + return [self mj_totalObjectsWithSelector:@selector(mj_ignoredPropertyNames) key:&MJIgnoredPropertyNamesKey]; +} + +#pragma mark - 归档属性黑名单配置 ++ (void)mj_setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames +{ + [self mj_setupBlockReturnValue:ignoredCodingPropertyNames key:&MJIgnoredCodingPropertyNamesKey]; +} + ++ (NSMutableArray *)mj_totalIgnoredCodingPropertyNames +{ + return [self mj_totalObjectsWithSelector:@selector(mj_ignoredCodingPropertyNames) key:&MJIgnoredCodingPropertyNamesKey]; +} + +#pragma mark - 属性白名单配置 ++ (void)mj_setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames; +{ + [self mj_setupBlockReturnValue:allowedPropertyNames key:&MJAllowedPropertyNamesKey]; +} + ++ (NSMutableArray *)mj_totalAllowedPropertyNames +{ + return [self mj_totalObjectsWithSelector:@selector(mj_allowedPropertyNames) key:&MJAllowedPropertyNamesKey]; +} + +#pragma mark - 归档属性白名单配置 ++ (void)mj_setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames +{ + [self mj_setupBlockReturnValue:allowedCodingPropertyNames key:&MJAllowedCodingPropertyNamesKey]; +} + ++ (NSMutableArray *)mj_totalAllowedCodingPropertyNames +{ + return [self mj_totalObjectsWithSelector:@selector(mj_allowedCodingPropertyNames) key:&MJAllowedCodingPropertyNamesKey]; +} + +#pragma mark - block和方法处理:存储block的返回值 ++ (void)mj_setupBlockReturnValue:(id (^)(void))block key:(const char *)key { + MJExtensionSemaphoreCreate + MJ_LOCK(mje_signalSemaphore); + if (block) { + objc_setAssociatedObject(self, key, block(), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } else { + objc_setAssociatedObject(self, key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + + // 清空数据 + [[self mj_classDictForKey:key] removeAllObjects]; + MJ_UNLOCK(mje_signalSemaphore); +} + ++ (NSMutableArray *)mj_totalObjectsWithSelector:(SEL)selector key:(const char *)key +{ + MJExtensionSemaphoreCreate + MJ_LOCK(mje_signalSemaphore); + NSMutableArray *array = [self mj_classDictForKey:key][NSStringFromClass(self)]; + if (array == nil) { + // 创建、存储 + [self mj_classDictForKey:key][NSStringFromClass(self)] = array = [NSMutableArray array]; + + if ([self respondsToSelector:selector]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + NSArray *subArray = [self performSelector:selector]; +#pragma clang diagnostic pop + if (subArray) { + [array addObjectsFromArray:subArray]; + } + } + + [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { + NSArray *subArray = objc_getAssociatedObject(c, key); + [array addObjectsFromArray:subArray]; + }]; + } + MJ_UNLOCK(mje_signalSemaphore); + return array; +} +@end diff --git a/Pods/MJExtension/MJExtension/NSObject+MJCoding.h b/Pods/MJExtension/MJExtension/NSObject+MJCoding.h new file mode 100755 index 0000000..aeeb4eb --- /dev/null +++ b/Pods/MJExtension/MJExtension/NSObject+MJCoding.h @@ -0,0 +1,66 @@ +// +// NSObject+MJCoding.h +// MJExtension +// +// Created by mj on 14-1-15. +// Copyright (c) 2014年 小码哥. All rights reserved. +// + +#import +#import "MJExtensionConst.h" + +/** + * Codeing协议 + */ +@protocol MJCoding +@optional +/** + * 这个数组中的属性名才会进行归档 + */ ++ (NSArray *)mj_allowedCodingPropertyNames; +/** + * 这个数组中的属性名将会被忽略:不进行归档 + */ ++ (NSArray *)mj_ignoredCodingPropertyNames; +@end + +@interface NSObject (MJCoding) +/** + * 解码(从文件中解析对象) + */ +- (void)mj_decode:(NSCoder *)decoder; +/** + * 编码(将对象写入文件中) + */ +- (void)mj_encode:(NSCoder *)encoder; +@end + +/** + 归档的实现 + */ +#define MJCodingImplementation \ +- (id)initWithCoder:(NSCoder *)decoder \ +{ \ +if (self = [super init]) { \ +[self mj_decode:decoder]; \ +} \ +return self; \ +} \ +\ +- (void)encodeWithCoder:(NSCoder *)encoder \ +{ \ +[self mj_encode:encoder]; \ +}\ + +#define MJExtensionCodingImplementation MJCodingImplementation + +#define MJSecureCodingImplementation(CLASS, FLAG) \ +@interface CLASS (MJSecureCoding) \ +@end \ +@implementation CLASS (MJSecureCoding) \ +MJCodingImplementation \ ++ (BOOL)supportsSecureCoding { \ +return FLAG; \ +} \ +@end \ + diff --git a/Pods/MJExtension/MJExtension/NSObject+MJCoding.m b/Pods/MJExtension/MJExtension/NSObject+MJCoding.m new file mode 100755 index 0000000..614514a --- /dev/null +++ b/Pods/MJExtension/MJExtension/NSObject+MJCoding.m @@ -0,0 +1,59 @@ +// +// NSObject+MJCoding.m +// MJExtension +// +// Created by mj on 14-1-15. +// Copyright (c) 2014年 小码哥. All rights reserved. +// + +#import "NSObject+MJCoding.h" +#import "NSObject+MJClass.h" +#import "NSObject+MJProperty.h" +#import "MJProperty.h" + +@implementation NSObject (MJCoding) + +- (void)mj_encode:(NSCoder *)encoder +{ + Class clazz = [self class]; + + NSArray *allowedCodingPropertyNames = [clazz mj_totalAllowedCodingPropertyNames]; + NSArray *ignoredCodingPropertyNames = [clazz mj_totalIgnoredCodingPropertyNames]; + + [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { + // 检测是否被忽略 + if (allowedCodingPropertyNames.count && ![allowedCodingPropertyNames containsObject:property.name]) return; + if ([ignoredCodingPropertyNames containsObject:property.name]) return; + + id value = [property valueForObject:self]; + if (value == nil) return; + [encoder encodeObject:value forKey:property.name]; + }]; +} + +- (void)mj_decode:(NSCoder *)decoder +{ + Class clazz = [self class]; + + NSArray *allowedCodingPropertyNames = [clazz mj_totalAllowedCodingPropertyNames]; + NSArray *ignoredCodingPropertyNames = [clazz mj_totalIgnoredCodingPropertyNames]; + + [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { + // 检测是否被忽略 + if (allowedCodingPropertyNames.count && ![allowedCodingPropertyNames containsObject:property.name]) return; + if ([ignoredCodingPropertyNames containsObject:property.name]) return; + + // fixed `-[NSKeyedUnarchiver validateAllowedClass:forKey:] allowed unarchiving safe plist type ''NSNumber'(This will be disallowed in the future.)` warning. + Class genericClass = [property objectClassInArrayForClass:property.srcClass]; + // If genericClass exists, property.type.typeClass would be a collection type(Array, Set, Dictionary). This scenario([obj, nil, obj, nil]) would not happened. + NSSet *classes = [NSSet setWithObjects:NSNumber.class, + property.type.typeClass, genericClass, nil]; + id value = [decoder decodeObjectOfClasses:classes forKey:property.name]; + if (value == nil) { // 兼容以前的MJExtension版本 + value = [decoder decodeObjectForKey:[@"_" stringByAppendingString:property.name]]; + } + if (value == nil) return; + [property setValue:value forObject:self]; + }]; +} +@end diff --git a/Pods/MJExtension/MJExtension/NSObject+MJKeyValue.h b/Pods/MJExtension/MJExtension/NSObject+MJKeyValue.h new file mode 100755 index 0000000..3609357 --- /dev/null +++ b/Pods/MJExtension/MJExtension/NSObject+MJKeyValue.h @@ -0,0 +1,194 @@ +// +// NSObject+MJKeyValue.h +// MJExtension +// +// Created by mj on 13-8-24. +// Copyright (c) 2013年 小码哥. All rights reserved. +// + +#import +#import "MJExtensionConst.h" +#import +#import "MJProperty.h" + +/** + * KeyValue协议 + */ +@protocol MJKeyValue +@optional +/** + * 只有这个数组中的属性名才允许进行字典和模型的转换 + */ ++ (NSArray *)mj_allowedPropertyNames; + +/** + * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 + */ ++ (NSArray *)mj_ignoredPropertyNames; + +/** + * 将属性名换为其他key去字典中取值 + * + * @return 字典中的key是属性名,value是从字典中取值用的key + */ ++ (NSDictionary *)mj_replacedKeyFromPropertyName; + +/** + * 将属性名换为其他key去字典中取值 + * + * @return 从字典中取值用的key + */ ++ (id)mj_replacedKeyFromPropertyName121:(NSString *)propertyName; + +/** + * 数组中需要转换的模型类 + * + * @return 字典中的key是数组属性名,value是数组中存放模型的Class(Class类型或者NSString类型) + */ ++ (NSDictionary *)mj_objectClassInArray; + + +/** 特殊地区在字符串格式化数字时使用 */ ++ (NSLocale *)mj_numberLocale; + +/** + * 旧值换新值,用于过滤字典中的值 + * + * @param oldValue 旧值 + * + * @return 新值 + */ +- (id)mj_newValueFromOldValue:(id)oldValue property:(MJProperty *)property; + +/** + * 当字典转模型完毕时调用 + */ +- (void)mj_keyValuesDidFinishConvertingToObject MJExtensionDeprecated("请使用`mj_didConvertToObjectWithKeyValues:`替代"); +- (void)mj_keyValuesDidFinishConvertingToObject:(NSDictionary *)keyValues MJExtensionDeprecated("请使用`mj_didConvertToObjectWithKeyValues:`替代"); +- (void)mj_didConvertToObjectWithKeyValues:(NSDictionary *)keyValues; + +/** + * 当模型转字典完毕时调用 + */ +- (void)mj_objectDidFinishConvertingToKeyValues MJExtensionDeprecated("请使用`mj_objectDidConvertToKeyValues:`替代"); +- (void)mj_objectDidConvertToKeyValues:(NSMutableDictionary *)keyValues; + +@end + +@interface NSObject (MJKeyValue) +#pragma mark - 类方法 +/** + * 字典转模型过程中遇到的错误 + */ ++ (NSError *)mj_error; + +/** + * 模型转字典时,字典的key是否参考replacedKeyFromPropertyName等方法(父类设置了,子类也会继承下来) + */ ++ (void)mj_referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference; + +#pragma mark - 对象方法 +/** + * 将字典的键值对转成模型属性 + * @param keyValues 字典(可以是NSDictionary、NSData、NSString) + */ +- (instancetype)mj_setKeyValues:(id)keyValues; + +/** + * 将字典的键值对转成模型属性 + * @param keyValues 字典(可以是NSDictionary、NSData、NSString) + * @param context CoreData上下文 + */ +- (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context; + +/** + * 将模型转成字典 + * @return 字典 + */ +- (NSMutableDictionary *)mj_keyValues; +- (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys; +- (NSMutableDictionary *)mj_keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys; + +/** + * 通过模型数组来创建一个字典数组 + * @param objectArray 模型数组 + * @return 字典数组 + */ ++ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray; ++ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys; ++ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys; + +#pragma mark - 字典转模型 +/** + * 通过字典来创建一个模型 + * @param keyValues 字典(可以是NSDictionary、NSData、NSString) + * @return 新建的对象 + */ ++ (instancetype)mj_objectWithKeyValues:(id)keyValues; + +/** + * 通过字典来创建一个CoreData模型 + * @param keyValues 字典(可以是NSDictionary、NSData、NSString) + * @param context CoreData上下文 + * @return 新建的对象 + */ ++ (instancetype)mj_objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context; + +/** + * 通过plist来创建一个模型 + * @param filename 文件名(仅限于mainBundle中的文件) + * @return 新建的对象 + */ ++ (instancetype)mj_objectWithFilename:(NSString *)filename; + +/** + * 通过plist来创建一个模型 + * @param file 文件全路径 + * @return 新建的对象 + */ ++ (instancetype)mj_objectWithFile:(NSString *)file; + +#pragma mark - 字典数组转模型数组 +/** + * 通过字典数组来创建一个模型数组 + * @param keyValuesArray 字典数组(可以是NSDictionary、NSData、NSString) + * @return 模型数组 + */ ++ (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray; + +/** + * 通过字典数组来创建一个模型数组 + * @param keyValuesArray 字典数组(可以是NSDictionary、NSData、NSString) + * @param context CoreData上下文 + * @return 模型数组 + */ ++ (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context; + +/** + * 通过plist来创建一个模型数组 + * @param filename 文件名(仅限于mainBundle中的文件) + * @return 模型数组 + */ ++ (NSMutableArray *)mj_objectArrayWithFilename:(NSString *)filename; + +/** + * 通过plist来创建一个模型数组 + * @param file 文件全路径 + * @return 模型数组 + */ ++ (NSMutableArray *)mj_objectArrayWithFile:(NSString *)file; + +#pragma mark - 转换为JSON +/** + * 转换为JSON Data + */ +- (NSData *)mj_JSONData; +/** + * 转换为字典或者数组 + */ +- (id)mj_JSONObject; +/** + * 转换为JSON 字符串 + */ +- (NSString *)mj_JSONString; +@end diff --git a/Pods/MJExtension/MJExtension/NSObject+MJKeyValue.m b/Pods/MJExtension/MJExtension/NSObject+MJKeyValue.m new file mode 100755 index 0000000..c48e003 --- /dev/null +++ b/Pods/MJExtension/MJExtension/NSObject+MJKeyValue.m @@ -0,0 +1,524 @@ +// +// NSObject+MJKeyValue.m +// MJExtension +// +// Created by mj on 13-8-24. +// Copyright (c) 2013年 小码哥. All rights reserved. +// + +#import "NSObject+MJKeyValue.h" +#import "NSObject+MJProperty.h" +#import "NSString+MJExtension.h" +#import "MJProperty.h" +#import "MJPropertyType.h" +#import "MJExtensionConst.h" +#import "MJFoundation.h" +#import "NSString+MJExtension.h" +#import "NSObject+MJClass.h" + +@implementation NSDecimalNumber(MJKeyValue) + +- (id)mj_standardValueWithTypeCode:(NSString *)typeCode { + // 由于这里涉及到编译器问题, 暂时保留 Long, 实际上在 64 位系统上, 这 2 个精度范围相同, + // 32 位略有不同, 其余都可使用 Double 进行强转不丢失精度 + if ([typeCode isEqualToString:MJPropertyTypeLongLong]) { + return @(self.longLongValue); + } else if ([typeCode isEqualToString:MJPropertyTypeLongLong.uppercaseString]) { + return @(self.unsignedLongLongValue); + } else if ([typeCode isEqualToString:MJPropertyTypeLong]) { + return @(self.longValue); + } else if ([typeCode isEqualToString:MJPropertyTypeLong.uppercaseString]) { + return @(self.unsignedLongValue); + } else { + return @(self.doubleValue); + } +} + +@end + +@implementation NSObject (MJKeyValue) + +#pragma mark - 错误 +static const char MJErrorKey = '\0'; ++ (NSError *)mj_error +{ + return objc_getAssociatedObject(self, &MJErrorKey); +} + ++ (void)setMj_error:(NSError *)error +{ + objc_setAssociatedObject(self, &MJErrorKey, error, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - 模型 -> 字典时的参考 +/** 模型转字典时,字典的key是否参考replacedKeyFromPropertyName等方法(父类设置了,子类也会继承下来) */ +static const char MJReferenceReplacedKeyWhenCreatingKeyValuesKey = '\0'; + ++ (void)mj_referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference +{ + objc_setAssociatedObject(self, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey, @(reference), OBJC_ASSOCIATION_ASSIGN); +} + ++ (BOOL)mj_isReferenceReplacedKeyWhenCreatingKeyValues +{ + __block id value = objc_getAssociatedObject(self, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey); + if (!value) { + [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { + value = objc_getAssociatedObject(c, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey); + + if (value) *stop = YES; + }]; + } + return [value boolValue]; +} + +#pragma mark - --常用的对象-- ++ (void)load +{ + // 默认设置 + [self mj_referenceReplacedKeyWhenCreatingKeyValues:YES]; +} + +#pragma mark - --公共方法-- +#pragma mark - 字典 -> 模型 +- (instancetype)mj_setKeyValues:(id)keyValues +{ + return [self mj_setKeyValues:keyValues context:nil]; +} + +/** + 核心代码: + */ +- (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context +{ + // 获得JSON对象 + keyValues = [keyValues mj_JSONObject]; + + MJExtensionAssertError([keyValues isKindOfClass:[NSDictionary class]], self, [self class], @"keyValues参数不是一个字典"); + + Class clazz = [self class]; + NSArray *allowedPropertyNames = [clazz mj_totalAllowedPropertyNames]; + NSArray *ignoredPropertyNames = [clazz mj_totalIgnoredPropertyNames]; + + NSLocale *numberLocale = nil; + if ([self.class respondsToSelector:@selector(mj_numberLocale)]) { + numberLocale = self.class.mj_numberLocale; + } + + //通过封装的方法回调一个通过运行时编写的,用于返回属性列表的方法。 + [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { + @try { + // 0.检测是否被忽略 + if (allowedPropertyNames.count && ![allowedPropertyNames containsObject:property.name]) return; + if ([ignoredPropertyNames containsObject:property.name]) return; + + // 1.取出属性值 + id value; + NSArray *propertyKeyses = [property propertyKeysForClass:clazz]; + for (NSArray *propertyKeys in propertyKeyses) { + value = keyValues; + for (MJPropertyKey *propertyKey in propertyKeys) { + value = [propertyKey valueInObject:value]; + } + if (value) break; + } + + // 值的过滤 + id newValue = [clazz mj_getNewValueFromObject:self oldValue:value property:property]; + if (newValue != value) { // 有过滤后的新值 + [property setValue:newValue forObject:self]; + return; + } + + // 如果没有值,就直接返回 + if (!value || value == [NSNull null]) return; + + // 2.复杂处理 + MJPropertyType *type = property.type; + Class propertyClass = type.typeClass; + Class objectClass = [property objectClassInArrayForClass:[self class]]; + + // 不可变 -> 可变处理 + if (propertyClass == [NSMutableArray class] && [value isKindOfClass:[NSArray class]]) { + value = [NSMutableArray arrayWithArray:value]; + } else if (propertyClass == [NSMutableDictionary class] && [value isKindOfClass:[NSDictionary class]]) { + value = [NSMutableDictionary dictionaryWithDictionary:value]; + } else if (propertyClass == [NSMutableString class] && [value isKindOfClass:[NSString class]]) { + value = [NSMutableString stringWithString:value]; + } else if (propertyClass == [NSMutableData class] && [value isKindOfClass:[NSData class]]) { + value = [NSMutableData dataWithData:value]; + } + + if (!type.isFromFoundation && propertyClass) { // 模型属性 + value = [propertyClass mj_objectWithKeyValues:value context:context]; + } else if (objectClass) { + if (objectClass == [NSURL class] && [value isKindOfClass:[NSArray class]]) { + // string array -> url array + NSMutableArray *urlArray = [NSMutableArray array]; + for (NSString *string in value) { + if (![string isKindOfClass:[NSString class]]) continue; + [urlArray addObject:string.mj_url]; + } + value = urlArray; + } else { // 字典数组-->模型数组 + value = [objectClass mj_objectArrayWithKeyValuesArray:value context:context]; + } + } else if (propertyClass == [NSString class]) { + if ([value isKindOfClass:[NSNumber class]]) { + // NSNumber -> NSString + value = [value description]; + } else if ([value isKindOfClass:[NSURL class]]) { + // NSURL -> NSString + value = [value absoluteString]; + } + } else if ([value isKindOfClass:[NSString class]]) { + if (propertyClass == [NSURL class]) { + // NSString -> NSURL + // 字符串转码 + value = [value mj_url]; + } else if (type.isNumberType) { + NSString *oldValue = value; + + // NSString -> NSDecimalNumber, 使用 DecimalNumber 来转换数字, 避免丢失精度以及溢出 + NSDecimalNumber *decimalValue = [NSDecimalNumber decimalNumberWithString:oldValue + locale:numberLocale]; + + // 检查特殊情况 + if (decimalValue == NSDecimalNumber.notANumber) { + value = @(0); + }else if (propertyClass != [NSDecimalNumber class]) { + value = [decimalValue mj_standardValueWithTypeCode:type.code]; + } else { + value = decimalValue; + } + + // 如果是BOOL + if (type.isBoolType) { + // 字符串转BOOL(字符串没有charValue方法) + // 系统会调用字符串的charValue转为BOOL类型 + NSString *lower = [oldValue lowercaseString]; + if ([lower isEqualToString:@"yes"] || [lower isEqualToString:@"true"]) { + value = @YES; + } else if ([lower isEqualToString:@"no"] || [lower isEqualToString:@"false"]) { + value = @NO; + } + } + } + } else if ([value isKindOfClass:[NSNumber class]] && propertyClass == [NSDecimalNumber class]){ + // 过滤 NSDecimalNumber类型 + if (![value isKindOfClass:[NSDecimalNumber class]]) { + value = [NSDecimalNumber decimalNumberWithDecimal:[((NSNumber *)value) decimalValue]]; + } + } + + // 经过转换后, 最终检查 value 与 property 是否匹配 + if (propertyClass && ![value isKindOfClass:propertyClass]) { + value = nil; + } + + // 3.赋值 + [property setValue:value forObject:self]; + } @catch (NSException *exception) { + MJExtensionBuildError([self class], exception.reason); + MJExtensionLog(@"%@", exception); +#ifdef DEBUG + [exception raise]; +#endif + } + }]; + + // 转换完毕 + if ([self respondsToSelector:@selector(mj_didConvertToObjectWithKeyValues:)]) { + [self mj_didConvertToObjectWithKeyValues:keyValues]; + } +#pragma clang diagnostic push +#pragma clang diagnostic ignored"-Wdeprecated-declarations" + if ([self respondsToSelector:@selector(mj_keyValuesDidFinishConvertingToObject)]) { + [self mj_keyValuesDidFinishConvertingToObject]; + } + if ([self respondsToSelector:@selector(mj_keyValuesDidFinishConvertingToObject:)]) { + [self mj_keyValuesDidFinishConvertingToObject:keyValues]; + } +#pragma clang diagnostic pop + return self; +} + ++ (instancetype)mj_objectWithKeyValues:(id)keyValues +{ + return [self mj_objectWithKeyValues:keyValues context:nil]; +} + ++ (instancetype)mj_objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context +{ + // 获得JSON对象 + keyValues = [keyValues mj_JSONObject]; + MJExtensionAssertError([keyValues isKindOfClass:[NSDictionary class]], nil, [self class], @"keyValues参数不是一个字典"); + + if ([self isSubclassOfClass:[NSManagedObject class]] && context) { + NSString *entityName = [(NSManagedObject *)self entity].name; + return [[NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context] mj_setKeyValues:keyValues context:context]; + } + return [[[self alloc] init] mj_setKeyValues:keyValues]; +} + ++ (instancetype)mj_objectWithFilename:(NSString *)filename +{ + MJExtensionAssertError(filename != nil, nil, [self class], @"filename参数为nil"); + + return [self mj_objectWithFile:[[NSBundle mainBundle] pathForResource:filename ofType:nil]]; +} + ++ (instancetype)mj_objectWithFile:(NSString *)file +{ + MJExtensionAssertError(file != nil, nil, [self class], @"file参数为nil"); + + return [self mj_objectWithKeyValues:[NSDictionary dictionaryWithContentsOfFile:file]]; +} + +#pragma mark - 字典数组 -> 模型数组 ++ (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(NSArray *)keyValuesArray +{ + return [self mj_objectArrayWithKeyValuesArray:keyValuesArray context:nil]; +} + ++ (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context +{ + // 如果是JSON字符串 + keyValuesArray = [keyValuesArray mj_JSONObject]; + + // 1.判断真实性 + MJExtensionAssertError([keyValuesArray isKindOfClass:[NSArray class]], nil, [self class], @"keyValuesArray参数不是一个数组"); + + // 如果数组里面放的是NSString、NSNumber等数据 + if ([MJFoundation isClassFromFoundation:self]) return [NSMutableArray arrayWithArray:keyValuesArray]; + + + // 2.创建数组 + NSMutableArray *modelArray = [NSMutableArray array]; + + // 3.遍历 + for (NSDictionary *keyValues in keyValuesArray) { + if ([keyValues isKindOfClass:[NSArray class]]){ + [modelArray addObject:[self mj_objectArrayWithKeyValuesArray:keyValues context:context]]; + } else { + id model = [self mj_objectWithKeyValues:keyValues context:context]; + if (model) [modelArray addObject:model]; + } + } + + return modelArray; +} + ++ (NSMutableArray *)mj_objectArrayWithFilename:(NSString *)filename +{ + MJExtensionAssertError(filename != nil, nil, [self class], @"filename参数为nil"); + + return [self mj_objectArrayWithFile:[[NSBundle mainBundle] pathForResource:filename ofType:nil]]; +} + ++ (NSMutableArray *)mj_objectArrayWithFile:(NSString *)file +{ + MJExtensionAssertError(file != nil, nil, [self class], @"file参数为nil"); + + return [self mj_objectArrayWithKeyValuesArray:[NSArray arrayWithContentsOfFile:file]]; +} + +#pragma mark - 模型 -> 字典 +- (NSMutableDictionary *)mj_keyValues +{ + return [self mj_keyValuesWithKeys:nil ignoredKeys:nil]; +} + +- (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys +{ + return [self mj_keyValuesWithKeys:keys ignoredKeys:nil]; +} + +- (NSMutableDictionary *)mj_keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys +{ + return [self mj_keyValuesWithKeys:nil ignoredKeys:ignoredKeys]; +} + +- (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys ignoredKeys:(NSArray *)ignoredKeys +{ + // 如果自己不是模型类, 那就返回自己 + // 模型类过滤掉 NSNull + // 唯一一个不返回自己的 + if ([self isMemberOfClass:NSNull.class]) { return nil; } + // 这里虽然返回了自己, 但是其实是有报错信息的. + // TODO: 报错机制不好, 需要重做 + MJExtensionAssertError(![MJFoundation isClassFromFoundation:[self class]], (NSMutableDictionary *)self, [self class], @"不是自定义的模型类") + + id keyValues = [NSMutableDictionary dictionary]; + + Class clazz = [self class]; + NSArray *allowedPropertyNames = [clazz mj_totalAllowedPropertyNames]; + NSArray *ignoredPropertyNames = [clazz mj_totalIgnoredPropertyNames]; + + [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { + @try { + // 0.检测是否被忽略 + if (allowedPropertyNames.count && ![allowedPropertyNames containsObject:property.name]) return; + if ([ignoredPropertyNames containsObject:property.name]) return; + if (keys.count && ![keys containsObject:property.name]) return; + if ([ignoredKeys containsObject:property.name]) return; + + // 1.取出属性值 + id value = [property valueForObject:self]; + if (!value) return; + + // 2.如果是模型属性 + MJPropertyType *type = property.type; + Class propertyClass = type.typeClass; + if (!type.isFromFoundation && propertyClass) { + value = [value mj_keyValues]; + } else if ([value isKindOfClass:[NSArray class]]) { + // 3.处理数组里面有模型的情况 + value = [NSObject mj_keyValuesArrayWithObjectArray:value]; + } else if (propertyClass == [NSURL class]) { + value = [value absoluteString]; + } + + // 4.赋值 + if ([clazz mj_isReferenceReplacedKeyWhenCreatingKeyValues]) { + NSArray *propertyKeys = [[property propertyKeysForClass:clazz] firstObject]; + NSUInteger keyCount = propertyKeys.count; + // 创建字典 + __block id innerContainer = keyValues; + [propertyKeys enumerateObjectsUsingBlock:^(MJPropertyKey *propertyKey, NSUInteger idx, BOOL *stop) { + // 下一个属性 + MJPropertyKey *nextPropertyKey = nil; + if (idx != keyCount - 1) { + nextPropertyKey = propertyKeys[idx + 1]; + } + + if (nextPropertyKey) { // 不是最后一个key + // 当前propertyKey对应的字典或者数组 + id tempInnerContainer = [propertyKey valueInObject:innerContainer]; + if (tempInnerContainer == nil || [tempInnerContainer isKindOfClass:[NSNull class]]) { + if (nextPropertyKey.type == MJPropertyKeyTypeDictionary) { + tempInnerContainer = [NSMutableDictionary dictionary]; + } else { + tempInnerContainer = [NSMutableArray array]; + } + if (propertyKey.type == MJPropertyKeyTypeDictionary) { + innerContainer[propertyKey.name] = tempInnerContainer; + } else { + innerContainer[propertyKey.name.intValue] = tempInnerContainer; + } + } + + if ([tempInnerContainer isKindOfClass:[NSMutableArray class]]) { + NSMutableArray *tempInnerContainerArray = tempInnerContainer; + int index = nextPropertyKey.name.intValue; + while (tempInnerContainerArray.count < index + 1) { + [tempInnerContainerArray addObject:[NSNull null]]; + } + } + + innerContainer = tempInnerContainer; + } else { // 最后一个key + if (propertyKey.type == MJPropertyKeyTypeDictionary) { + innerContainer[propertyKey.name] = value; + } else { + innerContainer[propertyKey.name.intValue] = value; + } + } + }]; + } else { + keyValues[property.name] = value; + } + } @catch (NSException *exception) { + MJExtensionBuildError([self class], exception.reason); + MJExtensionLog(@"%@", exception); +#ifdef DEBUG + [exception raise]; +#endif + } + }]; + + // 转换完毕 + if ([self respondsToSelector:@selector(mj_objectDidConvertToKeyValues:)]) { + [self mj_objectDidConvertToKeyValues:keyValues]; + } +#pragma clang diagnostic push +#pragma clang diagnostic ignored"-Wdeprecated-declarations" + if ([self respondsToSelector:@selector(mj_objectDidFinishConvertingToKeyValues)]) { + [self mj_objectDidFinishConvertingToKeyValues]; + } +#pragma clang diagnostic pop + + return keyValues; +} +#pragma mark - 模型数组 -> 字典数组 ++ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray +{ + return [self mj_keyValuesArrayWithObjectArray:objectArray keys:nil ignoredKeys:nil]; +} + ++ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys +{ + return [self mj_keyValuesArrayWithObjectArray:objectArray keys:keys ignoredKeys:nil]; +} + ++ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys +{ + return [self mj_keyValuesArrayWithObjectArray:objectArray keys:nil ignoredKeys:ignoredKeys]; +} + ++ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys ignoredKeys:(NSArray *)ignoredKeys +{ + // 0.判断真实性 + MJExtensionAssertError([objectArray isKindOfClass:[NSArray class]], nil, [self class], @"objectArray参数不是一个数组"); + + // 1.创建数组 + NSMutableArray *keyValuesArray = [NSMutableArray array]; + for (id object in objectArray) { + if (keys) { + id convertedObj = [object mj_keyValuesWithKeys:keys]; + if (!convertedObj) { continue; } + [keyValuesArray addObject:convertedObj]; + } else { + id convertedObj = [object mj_keyValuesWithIgnoredKeys:ignoredKeys]; + if (!convertedObj) { continue; } + [keyValuesArray addObject:convertedObj]; + } + } + return keyValuesArray; +} + +#pragma mark - 转换为JSON +- (NSData *)mj_JSONData +{ + if ([self isKindOfClass:[NSString class]]) { + return [((NSString *)self) dataUsingEncoding:NSUTF8StringEncoding]; + } else if ([self isKindOfClass:[NSData class]]) { + return (NSData *)self; + } + + return [NSJSONSerialization dataWithJSONObject:[self mj_JSONObject] options:kNilOptions error:nil]; +} + +- (id)mj_JSONObject +{ + if ([self isKindOfClass:[NSString class]]) { + return [NSJSONSerialization JSONObjectWithData:[((NSString *)self) dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:nil]; + } else if ([self isKindOfClass:[NSData class]]) { + return [NSJSONSerialization JSONObjectWithData:(NSData *)self options:kNilOptions error:nil]; + } + + return self.mj_keyValues; +} + +- (NSString *)mj_JSONString +{ + if ([self isKindOfClass:[NSString class]]) { + return (NSString *)self; + } else if ([self isKindOfClass:[NSData class]]) { + return [[NSString alloc] initWithData:(NSData *)self encoding:NSUTF8StringEncoding]; + } + + return [[NSString alloc] initWithData:[self mj_JSONData] encoding:NSUTF8StringEncoding]; +} + +@end diff --git a/Pods/MJExtension/MJExtension/NSObject+MJProperty.h b/Pods/MJExtension/MJExtension/NSObject+MJProperty.h new file mode 100644 index 0000000..1bf88e9 --- /dev/null +++ b/Pods/MJExtension/MJExtension/NSObject+MJProperty.h @@ -0,0 +1,70 @@ +// +// NSObject+MJProperty.h +// MJExtensionExample +// +// Created by MJ Lee on 15/4/17. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import +#import "MJExtensionConst.h" + +@class MJProperty; + +/** + * 遍历成员变量用的block + * + * @param property 成员的包装对象 + * @param stop YES代表停止遍历,NO代表继续遍历 + */ +typedef void (^MJPropertiesEnumeration)(MJProperty *property, BOOL *stop); + +/** 将属性名换为其他key去字典中取值 */ +typedef NSDictionary * (^MJReplacedKeyFromPropertyName)(void); +typedef id (^MJReplacedKeyFromPropertyName121)(NSString *propertyName); +/** 数组中需要转换的模型类 */ +typedef NSDictionary * (^MJObjectClassInArray)(void); +/** 用于过滤字典中的值 */ +typedef id (^MJNewValueFromOldValue)(id object, id oldValue, MJProperty *property); + +/** + * 成员属性相关的扩展 + */ +@interface NSObject (MJProperty) +#pragma mark - 遍历 +/** + * 遍历所有的成员 + */ ++ (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration; + +#pragma mark - 新值配置 +/** + * 用于过滤字典中的值 + * + * @param newValueFormOldValue 用于过滤字典中的值 + */ ++ (void)mj_setupNewValueFromOldValue:(MJNewValueFromOldValue)newValueFormOldValue; ++ (id)mj_getNewValueFromObject:(__unsafe_unretained id)object oldValue:(__unsafe_unretained id)oldValue property:(__unsafe_unretained MJProperty *)property; + +#pragma mark - key配置 +/** + * 将属性名换为其他key去字典中取值 + * + * @param replacedKeyFromPropertyName 将属性名换为其他key去字典中取值 + */ ++ (void)mj_setupReplacedKeyFromPropertyName:(MJReplacedKeyFromPropertyName)replacedKeyFromPropertyName; +/** + * 将属性名换为其他key去字典中取值 + * + * @param replacedKeyFromPropertyName121 将属性名换为其他key去字典中取值 + */ ++ (void)mj_setupReplacedKeyFromPropertyName121:(MJReplacedKeyFromPropertyName121)replacedKeyFromPropertyName121; + +#pragma mark - array model class配置 +/** + * 数组中需要转换的模型类 + * + * @param objectClassInArray 数组中需要转换的模型类 + */ ++ (void)mj_setupObjectClassInArray:(MJObjectClassInArray)objectClassInArray; +@end diff --git a/Pods/MJExtension/MJExtension/NSObject+MJProperty.m b/Pods/MJExtension/MJExtension/NSObject+MJProperty.m new file mode 100644 index 0000000..71b08f4 --- /dev/null +++ b/Pods/MJExtension/MJExtension/NSObject+MJProperty.m @@ -0,0 +1,240 @@ +// +// NSObject+MJProperty.m +// MJExtensionExample +// +// Created by MJ Lee on 15/4/17. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import "NSObject+MJProperty.h" +#import "NSObject+MJKeyValue.h" +#import "NSObject+MJCoding.h" +#import "NSObject+MJClass.h" +#import "MJProperty.h" +#import "MJFoundation.h" +#import + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundeclared-selector" +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + +static const char MJReplacedKeyFromPropertyNameKey = '\0'; +static const char MJReplacedKeyFromPropertyName121Key = '\0'; +static const char MJNewValueFromOldValueKey = '\0'; +static const char MJObjectClassInArrayKey = '\0'; + +static const char MJCachedPropertiesKey = '\0'; + +dispatch_semaphore_t mje_signalSemaphore; +dispatch_once_t mje_onceTokenSemaphore; + +@implementation NSObject (Property) + ++ (NSMutableDictionary *)mj_propertyDictForKey:(const void *)key +{ + static NSMutableDictionary *replacedKeyFromPropertyNameDict; + static NSMutableDictionary *replacedKeyFromPropertyName121Dict; + static NSMutableDictionary *newValueFromOldValueDict; + static NSMutableDictionary *objectClassInArrayDict; + static NSMutableDictionary *cachedPropertiesDict; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + replacedKeyFromPropertyNameDict = [NSMutableDictionary dictionary]; + replacedKeyFromPropertyName121Dict = [NSMutableDictionary dictionary]; + newValueFromOldValueDict = [NSMutableDictionary dictionary]; + objectClassInArrayDict = [NSMutableDictionary dictionary]; + cachedPropertiesDict = [NSMutableDictionary dictionary]; + }); + + if (key == &MJReplacedKeyFromPropertyNameKey) return replacedKeyFromPropertyNameDict; + if (key == &MJReplacedKeyFromPropertyName121Key) return replacedKeyFromPropertyName121Dict; + if (key == &MJNewValueFromOldValueKey) return newValueFromOldValueDict; + if (key == &MJObjectClassInArrayKey) return objectClassInArrayDict; + if (key == &MJCachedPropertiesKey) return cachedPropertiesDict; + return nil; +} + +#pragma mark - --私有方法-- ++ (id)mj_propertyKey:(NSString *)propertyName +{ + MJExtensionAssertParamNotNil2(propertyName, nil); + + __block id key = nil; + // 查看有没有需要替换的key + if ([self respondsToSelector:@selector(mj_replacedKeyFromPropertyName121:)]) { + key = [self mj_replacedKeyFromPropertyName121:propertyName]; + } + + // 调用block + if (!key) { + [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { + MJReplacedKeyFromPropertyName121 block = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyName121Key); + if (block) { + key = block(propertyName); + } + if (key) *stop = YES; + }]; + } + + // 查看有没有需要替换的key + if ((!key || [key isEqual:propertyName]) && [self respondsToSelector:@selector(mj_replacedKeyFromPropertyName)]) { + key = [self mj_replacedKeyFromPropertyName][propertyName]; + } + + if (!key || [key isEqual:propertyName]) { + [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { + NSDictionary *dict = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyNameKey); + if (dict) { + key = dict[propertyName]; + } + if (key && ![key isEqual:propertyName]) *stop = YES; + }]; + } + + // 2.用属性名作为key + if (!key) key = propertyName; + + return key; +} + ++ (Class)mj_propertyObjectClassInArray:(NSString *)propertyName +{ + __block id clazz = nil; + if ([self respondsToSelector:@selector(mj_objectClassInArray)]) { + clazz = [self mj_objectClassInArray][propertyName]; + } + + if (!clazz) { + [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { + NSDictionary *dict = objc_getAssociatedObject(c, &MJObjectClassInArrayKey); + if (dict) { + clazz = dict[propertyName]; + } + if (clazz) *stop = YES; + }]; + } + + // 如果是NSString类型 + if ([clazz isKindOfClass:[NSString class]]) { + clazz = NSClassFromString(clazz); + } + return clazz; +} + +#pragma mark - --公共方法-- ++ (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration +{ + // 获得成员变量 + NSArray *cachedProperties = [self mj_properties]; + // 遍历成员变量 + BOOL stop = NO; + for (MJProperty *property in cachedProperties) { + enumeration(property, &stop); + if (stop) break; + } +} + +#pragma mark - 公共方法 ++ (NSArray *)mj_properties +{ + MJExtensionSemaphoreCreate + MJ_LOCK(mje_signalSemaphore); + NSMutableDictionary *cachedInfo = [self mj_propertyDictForKey:&MJCachedPropertiesKey]; + NSMutableArray *cachedProperties = cachedInfo[NSStringFromClass(self)]; + if (cachedProperties == nil) { + cachedProperties = [NSMutableArray array]; + + [self mj_enumerateClasses:^(__unsafe_unretained Class c, BOOL *stop) { + // 1.获得所有的成员变量 + unsigned int outCount = 0; + objc_property_t *properties = class_copyPropertyList(c, &outCount); + + // 2.遍历每一个成员变量 + for (unsigned int i = 0; i +#import "MJExtensionConst.h" + +@interface NSString (MJExtension) +/** + * 驼峰转下划线(loveYou -> love_you) + */ +- (NSString *)mj_underlineFromCamel; +/** + * 下划线转驼峰(love_you -> loveYou) + */ +- (NSString *)mj_camelFromUnderline; +/** + * 首字母变大写 + */ +- (NSString *)mj_firstCharUpper; +/** + * 首字母变小写 + */ +- (NSString *)mj_firstCharLower; + +- (BOOL)mj_isPureInt; + +- (NSURL *)mj_url; +@end diff --git a/Pods/MJExtension/MJExtension/NSString+MJExtension.m b/Pods/MJExtension/MJExtension/NSString+MJExtension.m new file mode 100644 index 0000000..20533c5 --- /dev/null +++ b/Pods/MJExtension/MJExtension/NSString+MJExtension.m @@ -0,0 +1,80 @@ +// +// NSString+MJExtension.m +// MJExtensionExample +// +// Created by MJ Lee on 15/6/7. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import "NSString+MJExtension.h" + +@implementation NSString (MJExtension) +- (NSString *)mj_underlineFromCamel +{ + if (self.length == 0) return self; + NSMutableString *string = [NSMutableString string]; + for (NSUInteger i = 0; i= 2) [string appendString:[cmp substringFromIndex:1]]; + } else { + [string appendString:cmp]; + } + } + return string; +} + +- (NSString *)mj_firstCharLower +{ + if (self.length == 0) return self; + NSMutableString *string = [NSMutableString string]; + [string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].lowercaseString]; + if (self.length >= 2) [string appendString:[self substringFromIndex:1]]; + return string; +} + +- (NSString *)mj_firstCharUpper +{ + if (self.length == 0) return self; + NSMutableString *string = [NSMutableString string]; + [string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].uppercaseString]; + if (self.length >= 2) [string appendString:[self substringFromIndex:1]]; + return string; +} + +- (BOOL)mj_isPureInt +{ + NSScanner *scan = [NSScanner scannerWithString:self]; + int val; + return [scan scanInt:&val] && [scan isAtEnd]; +} + +- (NSURL *)mj_url +{ +// [self stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"!$&'()*+,-./:;=?@_~%#[]"]]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored"-Wdeprecated-declarations" + return [NSURL URLWithString:(NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)self, (CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]", NULL,kCFStringEncodingUTF8))]; +#pragma clang diagnostic pop +} +@end diff --git a/Pods/MJExtension/README.md b/Pods/MJExtension/README.md new file mode 100644 index 0000000..b638414 --- /dev/null +++ b/Pods/MJExtension/README.md @@ -0,0 +1,637 @@ +MJExtension +=== +[![SPM supported](https://img.shields.io/badge/SPM-supported-4BC51D.svg?style=flat)](https://github.com/apple/swift-package-manager) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![podversion](https://img.shields.io/cocoapods/v/MJExtension.svg)](https://cocoapods.org/pods/MJExtension) +![Platform](https://img.shields.io/cocoapods/p/MJExtension.svg?style=flat) + +- A fast, convenient and nonintrusive conversion framework between JSON and model. +- 转换速度快、使用简单方便的字典转模型框架 + +[📜✍🏻**Release Notes**: more details](https://github.com/CoderMJLee/MJExtension/releases) + +## Contents + +* [Getting Started 【开始使用】](#Getting_Started) + * [Features 【能做什么】](#Features) + * [Installation 【安装】](#Installation) +* [Examples 【示例】](#Examples) + * [Usage in Swift](#usage_in_swift) + * [JSON -> Model](#JSON_Model) + * [JSONString -> Model](#JSONString_Model) + * [Model contains model](#Model_contains_model) + * [Model contains model-array](#Model_contains_model_array) + * [Model name - JSON key mapping](#Model_name_JSON_key_mapping) + * [JSON array -> model array](#JSON_array_model_array) + * [Model -> JSON](#Model_JSON) + * [Model array -> JSON array](#Model_array_JSON_array) + * [Core Data](#Core_Data) + * [Coding](#Coding) + * [Secure Coding](#SecureCoding) + * [Camel -> underline](#Camel_underline) + * [NSString -> NSDate, nil -> @""](#NSString_NSDate) + * [NSDate -> NSString](#NSDate_NSString) + * [More use cases](#More_use_cases) + +--- + +## Getting Started【开始使用】 + +### Features【能做什么】 +- MJExtension是一套字典和模型之间互相转换的超轻量级框架 +* `JSON` --> `Model`、`Core Data Model` +* `JSONString` --> `Model`、`Core Data Model` +* `Model`、`Core Data Model` --> `JSON` +* `JSON Array` --> `Model Array`、`Core Data Model Array` +* `JSONString` --> `Model Array`、`Core Data Model Array` +* `Model Array`、`Core Data Model Array` --> `JSON Array` +* Coding all properties of a model with only one line of code. + * 只需要一行代码,就能实现模型的所有属性进行Coding / SecureCoding(归档和解档) + +### Installation【安装】 + +#### CocoaPods【使用CocoaPods】 + +```ruby +pod 'MJExtension' +``` + +#### Carthage + +```ruby +github "CoderMJLee/MJExtension" +``` + +#### Swift Package Manager + +Released from [`3.4.0`](https://github.com/CoderMJLee/MJExtension/releases/) + +#### Manually【手动导入】 + +- Drag all source files under folder `MJExtension` to your project.【将`MJExtension`文件夹中的所有源代码拽入项目中】 +- Import the main header file:`#import "MJExtension.h"`【导入主头文件:`#import "MJExtension.h"`】 + +## Examples【示例】 + +**Add `MJKeyValue` protocol to your model if needed【如果有需要, 请在模型中加入 `MJKeyValue` 协议】** + +### Usage in Swift [关于在Swift中使用MJExtension] ‼️ + +> Example: +> +> - [Model - MJTester.swift](MJExtensionTests/SwiftModel/MJTester.swift) +> +> - [Usage - SwiftModelTests.swift](MJExtensionTests/SwiftModelTests.swift) + +```swift +@objc(MJTester) +@objcMembers +class MJTester: NSObject { + // make sure to use `dynamic` attribute for basic type & must use as Non-Optional & must set initial value + dynamic var isSpecialAgent: Bool = false + dynamic var age: Int = 0 + + var name: String? + var identifier: String? +} +``` + +1. `@objc` or `@objcMembers` attributes should be added to class or property for declaration of Objc accessibility [在 Swift4 之后, 请在属性前加 `@objc` 修饰或在类前增加 `@objcMembers`. 以保证 Swift 的属性能够暴露给 Objc 使用. ] +2. If you let `Bool` & `Int` as property type, make sure that using `dynamic` to attribute it. It must be `Non-Optional` type and assign `a default value`.[如果要使用 `Bool` 和 `Int` 等 Swfit 专用基本类型, 请使用 `dynamic` 关键字修饰, 类型为 `Non-Optional`, 並且给定初始值.] + +> 纯Swift版的JSON与Model转换框架已经开源上架 +> +> - [KakaJSON](https://github.com/kakaopensource/KakaJSON) +> - [中文教程](https://www.cnblogs.com/mjios/p/11352776.html) +> - 如果你的项目是用Swift写的Model,墙裂推荐使用[KakaJSON](https://github.com/kakaopensource/KakaJSON) +> - 已经对各种常用的数据场景进行了大量的单元测试 +> - 简单易用、功能丰富、转换快速 + +### The most simple JSON -> Model【最简单的字典转模型】 + +```objc +typedef enum { + SexMale, + SexFemale +} Sex; + +@interface User : NSObject +@property (copy, nonatomic) NSString *name; +@property (copy, nonatomic) NSString *icon; +@property (assign, nonatomic) unsigned int age; +@property (copy, nonatomic) NSString *height; +@property (strong, nonatomic) NSNumber *money; +@property (assign, nonatomic) Sex sex; +@property (assign, nonatomic, getter=isGay) BOOL gay; +@end + +/***********************************************/ + +NSDictionary *dict = @{ + @"name" : @"Jack", + @"icon" : @"lufy.png", + @"age" : @20, + @"height" : @"1.55", + @"money" : @100.9, + @"sex" : @(SexFemale), + @"gay" : @"true" +// @"gay" : @"1" +// @"gay" : @"NO" +}; + +// JSON -> User +User *user = [User mj_objectWithKeyValues:dict]; + +NSLog(@"name=%@, icon=%@, age=%zd, height=%@, money=%@, sex=%d, gay=%d", user.name, user.icon, user.age, user.height, user.money, user.sex, user.gay); +// name=Jack, icon=lufy.png, age=20, height=1.550000, money=100.9, sex=1 +``` + +### JSONString -> Model【JSON字符串转模型】 + +```objc +// 1.Define a JSONString +NSString *jsonString = @"{\"name\":\"Jack\", \"icon\":\"lufy.png\", \"age\":20}"; + +// 2.JSONString -> User +User *user = [User mj_objectWithKeyValues:jsonString]; + +// 3.Print user's properties +NSLog(@"name=%@, icon=%@, age=%d", user.name, user.icon, user.age); +// name=Jack, icon=lufy.png, age=20 +``` + +### Model contains model【模型中嵌套模型】 + +```objc +@interface Status : NSObject +@property (copy, nonatomic) NSString *text; +@property (strong, nonatomic) User *user; +@property (strong, nonatomic) Status *retweetedStatus; +@end + +/***********************************************/ + +NSDictionary *dict = @{ + @"text" : @"Agree!Nice weather!", + @"user" : @{ + @"name" : @"Jack", + @"icon" : @"lufy.png" + }, + @"retweetedStatus" : @{ + @"text" : @"Nice weather!", + @"user" : @{ + @"name" : @"Rose", + @"icon" : @"nami.png" + } + } +}; + +// JSON -> Status +Status *status = [Status mj_objectWithKeyValues:dict]; + +NSString *text = status.text; +NSString *name = status.user.name; +NSString *icon = status.user.icon; +NSLog(@"text=%@, name=%@, icon=%@", text, name, icon); +// text=Agree!Nice weather!, name=Jack, icon=lufy.png + +NSString *text2 = status.retweetedStatus.text; +NSString *name2 = status.retweetedStatus.user.name; +NSString *icon2 = status.retweetedStatus.user.icon; +NSLog(@"text2=%@, name2=%@, icon2=%@", text2, name2, icon2); +// text2=Nice weather!, name2=Rose, icon2=nami.png +``` + +### Model contains model-array【模型中有个数组属性,数组里面又要装着其他模型】 + +```objc +@interface Ad : NSObject +@property (copy, nonatomic) NSString *image; +@property (copy, nonatomic) NSString *url; +@end + +@interface StatusResult : NSObject +/** Contatins status model */ +@property (strong, nonatomic) NSMutableArray *statuses; +/** Contatins ad model */ +@property (strong, nonatomic) NSArray *ads; +@property (strong, nonatomic) NSNumber *totalNumber; +@end + +/***********************************************/ + +// Tell MJExtension what type of model will be contained in statuses and ads. +[StatusResult mj_setupObjectClassInArray:^NSDictionary *{ + return @{ + @"statuses" : @"Status", + // @"statuses" : [Status class], + @"ads" : @"Ad" + // @"ads" : [Ad class] + }; +}]; +// Equals: StatusResult.m implements +mj_objectClassInArray method. + +NSDictionary *dict = @{ + @"statuses" : @[ + @{ + @"text" : @"Nice weather!", + @"user" : @{ + @"name" : @"Rose", + @"icon" : @"nami.png" + } + }, + @{ + @"text" : @"Go camping tomorrow!", + @"user" : @{ + @"name" : @"Jack", + @"icon" : @"lufy.png" + } + } + ], + @"ads" : @[ + @{ + @"image" : @"ad01.png", + @"url" : @"http://www.ad01.com" + }, + @{ + @"image" : @"ad02.png", + @"url" : @"http://www.ad02.com" + } + ], + @"totalNumber" : @"2014" +}; + +// JSON -> StatusResult +StatusResult *result = [StatusResult mj_objectWithKeyValues:dict]; + +NSLog(@"totalNumber=%@", result.totalNumber); +// totalNumber=2014 + +// Printing +for (Status *status in result.statuses) { + NSString *text = status.text; + NSString *name = status.user.name; + NSString *icon = status.user.icon; + NSLog(@"text=%@, name=%@, icon=%@", text, name, icon); +} +// text=Nice weather!, name=Rose, icon=nami.png +// text=Go camping tomorrow!, name=Jack, icon=lufy.png + +// Printing +for (Ad *ad in result.ads) { + NSLog(@"image=%@, url=%@", ad.image, ad.url); +} +// image=ad01.png, url=http://www.ad01.com +// image=ad02.png, url=http://www.ad02.com +``` + +### Model name - JSON key mapping【模型中的属性名和字典中的key不相同(或者需要多级映射)】 + +```objc +@interface Bag : NSObject +@property (copy, nonatomic) NSString *name; +@property (assign, nonatomic) double price; +@end + +@interface Student : NSObject +@property (copy, nonatomic) NSString *ID; +@property (copy, nonatomic) NSString *desc; +@property (copy, nonatomic) NSString *nowName; +@property (copy, nonatomic) NSString *oldName; +@property (copy, nonatomic) NSString *nameChangedTime; +@property (strong, nonatomic) Bag *bag; +@end + +/***********************************************/ + +// How to map +[Student mj_setupReplacedKeyFromPropertyName:^NSDictionary *{ + return @{ + @"ID" : @"id", + @"desc" : @"description", + @"oldName" : @"name.oldName", + @"nowName" : @"name.newName", + @"nameChangedTime" : @"name.info[1].nameChangedTime", + @"bag" : @"other.bag" + }; +}]; +// Equals: Student.m implements +mj_replacedKeyFromPropertyName method. + +NSDictionary *dict = @{ + @"id" : @"20", + @"description" : @"kids", + @"name" : @{ + @"newName" : @"lufy", + @"oldName" : @"kitty", + @"info" : @[ + @"test-data", + @{ + @"nameChangedTime" : @"2013-08" + } + ] + }, + @"other" : @{ + @"bag" : @{ + @"name" : @"a red bag", + @"price" : @100.7 + } + } +}; + +// JSON -> Student +Student *stu = [Student mj_objectWithKeyValues:dict]; + +// Printing +NSLog(@"ID=%@, desc=%@, oldName=%@, nowName=%@, nameChangedTime=%@", + stu.ID, stu.desc, stu.oldName, stu.nowName, stu.nameChangedTime); +// ID=20, desc=kids, oldName=kitty, nowName=lufy, nameChangedTime=2013-08 +NSLog(@"bagName=%@, bagPrice=%f", stu.bag.name, stu.bag.price); +// bagName=a red bag, bagPrice=100.700000 +``` + + +### JSON array -> model array【将一个字典数组转成模型数组】 + +```objc +NSArray *dictArray = @[ + @{ + @"name" : @"Jack", + @"icon" : @"lufy.png" + }, + @{ + @"name" : @"Rose", + @"icon" : @"nami.png" + } + ]; + +// JSON array -> User array +NSArray *userArray = [User mj_objectArrayWithKeyValuesArray:dictArray]; + +// Printing +for (User *user in userArray) { + NSLog(@"name=%@, icon=%@", user.name, user.icon); +} +// name=Jack, icon=lufy.png +// name=Rose, icon=nami.png +``` + +### Model -> JSON【将一个模型转成字典】 +```objc +// New model +User *user = [[User alloc] init]; +user.name = @"Jack"; +user.icon = @"lufy.png"; + +Status *status = [[Status alloc] init]; +status.user = user; +status.text = @"Nice mood!"; + +// Status -> JSON +NSDictionary *statusDict = status.mj_keyValues; +NSLog(@"%@", statusDict); +/* + { + text = "Nice mood!"; + user = { + icon = "lufy.png"; + name = Jack; + }; + } + */ + +// More complex situation +Student *stu = [[Student alloc] init]; +stu.ID = @"123"; +stu.oldName = @"rose"; +stu.nowName = @"jack"; +stu.desc = @"handsome"; +stu.nameChangedTime = @"2018-09-08"; + +Bag *bag = [[Bag alloc] init]; +bag.name = @"a red bag"; +bag.price = 205; +stu.bag = bag; + +NSDictionary *stuDict = stu.mj_keyValues; +NSLog(@"%@", stuDict); +/* +{ + ID = 123; + bag = { + name = "\U5c0f\U4e66\U5305"; + price = 205; + }; + desc = handsome; + nameChangedTime = "2018-09-08"; + nowName = jack; + oldName = rose; +} + */ +``` + +### Model array -> JSON array【将一个模型数组转成字典数组】 + +```objc +// New model array +User *user1 = [[User alloc] init]; +user1.name = @"Jack"; +user1.icon = @"lufy.png"; + +User *user2 = [[User alloc] init]; +user2.name = @"Rose"; +user2.icon = @"nami.png"; + +NSArray *userArray = @[user1, user2]; + +// Model array -> JSON array +NSArray *dictArray = [User mj_keyValuesArrayWithObjectArray:userArray]; +NSLog(@"%@", dictArray); +/* + ( + { + icon = "lufy.png"; + name = Jack; + }, + { + icon = "nami.png"; + name = Rose; + } + ) + */ +``` + +### Core Data + +```swift +func json2CoreDataObject() { + context.performAndWait { + let object = MJCoreDataTester.mj_object(withKeyValues: Values.testJSONObject, context: context) + // use the object + } +} + +func coreDataObject2JSON() { + context.performAndWait { + let dict = coreDataObject.mj_keyValues() + // use dict + } +} +``` + +### Coding (Archive & Unarchive methods are deprecated in iOS 12) + +```objc +#import "MJExtension.h" + +@implementation MJBag +// NSCoding Implementation +MJCodingImplementation +@end + +/***********************************************/ + +// what properties not to be coded +[MJBag mj_setupIgnoredCodingPropertyNames:^NSArray *{ + return @[@"name"]; +}]; +// Equals: MJBag.m implements +mj_ignoredCodingPropertyNames method. + +// Create model +MJBag *bag = [[MJBag alloc] init]; +bag.name = @"Red bag"; +bag.price = 200.8; + +NSString *file = [NSHomeDirectory() stringByAppendingPathComponent:@"Desktop/bag.data"]; +// Encoding by archiving +[NSKeyedArchiver archiveRootObject:bag toFile:file]; + +// Decoding by unarchiving +MJBag *decodedBag = [NSKeyedUnarchiver unarchiveObjectWithFile:file]; +NSLog(@"name=%@, price=%f", decodedBag.name, decodedBag.price); +// name=(null), price=200.800000 +``` + +### Secure Coding + +Using `MJSecureCodingImplementation(class, isSupport)` macro. + +```objc +@import MJExtension; + +// NSSecureCoding Implementation +MJSecureCodingImplementation(MJBag, YES) + +@implementation MJBag +@end + + /***********************************************/ + +// what properties not to be coded +[MJBag mj_setupIgnoredCodingPropertyNames:^NSArray *{ + return @[@"name"]; +}]; +// Equals: MJBag.m implements +mj_ignoredCodingPropertyNames method. + +// Create model +MJBag *bag = [[MJBag alloc] init]; +bag.name = @"Red bag"; +bag.price = 200.8; +bag.isBig = YES; +bag.weight = 200; + +NSString *file = [NSTemporaryDirectory() stringByAppendingPathComponent:@"bag.data"]; + +NSError *error = nil; +// Encoding by archiving +NSData *data = [NSKeyedArchiver archivedDataWithRootObject:bag requiringSecureCoding:YES error:&error]; +[data writeToFile:file atomically:true]; + +// Decoding by unarchiving +NSData *readData = [NSFileManager.defaultManager contentsAtPath:file]; +error = nil; +MJBag *decodedBag = [NSKeyedUnarchiver unarchivedObjectOfClass:MJBag.class fromData:readData error:&error]; +MJExtensionLog(@"name=%@, price=%f", decodedBag.name, decodedBag.price); +``` + +### Camel -> underline【统一转换属性名(比如驼峰转下划线)】 + +```objc +// Dog +#import "MJExtension.h" + +@implementation Dog ++ (NSString *)mj_replacedKeyFromPropertyName121:(NSString *)propertyName +{ + // nickName -> nick_name + return [propertyName mj_underlineFromCamel]; +} +@end + +// NSDictionary +NSDictionary *dict = @{ + @"nick_name" : @"旺财", + @"sale_price" : @"10.5", + @"run_speed" : @"100.9" + }; +// NSDictionary -> Dog +Dog *dog = [Dog mj_objectWithKeyValues:dict]; + +// printing +NSLog(@"nickName=%@, scalePrice=%f runSpeed=%f", dog.nickName, dog.salePrice, dog.runSpeed); +``` + +### NSString -> NSDate, nil -> @""【过滤字典的值(比如字符串日期处理为NSDate、字符串nil处理为@"")】 +```objc +// Book +#import "MJExtension.h" + +@implementation Book +- (id)mj_newValueFromOldValue:(id)oldValue property:(MJProperty *)property +{ + if ([property.name isEqualToString:@"publisher"]) { + if (oldValue == nil) return @""; + } else if (property.type.typeClass == [NSDate class]) { + NSDateFormatter *fmt = [[NSDateFormatter alloc] init]; + fmt.dateFormat = @"yyyy-MM-dd"; + return [fmt dateFromString:oldValue]; + } + + return oldValue; +} +@end + +// NSDictionary +NSDictionary *dict = @{ + @"name" : @"5分钟突破iOS开发", + @"publishedTime" : @"2011-09-10" + }; +// NSDictionary -> Book +Book *book = [Book mj_objectWithKeyValues:dict]; + +// printing +NSLog(@"name=%@, publisher=%@, publishedTime=%@", book.name, book.publisher, book.publishedTime); +``` + +### NSDate -> NSString【模型转字典时, 修改 Date 类型至 String】 + +```objc +- (void)mj_objectDidConvertToKeyValues:(NSMutableDictionary *)keyValues { + // NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + // formatter.dateFormat = @"yyy-MM-dd"; + // should use sharedFormatter for better performance + keyValues[@"publishedTime"] = [sharedFormatter stringFromDate:self.publishedTime]; +} +``` + + + +### More use cases【更多用法】 + +- Please reference to `NSObject+MJKeyValue.h` and `NSObject+MJCoding.h` + + +## 期待 +* 如果在使用过程中遇到BUG,希望你能Issues我,谢谢(或者尝试下载最新的框架代码看看BUG修复没有) +* 如果在使用过程中发现功能不够用,希望你能Issues我,我非常想为这个框架增加更多好用的功能,谢谢 +* 如果你想为MJExtension输出代码,请拼命Pull Requests我 + diff --git a/Pods/MJRefresh/LICENSE b/Pods/MJRefresh/LICENSE new file mode 100644 index 0000000..11bf234 --- /dev/null +++ b/Pods/MJRefresh/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2013-2015 MJRefresh (https://github.com/CoderMJLee/MJRefresh) + +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. diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h b/Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h new file mode 100644 index 0000000..e4eb872 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h @@ -0,0 +1,34 @@ +// +// MJRefreshAutoFooter.h +// MJRefresh +// +// Created by MJ Lee on 15/4/24. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#if __has_include() +#import +#else +#import "MJRefreshFooter.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface MJRefreshAutoFooter : MJRefreshFooter +/** 是否自动刷新(默认为YES) */ +@property (assign, nonatomic, getter=isAutomaticallyRefresh) BOOL automaticallyRefresh; + +/** 当底部控件出现多少时就自动刷新(默认为1.0,也就是底部控件完全出现时,才会自动刷新) */ +@property (assign, nonatomic) CGFloat appearencePercentTriggerAutoRefresh MJRefreshDeprecated("请使用triggerAutomaticallyRefreshPercent属性"); + +/** 当底部控件出现多少时就自动刷新(默认为1.0,也就是底部控件完全出现时,才会自动刷新) */ +@property (assign, nonatomic) CGFloat triggerAutomaticallyRefreshPercent; + +/** 自动触发次数, 默认为 1, 仅在拖拽 ScrollView 时才生效, + + 如果为 -1, 则为无限触发 + */ +@property (nonatomic) NSInteger autoTriggerTimes; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.m b/Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.m new file mode 100644 index 0000000..b1b93fb --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.m @@ -0,0 +1,216 @@ +// +// MJRefreshAutoFooter.m +// MJRefresh +// +// Created by MJ Lee on 15/4/24. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import "MJRefreshAutoFooter.h" +#import "NSBundle+MJRefresh.h" +#import "UIView+MJExtension.h" +#import "UIScrollView+MJExtension.h" +#import "UIScrollView+MJRefresh.h" + +@interface MJRefreshAutoFooter() +/** 一个新的拖拽 */ +@property (nonatomic) BOOL triggerByDrag; +@property (nonatomic) NSInteger leftTriggerTimes; +@end + +@implementation MJRefreshAutoFooter + +#pragma mark - 初始化 +- (void)willMoveToSuperview:(UIView *)newSuperview +{ + [super willMoveToSuperview:newSuperview]; + + if (newSuperview) { // 新的父控件 + if (self.hidden == NO) { + self.scrollView.mj_insetB += self.mj_h; + } + + // 设置位置 + self.mj_y = _scrollView.mj_contentH; + } else { // 被移除了 + if (self.hidden == NO) { + self.scrollView.mj_insetB -= self.mj_h; + } + } +} + +#pragma mark - 过期方法 +- (void)setAppearencePercentTriggerAutoRefresh:(CGFloat)appearencePercentTriggerAutoRefresh +{ + self.triggerAutomaticallyRefreshPercent = appearencePercentTriggerAutoRefresh; +} + +- (CGFloat)appearencePercentTriggerAutoRefresh +{ + return self.triggerAutomaticallyRefreshPercent; +} + +#pragma mark - 实现父类的方法 +- (void)prepare +{ + [super prepare]; + + // 默认底部控件100%出现时才会自动刷新 + self.triggerAutomaticallyRefreshPercent = 1.0; + + // 设置为默认状态 + self.automaticallyRefresh = YES; + + self.autoTriggerTimes = 1; +} + +- (void)scrollViewContentSizeDidChange:(NSDictionary *)change +{ + [super scrollViewContentSizeDidChange:change]; + + CGSize size = [change[NSKeyValueChangeNewKey] CGSizeValue]; + CGFloat contentHeight = size.height == 0 ? self.scrollView.mj_contentH : size.height; + // 设置位置 + CGFloat y = contentHeight + self.ignoredScrollViewContentInsetBottom; + if (self.mj_y != y) { + self.mj_y = y; + } +} + +- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change +{ + [super scrollViewContentOffsetDidChange:change]; + + if (self.state != MJRefreshStateIdle || !self.automaticallyRefresh || self.mj_y == 0) return; + + if (_scrollView.mj_insetT + _scrollView.mj_contentH > _scrollView.mj_h) { // 内容超过一个屏幕 + // 这里的_scrollView.mj_contentH替换掉self.mj_y更为合理 + if (_scrollView.mj_offsetY >= _scrollView.mj_contentH - _scrollView.mj_h + self.mj_h * self.triggerAutomaticallyRefreshPercent + _scrollView.mj_insetB - self.mj_h) { + // 防止手松开时连续调用 + CGPoint old = [change[@"old"] CGPointValue]; + CGPoint new = [change[@"new"] CGPointValue]; + if (new.y <= old.y) return; + + if (_scrollView.isDragging) { + self.triggerByDrag = YES; + } + // 当底部刷新控件完全出现时,才刷新 + [self beginRefreshing]; + } + } +} + +- (void)scrollViewPanStateDidChange:(NSDictionary *)change +{ + [super scrollViewPanStateDidChange:change]; + + if (self.state != MJRefreshStateIdle) return; + + UIGestureRecognizerState panState = _scrollView.panGestureRecognizer.state; + + switch (panState) { + // 手松开 + case UIGestureRecognizerStateEnded: { + if (_scrollView.mj_insetT + _scrollView.mj_contentH <= _scrollView.mj_h) { // 不够一个屏幕 + if (_scrollView.mj_offsetY >= - _scrollView.mj_insetT) { // 向上拽 + self.triggerByDrag = YES; + [self beginRefreshing]; + } + } else { // 超出一个屏幕 + if (_scrollView.mj_offsetY >= _scrollView.mj_contentH + _scrollView.mj_insetB - _scrollView.mj_h) { + self.triggerByDrag = YES; + [self beginRefreshing]; + } + } + } + break; + + case UIGestureRecognizerStateBegan: { + [self resetTriggerTimes]; + } + break; + + default: + break; + } +} + +- (BOOL)unlimitedTrigger { + return self.leftTriggerTimes == -1; +} + +- (void)beginRefreshing +{ + if (self.triggerByDrag && self.leftTriggerTimes <= 0 && !self.unlimitedTrigger) { + return; + } + + [super beginRefreshing]; +} + +- (void)setState:(MJRefreshState)state +{ + MJRefreshCheckState + + if (state == MJRefreshStateRefreshing) { + [self executeRefreshingCallback]; + } else if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) { + if (self.triggerByDrag) { + if (!self.unlimitedTrigger) { + self.leftTriggerTimes -= 1; + } + self.triggerByDrag = NO; + } + + if (MJRefreshStateRefreshing == oldState) { + if (self.scrollView.pagingEnabled) { + CGPoint offset = self.scrollView.contentOffset; + offset.y -= self.scrollView.mj_insetB; + [UIView animateWithDuration:self.slowAnimationDuration animations:^{ + self.scrollView.contentOffset = offset; + + if (self.endRefreshingAnimationBeginAction) { + self.endRefreshingAnimationBeginAction(); + } + } completion:^(BOOL finished) { + if (self.endRefreshingCompletionBlock) { + self.endRefreshingCompletionBlock(); + } + }]; + return; + } + + if (self.endRefreshingCompletionBlock) { + self.endRefreshingCompletionBlock(); + } + } + } +} + +- (void)resetTriggerTimes { + self.leftTriggerTimes = self.autoTriggerTimes; +} + +- (void)setHidden:(BOOL)hidden +{ + BOOL lastHidden = self.isHidden; + + [super setHidden:hidden]; + + if (!lastHidden && hidden) { + self.state = MJRefreshStateIdle; + + self.scrollView.mj_insetB -= self.mj_h; + } else if (lastHidden && !hidden) { + self.scrollView.mj_insetB += self.mj_h; + + // 设置位置 + self.mj_y = _scrollView.mj_contentH; + } +} + +- (void)setAutoTriggerTimes:(NSInteger)autoTriggerTimes { + _autoTriggerTimes = autoTriggerTimes; + self.leftTriggerTimes = autoTriggerTimes; +} +@end diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h b/Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h new file mode 100644 index 0000000..8484372 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h @@ -0,0 +1,21 @@ +// +// MJRefreshBackFooter.h +// MJRefresh +// +// Created by MJ Lee on 15/4/24. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#if __has_include() +#import +#else +#import "MJRefreshFooter.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface MJRefreshBackFooter : MJRefreshFooter + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.m b/Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.m new file mode 100644 index 0000000..4990ca5 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.m @@ -0,0 +1,158 @@ +// +// MJRefreshBackFooter.m +// MJRefresh +// +// Created by MJ Lee on 15/4/24. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import "MJRefreshBackFooter.h" +#import "NSBundle+MJRefresh.h" +#import "UIView+MJExtension.h" +#import "UIScrollView+MJExtension.h" +#import "UIScrollView+MJRefresh.h" + +@interface MJRefreshBackFooter() +@property (assign, nonatomic) NSInteger lastRefreshCount; +@property (assign, nonatomic) CGFloat lastBottomDelta; +@end + +@implementation MJRefreshBackFooter + +#pragma mark - 初始化 +- (void)willMoveToSuperview:(UIView *)newSuperview +{ + [super willMoveToSuperview:newSuperview]; + + [self scrollViewContentSizeDidChange:nil]; +} + +#pragma mark - 实现父类的方法 +- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change +{ + [super scrollViewContentOffsetDidChange:change]; + + // 如果正在刷新,直接返回 + if (self.state == MJRefreshStateRefreshing) return; + + _scrollViewOriginalInset = self.scrollView.mj_inset; + + // 当前的contentOffset + CGFloat currentOffsetY = self.scrollView.mj_offsetY; + // 尾部控件刚好出现的offsetY + CGFloat happenOffsetY = [self happenOffsetY]; + // 如果是向下滚动到看不见尾部控件,直接返回 + if (currentOffsetY <= happenOffsetY) return; + + CGFloat pullingPercent = (currentOffsetY - happenOffsetY) / self.mj_h; + + // 如果已全部加载,仅设置pullingPercent,然后返回 + if (self.state == MJRefreshStateNoMoreData) { + self.pullingPercent = pullingPercent; + return; + } + + if (self.scrollView.isDragging) { + self.pullingPercent = pullingPercent; + // 普通 和 即将刷新 的临界点 + CGFloat normal2pullingOffsetY = happenOffsetY + self.mj_h; + + if (self.state == MJRefreshStateIdle && currentOffsetY > normal2pullingOffsetY) { + // 转为即将刷新状态 + self.state = MJRefreshStatePulling; + } else if (self.state == MJRefreshStatePulling && currentOffsetY <= normal2pullingOffsetY) { + // 转为普通状态 + self.state = MJRefreshStateIdle; + } + } else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开 + // 开始刷新 + [self beginRefreshing]; + } else if (pullingPercent < 1) { + self.pullingPercent = pullingPercent; + } +} + +- (void)scrollViewContentSizeDidChange:(NSDictionary *)change +{ + [super scrollViewContentSizeDidChange:change]; + + CGSize size = [change[NSKeyValueChangeNewKey] CGSizeValue]; + CGFloat contentHeight = size.height == 0 ? self.scrollView.mj_contentH : size.height; + // 内容的高度 + contentHeight += self.ignoredScrollViewContentInsetBottom; + // 表格的高度 + CGFloat scrollHeight = self.scrollView.mj_h - self.scrollViewOriginalInset.top - self.scrollViewOriginalInset.bottom + self.ignoredScrollViewContentInsetBottom; + // 设置位置 + CGFloat y = MAX(contentHeight, scrollHeight); + if (self.mj_y != y) { + self.mj_y = y; + } +} + +- (void)setState:(MJRefreshState)state +{ + MJRefreshCheckState + + // 根据状态来设置属性 + if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) { + // 刷新完毕 + if (MJRefreshStateRefreshing == oldState) { + [UIView animateWithDuration:self.slowAnimationDuration animations:^{ + if (self.endRefreshingAnimationBeginAction) { + self.endRefreshingAnimationBeginAction(); + } + + self.scrollView.mj_insetB -= self.lastBottomDelta; + // 自动调整透明度 + if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0; + } completion:^(BOOL finished) { + self.pullingPercent = 0.0; + + if (self.endRefreshingCompletionBlock) { + self.endRefreshingCompletionBlock(); + } + }]; + } + + CGFloat deltaH = [self heightForContentBreakView]; + // 刚刷新完毕 + if (MJRefreshStateRefreshing == oldState && deltaH > 0 && self.scrollView.mj_totalDataCount != self.lastRefreshCount) { + self.scrollView.mj_offsetY = self.scrollView.mj_offsetY; + } + } else if (state == MJRefreshStateRefreshing) { + // 记录刷新前的数量 + self.lastRefreshCount = self.scrollView.mj_totalDataCount; + + [UIView animateWithDuration:self.fastAnimationDuration animations:^{ + CGFloat bottom = self.mj_h + self.scrollViewOriginalInset.bottom; + CGFloat deltaH = [self heightForContentBreakView]; + if (deltaH < 0) { // 如果内容高度小于view的高度 + bottom -= deltaH; + } + self.lastBottomDelta = bottom - self.scrollView.mj_insetB; + self.scrollView.mj_insetB = bottom; + self.scrollView.mj_offsetY = [self happenOffsetY] + self.mj_h; + } completion:^(BOOL finished) { + [self executeRefreshingCallback]; + }]; + } +} +#pragma mark - 私有方法 +#pragma mark 获得scrollView的内容 超出 view 的高度 +- (CGFloat)heightForContentBreakView +{ + CGFloat h = self.scrollView.frame.size.height - self.scrollViewOriginalInset.bottom - self.scrollViewOriginalInset.top; + return self.scrollView.contentSize.height - h; +} + +#pragma mark 刚好看到上拉刷新控件时的contentOffset.y +- (CGFloat)happenOffsetY +{ + CGFloat deltaH = [self heightForContentBreakView]; + if (deltaH > 0) { + return deltaH - self.scrollViewOriginalInset.top; + } else { + return - self.scrollViewOriginalInset.top; + } +} +@end diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.h b/Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.h new file mode 100644 index 0000000..f098101 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.h @@ -0,0 +1,151 @@ +// 代码地址: https://github.com/CoderMJLee/MJRefresh +// MJRefreshComponent.h +// MJRefresh +// +// Created by MJ Lee on 15/3/4. +// Copyright (c) 2015年 小码哥. All rights reserved. +// 刷新控件的基类 + +#import +#if __has_include() +#import +#else +#import "MJRefreshConst.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** 刷新控件的状态 */ +typedef NS_ENUM(NSInteger, MJRefreshState) { + /** 普通闲置状态 */ + MJRefreshStateIdle = 1, + /** 松开就可以进行刷新的状态 */ + MJRefreshStatePulling, + /** 正在刷新中的状态 */ + MJRefreshStateRefreshing, + /** 即将刷新的状态 */ + MJRefreshStateWillRefresh, + /** 所有数据加载完毕,没有更多的数据了 */ + MJRefreshStateNoMoreData +}; + +/** 进入刷新状态的回调 */ +typedef void (^MJRefreshComponentRefreshingBlock)(void) MJRefreshDeprecated("first deprecated in 3.3.0 - Use `MJRefreshComponentAction` instead"); +/** 开始刷新后的回调(进入刷新状态后的回调) */ +typedef void (^MJRefreshComponentBeginRefreshingCompletionBlock)(void) MJRefreshDeprecated("first deprecated in 3.3.0 - Use `MJRefreshComponentAction` instead"); +/** 结束刷新后的回调 */ +typedef void (^MJRefreshComponentEndRefreshingCompletionBlock)(void) MJRefreshDeprecated("first deprecated in 3.3.0 - Use `MJRefreshComponentAction` instead"); + +/** 刷新用到的回调类型 */ +typedef void (^MJRefreshComponentAction)(void); + +/** 刷新控件的基类 */ +@interface MJRefreshComponent : UIView +{ + /** 记录scrollView刚开始的inset */ + UIEdgeInsets _scrollViewOriginalInset; + /** 父控件 */ + __weak UIScrollView *_scrollView; +} + +#pragma mark - 刷新动画时间控制 +/** 快速动画时间(一般用在刷新开始的回弹动画), 默认 0.25 */ +@property (nonatomic) NSTimeInterval fastAnimationDuration; +/** 慢速动画时间(一般用在刷新结束后的回弹动画), 默认 0.4*/ +@property (nonatomic) NSTimeInterval slowAnimationDuration; +/** 关闭全部默认动画效果, 可以简单粗暴地解决 CollectionView 的回弹动画 bug */ +- (instancetype)setAnimationDisabled; + +#pragma mark - 刷新回调 +/** 正在刷新的回调 */ +@property (copy, nonatomic, nullable) MJRefreshComponentAction refreshingBlock; +/** 设置回调对象和回调方法 */ +- (void)setRefreshingTarget:(id)target refreshingAction:(SEL)action; + +/** 回调对象 */ +@property (weak, nonatomic) id refreshingTarget; +/** 回调方法 */ +@property (assign, nonatomic) SEL refreshingAction; +/** 触发回调(交给子类去调用) */ +- (void)executeRefreshingCallback; + +#pragma mark - 刷新状态控制 +/** 进入刷新状态 */ +- (void)beginRefreshing; +- (void)beginRefreshingWithCompletionBlock:(void (^)(void))completionBlock; +/** 开始刷新后的回调(进入刷新状态后的回调) */ +@property (copy, nonatomic, nullable) MJRefreshComponentAction beginRefreshingCompletionBlock; +/** 带动画的结束刷新的回调 */ +@property (copy, nonatomic, nullable) MJRefreshComponentAction endRefreshingAnimateCompletionBlock MJRefreshDeprecated("first deprecated in 3.3.0 - Use `endRefreshingAnimationBeginAction` instead"); +@property (copy, nonatomic, nullable) MJRefreshComponentAction endRefreshingAnimationBeginAction; +/** 结束刷新的回调 */ +@property (copy, nonatomic, nullable) MJRefreshComponentAction endRefreshingCompletionBlock; +/** 结束刷新状态 */ +- (void)endRefreshing; +- (void)endRefreshingWithCompletionBlock:(void (^)(void))completionBlock; +/** 是否正在刷新 */ +@property (assign, nonatomic, readonly, getter=isRefreshing) BOOL refreshing; + +/** 刷新状态 一般交给子类内部实现 */ +@property (assign, nonatomic) MJRefreshState state; + +#pragma mark - 交给子类去访问 +/** 记录scrollView刚开始的inset */ +@property (assign, nonatomic, readonly) UIEdgeInsets scrollViewOriginalInset; +/** 父控件 */ +@property (weak, nonatomic, readonly) UIScrollView *scrollView; + +#pragma mark - 交给子类们去实现 +/** 初始化 */ +- (void)prepare NS_REQUIRES_SUPER; +/** 摆放子控件frame */ +- (void)placeSubviews NS_REQUIRES_SUPER; +/** 当scrollView的contentOffset发生改变的时候调用 */ +- (void)scrollViewContentOffsetDidChange:(nullable NSDictionary *)change NS_REQUIRES_SUPER; +/** 当scrollView的contentSize发生改变的时候调用 */ +- (void)scrollViewContentSizeDidChange:(nullable NSDictionary *)change NS_REQUIRES_SUPER; +/** 当scrollView的拖拽状态发生改变的时候调用 */ +- (void)scrollViewPanStateDidChange:(nullable NSDictionary *)change NS_REQUIRES_SUPER; + +/** 多语言配置 language 发生变化时调用 + + `MJRefreshConfig.defaultConfig.language` 发生改变时调用. + + ⚠️ 父类会调用 `placeSubviews` 方法, 请勿在 placeSubviews 中调用本方法, 造成死循环. 子类在需要重新布局时, 在配置完修改后, 最后再调用 super 方法, 否则可能导致配置修改后, 定位先于修改执行. + */ +- (void)i18nDidChange NS_REQUIRES_SUPER; + +#pragma mark - 其他 +/** 拉拽的百分比(交给子类重写) */ +@property (assign, nonatomic) CGFloat pullingPercent; +/** 根据拖拽比例自动切换透明度 */ +@property (assign, nonatomic, getter=isAutoChangeAlpha) BOOL autoChangeAlpha MJRefreshDeprecated("请使用automaticallyChangeAlpha属性"); +/** 根据拖拽比例自动切换透明度 */ +@property (assign, nonatomic, getter=isAutomaticallyChangeAlpha) BOOL automaticallyChangeAlpha; +@end + +@interface UILabel(MJRefresh) ++ (instancetype)mj_label; +- (CGFloat)mj_textWidth; +@end + +@interface MJRefreshComponent (ChainingGrammar) + +#pragma mark - <<< 为 Swift 扩展链式语法 >>> - +/// 自动变化透明度 +- (instancetype)autoChangeTransparency:(BOOL)isAutoChange; +/// 刷新开始后立即调用的回调 +- (instancetype)afterBeginningAction:(MJRefreshComponentAction)action; +/// 刷新动画开始后立即调用的回调 +- (instancetype)endingAnimationBeginningAction:(MJRefreshComponentAction)action; +/// 刷新结束后立即调用的回调 +- (instancetype)afterEndingAction:(MJRefreshComponentAction)action; + + +/// 需要子类必须实现 +/// @param scrollView 赋值给的 ScrollView 的 Header/Footer/Trailer +- (instancetype)linkTo:(UIScrollView *)scrollView; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.m b/Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.m new file mode 100644 index 0000000..785df18 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.m @@ -0,0 +1,323 @@ +// 代码地址: https://github.com/CoderMJLee/MJRefresh +// MJRefreshComponent.m +// MJRefresh +// +// Created by MJ Lee on 15/3/4. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import "MJRefreshComponent.h" +#import "MJRefreshConst.h" +#import "MJRefreshConfig.h" +#import "UIView+MJExtension.h" +#import "UIScrollView+MJExtension.h" +#import "UIScrollView+MJRefresh.h" +#import "NSBundle+MJRefresh.h" + +@interface MJRefreshComponent() +@property (strong, nonatomic) UIPanGestureRecognizer *pan; +@end + +@implementation MJRefreshComponent +#pragma mark - 初始化 +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + // 准备工作 + [self prepare]; + + // 默认是普通状态 + self.state = MJRefreshStateIdle; + self.fastAnimationDuration = 0.25; + self.slowAnimationDuration = 0.4; + } + return self; +} + +- (void)prepare +{ + // 基本属性 + self.autoresizingMask = UIViewAutoresizingFlexibleWidth; + self.backgroundColor = [UIColor clearColor]; +} + +- (void)layoutSubviews +{ + [self placeSubviews]; + + [super layoutSubviews]; +} + +- (void)placeSubviews{} + +- (void)willMoveToSuperview:(UIView *)newSuperview +{ + [super willMoveToSuperview:newSuperview]; + + // 如果不是UIScrollView,不做任何事情 + if (newSuperview && ![newSuperview isKindOfClass:[UIScrollView class]]) return; + + // 旧的父控件移除监听 + [self removeObservers]; + + if (newSuperview) { // 新的父控件 + // 记录UIScrollView + _scrollView = (UIScrollView *)newSuperview; + + // 设置宽度 + self.mj_w = _scrollView.mj_w; + // 设置位置 + self.mj_x = -_scrollView.mj_insetL; + + // 设置永远支持垂直弹簧效果 + _scrollView.alwaysBounceVertical = YES; + // 记录UIScrollView最开始的contentInset + _scrollViewOriginalInset = _scrollView.mj_inset; + + // 添加监听 + [self addObservers]; + } +} + +- (void)drawRect:(CGRect)rect +{ + [super drawRect:rect]; + + if (self.state == MJRefreshStateWillRefresh) { + // 预防view还没显示出来就调用了beginRefreshing + self.state = MJRefreshStateRefreshing; + } +} + +#pragma mark - KVO监听 +- (void)addObservers +{ + NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld; + [self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentOffset options:options context:nil]; + [self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentSize options:options context:nil]; + self.pan = self.scrollView.panGestureRecognizer; + [self.pan addObserver:self forKeyPath:MJRefreshKeyPathPanState options:options context:nil]; + + [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(i18nDidChange) name:MJRefreshDidChangeLanguageNotification object:MJRefreshConfig.defaultConfig]; +} + +- (void)removeObservers +{ + [self.superview removeObserver:self forKeyPath:MJRefreshKeyPathContentOffset]; + [self.superview removeObserver:self forKeyPath:MJRefreshKeyPathContentSize]; + [self.pan removeObserver:self forKeyPath:MJRefreshKeyPathPanState]; + self.pan = nil; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + // 遇到这些情况就直接返回 + if (!self.userInteractionEnabled) return; + + // 这个就算看不见也需要处理 + if ([keyPath isEqualToString:MJRefreshKeyPathContentSize]) { + [self scrollViewContentSizeDidChange:change]; + } + + // 看不见 + if (self.hidden) return; + if ([keyPath isEqualToString:MJRefreshKeyPathContentOffset]) { + [self scrollViewContentOffsetDidChange:change]; + } else if ([keyPath isEqualToString:MJRefreshKeyPathPanState]) { + [self scrollViewPanStateDidChange:change]; + } +} + +- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change{} +- (void)scrollViewContentSizeDidChange:(NSDictionary *)change{} +- (void)scrollViewPanStateDidChange:(NSDictionary *)change{} + +- (void)i18nDidChange { + [self placeSubviews]; +} + +#pragma mark - 公共方法 +#pragma mark 设置回调对象和回调方法 +- (void)setRefreshingTarget:(id)target refreshingAction:(SEL)action +{ + self.refreshingTarget = target; + self.refreshingAction = action; +} + +- (void)setState:(MJRefreshState)state +{ + _state = state; + + // 加入主队列的目的是等setState:方法调用完毕、设置完文字后再去布局子控件 + MJRefreshDispatchAsyncOnMainQueue([self setNeedsLayout];) +} + +#pragma mark 进入刷新状态 +- (void)beginRefreshing +{ + [UIView animateWithDuration:self.fastAnimationDuration animations:^{ + self.alpha = 1.0; + }]; + self.pullingPercent = 1.0; + // 只要正在刷新,就完全显示 + if (self.window) { + self.state = MJRefreshStateRefreshing; + } else { + // 预防正在刷新中时,调用本方法使得header inset回置失败 + if (self.state != MJRefreshStateRefreshing) { + self.state = MJRefreshStateWillRefresh; + // 刷新(预防从另一个控制器回到这个控制器的情况,回来要重新刷新一下) + [self setNeedsDisplay]; + } + } +} + +- (void)beginRefreshingWithCompletionBlock:(void (^)(void))completionBlock +{ + self.beginRefreshingCompletionBlock = completionBlock; + + [self beginRefreshing]; +} + +#pragma mark 结束刷新状态 +- (void)endRefreshing +{ + MJRefreshDispatchAsyncOnMainQueue(self.state = MJRefreshStateIdle;) +} + +- (void)endRefreshingWithCompletionBlock:(void (^)(void))completionBlock +{ + self.endRefreshingCompletionBlock = completionBlock; + + [self endRefreshing]; +} + +#pragma mark 是否正在刷新 +- (BOOL)isRefreshing +{ + return self.state == MJRefreshStateRefreshing || self.state == MJRefreshStateWillRefresh; +} + +#pragma mark 自动切换透明度 +- (void)setAutoChangeAlpha:(BOOL)autoChangeAlpha +{ + self.automaticallyChangeAlpha = autoChangeAlpha; +} + +- (BOOL)isAutoChangeAlpha +{ + return self.isAutomaticallyChangeAlpha; +} + +- (void)setAutomaticallyChangeAlpha:(BOOL)automaticallyChangeAlpha +{ + _automaticallyChangeAlpha = automaticallyChangeAlpha; + + if (self.isRefreshing) return; + + if (automaticallyChangeAlpha) { + self.alpha = self.pullingPercent; + } else { + self.alpha = 1.0; + } +} + +#pragma mark 根据拖拽进度设置透明度 +- (void)setPullingPercent:(CGFloat)pullingPercent +{ + _pullingPercent = pullingPercent; + + if (self.isRefreshing) return; + + if (self.isAutomaticallyChangeAlpha) { + self.alpha = pullingPercent; + } +} + +#pragma mark - 内部方法 +- (void)executeRefreshingCallback +{ + if (self.refreshingBlock) { + self.refreshingBlock(); + } + if ([self.refreshingTarget respondsToSelector:self.refreshingAction]) { + MJRefreshMsgSend(MJRefreshMsgTarget(self.refreshingTarget), self.refreshingAction, self); + } + if (self.beginRefreshingCompletionBlock) { + self.beginRefreshingCompletionBlock(); + } +} + +#pragma mark - 刷新动画时间控制 +- (instancetype)setAnimationDisabled { + self.fastAnimationDuration = 0; + self.slowAnimationDuration = 0; + + return self; +} + +#pragma mark - <<< Deprecation compatible function >>> - +- (void)setEndRefreshingAnimateCompletionBlock:(MJRefreshComponentEndRefreshingCompletionBlock)endRefreshingAnimateCompletionBlock { + _endRefreshingAnimationBeginAction = endRefreshingAnimateCompletionBlock; +} +@end + +@implementation UILabel(MJRefresh) ++ (instancetype)mj_label +{ + UILabel *label = [[self alloc] init]; + label.font = MJRefreshLabelFont; + label.textColor = MJRefreshLabelTextColor; + label.autoresizingMask = UIViewAutoresizingFlexibleWidth; + label.textAlignment = NSTextAlignmentCenter; + label.backgroundColor = [UIColor clearColor]; + return label; +} + +- (CGFloat)mj_textWidth { + CGFloat stringWidth = 0; + CGSize size = CGSizeMake(MAXFLOAT, MAXFLOAT); + + if (self.attributedText) { + if (self.attributedText.length == 0) { return 0; } + stringWidth = [self.attributedText boundingRectWithSize:size + options:NSStringDrawingUsesLineFragmentOrigin + context:nil].size.width; + } else { + if (self.text.length == 0) { return 0; } + NSAssert(self.font != nil, @"请检查 mj_label's `font` 是否设置正确"); + stringWidth = [self.text boundingRectWithSize:size + options:NSStringDrawingUsesLineFragmentOrigin + attributes:@{NSFontAttributeName:self.font} + context:nil].size.width; + } + return stringWidth; +} +@end + + +#pragma mark - <<< 为 Swift 扩展链式语法 >>> - +@implementation MJRefreshComponent (ChainingGrammar) + +- (instancetype)autoChangeTransparency:(BOOL)isAutoChange { + self.automaticallyChangeAlpha = isAutoChange; + return self; +} +- (instancetype)afterBeginningAction:(MJRefreshComponentAction)action { + self.beginRefreshingCompletionBlock = action; + return self; +} +- (instancetype)endingAnimationBeginningAction:(MJRefreshComponentAction)action { + self.endRefreshingAnimationBeginAction = action; + return self; +} +- (instancetype)afterEndingAction:(MJRefreshComponentAction)action { + self.endRefreshingCompletionBlock = action; + return self; +} + +- (instancetype)linkTo:(UIScrollView *)scrollView { + return self; +} + +@end diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.h b/Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.h new file mode 100644 index 0000000..7b7c7b6 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.h @@ -0,0 +1,37 @@ +// 代码地址: https://github.com/CoderMJLee/MJRefresh +// MJRefreshFooter.h +// MJRefresh +// +// Created by MJ Lee on 15/3/5. +// Copyright (c) 2015年 小码哥. All rights reserved. +// 上拉刷新控件 + +#if __has_include() +#import +#else +#import "MJRefreshComponent.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface MJRefreshFooter : MJRefreshComponent +/** 创建footer */ ++ (instancetype)footerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock; +/** 创建footer */ ++ (instancetype)footerWithRefreshingTarget:(id)target refreshingAction:(SEL)action; + +/** 提示没有更多的数据 */ +- (void)endRefreshingWithNoMoreData; +- (void)noticeNoMoreData MJRefreshDeprecated("使用endRefreshingWithNoMoreData"); + +/** 重置没有更多的数据(消除没有更多数据的状态) */ +- (void)resetNoMoreData; + +/** 忽略多少scrollView的contentInset的bottom */ +@property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetBottom; + +/** 自动根据有无数据来显示和隐藏(有数据就显示,没有数据隐藏。默认是NO) */ +@property (assign, nonatomic, getter=isAutomaticallyHidden) BOOL automaticallyHidden MJRefreshDeprecated("已废弃此属性,开发者请自行控制footer的显示和隐藏"); +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.m b/Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.m new file mode 100644 index 0000000..8096fdb --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.m @@ -0,0 +1,71 @@ +// 代码地址: https://github.com/CoderMJLee/MJRefresh +// MJRefreshFooter.m +// MJRefresh +// +// Created by MJ Lee on 15/3/5. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import "MJRefreshFooter.h" +#import "UIScrollView+MJRefresh.h" +#import "UIView+MJExtension.h" + +@interface MJRefreshFooter() + +@end + +@implementation MJRefreshFooter +#pragma mark - 构造方法 ++ (instancetype)footerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock +{ + MJRefreshFooter *cmp = [[self alloc] init]; + cmp.refreshingBlock = refreshingBlock; + return cmp; +} ++ (instancetype)footerWithRefreshingTarget:(id)target refreshingAction:(SEL)action +{ + MJRefreshFooter *cmp = [[self alloc] init]; + [cmp setRefreshingTarget:target refreshingAction:action]; + return cmp; +} + +#pragma mark - 重写父类的方法 +- (void)prepare +{ + [super prepare]; + + // 设置自己的高度 + self.mj_h = MJRefreshFooterHeight; + + // 默认不会自动隐藏 +// self.automaticallyHidden = NO; +} + +#pragma mark . 链式语法部分 . + +- (instancetype)linkTo:(UIScrollView *)scrollView { + scrollView.mj_footer = self; + return self; +} + +#pragma mark - 公共方法 +- (void)endRefreshingWithNoMoreData +{ + MJRefreshDispatchAsyncOnMainQueue(self.state = MJRefreshStateNoMoreData;) +} + +- (void)noticeNoMoreData +{ + [self endRefreshingWithNoMoreData]; +} + +- (void)resetNoMoreData +{ + MJRefreshDispatchAsyncOnMainQueue(self.state = MJRefreshStateIdle;) +} + +- (void)setAutomaticallyHidden:(BOOL)automaticallyHidden +{ + _automaticallyHidden = automaticallyHidden; +} +@end diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.h b/Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.h new file mode 100644 index 0000000..95d8cb2 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.h @@ -0,0 +1,35 @@ +// 代码地址: https://github.com/CoderMJLee/MJRefresh +// MJRefreshHeader.h +// MJRefresh +// +// Created by MJ Lee on 15/3/4. +// Copyright (c) 2015年 小码哥. All rights reserved. +// 下拉刷新控件:负责监控用户下拉的状态 + +#if __has_include() +#import +#else +#import "MJRefreshComponent.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface MJRefreshHeader : MJRefreshComponent +/** 创建header */ ++ (instancetype)headerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock; +/** 创建header */ ++ (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action; + +/** 这个key用来存储上一次下拉刷新成功的时间 */ +@property (copy, nonatomic) NSString *lastUpdatedTimeKey; +/** 上一次下拉刷新成功的时间 */ +@property (strong, nonatomic, readonly, nullable) NSDate *lastUpdatedTime; + +/** 忽略多少scrollView的contentInset的top */ +@property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetTop; + +/** 默认是关闭状态, 如果遇到 CollectionView 的动画异常问题可以尝试打开 */ +@property (nonatomic) BOOL isCollectionViewAnimationBug; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.m b/Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.m new file mode 100644 index 0000000..b276412 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.m @@ -0,0 +1,297 @@ +// 代码地址: https://github.com/CoderMJLee/MJRefresh +// MJRefreshHeader.m +// MJRefresh +// +// Created by MJ Lee on 15/3/4. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import "MJRefreshHeader.h" +#import "UIView+MJExtension.h" +#import "UIScrollView+MJExtension.h" +#import "UIScrollView+MJRefresh.h" + +NSString * const MJRefreshHeaderRefreshing2IdleBoundsKey = @"MJRefreshHeaderRefreshing2IdleBounds"; +NSString * const MJRefreshHeaderRefreshingBoundsKey = @"MJRefreshHeaderRefreshingBounds"; + +@interface MJRefreshHeader() +@property (assign, nonatomic) CGFloat insetTDelta; +@end + +@implementation MJRefreshHeader +#pragma mark - 构造方法 ++ (instancetype)headerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock +{ + MJRefreshHeader *cmp = [[self alloc] init]; + cmp.refreshingBlock = refreshingBlock; + return cmp; +} ++ (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action +{ + MJRefreshHeader *cmp = [[self alloc] init]; + [cmp setRefreshingTarget:target refreshingAction:action]; + return cmp; +} + +#pragma mark - 覆盖父类的方法 +- (void)prepare +{ + [super prepare]; + + // 设置key + self.lastUpdatedTimeKey = MJRefreshHeaderLastUpdatedTimeKey; + + // 设置高度 + self.mj_h = MJRefreshHeaderHeight; +} + +- (void)placeSubviews +{ + [super placeSubviews]; + + // 设置y值(当自己的高度发生改变了,肯定要重新调整Y值,所以放到placeSubviews方法中设置y值) + self.mj_y = - self.mj_h - self.ignoredScrollViewContentInsetTop; +} + +- (void)resetInset { + if (@available(iOS 11.0, *)) { + } else { + // 如果 iOS 10 及以下系统在刷新时, push 新的 VC, 等待刷新完成后回来, 会导致顶部 Insets.top 异常, 不能 resetInset, 检查一下这种特殊情况 + if (!self.window) { return; } + } + + // sectionheader停留解决 + CGFloat insetT = - self.scrollView.mj_offsetY > _scrollViewOriginalInset.top ? - self.scrollView.mj_offsetY : _scrollViewOriginalInset.top; + insetT = insetT > self.mj_h + _scrollViewOriginalInset.top ? self.mj_h + _scrollViewOriginalInset.top : insetT; + self.insetTDelta = _scrollViewOriginalInset.top - insetT; + // 避免 CollectionView 在使用根据 Autolayout 和 内容自动伸缩 Cell, 刷新时导致的 Layout 异常渲染问题 + if (fabs(self.scrollView.mj_insetT - insetT) > FLT_EPSILON) { + self.scrollView.mj_insetT = insetT; + } +} + +- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change +{ + [super scrollViewContentOffsetDidChange:change]; + + // 在刷新的refreshing状态 + if (self.state == MJRefreshStateRefreshing) { + [self resetInset]; + return; + } + + // 跳转到下一个控制器时,contentInset可能会变 + _scrollViewOriginalInset = self.scrollView.mj_inset; + + // 当前的contentOffset + CGFloat offsetY = self.scrollView.mj_offsetY; + // 头部控件刚好出现的offsetY + CGFloat happenOffsetY = - self.scrollViewOriginalInset.top; + + // 如果是向上滚动到看不见头部控件,直接返回 + // >= -> > + if (offsetY > happenOffsetY) return; + + // 普通 和 即将刷新 的临界点 + CGFloat normal2pullingOffsetY = happenOffsetY - self.mj_h; + CGFloat pullingPercent = (happenOffsetY - offsetY) / self.mj_h; + + if (self.scrollView.isDragging) { // 如果正在拖拽 + self.pullingPercent = pullingPercent; + if (self.state == MJRefreshStateIdle && offsetY < normal2pullingOffsetY) { + // 转为即将刷新状态 + self.state = MJRefreshStatePulling; + } else if (self.state == MJRefreshStatePulling && offsetY >= normal2pullingOffsetY) { + // 转为普通状态 + self.state = MJRefreshStateIdle; + } + } else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开 + // 开始刷新 + [self beginRefreshing]; + } else if (pullingPercent < 1) { + self.pullingPercent = pullingPercent; + } +} + +- (void)setState:(MJRefreshState)state +{ + MJRefreshCheckState + + // 根据状态做事情 + if (state == MJRefreshStateIdle) { + if (oldState != MJRefreshStateRefreshing) return; + + [self headerEndingAction]; + } else if (state == MJRefreshStateRefreshing) { + [self headerRefreshingAction]; + } +} + +- (void)headerEndingAction { + // 保存刷新时间 + [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:self.lastUpdatedTimeKey]; + [[NSUserDefaults standardUserDefaults] synchronize]; + + // 默认使用 UIViewAnimation 动画 + if (!self.isCollectionViewAnimationBug) { + // 恢复inset和offset + [UIView animateWithDuration:self.slowAnimationDuration animations:^{ + self.scrollView.mj_insetT += self.insetTDelta; + + if (self.endRefreshingAnimationBeginAction) { + self.endRefreshingAnimationBeginAction(); + } + // 自动调整透明度 + if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0; + } completion:^(BOOL finished) { + self.pullingPercent = 0.0; + + if (self.endRefreshingCompletionBlock) { + self.endRefreshingCompletionBlock(); + } + }]; + + return; + } + + /** + 这个解决方法的思路出自 https://github.com/CoderMJLee/MJRefresh/pull/844 + 修改了用+ [UIView animateWithDuration: animations:]实现的修改contentInset的动画 + fix issue#225 https://github.com/CoderMJLee/MJRefresh/issues/225 + 另一种解法 pull#737 https://github.com/CoderMJLee/MJRefresh/pull/737 + + 同时, 处理了 Refreshing 中的动画替换. + */ + + // 由于修改 Inset 会导致 self.pullingPercent 联动设置 self.alpha, 故提前获取 alpha 值, 后续用于还原 alpha 动画 + CGFloat viewAlpha = self.alpha; + + self.scrollView.mj_insetT += self.insetTDelta; + // 禁用交互, 如果不禁用可能会引起渲染问题. + self.scrollView.userInteractionEnabled = NO; + + //CAAnimation keyPath 不支持 contentInset 用Bounds的动画代替 + CABasicAnimation *boundsAnimation = [CABasicAnimation animationWithKeyPath:@"bounds"]; + boundsAnimation.fromValue = [NSValue valueWithCGRect:CGRectOffset(self.scrollView.bounds, 0, self.insetTDelta)]; + boundsAnimation.duration = self.slowAnimationDuration; + //在delegate里移除 + boundsAnimation.removedOnCompletion = NO; + boundsAnimation.fillMode = kCAFillModeBoth; + boundsAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + boundsAnimation.delegate = self; + [boundsAnimation setValue:MJRefreshHeaderRefreshing2IdleBoundsKey forKey:@"identity"]; + + [self.scrollView.layer addAnimation:boundsAnimation forKey:MJRefreshHeaderRefreshing2IdleBoundsKey]; + + if (self.endRefreshingAnimationBeginAction) { + self.endRefreshingAnimationBeginAction(); + } + // 自动调整透明度的动画 + if (self.isAutomaticallyChangeAlpha) { + CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; + opacityAnimation.fromValue = @(viewAlpha); + opacityAnimation.toValue = @(0.0); + opacityAnimation.duration = self.slowAnimationDuration; + opacityAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + [self.layer addAnimation:opacityAnimation forKey:@"MJRefreshHeaderRefreshing2IdleOpacity"]; + + // 由于修改了 inset 导致, pullingPercent 被设置值, alpha 已经被提前修改为 0 了. 所以这里不用置 0, 但为了代码的严谨性, 不依赖其他的特殊实现方式, 这里还是置 0. + self.alpha = 0; + } +} + +- (void)headerRefreshingAction { + // 默认使用 UIViewAnimation 动画 + if (!self.isCollectionViewAnimationBug) { + [UIView animateWithDuration:self.fastAnimationDuration animations:^{ + if (self.scrollView.panGestureRecognizer.state != UIGestureRecognizerStateCancelled) { + CGFloat top = self.scrollViewOriginalInset.top + self.mj_h; + // 增加滚动区域top + self.scrollView.mj_insetT = top; + // 设置滚动位置 + CGPoint offset = self.scrollView.contentOffset; + offset.y = -top; + [self.scrollView setContentOffset:offset animated:NO]; + } + } completion:^(BOOL finished) { + [self executeRefreshingCallback]; + }]; + return; + } + + if (self.scrollView.panGestureRecognizer.state != UIGestureRecognizerStateCancelled) { + CGFloat top = self.scrollViewOriginalInset.top + self.mj_h; + // 禁用交互, 如果不禁用可能会引起渲染问题. + self.scrollView.userInteractionEnabled = NO; + + // CAAnimation keyPath不支持 contentOffset 用Bounds的动画代替 + CABasicAnimation *boundsAnimation = [CABasicAnimation animationWithKeyPath:@"bounds"]; + CGRect bounds = self.scrollView.bounds; + bounds.origin.y = -top; + boundsAnimation.fromValue = [NSValue valueWithCGRect:self.scrollView.bounds]; + boundsAnimation.toValue = [NSValue valueWithCGRect:bounds]; + boundsAnimation.duration = self.fastAnimationDuration; + //在delegate里移除 + boundsAnimation.removedOnCompletion = NO; + boundsAnimation.fillMode = kCAFillModeBoth; + boundsAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + boundsAnimation.delegate = self; + [boundsAnimation setValue:MJRefreshHeaderRefreshingBoundsKey forKey:@"identity"]; + [self.scrollView.layer addAnimation:boundsAnimation forKey:MJRefreshHeaderRefreshingBoundsKey]; + } else { + [self executeRefreshingCallback]; + } +} + +#pragma mark . 链式语法部分 . + +- (instancetype)linkTo:(UIScrollView *)scrollView { + scrollView.mj_header = self; + return self; +} + +#pragma mark - CAAnimationDelegate +- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { + NSString *identity = [anim valueForKey:@"identity"]; + if ([identity isEqualToString:MJRefreshHeaderRefreshing2IdleBoundsKey]) { + self.pullingPercent = 0.0; + self.scrollView.userInteractionEnabled = YES; + if (self.endRefreshingCompletionBlock) { + self.endRefreshingCompletionBlock(); + } + } else if ([identity isEqualToString:MJRefreshHeaderRefreshingBoundsKey]) { + // 避免出现 end 先于 Refreshing 状态 + if (self.state != MJRefreshStateIdle) { + CGFloat top = self.scrollViewOriginalInset.top + self.mj_h; + self.scrollView.mj_insetT = top; + // 设置最终滚动位置 + CGPoint offset = self.scrollView.contentOffset; + offset.y = -top; + [self.scrollView setContentOffset:offset animated:NO]; + } + self.scrollView.userInteractionEnabled = YES; + [self executeRefreshingCallback]; + } + + if ([self.scrollView.layer animationForKey:MJRefreshHeaderRefreshing2IdleBoundsKey]) { + [self.scrollView.layer removeAnimationForKey:MJRefreshHeaderRefreshing2IdleBoundsKey]; + } + + if ([self.scrollView.layer animationForKey:MJRefreshHeaderRefreshingBoundsKey]) { + [self.scrollView.layer removeAnimationForKey:MJRefreshHeaderRefreshingBoundsKey]; + } +} + +#pragma mark - 公共方法 +- (NSDate *)lastUpdatedTime +{ + return [[NSUserDefaults standardUserDefaults] objectForKey:self.lastUpdatedTimeKey]; +} + +- (void)setIgnoredScrollViewContentInsetTop:(CGFloat)ignoredScrollViewContentInsetTop { + _ignoredScrollViewContentInsetTop = ignoredScrollViewContentInsetTop; + + self.mj_y = - self.mj_h - _ignoredScrollViewContentInsetTop; +} + +@end diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshTrailer.h b/Pods/MJRefresh/MJRefresh/Base/MJRefreshTrailer.h new file mode 100644 index 0000000..ca4c7ea --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Base/MJRefreshTrailer.h @@ -0,0 +1,30 @@ +// +// MJRefreshTrailer.h +// MJRefresh +// +// Created by kinarobin on 2020/5/3. +// Copyright © 2020 小码哥. All rights reserved. +// + +#if __has_include() +#import +#else +#import "MJRefreshComponent.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface MJRefreshTrailer : MJRefreshComponent + +/** 创建trailer*/ ++ (instancetype)trailerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock; +/** 创建trailer */ ++ (instancetype)trailerWithRefreshingTarget:(id)target refreshingAction:(SEL)action; + +/** 忽略多少scrollView的contentInset的right */ +@property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetRight; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/Base/MJRefreshTrailer.m b/Pods/MJRefresh/MJRefresh/Base/MJRefreshTrailer.m new file mode 100644 index 0000000..da66f20 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Base/MJRefreshTrailer.m @@ -0,0 +1,179 @@ +// +// MJRefreshTrailer.m +// MJRefresh +// +// Created by kinarobin on 2020/5/3. +// Copyright © 2020 小码哥. All rights reserved. +// + +#import "MJRefreshTrailer.h" +#import "UIView+MJExtension.h" +#import "UIScrollView+MJRefresh.h" +#import "UIScrollView+MJExtension.h" + +@interface MJRefreshTrailer() +@property (assign, nonatomic) NSInteger lastRefreshCount; +@property (assign, nonatomic) CGFloat lastRightDelta; +@end + +@implementation MJRefreshTrailer + +#pragma mark - 构造方法 ++ (instancetype)trailerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock { + MJRefreshTrailer *cmp = [[self alloc] init]; + cmp.refreshingBlock = refreshingBlock; + return cmp; +} + ++ (instancetype)trailerWithRefreshingTarget:(id)target refreshingAction:(SEL)action { + MJRefreshTrailer *cmp = [[self alloc] init]; + [cmp setRefreshingTarget:target refreshingAction:action]; + return cmp; +} + +- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change { + [super scrollViewContentOffsetDidChange:change]; + + // 如果正在刷新,直接返回 + if (self.state == MJRefreshStateRefreshing) return; + + _scrollViewOriginalInset = self.scrollView.mj_inset; + + // 当前的contentOffset + CGFloat currentOffsetX = self.scrollView.mj_offsetX; + // 尾部控件刚好出现的offsetX + CGFloat happenOffsetX = [self happenOffsetX]; + // 如果是向右滚动到看不见右边控件,直接返回 + if (currentOffsetX <= happenOffsetX) return; + + CGFloat pullingPercent = (currentOffsetX - happenOffsetX) / self.mj_w; + + // 如果已全部加载,仅设置pullingPercent,然后返回 + if (self.state == MJRefreshStateNoMoreData) { + self.pullingPercent = pullingPercent; + return; + } + + if (self.scrollView.isDragging) { + self.pullingPercent = pullingPercent; + // 普通 和 即将刷新 的临界点 + CGFloat normal2pullingOffsetX = happenOffsetX + self.mj_w; + + if (self.state == MJRefreshStateIdle && currentOffsetX > normal2pullingOffsetX) { + self.state = MJRefreshStatePulling; + } else if (self.state == MJRefreshStatePulling && currentOffsetX <= normal2pullingOffsetX) { + // 转为普通状态 + self.state = MJRefreshStateIdle; + } + } else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开 + // 开始刷新 + [self beginRefreshing]; + } else if (pullingPercent < 1) { + self.pullingPercent = pullingPercent; + } +} + +- (void)setState:(MJRefreshState)state { + MJRefreshCheckState + // 根据状态来设置属性 + if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) { + // 刷新完毕 + if (MJRefreshStateRefreshing == oldState) { + [UIView animateWithDuration:self.slowAnimationDuration animations:^{ + if (self.endRefreshingAnimationBeginAction) { + self.endRefreshingAnimationBeginAction(); + } + + self.scrollView.mj_insetR -= self.lastRightDelta; + // 自动调整透明度 + if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0; + } completion:^(BOOL finished) { + self.pullingPercent = 0.0; + + if (self.endRefreshingCompletionBlock) { + self.endRefreshingCompletionBlock(); + } + }]; + } + + CGFloat deltaW = [self widthForContentBreakView]; + // 刚刷新完毕 + if (MJRefreshStateRefreshing == oldState && deltaW > 0 && self.scrollView.mj_totalDataCount != self.lastRefreshCount) { + self.scrollView.mj_offsetX = self.scrollView.mj_offsetX; + } + } else if (state == MJRefreshStateRefreshing) { + // 记录刷新前的数量 + self.lastRefreshCount = self.scrollView.mj_totalDataCount; + + [UIView animateWithDuration:self.fastAnimationDuration animations:^{ + CGFloat right = self.mj_w + self.scrollViewOriginalInset.right; + CGFloat deltaW = [self widthForContentBreakView]; + if (deltaW < 0) { // 如果内容宽度小于view的宽度 + right -= deltaW; + } + self.lastRightDelta = right - self.scrollView.mj_insetR; + self.scrollView.mj_insetR = right; + + // 设置滚动位置 + CGPoint offset = self.scrollView.contentOffset; + offset.x = [self happenOffsetX] + self.mj_w; + [self.scrollView setContentOffset:offset animated:NO]; + } completion:^(BOOL finished) { + [self executeRefreshingCallback]; + }]; + } +} + +- (void)scrollViewContentSizeDidChange:(NSDictionary *)change { + [super scrollViewContentSizeDidChange:change]; + + // 内容的宽度 + CGFloat contentWidth = self.scrollView.mj_contentW + self.ignoredScrollViewContentInsetRight; + // 表格的宽度 + CGFloat scrollWidth = self.scrollView.mj_w - self.scrollViewOriginalInset.left - self.scrollViewOriginalInset.right + self.ignoredScrollViewContentInsetRight; + // 设置位置和尺寸 + self.mj_x = MAX(contentWidth, scrollWidth); +} + +- (void)placeSubviews { + [super placeSubviews]; + + self.mj_h = _scrollView.mj_h; + // 设置自己的宽度 + self.mj_w = MJRefreshTrailWidth; +} + +- (void)willMoveToSuperview:(UIView *)newSuperview { + [super willMoveToSuperview:newSuperview]; + + if (newSuperview) { + // 设置支持水平弹簧效果 + _scrollView.alwaysBounceHorizontal = YES; + _scrollView.alwaysBounceVertical = NO; + } +} + +#pragma mark . 链式语法部分 . + +- (instancetype)linkTo:(UIScrollView *)scrollView { + scrollView.mj_trailer = self; + return self; +} + +#pragma mark - 刚好看到上拉刷新控件时的contentOffset.x +- (CGFloat)happenOffsetX { + CGFloat deltaW = [self widthForContentBreakView]; + if (deltaW > 0) { + return deltaW - self.scrollViewOriginalInset.left; + } else { + return - self.scrollViewOriginalInset.left; + } +} + +#pragma mark 获得scrollView的内容 超出 view 的宽度 +- (CGFloat)widthForContentBreakView { + CGFloat w = self.scrollView.frame.size.width - self.scrollViewOriginalInset.right - self.scrollViewOriginalInset.left; + return self.scrollView.contentSize.width - w; +} + +@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h b/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h new file mode 100644 index 0000000..f346157 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h @@ -0,0 +1,25 @@ +// +// MJRefreshAutoGifFooter.h +// MJRefresh +// +// Created by MJ Lee on 15/4/24. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#if __has_include() +#import +#else +#import "MJRefreshAutoStateFooter.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface MJRefreshAutoGifFooter : MJRefreshAutoStateFooter +@property (weak, nonatomic, readonly) UIImageView *gifView; + +/** 设置state状态下的动画图片images 动画持续时间duration*/ +- (instancetype)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state; +- (instancetype)setImages:(NSArray *)images forState:(MJRefreshState)state; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.m b/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.m new file mode 100644 index 0000000..213f69f --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.m @@ -0,0 +1,121 @@ +// +// MJRefreshAutoGifFooter.m +// MJRefresh +// +// Created by MJ Lee on 15/4/24. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import "MJRefreshAutoGifFooter.h" +#import "NSBundle+MJRefresh.h" +#import "UIView+MJExtension.h" +#import "UIScrollView+MJExtension.h" +#import "UIScrollView+MJRefresh.h" + +@interface MJRefreshAutoGifFooter() +{ + __unsafe_unretained UIImageView *_gifView; +} +/** 所有状态对应的动画图片 */ +@property (strong, nonatomic) NSMutableDictionary *stateImages; +/** 所有状态对应的动画时间 */ +@property (strong, nonatomic) NSMutableDictionary *stateDurations; +@end + +@implementation MJRefreshAutoGifFooter +#pragma mark - 懒加载 +- (UIImageView *)gifView +{ + if (!_gifView) { + UIImageView *gifView = [[UIImageView alloc] init]; + [self addSubview:_gifView = gifView]; + } + return _gifView; +} + +- (NSMutableDictionary *)stateImages +{ + if (!_stateImages) { + self.stateImages = [NSMutableDictionary dictionary]; + } + return _stateImages; +} + +- (NSMutableDictionary *)stateDurations +{ + if (!_stateDurations) { + self.stateDurations = [NSMutableDictionary dictionary]; + } + return _stateDurations; +} + +#pragma mark - 公共方法 +- (instancetype)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state +{ + if (images == nil) return self; + + self.stateImages[@(state)] = images; + self.stateDurations[@(state)] = @(duration); + + /* 根据图片设置控件的高度 */ + UIImage *image = [images firstObject]; + if (image.size.height > self.mj_h) { + self.mj_h = image.size.height; + } + return self; +} + +- (instancetype)setImages:(NSArray *)images forState:(MJRefreshState)state +{ + return [self setImages:images duration:images.count * 0.1 forState:state]; +} + +#pragma mark - 实现父类的方法 +- (void)prepare +{ + [super prepare]; + + // 初始化间距 + self.labelLeftInset = 20; +} + +- (void)placeSubviews +{ + [super placeSubviews]; + + if (self.gifView.constraints.count) return; + + self.gifView.frame = self.bounds; + if (self.isRefreshingTitleHidden) { + self.gifView.contentMode = UIViewContentModeCenter; + } else { + self.gifView.contentMode = UIViewContentModeRight; + self.gifView.mj_w = self.mj_w * 0.5 - self.labelLeftInset - self.stateLabel.mj_textWidth * 0.5; + } +} + +- (void)setState:(MJRefreshState)state +{ + MJRefreshCheckState + + // 根据状态做事情 + if (state == MJRefreshStateRefreshing) { + NSArray *images = self.stateImages[@(state)]; + if (images.count == 0) return; + [self.gifView stopAnimating]; + + self.gifView.hidden = NO; + if (images.count == 1) { // 单张图片 + self.gifView.image = [images lastObject]; + } else { // 多张图片 + self.gifView.animationImages = images; + self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue]; + [self.gifView startAnimating]; + } + } else if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) { + [self.gifView stopAnimating]; + self.gifView.hidden = YES; + } +} +@end + diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h b/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h new file mode 100644 index 0000000..f8d1e04 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h @@ -0,0 +1,25 @@ +// +// MJRefreshAutoNormalFooter.h +// MJRefresh +// +// Created by MJ Lee on 15/4/24. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#if __has_include() +#import +#else +#import "MJRefreshAutoStateFooter.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface MJRefreshAutoNormalFooter : MJRefreshAutoStateFooter +@property (weak, nonatomic, readonly) UIActivityIndicatorView *loadingView; + +/** 菊花的样式 */ +@property (assign, nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle MJRefreshDeprecated("first deprecated in 3.2.2 - Use `loadingView` property"); +@end + + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.m b/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.m new file mode 100644 index 0000000..9e6a03d --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.m @@ -0,0 +1,81 @@ +// +// MJRefreshAutoNormalFooter.m +// MJRefresh +// +// Created by MJ Lee on 15/4/24. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import "MJRefreshAutoNormalFooter.h" +#import "NSBundle+MJRefresh.h" +#import "UIView+MJExtension.h" +#import "UIScrollView+MJExtension.h" +#import "UIScrollView+MJRefresh.h" + +@interface MJRefreshAutoNormalFooter() +@property (weak, nonatomic) UIActivityIndicatorView *loadingView; +@end + +@implementation MJRefreshAutoNormalFooter +#pragma mark - 懒加载子控件 +- (UIActivityIndicatorView *)loadingView +{ + if (!_loadingView) { + UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:_activityIndicatorViewStyle]; + loadingView.hidesWhenStopped = YES; + [self addSubview:_loadingView = loadingView]; + } + return _loadingView; +} + +- (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle +{ + _activityIndicatorViewStyle = activityIndicatorViewStyle; + + [self.loadingView removeFromSuperview]; + self.loadingView = nil; + [self setNeedsLayout]; +} +#pragma mark - 重写父类的方法 +- (void)prepare +{ + [super prepare]; + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 + if (@available(iOS 13.0, *)) { + _activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium; + return; + } +#endif + + _activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray; +} + +- (void)placeSubviews +{ + [super placeSubviews]; + + if (self.loadingView.constraints.count) return; + + // 圈圈 + CGFloat loadingCenterX = self.mj_w * 0.5; + if (!self.isRefreshingTitleHidden) { + loadingCenterX -= self.stateLabel.mj_textWidth * 0.5 + self.labelLeftInset; + } + CGFloat loadingCenterY = self.mj_h * 0.5; + self.loadingView.center = CGPointMake(loadingCenterX, loadingCenterY); +} + +- (void)setState:(MJRefreshState)state +{ + MJRefreshCheckState + + // 根据状态做事情 + if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) { + [self.loadingView stopAnimating]; + } else if (state == MJRefreshStateRefreshing) { + [self.loadingView startAnimating]; + } +} + +@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h b/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h new file mode 100644 index 0000000..c83622d --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h @@ -0,0 +1,30 @@ +// +// MJRefreshAutoStateFooter.h +// MJRefresh +// +// Created by MJ Lee on 15/6/13. +// Copyright © 2015年 小码哥. All rights reserved. +// + +#if __has_include() +#import +#else +#import "MJRefreshAutoFooter.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface MJRefreshAutoStateFooter : MJRefreshAutoFooter +/** 文字距离圈圈、箭头的距离 */ +@property (assign, nonatomic) CGFloat labelLeftInset; +/** 显示刷新状态的label */ +@property (weak, nonatomic, readonly) UILabel *stateLabel; + +/** 设置state状态下的文字 */ +- (instancetype)setTitle:(NSString *)title forState:(MJRefreshState)state; + +/** 隐藏刷新状态的文字 */ +@property (assign, nonatomic, getter=isRefreshingTitleHidden) BOOL refreshingTitleHidden; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.m b/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.m new file mode 100644 index 0000000..e5ff652 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.m @@ -0,0 +1,119 @@ +// +// MJRefreshAutoStateFooter.m +// MJRefresh +// +// Created by MJ Lee on 15/6/13. +// Copyright © 2015年 小码哥. All rights reserved. +// + +#import "MJRefreshAutoStateFooter.h" +#import "NSBundle+MJRefresh.h" + +@interface MJRefreshAutoFooter (TapTriggerFix) + +- (void)beginRefreshingWithoutValidation; +@end + + +@implementation MJRefreshAutoFooter (TapTriggerFix) + +- (void)beginRefreshingWithoutValidation { + [super beginRefreshing]; +} + +@end + +@interface MJRefreshAutoStateFooter() +{ + /** 显示刷新状态的label */ + __unsafe_unretained UILabel *_stateLabel; +} +/** 所有状态对应的文字 */ +@property (strong, nonatomic) NSMutableDictionary *stateTitles; +@end + +@implementation MJRefreshAutoStateFooter +#pragma mark - 懒加载 +- (NSMutableDictionary *)stateTitles +{ + if (!_stateTitles) { + self.stateTitles = [NSMutableDictionary dictionary]; + } + return _stateTitles; +} + +- (UILabel *)stateLabel +{ + if (!_stateLabel) { + [self addSubview:_stateLabel = [UILabel mj_label]]; + } + return _stateLabel; +} + +#pragma mark - 公共方法 +- (instancetype)setTitle:(NSString *)title forState:(MJRefreshState)state +{ + if (title == nil) return self; + self.stateTitles[@(state)] = title; + self.stateLabel.text = self.stateTitles[@(self.state)]; + return self; +} + +#pragma mark - 私有方法 +- (void)stateLabelClick +{ + if (self.state == MJRefreshStateIdle) { + [super beginRefreshingWithoutValidation]; + } +} + +- (void)textConfiguration { + // 初始化文字 + [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshAutoFooterIdleText] forState:MJRefreshStateIdle]; + [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshAutoFooterRefreshingText] forState:MJRefreshStateRefreshing]; + [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshAutoFooterNoMoreDataText] forState:MJRefreshStateNoMoreData]; +} + +#pragma mark - 重写父类的方法 +- (void)prepare +{ + [super prepare]; + + // 初始化间距 + self.labelLeftInset = MJRefreshLabelLeftInset; + + [self textConfiguration]; + + // 监听label + self.stateLabel.userInteractionEnabled = YES; + [self.stateLabel addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(stateLabelClick)]]; +} + +- (void)i18nDidChange { + [self textConfiguration]; + + [super i18nDidChange]; +} + + +- (void)placeSubviews +{ + [super placeSubviews]; + + if (self.stateLabel.constraints.count) return; + + // 状态标签 + self.stateLabel.frame = self.bounds; +} + +- (void)setState:(MJRefreshState)state +{ + MJRefreshCheckState + + if (self.isRefreshingTitleHidden && state == MJRefreshStateRefreshing) { + self.stateLabel.text = nil; + } else { + self.stateLabel.text = self.stateTitles[@(state)]; + } +} +@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h b/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h new file mode 100644 index 0000000..a7ba065 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h @@ -0,0 +1,25 @@ +// +// MJRefreshBackGifFooter.h +// MJRefresh +// +// Created by MJ Lee on 15/4/24. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#if __has_include() +#import +#else +#import "MJRefreshBackStateFooter.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface MJRefreshBackGifFooter : MJRefreshBackStateFooter +@property (weak, nonatomic, readonly) UIImageView *gifView; + +/** 设置state状态下的动画图片images 动画持续时间duration*/ +- (instancetype)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state; +- (instancetype)setImages:(NSArray *)images forState:(MJRefreshState)state; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.m b/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.m new file mode 100644 index 0000000..23c626c --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.m @@ -0,0 +1,132 @@ +// +// MJRefreshBackGifFooter.m +// MJRefresh +// +// Created by MJ Lee on 15/4/24. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import "MJRefreshBackGifFooter.h" +#import "NSBundle+MJRefresh.h" +#import "UIView+MJExtension.h" +#import "UIScrollView+MJExtension.h" +#import "UIScrollView+MJRefresh.h" + +@interface MJRefreshBackGifFooter() +{ + __unsafe_unretained UIImageView *_gifView; +} +/** 所有状态对应的动画图片 */ +@property (strong, nonatomic) NSMutableDictionary *stateImages; +/** 所有状态对应的动画时间 */ +@property (strong, nonatomic) NSMutableDictionary *stateDurations; +@end + +@implementation MJRefreshBackGifFooter +#pragma mark - 懒加载 +- (UIImageView *)gifView +{ + if (!_gifView) { + UIImageView *gifView = [[UIImageView alloc] init]; + [self addSubview:_gifView = gifView]; + } + return _gifView; +} + +- (NSMutableDictionary *)stateImages +{ + if (!_stateImages) { + self.stateImages = [NSMutableDictionary dictionary]; + } + return _stateImages; +} + +- (NSMutableDictionary *)stateDurations +{ + if (!_stateDurations) { + self.stateDurations = [NSMutableDictionary dictionary]; + } + return _stateDurations; +} + +#pragma mark - 公共方法 +- (instancetype)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state +{ + if (images == nil) return self; + + self.stateImages[@(state)] = images; + self.stateDurations[@(state)] = @(duration); + + /* 根据图片设置控件的高度 */ + UIImage *image = [images firstObject]; + if (image.size.height > self.mj_h) { + self.mj_h = image.size.height; + } + return self; +} + +- (instancetype)setImages:(NSArray *)images forState:(MJRefreshState)state +{ + return [self setImages:images duration:images.count * 0.1 forState:state]; +} + +#pragma mark - 实现父类的方法 +- (void)prepare +{ + [super prepare]; + + // 初始化间距 + self.labelLeftInset = 20; +} + +- (void)setPullingPercent:(CGFloat)pullingPercent +{ + [super setPullingPercent:pullingPercent]; + NSArray *images = self.stateImages[@(MJRefreshStateIdle)]; + if (self.state != MJRefreshStateIdle || images.count == 0) return; + [self.gifView stopAnimating]; + NSUInteger index = images.count * pullingPercent; + if (index >= images.count) index = images.count - 1; + self.gifView.image = images[index]; +} + +- (void)placeSubviews +{ + [super placeSubviews]; + + if (self.gifView.constraints.count) return; + + self.gifView.frame = self.bounds; + if (self.stateLabel.hidden) { + self.gifView.contentMode = UIViewContentModeCenter; + } else { + self.gifView.contentMode = UIViewContentModeRight; + self.gifView.mj_w = self.mj_w * 0.5 - self.labelLeftInset - self.stateLabel.mj_textWidth * 0.5; + } +} + +- (void)setState:(MJRefreshState)state +{ + MJRefreshCheckState + + // 根据状态做事情 + if (state == MJRefreshStatePulling || state == MJRefreshStateRefreshing) { + NSArray *images = self.stateImages[@(state)]; + if (images.count == 0) return; + + self.gifView.hidden = NO; + [self.gifView stopAnimating]; + if (images.count == 1) { // 单张图片 + self.gifView.image = [images lastObject]; + } else { // 多张图片 + self.gifView.animationImages = images; + self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue]; + [self.gifView startAnimating]; + } + } else if (state == MJRefreshStateIdle) { + self.gifView.hidden = NO; + } else if (state == MJRefreshStateNoMoreData) { + self.gifView.hidden = YES; + } +} +@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h b/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h new file mode 100644 index 0000000..d255807 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h @@ -0,0 +1,25 @@ +// +// MJRefreshBackNormalFooter.h +// MJRefresh +// +// Created by MJ Lee on 15/4/24. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#if __has_include() +#import +#else +#import "MJRefreshBackStateFooter.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface MJRefreshBackNormalFooter : MJRefreshBackStateFooter +@property (weak, nonatomic, readonly) UIImageView *arrowView; +@property (weak, nonatomic, readonly) UIActivityIndicatorView *loadingView; + +/** 菊花的样式 */ +@property (assign, nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle MJRefreshDeprecated("first deprecated in 3.2.2 - Use `loadingView` property"); +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.m b/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.m new file mode 100644 index 0000000..932af76 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.m @@ -0,0 +1,132 @@ +// +// MJRefreshBackNormalFooter.m +// MJRefresh +// +// Created by MJ Lee on 15/4/24. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import "MJRefreshBackNormalFooter.h" +#import "NSBundle+MJRefresh.h" +#import "UIView+MJExtension.h" + +@interface MJRefreshBackNormalFooter() +{ + __unsafe_unretained UIImageView *_arrowView; +} +@property (weak, nonatomic) UIActivityIndicatorView *loadingView; +@end + +@implementation MJRefreshBackNormalFooter +#pragma mark - 懒加载子控件 +- (UIImageView *)arrowView +{ + if (!_arrowView) { + UIImageView *arrowView = [[UIImageView alloc] initWithImage:[NSBundle mj_arrowImage]]; + [self addSubview:_arrowView = arrowView]; + } + return _arrowView; +} + + +- (UIActivityIndicatorView *)loadingView +{ + if (!_loadingView) { + UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:_activityIndicatorViewStyle]; + loadingView.hidesWhenStopped = YES; + [self addSubview:_loadingView = loadingView]; + } + return _loadingView; +} + +- (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle +{ + _activityIndicatorViewStyle = activityIndicatorViewStyle; + + [self.loadingView removeFromSuperview]; + self.loadingView = nil; + [self setNeedsLayout]; +} +#pragma mark - 重写父类的方法 +- (void)prepare +{ + [super prepare]; + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 + if (@available(iOS 13.0, *)) { + _activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium; + return; + } +#endif + + _activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray; +} + +- (void)placeSubviews +{ + [super placeSubviews]; + + // 箭头的中心点 + CGFloat arrowCenterX = self.mj_w * 0.5; + if (!self.stateLabel.hidden) { + arrowCenterX -= self.labelLeftInset + self.stateLabel.mj_textWidth * 0.5; + } + CGFloat arrowCenterY = self.mj_h * 0.5; + CGPoint arrowCenter = CGPointMake(arrowCenterX, arrowCenterY); + + // 箭头 + if (self.arrowView.constraints.count == 0) { + self.arrowView.mj_size = self.arrowView.image.size; + self.arrowView.center = arrowCenter; + } + + // 圈圈 + if (self.loadingView.constraints.count == 0) { + self.loadingView.center = arrowCenter; + } + + self.arrowView.tintColor = self.stateLabel.textColor; +} + +- (void)setState:(MJRefreshState)state +{ + MJRefreshCheckState + + // 根据状态做事情 + if (state == MJRefreshStateIdle) { + if (oldState == MJRefreshStateRefreshing) { + self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI); + [UIView animateWithDuration:self.slowAnimationDuration animations:^{ + self.loadingView.alpha = 0.0; + } completion:^(BOOL finished) { + // 防止动画结束后,状态已经不是MJRefreshStateIdle + if (self.state != MJRefreshStateIdle) return; + + self.loadingView.alpha = 1.0; + [self.loadingView stopAnimating]; + + self.arrowView.hidden = NO; + }]; + } else { + self.arrowView.hidden = NO; + [self.loadingView stopAnimating]; + [UIView animateWithDuration:self.fastAnimationDuration animations:^{ + self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI); + }]; + } + } else if (state == MJRefreshStatePulling) { + self.arrowView.hidden = NO; + [self.loadingView stopAnimating]; + [UIView animateWithDuration:self.fastAnimationDuration animations:^{ + self.arrowView.transform = CGAffineTransformIdentity; + }]; + } else if (state == MJRefreshStateRefreshing) { + self.arrowView.hidden = YES; + [self.loadingView startAnimating]; + } else if (state == MJRefreshStateNoMoreData) { + self.arrowView.hidden = YES; + [self.loadingView stopAnimating]; + } +} + +@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h b/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h new file mode 100644 index 0000000..c6897f4 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h @@ -0,0 +1,29 @@ +// +// MJRefreshBackStateFooter.h +// MJRefresh +// +// Created by MJ Lee on 15/6/13. +// Copyright © 2015年 小码哥. All rights reserved. +// + +#if __has_include() +#import +#else +#import "MJRefreshBackFooter.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface MJRefreshBackStateFooter : MJRefreshBackFooter +/** 文字距离圈圈、箭头的距离 */ +@property (assign, nonatomic) CGFloat labelLeftInset; +/** 显示刷新状态的label */ +@property (weak, nonatomic, readonly) UILabel *stateLabel; +/** 设置state状态下的文字 */ +- (instancetype)setTitle:(NSString *)title forState:(MJRefreshState)state; + +/** 获取state状态下的title */ +- (NSString *)titleForState:(MJRefreshState)state; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.m b/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.m new file mode 100644 index 0000000..70f4024 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.m @@ -0,0 +1,93 @@ +// +// MJRefreshBackStateFooter.m +// MJRefresh +// +// Created by MJ Lee on 15/6/13. +// Copyright © 2015年 小码哥. All rights reserved. +// + +#import "MJRefreshBackStateFooter.h" +#import "NSBundle+MJRefresh.h" + +@interface MJRefreshBackStateFooter() +{ + /** 显示刷新状态的label */ + __unsafe_unretained UILabel *_stateLabel; +} +/** 所有状态对应的文字 */ +@property (strong, nonatomic) NSMutableDictionary *stateTitles; +@end + +@implementation MJRefreshBackStateFooter +#pragma mark - 懒加载 +- (NSMutableDictionary *)stateTitles +{ + if (!_stateTitles) { + self.stateTitles = [NSMutableDictionary dictionary]; + } + return _stateTitles; +} + +- (UILabel *)stateLabel +{ + if (!_stateLabel) { + [self addSubview:_stateLabel = [UILabel mj_label]]; + } + return _stateLabel; +} + +#pragma mark - 公共方法 +- (instancetype)setTitle:(NSString *)title forState:(MJRefreshState)state +{ + if (title == nil) return self; + self.stateTitles[@(state)] = title; + self.stateLabel.text = self.stateTitles[@(self.state)]; + return self; +} + +- (NSString *)titleForState:(MJRefreshState)state { + return self.stateTitles[@(state)]; +} + +- (void)textConfiguration { + // 初始化文字 + [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshBackFooterIdleText] forState:MJRefreshStateIdle]; + [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshBackFooterPullingText] forState:MJRefreshStatePulling]; + [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshBackFooterRefreshingText] forState:MJRefreshStateRefreshing]; + [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshBackFooterNoMoreDataText] forState:MJRefreshStateNoMoreData]; +} + +#pragma mark - 重写父类的方法 +- (void)prepare +{ + [super prepare]; + + // 初始化间距 + self.labelLeftInset = MJRefreshLabelLeftInset; + [self textConfiguration]; +} + +- (void)i18nDidChange { + [self textConfiguration]; + + [super i18nDidChange]; +} + +- (void)placeSubviews +{ + [super placeSubviews]; + + if (self.stateLabel.constraints.count) return; + + // 状态标签 + self.stateLabel.frame = self.bounds; +} + +- (void)setState:(MJRefreshState)state +{ + MJRefreshCheckState + + // 设置状态文字 + self.stateLabel.text = self.stateTitles[@(state)]; +} +@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.h b/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.h new file mode 100644 index 0000000..afa4a13 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.h @@ -0,0 +1,25 @@ +// +// MJRefreshGifHeader.h +// MJRefresh +// +// Created by MJ Lee on 15/4/24. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#if __has_include() +#import +#else +#import "MJRefreshStateHeader.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface MJRefreshGifHeader : MJRefreshStateHeader +@property (weak, nonatomic, readonly) UIImageView *gifView; + +/** 设置state状态下的动画图片images 动画持续时间duration*/ +- (instancetype)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state; +- (instancetype)setImages:(NSArray *)images forState:(MJRefreshState)state; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.m b/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.m new file mode 100644 index 0000000..707e466 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.m @@ -0,0 +1,135 @@ +// +// MJRefreshGifHeader.m +// MJRefresh +// +// Created by MJ Lee on 15/4/24. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import "MJRefreshGifHeader.h" +#import "UIView+MJExtension.h" +#import "UIScrollView+MJExtension.h" + +@interface MJRefreshGifHeader() +{ + __unsafe_unretained UIImageView *_gifView; +} +/** 所有状态对应的动画图片 */ +@property (strong, nonatomic) NSMutableDictionary *stateImages; +/** 所有状态对应的动画时间 */ +@property (strong, nonatomic) NSMutableDictionary *stateDurations; +@end + +@implementation MJRefreshGifHeader +#pragma mark - 懒加载 +- (UIImageView *)gifView +{ + if (!_gifView) { + UIImageView *gifView = [[UIImageView alloc] init]; + [self addSubview:_gifView = gifView]; + } + return _gifView; +} + +- (NSMutableDictionary *)stateImages +{ + if (!_stateImages) { + self.stateImages = [NSMutableDictionary dictionary]; + } + return _stateImages; +} + +- (NSMutableDictionary *)stateDurations +{ + if (!_stateDurations) { + self.stateDurations = [NSMutableDictionary dictionary]; + } + return _stateDurations; +} + +#pragma mark - 公共方法 +- (instancetype)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state { + if (images == nil) return self; + + self.stateImages[@(state)] = images; + self.stateDurations[@(state)] = @(duration); + + /* 根据图片设置控件的高度 */ + UIImage *image = [images firstObject]; + if (image.size.height > self.mj_h) { + self.mj_h = image.size.height; + } + return self; +} + +- (instancetype)setImages:(NSArray *)images forState:(MJRefreshState)state +{ + return [self setImages:images duration:images.count * 0.1 forState:state]; +} + +#pragma mark - 实现父类的方法 +- (void)prepare +{ + [super prepare]; + + // 初始化间距 + self.labelLeftInset = 20; +} + +- (void)setPullingPercent:(CGFloat)pullingPercent +{ + [super setPullingPercent:pullingPercent]; + NSArray *images = self.stateImages[@(MJRefreshStateIdle)]; + if (self.state != MJRefreshStateIdle || images.count == 0) return; + // 停止动画 + [self.gifView stopAnimating]; + // 设置当前需要显示的图片 + NSUInteger index = images.count * pullingPercent; + if (index >= images.count) index = images.count - 1; + self.gifView.image = images[index]; +} + +- (void)placeSubviews +{ + [super placeSubviews]; + + if (self.gifView.constraints.count) return; + + self.gifView.frame = self.bounds; + if (self.stateLabel.hidden && self.lastUpdatedTimeLabel.hidden) { + self.gifView.contentMode = UIViewContentModeCenter; + } else { + self.gifView.contentMode = UIViewContentModeRight; + + CGFloat stateWidth = self.stateLabel.mj_textWidth; + CGFloat timeWidth = 0.0; + if (!self.lastUpdatedTimeLabel.hidden) { + timeWidth = self.lastUpdatedTimeLabel.mj_textWidth; + } + CGFloat textWidth = MAX(stateWidth, timeWidth); + self.gifView.mj_w = self.mj_w * 0.5 - textWidth * 0.5 - self.labelLeftInset; + } +} + +- (void)setState:(MJRefreshState)state +{ + MJRefreshCheckState + + // 根据状态做事情 + if (state == MJRefreshStatePulling || state == MJRefreshStateRefreshing) { + NSArray *images = self.stateImages[@(state)]; + if (images.count == 0) return; + + [self.gifView stopAnimating]; + if (images.count == 1) { // 单张图片 + self.gifView.image = [images lastObject]; + } else { // 多张图片 + self.gifView.animationImages = images; + self.gifView.animationDuration = [self.stateDurations[@(state)] doubleValue]; + [self.gifView startAnimating]; + } + } else if (state == MJRefreshStateIdle) { + [self.gifView stopAnimating]; + } +} +@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.h b/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.h new file mode 100644 index 0000000..2bfef39 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.h @@ -0,0 +1,26 @@ +// +// MJRefreshNormalHeader.h +// MJRefresh +// +// Created by MJ Lee on 15/4/24. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#if __has_include() +#import +#else +#import "MJRefreshStateHeader.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface MJRefreshNormalHeader : MJRefreshStateHeader +@property (weak, nonatomic, readonly) UIImageView *arrowView; +@property (weak, nonatomic, readonly) UIActivityIndicatorView *loadingView; + + +/** 菊花的样式 */ +@property (assign, nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle MJRefreshDeprecated("first deprecated in 3.2.2 - Use `loadingView` property"); +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.m b/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.m new file mode 100644 index 0000000..84a66cb --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.m @@ -0,0 +1,137 @@ +// +// MJRefreshNormalHeader.m +// MJRefresh +// +// Created by MJ Lee on 15/4/24. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import "MJRefreshNormalHeader.h" +#import "NSBundle+MJRefresh.h" +#import "UIScrollView+MJRefresh.h" +#import "UIView+MJExtension.h" + +@interface MJRefreshNormalHeader() +{ + __unsafe_unretained UIImageView *_arrowView; +} +@property (weak, nonatomic) UIActivityIndicatorView *loadingView; +@end + +@implementation MJRefreshNormalHeader +#pragma mark - 懒加载子控件 +- (UIImageView *)arrowView +{ + if (!_arrowView) { + UIImageView *arrowView = [[UIImageView alloc] initWithImage:[NSBundle mj_arrowImage]]; + [self addSubview:_arrowView = arrowView]; + } + return _arrowView; +} + +- (UIActivityIndicatorView *)loadingView +{ + if (!_loadingView) { + UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:_activityIndicatorViewStyle]; + loadingView.hidesWhenStopped = YES; + [self addSubview:_loadingView = loadingView]; + } + return _loadingView; +} + +#pragma mark - 公共方法 +- (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle +{ + _activityIndicatorViewStyle = activityIndicatorViewStyle; + + [self.loadingView removeFromSuperview]; + self.loadingView = nil; + [self setNeedsLayout]; +} + +#pragma mark - 重写父类的方法 +- (void)prepare +{ + [super prepare]; + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 + if (@available(iOS 13.0, *)) { + _activityIndicatorViewStyle = UIActivityIndicatorViewStyleMedium; + return; + } +#endif + + _activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray; +} + +- (void)placeSubviews +{ + [super placeSubviews]; + + // 箭头的中心点 + CGFloat arrowCenterX = self.mj_w * 0.5; + if (!self.stateLabel.hidden) { + CGFloat stateWidth = self.stateLabel.mj_textWidth; + CGFloat timeWidth = 0.0; + if (!self.lastUpdatedTimeLabel.hidden) { + timeWidth = self.lastUpdatedTimeLabel.mj_textWidth; + } + CGFloat textWidth = MAX(stateWidth, timeWidth); + arrowCenterX -= textWidth / 2 + self.labelLeftInset; + } + CGFloat arrowCenterY = self.mj_h * 0.5; + CGPoint arrowCenter = CGPointMake(arrowCenterX, arrowCenterY); + + // 箭头 + if (self.arrowView.constraints.count == 0) { + self.arrowView.mj_size = self.arrowView.image.size; + self.arrowView.center = arrowCenter; + } + + // 圈圈 + if (self.loadingView.constraints.count == 0) { + self.loadingView.center = arrowCenter; + } + + self.arrowView.tintColor = self.stateLabel.textColor; +} + +- (void)setState:(MJRefreshState)state +{ + MJRefreshCheckState + + // 根据状态做事情 + if (state == MJRefreshStateIdle) { + if (oldState == MJRefreshStateRefreshing) { + self.arrowView.transform = CGAffineTransformIdentity; + + [UIView animateWithDuration:self.slowAnimationDuration animations:^{ + self.loadingView.alpha = 0.0; + } completion:^(BOOL finished) { + // 如果执行完动画发现不是idle状态,就直接返回,进入其他状态 + if (self.state != MJRefreshStateIdle) return; + + self.loadingView.alpha = 1.0; + [self.loadingView stopAnimating]; + self.arrowView.hidden = NO; + }]; + } else { + [self.loadingView stopAnimating]; + self.arrowView.hidden = NO; + [UIView animateWithDuration:self.fastAnimationDuration animations:^{ + self.arrowView.transform = CGAffineTransformIdentity; + }]; + } + } else if (state == MJRefreshStatePulling) { + [self.loadingView stopAnimating]; + self.arrowView.hidden = NO; + [UIView animateWithDuration:self.fastAnimationDuration animations:^{ + self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI); + }]; + } else if (state == MJRefreshStateRefreshing) { + self.loadingView.alpha = 1.0; // 防止refreshing -> idle的动画完毕动作没有被执行 + [self.loadingView startAnimating]; + self.arrowView.hidden = YES; + } +} +@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.h b/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.h new file mode 100644 index 0000000..8e1d108 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.h @@ -0,0 +1,39 @@ +// +// MJRefreshStateHeader.h +// MJRefresh +// +// Created by MJ Lee on 15/4/24. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#if __has_include() +#import +#else +#import "MJRefreshHeader.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface MJRefreshStateHeader : MJRefreshHeader +#pragma mark - 刷新时间相关 +/** 利用这个block来决定显示的更新时间文字 */ +@property (copy, nonatomic, nullable) NSString *(^lastUpdatedTimeText)(NSDate * _Nullable lastUpdatedTime); +/** 显示上一次刷新时间的label */ +@property (weak, nonatomic, readonly) UILabel *lastUpdatedTimeLabel; + +#pragma mark - 状态相关 +/** 文字距离圈圈、箭头的距离 */ +@property (assign, nonatomic) CGFloat labelLeftInset; +/** 显示刷新状态的label */ +@property (weak, nonatomic, readonly) UILabel *stateLabel; +/** 设置state状态下的文字 */ +- (instancetype)setTitle:(NSString *)title forState:(MJRefreshState)state; +@end + +@interface MJRefreshStateHeader (ChainingGrammar) + +- (instancetype)modifyLastUpdatedTimeText:(NSString * (^)(NSDate * _Nullable lastUpdatedTime))handler; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.m b/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.m new file mode 100644 index 0000000..62d1ddc --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.m @@ -0,0 +1,191 @@ +// +// MJRefreshStateHeader.m +// MJRefresh +// +// Created by MJ Lee on 15/4/24. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import "MJRefreshStateHeader.h" +#import "MJRefreshConst.h" +#import "NSBundle+MJRefresh.h" +#import "UIView+MJExtension.h" +#import "UIScrollView+MJExtension.h" + +@interface MJRefreshStateHeader() +{ + /** 显示上一次刷新时间的label */ + __unsafe_unretained UILabel *_lastUpdatedTimeLabel; + /** 显示刷新状态的label */ + __unsafe_unretained UILabel *_stateLabel; +} +/** 所有状态对应的文字 */ +@property (strong, nonatomic) NSMutableDictionary *stateTitles; +@end + +@implementation MJRefreshStateHeader +#pragma mark - 懒加载 +- (NSMutableDictionary *)stateTitles +{ + if (!_stateTitles) { + self.stateTitles = [NSMutableDictionary dictionary]; + } + return _stateTitles; +} + +- (UILabel *)stateLabel +{ + if (!_stateLabel) { + [self addSubview:_stateLabel = [UILabel mj_label]]; + } + return _stateLabel; +} + +- (UILabel *)lastUpdatedTimeLabel +{ + if (!_lastUpdatedTimeLabel) { + [self addSubview:_lastUpdatedTimeLabel = [UILabel mj_label]]; + } + return _lastUpdatedTimeLabel; +} + +- (void)setLastUpdatedTimeText:(NSString * _Nonnull (^)(NSDate * _Nullable))lastUpdatedTimeText{ + _lastUpdatedTimeText = lastUpdatedTimeText; + // 重新设置key(重新显示时间) + self.lastUpdatedTimeKey = self.lastUpdatedTimeKey; +} + +#pragma mark - 公共方法 +- (instancetype)setTitle:(NSString *)title forState:(MJRefreshState)state +{ + if (title == nil) return self; + self.stateTitles[@(state)] = title; + self.stateLabel.text = self.stateTitles[@(self.state)]; + return self; +} + +#pragma mark key的处理 +- (void)setLastUpdatedTimeKey:(NSString *)lastUpdatedTimeKey +{ + [super setLastUpdatedTimeKey:lastUpdatedTimeKey]; + + // 如果label隐藏了,就不用再处理 + if (self.lastUpdatedTimeLabel.hidden) return; + + NSDate *lastUpdatedTime = [[NSUserDefaults standardUserDefaults] objectForKey:lastUpdatedTimeKey]; + + // 如果有block + if (self.lastUpdatedTimeText) { + self.lastUpdatedTimeLabel.text = self.lastUpdatedTimeText(lastUpdatedTime); + return; + } + + if (lastUpdatedTime) { + // 1.获得年月日 + NSCalendar *calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]; + NSUInteger unitFlags = NSCalendarUnitYear| NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute; + NSDateComponents *cmp1 = [calendar components:unitFlags fromDate:lastUpdatedTime]; + NSDateComponents *cmp2 = [calendar components:unitFlags fromDate:[NSDate date]]; + + // 2.格式化日期 + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + BOOL isToday = NO; + if ([cmp1 day] == [cmp2 day]) { // 今天 + formatter.dateFormat = @" HH:mm"; + isToday = YES; + } else if ([cmp1 year] == [cmp2 year]) { // 今年 + formatter.dateFormat = @"MM-dd HH:mm"; + } else { + formatter.dateFormat = @"yyyy-MM-dd HH:mm"; + } + NSString *time = [formatter stringFromDate:lastUpdatedTime]; + + // 3.显示日期 + self.lastUpdatedTimeLabel.text = [NSString stringWithFormat:@"%@%@%@", + [NSBundle mj_localizedStringForKey:MJRefreshHeaderLastTimeText], + isToday ? [NSBundle mj_localizedStringForKey:MJRefreshHeaderDateTodayText] : @"", + time]; + } else { + self.lastUpdatedTimeLabel.text = [NSString stringWithFormat:@"%@%@", + [NSBundle mj_localizedStringForKey:MJRefreshHeaderLastTimeText], + [NSBundle mj_localizedStringForKey:MJRefreshHeaderNoneLastDateText]]; + } +} + + +- (void)textConfiguration { + // 初始化文字 + [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshHeaderIdleText] forState:MJRefreshStateIdle]; + [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshHeaderPullingText] forState:MJRefreshStatePulling]; + [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshHeaderRefreshingText] forState:MJRefreshStateRefreshing]; + self.lastUpdatedTimeKey = MJRefreshHeaderLastUpdatedTimeKey; +} + +#pragma mark - 覆盖父类的方法 +- (void)prepare +{ + [super prepare]; + + // 初始化间距 + self.labelLeftInset = MJRefreshLabelLeftInset; + [self textConfiguration]; +} + +- (void)i18nDidChange { + [self textConfiguration]; + + [super i18nDidChange]; +} + +- (void)placeSubviews +{ + [super placeSubviews]; + + if (self.stateLabel.hidden) return; + + BOOL noConstrainsOnStatusLabel = self.stateLabel.constraints.count == 0; + + if (self.lastUpdatedTimeLabel.hidden) { + // 状态 + if (noConstrainsOnStatusLabel) self.stateLabel.frame = self.bounds; + } else { + CGFloat stateLabelH = self.mj_h * 0.5; + // 状态 + if (noConstrainsOnStatusLabel) { + self.stateLabel.mj_x = 0; + self.stateLabel.mj_y = 0; + self.stateLabel.mj_w = self.mj_w; + self.stateLabel.mj_h = stateLabelH; + } + + // 更新时间 + if (self.lastUpdatedTimeLabel.constraints.count == 0) { + self.lastUpdatedTimeLabel.mj_x = 0; + self.lastUpdatedTimeLabel.mj_y = stateLabelH; + self.lastUpdatedTimeLabel.mj_w = self.mj_w; + self.lastUpdatedTimeLabel.mj_h = self.mj_h - self.lastUpdatedTimeLabel.mj_y; + } + } +} + +- (void)setState:(MJRefreshState)state +{ + MJRefreshCheckState + + // 设置状态文字 + self.stateLabel.text = self.stateTitles[@(state)]; + + // 重新设置key(重新显示时间) + self.lastUpdatedTimeKey = self.lastUpdatedTimeKey; +} +@end + +#pragma mark - <<< 为 Swift 扩展链式语法 >>> - +@implementation MJRefreshStateHeader (ChainingGrammar) + +- (instancetype)modifyLastUpdatedTimeText:(NSString * _Nonnull (^)(NSDate * _Nullable))handler { + self.lastUpdatedTimeText = handler; + return self; +} + +@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshNormalTrailer.h b/Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshNormalTrailer.h new file mode 100644 index 0000000..97385d7 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshNormalTrailer.h @@ -0,0 +1,23 @@ +// +// MJRefreshNormalTrailer.h +// MJRefresh +// +// Created by kinarobin on 2020/5/3. +// Copyright © 2020 小码哥. All rights reserved. +// + +#if __has_include() +#import +#else +#import "MJRefreshStateTrailer.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface MJRefreshNormalTrailer : MJRefreshStateTrailer + +@property (weak, nonatomic, readonly) UIImageView *arrowView; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshNormalTrailer.m b/Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshNormalTrailer.m new file mode 100644 index 0000000..4b269f2 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshNormalTrailer.m @@ -0,0 +1,80 @@ +// +// MJRefreshNormalTrailer.m +// MJRefresh +// +// Created by kinarobin on 2020/5/3. +// Copyright © 2020 小码哥. All rights reserved. +// + +#import "MJRefreshNormalTrailer.h" +#import "NSBundle+MJRefresh.h" +#import "UIView+MJExtension.h" + +@interface MJRefreshNormalTrailer() { + __unsafe_unretained UIImageView *_arrowView; +} +@end + +@implementation MJRefreshNormalTrailer +#pragma mark - 懒加载子控件 +- (UIImageView *)arrowView { + if (!_arrowView) { + UIImageView *arrowView = [[UIImageView alloc] initWithImage:[NSBundle mj_trailArrowImage]]; + [self addSubview:_arrowView = arrowView]; + } + return _arrowView; +} + +- (void)placeSubviews { + [super placeSubviews]; + + CGSize arrowSize = self.arrowView.image.size; + // 箭头的中心点 + CGPoint selfCenter = CGPointMake(self.mj_w * 0.5, self.mj_h * 0.5); + CGPoint arrowCenter = CGPointMake(arrowSize.width * 0.5 + 5, self.mj_h * 0.5); + BOOL stateHidden = self.stateLabel.isHidden; + + if (self.arrowView.constraints.count == 0) { + self.arrowView.mj_size = self.arrowView.image.size; + self.arrowView.center = stateHidden ? selfCenter : arrowCenter ; + } + self.arrowView.tintColor = self.stateLabel.textColor; + + if (stateHidden) return; + + BOOL noConstrainsOnStatusLabel = self.stateLabel.constraints.count == 0; + CGFloat stateLabelW = ceil(self.stateLabel.font.pointSize); + // 状态 + if (noConstrainsOnStatusLabel) { + BOOL arrowHidden = self.arrowView.isHidden; + CGFloat stateCenterX = (self.mj_w + arrowSize.width) * 0.5; + self.stateLabel.center = arrowHidden ? selfCenter : CGPointMake(stateCenterX, self.mj_h * 0.5); + self.stateLabel.mj_size = CGSizeMake(stateLabelW, self.mj_h) ; + } +} + +- (void)setState:(MJRefreshState)state { + MJRefreshCheckState + // 根据状态做事情 + if (state == MJRefreshStateIdle) { + if (oldState == MJRefreshStateRefreshing) { + [UIView animateWithDuration:self.fastAnimationDuration animations:^{ + self.arrowView.transform = CGAffineTransformMakeRotation(M_PI); + } completion:^(BOOL finished) { + self.arrowView.transform = CGAffineTransformIdentity; + }]; + } else { + [UIView animateWithDuration:self.fastAnimationDuration animations:^{ + self.arrowView.transform = CGAffineTransformIdentity; + }]; + } + } else if (state == MJRefreshStatePulling) { + [UIView animateWithDuration:self.fastAnimationDuration animations:^{ + self.arrowView.transform = CGAffineTransformMakeRotation(M_PI); + }]; + } +} + + + +@end diff --git a/Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshStateTrailer.h b/Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshStateTrailer.h new file mode 100644 index 0000000..92ac203 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshStateTrailer.h @@ -0,0 +1,28 @@ +// +// MJRefreshStateTrailer.h +// MJRefresh +// +// Created by kinarobin on 2020/5/3. +// Copyright © 2020 小码哥. All rights reserved. +// + +#if __has_include() +#import +#else +#import "MJRefreshTrailer.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + + +@interface MJRefreshStateTrailer : MJRefreshTrailer + +#pragma mark - 状态相关 +/** 显示刷新状态的label */ +@property (weak, nonatomic, readonly) UILabel *stateLabel; +/** 设置state状态下的文字 */ +- (instancetype)setTitle:(NSString *)title forState:(MJRefreshState)state; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshStateTrailer.m b/Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshStateTrailer.m new file mode 100644 index 0000000..3ce0ba6 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshStateTrailer.m @@ -0,0 +1,87 @@ +// +// MJRefreshStateTrailer.m +// MJRefresh +// +// Created by kinarobin on 2020/5/3. +// Copyright © 2020 小码哥. All rights reserved. +// + +#import "MJRefreshStateTrailer.h" +#import "NSBundle+MJRefresh.h" +#import "UIView+MJExtension.h" + +@interface MJRefreshStateTrailer() { + /** 显示刷新状态的label */ + __unsafe_unretained UILabel *_stateLabel; +} +/** 所有状态对应的文字 */ +@property (strong, nonatomic) NSMutableDictionary *stateTitles; +@end + +@implementation MJRefreshStateTrailer +#pragma mark - 懒加载 +- (NSMutableDictionary *)stateTitles { + if (!_stateTitles) { + self.stateTitles = [NSMutableDictionary dictionary]; + } + return _stateTitles; +} + +- (UILabel *)stateLabel { + if (!_stateLabel) { + UILabel *stateLabel = [UILabel mj_label]; + stateLabel.numberOfLines = 0; + [self addSubview:_stateLabel = stateLabel]; + } + return _stateLabel; +} + +#pragma mark - 公共方法 +- (instancetype)setTitle:(NSString *)title forState:(MJRefreshState)state { + if (title == nil) return self; + self.stateTitles[@(state)] = title; + self.stateLabel.text = self.stateTitles[@(self.state)]; + return self; +} + +- (void)textConfiguration { + // 初始化文字 + [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshTrailerIdleText] forState:MJRefreshStateIdle]; + [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshTrailerPullingText] forState:MJRefreshStatePulling]; + [self setTitle:[NSBundle mj_localizedStringForKey:MJRefreshTrailerPullingText] forState:MJRefreshStateRefreshing]; +} + +#pragma mark - 覆盖父类的方法 +- (void)prepare { + [super prepare]; + + [self textConfiguration]; +} + +- (void)i18nDidChange { + [self textConfiguration]; + + [super i18nDidChange]; +} + +- (void)setState:(MJRefreshState)state { + MJRefreshCheckState + // 设置状态文字 + self.stateLabel.text = self.stateTitles[@(state)]; +} + +- (void)placeSubviews { + [super placeSubviews]; + + if (self.stateLabel.hidden) return; + + BOOL noConstrainsOnStatusLabel = self.stateLabel.constraints.count == 0; + CGFloat stateLabelW = ceil(self.stateLabel.font.pointSize); + // 状态 + if (noConstrainsOnStatusLabel) { + self.stateLabel.center = CGPointMake(self.mj_w * 0.5, self.mj_h * 0.5); + self.stateLabel.mj_size = CGSizeMake(stateLabelW, self.mj_h) ; + } +} + +@end diff --git a/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/arrow@2x.png b/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/arrow@2x.png new file mode 100755 index 0000000..b1078de Binary files /dev/null and b/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/arrow@2x.png differ diff --git a/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/en.lproj/Localizable.strings b/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/en.lproj/Localizable.strings new file mode 100644 index 0000000..bf56786 Binary files /dev/null and b/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/en.lproj/Localizable.strings differ diff --git a/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/ko.lproj/Localizable.strings b/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/ko.lproj/Localizable.strings new file mode 100644 index 0000000..ac25579 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/ko.lproj/Localizable.strings @@ -0,0 +1,16 @@ +"MJRefreshHeaderIdleText" = "아래로 당겨 새로고침"; +"MJRefreshHeaderPullingText" = "놓으면 새로고침"; +"MJRefreshHeaderRefreshingText" = "로딩중..."; + +"MJRefreshAutoFooterIdleText" = "탭 또는 위로 당겨 로드함"; +"MJRefreshAutoFooterRefreshingText" = "로딩중..."; +"MJRefreshAutoFooterNoMoreDataText" = "더이상 데이터 없음"; + +"MJRefreshBackFooterIdleText" = "위로 당겨 더 로드 가능"; +"MJRefreshBackFooterPullingText" = "놓으면 더 로드됨."; +"MJRefreshBackFooterRefreshingText" = "로딩중..."; +"MJRefreshBackFooterNoMoreDataText" = "더이상 데이터 없음"; + +"MJRefreshHeaderLastTimeText" = "마지막 업데이트: "; +"MJRefreshHeaderDateTodayText" = "오늘"; +"MJRefreshHeaderNoneLastDateText" = "기록 없음"; diff --git a/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/ru.lproj/Localizable.strings b/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/ru.lproj/Localizable.strings new file mode 100644 index 0000000..7890e7b Binary files /dev/null and b/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/ru.lproj/Localizable.strings differ diff --git a/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/trail_arrow@2x.png b/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/trail_arrow@2x.png new file mode 100644 index 0000000..a45f933 Binary files /dev/null and b/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/trail_arrow@2x.png differ diff --git a/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/uk.lproj/Localizable.strings b/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/uk.lproj/Localizable.strings new file mode 100644 index 0000000..3557940 Binary files /dev/null and b/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/uk.lproj/Localizable.strings differ diff --git a/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/zh-Hans.lproj/Localizable.strings b/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/zh-Hans.lproj/Localizable.strings new file mode 100644 index 0000000..1066e3d Binary files /dev/null and b/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/zh-Hans.lproj/Localizable.strings differ diff --git a/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/zh-Hant.lproj/Localizable.strings b/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/zh-Hant.lproj/Localizable.strings new file mode 100644 index 0000000..17417b5 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/MJRefresh.bundle/zh-Hant.lproj/Localizable.strings @@ -0,0 +1,19 @@ +"MJRefreshHeaderIdleText" = "下拉可以刷新"; +"MJRefreshHeaderPullingText" = "鬆開立即刷新"; +"MJRefreshHeaderRefreshingText" = "正在刷新數據中..."; + +"MJRefreshTrailerIdleText" = "滑動查看圖文詳情"; +"MJRefreshTrailerPullingText" = "釋放查看圖文詳情"; + +"MJRefreshAutoFooterIdleText" = "點擊或上拉加載更多"; +"MJRefreshAutoFooterRefreshingText" = "正在加載更多的數據..."; +"MJRefreshAutoFooterNoMoreDataText" = "已經全部加載完畢"; + +"MJRefreshBackFooterIdleText" = "上拉可以加載更多"; +"MJRefreshBackFooterPullingText" = "鬆開立即加載更多"; +"MJRefreshBackFooterRefreshingText" = "正在加載更多的數據..."; +"MJRefreshBackFooterNoMoreDataText" = "已經全部加載完畢"; + +"MJRefreshHeaderLastTimeText" = "最後更新:"; +"MJRefreshHeaderDateTodayText" = "今天"; +"MJRefreshHeaderNoneLastDateText" = "無記錄"; diff --git a/Pods/MJRefresh/MJRefresh/MJRefresh.h b/Pods/MJRefresh/MJRefresh/MJRefresh.h new file mode 100644 index 0000000..d878212 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/MJRefresh.h @@ -0,0 +1,42 @@ +// 代码地址: https://github.com/CoderMJLee/MJRefresh + +#import + +#if __has_include() +FOUNDATION_EXPORT double MJRefreshVersionNumber; +FOUNDATION_EXPORT const unsigned char MJRefreshVersionString[]; + +#import +#import +#import + +#import +#import + +#import +#import +#import +#import + +#import +#import +#import +#import +#else +#import "UIScrollView+MJRefresh.h" +#import "UIScrollView+MJExtension.h" +#import "UIView+MJExtension.h" + +#import "MJRefreshNormalHeader.h" +#import "MJRefreshGifHeader.h" + +#import "MJRefreshBackNormalFooter.h" +#import "MJRefreshBackGifFooter.h" +#import "MJRefreshAutoNormalFooter.h" +#import "MJRefreshAutoGifFooter.h" + +#import "MJRefreshNormalTrailer.h" +#import "MJRefreshConfig.h" +#import "NSBundle+MJRefresh.h" +#import "MJRefreshConst.h" +#endif diff --git a/Pods/MJRefresh/MJRefresh/MJRefreshConfig.h b/Pods/MJRefresh/MJRefresh/MJRefreshConfig.h new file mode 100644 index 0000000..b2c808f --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/MJRefreshConfig.h @@ -0,0 +1,36 @@ +// +// MJRefreshConfig.h +// +// Created by Frank on 2018/11/27. +// Copyright © 2018 小码哥. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface MJRefreshConfig : NSObject + +/** 默认使用的语言版本, 默认为 nil. 将随系统的语言自动改变 */ +@property (copy, nonatomic, nullable) NSString *languageCode; + +/** 默认使用的语言资源文件名, 默认为 nil, 即默认的 Localizable.strings. + + - Attention: 文件名不包含后缀.strings + */ +@property (copy, nonatomic, nullable) NSString *i18nFilename; +/** i18n 多语言资源加载自定义 Bundle. + + - Attention: 默认为 nil 采用内置逻辑. 这里设置后将忽略内置逻辑的多语言模式, 采用自定义的多语言 bundle + */ +@property (nonatomic, nullable) NSBundle *i18nBundle; + +/** Singleton Config instance */ +@property (class, nonatomic, readonly) MJRefreshConfig *defaultConfig; + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/MJRefreshConfig.m b/Pods/MJRefresh/MJRefresh/MJRefreshConfig.m new file mode 100644 index 0000000..680b95a --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/MJRefreshConfig.m @@ -0,0 +1,42 @@ +// +// MJRefreshConfig.m +// +// Created by Frank on 2018/11/27. +// Copyright © 2018 小码哥. All rights reserved. +// + +#import "MJRefreshConfig.h" +#import "MJRefreshConst.h" +#import "NSBundle+MJRefresh.h" + +@interface MJRefreshConfig (Bundle) + ++ (void)resetLanguageResourceCache; + +@end + +@implementation MJRefreshConfig + +static MJRefreshConfig *mj_RefreshConfig = nil; + ++ (instancetype)defaultConfig { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + mj_RefreshConfig = [[self alloc] init]; + }); + return mj_RefreshConfig; +} + +- (void)setLanguageCode:(NSString *)languageCode { + if ([languageCode isEqualToString:_languageCode]) { + return; + } + + _languageCode = languageCode; + // 重置语言资源 + [MJRefreshConfig resetLanguageResourceCache]; + [NSNotificationCenter.defaultCenter + postNotificationName:MJRefreshDidChangeLanguageNotification object:self]; +} + +@end diff --git a/Pods/MJRefresh/MJRefresh/MJRefreshConst.h b/Pods/MJRefresh/MJRefresh/MJRefreshConst.h new file mode 100644 index 0000000..6881115 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/MJRefreshConst.h @@ -0,0 +1,115 @@ +// 代码地址: https://github.com/CoderMJLee/MJRefresh +#import +#import +#import + +// 弱引用 +#define MJWeakSelf __weak typeof(self) weakSelf = self; + +// 日志输出 +#ifdef DEBUG +#define MJRefreshLog(...) NSLog(__VA_ARGS__) +#else +#define MJRefreshLog(...) +#endif + +// 过期提醒 +#define MJRefreshDeprecated(DESCRIPTION) __attribute__((deprecated(DESCRIPTION))) + +// 运行时objc_msgSend +#define MJRefreshMsgSend(...) ((void (*)(void *, SEL, UIView *))objc_msgSend)(__VA_ARGS__) +#define MJRefreshMsgTarget(target) (__bridge void *)(target) + +// RGB颜色 +#define MJRefreshColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0] + +// 文字颜色 +#define MJRefreshLabelTextColor MJRefreshColor(90, 90, 90) + +// 字体大小 +#define MJRefreshLabelFont [UIFont boldSystemFontOfSize:14] + +// 常量 +UIKIT_EXTERN const CGFloat MJRefreshLabelLeftInset; +UIKIT_EXTERN const CGFloat MJRefreshHeaderHeight; +UIKIT_EXTERN const CGFloat MJRefreshFooterHeight; +UIKIT_EXTERN const CGFloat MJRefreshTrailWidth; +UIKIT_EXTERN const CGFloat MJRefreshFastAnimationDuration; +UIKIT_EXTERN const CGFloat MJRefreshSlowAnimationDuration; + + +UIKIT_EXTERN NSString *const MJRefreshKeyPathContentOffset; +UIKIT_EXTERN NSString *const MJRefreshKeyPathContentSize; +UIKIT_EXTERN NSString *const MJRefreshKeyPathContentInset; +UIKIT_EXTERN NSString *const MJRefreshKeyPathPanState; + +UIKIT_EXTERN NSString *const MJRefreshHeaderLastUpdatedTimeKey; + +UIKIT_EXTERN NSString *const MJRefreshHeaderIdleText; +UIKIT_EXTERN NSString *const MJRefreshHeaderPullingText; +UIKIT_EXTERN NSString *const MJRefreshHeaderRefreshingText; + +UIKIT_EXTERN NSString *const MJRefreshTrailerIdleText; +UIKIT_EXTERN NSString *const MJRefreshTrailerPullingText; + +UIKIT_EXTERN NSString *const MJRefreshAutoFooterIdleText; +UIKIT_EXTERN NSString *const MJRefreshAutoFooterRefreshingText; +UIKIT_EXTERN NSString *const MJRefreshAutoFooterNoMoreDataText; + +UIKIT_EXTERN NSString *const MJRefreshBackFooterIdleText; +UIKIT_EXTERN NSString *const MJRefreshBackFooterPullingText; +UIKIT_EXTERN NSString *const MJRefreshBackFooterRefreshingText; +UIKIT_EXTERN NSString *const MJRefreshBackFooterNoMoreDataText; + +UIKIT_EXTERN NSString *const MJRefreshHeaderLastTimeText; +UIKIT_EXTERN NSString *const MJRefreshHeaderDateTodayText; +UIKIT_EXTERN NSString *const MJRefreshHeaderNoneLastDateText; + +UIKIT_EXTERN NSString *const MJRefreshDidChangeLanguageNotification; + +// 状态检查 +#define MJRefreshCheckState \ +MJRefreshState oldState = self.state; \ +if (state == oldState) return; \ +[super setState:state]; + +// 异步主线程执行,不强持有Self +#define MJRefreshDispatchAsyncOnMainQueue(x) \ +__weak typeof(self) weakSelf = self; \ +dispatch_async(dispatch_get_main_queue(), ^{ \ +typeof(weakSelf) self = weakSelf; \ +{x} \ +}); + +/// 替换方法实现 +/// @param _fromClass 源类 +/// @param _originSelector 源类的 Selector +/// @param _toClass 目标类 +/// @param _newSelector 目标类的 Selector +CG_INLINE BOOL MJRefreshExchangeImplementations( + Class _fromClass, SEL _originSelector, + Class _toClass, SEL _newSelector) { + if (!_fromClass || !_toClass) { + return NO; + } + + Method oriMethod = class_getInstanceMethod(_fromClass, _originSelector); + Method newMethod = class_getInstanceMethod(_toClass, _newSelector); + if (!newMethod) { + return NO; + } + + BOOL isAddedMethod = class_addMethod(_fromClass, _originSelector, + method_getImplementation(newMethod), + method_getTypeEncoding(newMethod)); + if (isAddedMethod) { + // 如果 class_addMethod 成功了,说明之前 fromClass 里并不存在 originSelector,所以要用一个空的方法代替它,以避免 class_replaceMethod 后,后续 toClass 的这个方法被调用时可能会 crash + IMP emptyIMP = imp_implementationWithBlock(^(id selfObject) {}); + IMP oriMethodIMP = method_getImplementation(oriMethod) ?: emptyIMP; + const char *oriMethodTypeEncoding = method_getTypeEncoding(oriMethod) ?: "v@:"; + class_replaceMethod(_toClass, _newSelector, oriMethodIMP, oriMethodTypeEncoding); + } else { + method_exchangeImplementations(oriMethod, newMethod); + } + return YES; +} diff --git a/Pods/MJRefresh/MJRefresh/MJRefreshConst.m b/Pods/MJRefresh/MJRefresh/MJRefreshConst.m new file mode 100644 index 0000000..704d4c7 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/MJRefreshConst.m @@ -0,0 +1,39 @@ +// 代码地址: https://github.com/CoderMJLee/MJRefresh +#import + +const CGFloat MJRefreshLabelLeftInset = 25; +const CGFloat MJRefreshHeaderHeight = 54.0; +const CGFloat MJRefreshFooterHeight = 44.0; +const CGFloat MJRefreshTrailWidth = 60.0; +const CGFloat MJRefreshFastAnimationDuration = 0.25; +const CGFloat MJRefreshSlowAnimationDuration = 0.4; + + +NSString *const MJRefreshKeyPathContentOffset = @"contentOffset"; +NSString *const MJRefreshKeyPathContentInset = @"contentInset"; +NSString *const MJRefreshKeyPathContentSize = @"contentSize"; +NSString *const MJRefreshKeyPathPanState = @"state"; + +NSString *const MJRefreshHeaderLastUpdatedTimeKey = @"MJRefreshHeaderLastUpdatedTimeKey"; + +NSString *const MJRefreshHeaderIdleText = @"MJRefreshHeaderIdleText"; +NSString *const MJRefreshHeaderPullingText = @"MJRefreshHeaderPullingText"; +NSString *const MJRefreshHeaderRefreshingText = @"MJRefreshHeaderRefreshingText"; + +NSString *const MJRefreshTrailerIdleText = @"MJRefreshTrailerIdleText"; +NSString *const MJRefreshTrailerPullingText = @"MJRefreshTrailerPullingText"; + +NSString *const MJRefreshAutoFooterIdleText = @"MJRefreshAutoFooterIdleText"; +NSString *const MJRefreshAutoFooterRefreshingText = @"MJRefreshAutoFooterRefreshingText"; +NSString *const MJRefreshAutoFooterNoMoreDataText = @"MJRefreshAutoFooterNoMoreDataText"; + +NSString *const MJRefreshBackFooterIdleText = @"MJRefreshBackFooterIdleText"; +NSString *const MJRefreshBackFooterPullingText = @"MJRefreshBackFooterPullingText"; +NSString *const MJRefreshBackFooterRefreshingText = @"MJRefreshBackFooterRefreshingText"; +NSString *const MJRefreshBackFooterNoMoreDataText = @"MJRefreshBackFooterNoMoreDataText"; + +NSString *const MJRefreshHeaderLastTimeText = @"MJRefreshHeaderLastTimeText"; +NSString *const MJRefreshHeaderDateTodayText = @"MJRefreshHeaderDateTodayText"; +NSString *const MJRefreshHeaderNoneLastDateText = @"MJRefreshHeaderNoneLastDateText"; + +NSString *const MJRefreshDidChangeLanguageNotification = @"MJRefreshDidChangeLanguageNotification"; diff --git a/Pods/MJRefresh/MJRefresh/NSBundle+MJRefresh.h b/Pods/MJRefresh/MJRefresh/NSBundle+MJRefresh.h new file mode 100644 index 0000000..a1f56f4 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/NSBundle+MJRefresh.h @@ -0,0 +1,21 @@ +// +// NSBundle+MJRefresh.h +// MJRefresh +// +// Created by MJ Lee on 16/6/13. +// Copyright © 2016年 小码哥. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSBundle (MJRefresh) ++ (instancetype)mj_refreshBundle; ++ (UIImage *)mj_arrowImage; ++ (UIImage *)mj_trailArrowImage; ++ (NSString *)mj_localizedStringForKey:(NSString *)key value:(nullable NSString *)value; ++ (NSString *)mj_localizedStringForKey:(NSString *)key; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/NSBundle+MJRefresh.m b/Pods/MJRefresh/MJRefresh/NSBundle+MJRefresh.m new file mode 100644 index 0000000..c19cbe8 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/NSBundle+MJRefresh.m @@ -0,0 +1,116 @@ +// +// NSBundle+MJRefresh.m +// MJRefresh +// +// Created by MJ Lee on 16/6/13. +// Copyright © 2016年 小码哥. All rights reserved. +// + +#import "NSBundle+MJRefresh.h" +#import "MJRefreshComponent.h" +#import "MJRefreshConfig.h" + +static NSBundle *mj_defaultI18nBundle = nil; +static NSBundle *mj_systemI18nBundle = nil; + +@implementation NSBundle (MJRefresh) ++ (instancetype)mj_refreshBundle +{ + static NSBundle *refreshBundle = nil; + if (refreshBundle == nil) { +#ifdef SWIFT_PACKAGE + NSBundle *containnerBundle = SWIFTPM_MODULE_BUNDLE; +#else + NSBundle *containnerBundle = [NSBundle bundleForClass:[MJRefreshComponent class]]; +#endif + refreshBundle = [NSBundle bundleWithPath:[containnerBundle pathForResource:@"MJRefresh" ofType:@"bundle"]]; + } + return refreshBundle; +} + ++ (UIImage *)mj_arrowImage +{ + static UIImage *arrowImage = nil; + if (arrowImage == nil) { + arrowImage = [[UIImage imageWithContentsOfFile:[[self mj_refreshBundle] pathForResource:@"arrow@2x" ofType:@"png"]] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + } + return arrowImage; +} + ++ (UIImage *)mj_trailArrowImage { + static UIImage *arrowImage = nil; + if (arrowImage == nil) { + arrowImage = [[UIImage imageWithContentsOfFile:[[self mj_refreshBundle] pathForResource:@"trail_arrow@2x" ofType:@"png"]] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + } + return arrowImage; +} + ++ (NSString *)mj_localizedStringForKey:(NSString *)key +{ + return [self mj_localizedStringForKey:key value:nil]; +} + ++ (NSString *)mj_localizedStringForKey:(NSString *)key value:(NSString *)value +{ + NSString *table = MJRefreshConfig.defaultConfig.i18nFilename; + + // 如果没有缓存, 则走初始化逻辑 + if (mj_defaultI18nBundle == nil) { + NSString *language = MJRefreshConfig.defaultConfig.languageCode; + // 如果配置中没有配置语言 + if (!language) { + language = [NSLocale preferredLanguages].firstObject; + } + NSBundle *bundle = MJRefreshConfig.defaultConfig.i18nBundle; + // 首先优先使用公共配置中的 i18nBundle, 如果为空则使用 mainBundle + bundle = bundle ? bundle : NSBundle.mainBundle; + // 按语言选取语言包 + NSString *i18nFolderPath = [bundle pathForResource:language ofType:@"lproj"]; + mj_defaultI18nBundle = [NSBundle bundleWithPath:i18nFolderPath]; + // 检查语言包, 如果没有查找到, 则默认使用 mainBundle + mj_defaultI18nBundle = mj_defaultI18nBundle ? mj_defaultI18nBundle : NSBundle.mainBundle; + + // 获取 MJRefresh 自有的语言包 + if (mj_systemI18nBundle == nil) { + mj_systemI18nBundle = [self mj_defaultI18nBundleWithLanguage:language]; + } + } + // 首先在 MJRefresh 内置语言文件中寻找 + value = [mj_systemI18nBundle localizedStringForKey:key value:value table:nil]; + // 然后在 MainBundle 对应语言文件中寻找 + value = [mj_defaultI18nBundle localizedStringForKey:key value:value table:table]; + return value; +} + ++ (NSBundle *)mj_defaultI18nBundleWithLanguage:(NSString *)language { + if ([language hasPrefix:@"en"]) { + language = @"en"; + } else if ([language hasPrefix:@"zh"]) { + if ([language rangeOfString:@"Hans"].location != NSNotFound) { + language = @"zh-Hans"; // 简体中文 + } else { // zh-Hant\zh-HK\zh-TW + language = @"zh-Hant"; // 繁體中文 + } + } else if ([language hasPrefix:@"ko"]) { + language = @"ko"; + } else if ([language hasPrefix:@"ru"]) { + language = @"ru"; + } else if ([language hasPrefix:@"uk"]) { + language = @"uk"; + } else { + language = @"en"; + } + + // 从MJRefresh.bundle中查找资源 + return [NSBundle bundleWithPath:[[NSBundle mj_refreshBundle] pathForResource:language ofType:@"lproj"]]; +} +@end + +@implementation MJRefreshConfig (Bundle) + ++ (void)resetLanguageResourceCache { + mj_defaultI18nBundle = nil; + mj_systemI18nBundle = nil; +} + +@end diff --git a/Pods/MJRefresh/MJRefresh/UICollectionViewLayout+MJRefresh.h b/Pods/MJRefresh/MJRefresh/UICollectionViewLayout+MJRefresh.h new file mode 100644 index 0000000..df0423d --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/UICollectionViewLayout+MJRefresh.h @@ -0,0 +1,20 @@ +// +// UICollectionViewLayout+MJRefresh.h +// +// 该类是用来解决 Footer 在底端加载完成后, 仍停留在原处的 bug. +// 此问题出现在 iOS 14 及以下系统上. +// Reference: https://github.com/CoderMJLee/MJRefresh/issues/1552 +// +// Created by jiasong on 2021/11/15. +// Copyright © 2021 小码哥. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UICollectionViewLayout (MJRefresh) + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/UICollectionViewLayout+MJRefresh.m b/Pods/MJRefresh/MJRefresh/UICollectionViewLayout+MJRefresh.m new file mode 100644 index 0000000..00d030e --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/UICollectionViewLayout+MJRefresh.m @@ -0,0 +1,45 @@ +// +// UICollectionViewLayout+MJRefresh.m +// +// 该类是用来解决 Footer 在底端加载完成后, 仍停留在原处的 bug. +// 此问题出现在 iOS 14 及以下系统上. +// Reference: https://github.com/CoderMJLee/MJRefresh/issues/1552 +// +// Created by jiasong on 2021/11/15. +// Copyright © 2021 小码哥. All rights reserved. +// + +#import "UICollectionViewLayout+MJRefresh.h" +#import "MJRefreshConst.h" +#import "MJRefreshFooter.h" +#import "UIScrollView+MJRefresh.h" + +@implementation UICollectionViewLayout (MJRefresh) + ++ (void)load { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + MJRefreshExchangeImplementations(self.class, @selector(finalizeCollectionViewUpdates), + self.class, @selector(mj_finalizeCollectionViewUpdates)); + }); +} + +- (void)mj_finalizeCollectionViewUpdates { + [self mj_finalizeCollectionViewUpdates]; + + __kindof MJRefreshFooter *footer = self.collectionView.mj_footer; + CGSize newSize = self.collectionViewContentSize; + CGSize oldSize = self.collectionView.contentSize; + if (footer != nil && !CGSizeEqualToSize(newSize, oldSize)) { + NSDictionary *changed = @{ + NSKeyValueChangeNewKey: [NSValue valueWithCGSize:newSize], + NSKeyValueChangeOldKey: [NSValue valueWithCGSize:oldSize], + }; + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + [footer scrollViewContentSizeDidChange:changed]; + [CATransaction commit]; + } +} + +@end diff --git a/Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.h b/Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.h new file mode 100644 index 0000000..1b46d59 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.h @@ -0,0 +1,28 @@ +// 代码地址: https://github.com/CoderMJLee/MJRefresh +// UIScrollView+Extension.h +// MJRefresh +// +// Created by MJ Lee on 14-5-28. +// Copyright (c) 2014年 小码哥. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UIScrollView (MJExtension) +@property (readonly, nonatomic) UIEdgeInsets mj_inset; + +@property (assign, nonatomic) CGFloat mj_insetT; +@property (assign, nonatomic) CGFloat mj_insetB; +@property (assign, nonatomic) CGFloat mj_insetL; +@property (assign, nonatomic) CGFloat mj_insetR; + +@property (assign, nonatomic) CGFloat mj_offsetX; +@property (assign, nonatomic) CGFloat mj_offsetY; + +@property (assign, nonatomic) CGFloat mj_contentW; +@property (assign, nonatomic) CGFloat mj_contentH; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.m b/Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.m new file mode 100644 index 0000000..1c43721 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.m @@ -0,0 +1,153 @@ +// 代码地址: https://github.com/CoderMJLee/MJRefresh +// UIScrollView+Extension.m +// MJRefresh +// +// Created by MJ Lee on 14-5-28. +// Copyright (c) 2014年 小码哥. All rights reserved. +// + +#import "UIScrollView+MJExtension.h" +#import + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability-new" + +@implementation UIScrollView (MJExtension) + +static BOOL respondsToAdjustedContentInset_; + ++ (void)load +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + respondsToAdjustedContentInset_ = [self instancesRespondToSelector:@selector(adjustedContentInset)]; + }); +} + +- (UIEdgeInsets)mj_inset +{ +#ifdef __IPHONE_11_0 + if (respondsToAdjustedContentInset_) { + return self.adjustedContentInset; + } +#endif + return self.contentInset; +} + +- (void)setMj_insetT:(CGFloat)mj_insetT +{ + UIEdgeInsets inset = self.contentInset; + inset.top = mj_insetT; +#ifdef __IPHONE_11_0 + if (respondsToAdjustedContentInset_) { + inset.top -= (self.adjustedContentInset.top - self.contentInset.top); + } +#endif + self.contentInset = inset; +} + +- (CGFloat)mj_insetT +{ + return self.mj_inset.top; +} + +- (void)setMj_insetB:(CGFloat)mj_insetB +{ + UIEdgeInsets inset = self.contentInset; + inset.bottom = mj_insetB; +#ifdef __IPHONE_11_0 + if (respondsToAdjustedContentInset_) { + inset.bottom -= (self.adjustedContentInset.bottom - self.contentInset.bottom); + } +#endif + self.contentInset = inset; +} + +- (CGFloat)mj_insetB +{ + return self.mj_inset.bottom; +} + +- (void)setMj_insetL:(CGFloat)mj_insetL +{ + UIEdgeInsets inset = self.contentInset; + inset.left = mj_insetL; +#ifdef __IPHONE_11_0 + if (respondsToAdjustedContentInset_) { + inset.left -= (self.adjustedContentInset.left - self.contentInset.left); + } +#endif + self.contentInset = inset; +} + +- (CGFloat)mj_insetL +{ + return self.mj_inset.left; +} + +- (void)setMj_insetR:(CGFloat)mj_insetR +{ + UIEdgeInsets inset = self.contentInset; + inset.right = mj_insetR; +#ifdef __IPHONE_11_0 + if (respondsToAdjustedContentInset_) { + inset.right -= (self.adjustedContentInset.right - self.contentInset.right); + } +#endif + self.contentInset = inset; +} + +- (CGFloat)mj_insetR +{ + return self.mj_inset.right; +} + +- (void)setMj_offsetX:(CGFloat)mj_offsetX +{ + CGPoint offset = self.contentOffset; + offset.x = mj_offsetX; + self.contentOffset = offset; +} + +- (CGFloat)mj_offsetX +{ + return self.contentOffset.x; +} + +- (void)setMj_offsetY:(CGFloat)mj_offsetY +{ + CGPoint offset = self.contentOffset; + offset.y = mj_offsetY; + self.contentOffset = offset; +} + +- (CGFloat)mj_offsetY +{ + return self.contentOffset.y; +} + +- (void)setMj_contentW:(CGFloat)mj_contentW +{ + CGSize size = self.contentSize; + size.width = mj_contentW; + self.contentSize = size; +} + +- (CGFloat)mj_contentW +{ + return self.contentSize.width; +} + +- (void)setMj_contentH:(CGFloat)mj_contentH +{ + CGSize size = self.contentSize; + size.height = mj_contentH; + self.contentSize = size; +} + +- (CGFloat)mj_contentH +{ + return self.contentSize.height; +} +@end +#pragma clang diagnostic pop diff --git a/Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.h b/Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.h new file mode 100644 index 0000000..8ce3282 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.h @@ -0,0 +1,36 @@ +// 代码地址: https://github.com/CoderMJLee/MJRefresh +// UIScrollView+MJRefresh.h +// MJRefresh +// +// Created by MJ Lee on 15/3/4. +// Copyright (c) 2015年 小码哥. All rights reserved. +// 给ScrollView增加下拉刷新、上拉刷新、 左滑刷新的功能 + +#import +#if __has_include() +#import +#else +#import "MJRefreshConst.h" +#endif + +@class MJRefreshHeader, MJRefreshFooter, MJRefreshTrailer; + +NS_ASSUME_NONNULL_BEGIN + +@interface UIScrollView (MJRefresh) +/** 下拉刷新控件 */ +@property (strong, nonatomic, nullable) MJRefreshHeader *mj_header; +@property (strong, nonatomic, nullable) MJRefreshHeader *header MJRefreshDeprecated("使用mj_header"); +/** 上拉刷新控件 */ +@property (strong, nonatomic, nullable) MJRefreshFooter *mj_footer; +@property (strong, nonatomic, nullable) MJRefreshFooter *footer MJRefreshDeprecated("使用mj_footer"); + +/** 左滑刷新控件 */ +@property (strong, nonatomic, nullable) MJRefreshTrailer *mj_trailer; + +#pragma mark - other +- (NSInteger)mj_totalDataCount; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.m b/Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.m new file mode 100644 index 0000000..3bb85da --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.m @@ -0,0 +1,120 @@ +// 代码地址: https://github.com/CoderMJLee/MJRefresh +// UIScrollView+MJRefresh.m +// MJRefresh +// +// Created by MJ Lee on 15/3/4. +// Copyright (c) 2015年 小码哥. All rights reserved. +// + +#import "UIScrollView+MJRefresh.h" +#import "MJRefreshHeader.h" +#import "MJRefreshFooter.h" +#import "MJRefreshTrailer.h" +#import + +@implementation UIScrollView (MJRefresh) + +#pragma mark - header +static const char MJRefreshHeaderKey = '\0'; +- (void)setMj_header:(MJRefreshHeader *)mj_header +{ + if (mj_header != self.mj_header) { + // 删除旧的,添加新的 + [self.mj_header removeFromSuperview]; + + if (mj_header) { + [self insertSubview:mj_header atIndex:0]; + } + // 存储新的 + objc_setAssociatedObject(self, &MJRefreshHeaderKey, + mj_header, OBJC_ASSOCIATION_RETAIN); + } +} + +- (MJRefreshHeader *)mj_header +{ + return objc_getAssociatedObject(self, &MJRefreshHeaderKey); +} + +#pragma mark - footer +static const char MJRefreshFooterKey = '\0'; +- (void)setMj_footer:(MJRefreshFooter *)mj_footer +{ + if (mj_footer != self.mj_footer) { + // 删除旧的,添加新的 + [self.mj_footer removeFromSuperview]; + if (mj_footer) { + [self insertSubview:mj_footer atIndex:0]; + } + // 存储新的 + objc_setAssociatedObject(self, &MJRefreshFooterKey, + mj_footer, OBJC_ASSOCIATION_RETAIN); + } +} + +- (MJRefreshFooter *)mj_footer +{ + return objc_getAssociatedObject(self, &MJRefreshFooterKey); +} + +#pragma mark - footer +static const char MJRefreshTrailerKey = '\0'; +- (void)setMj_trailer:(MJRefreshTrailer *)mj_trailer { + if (mj_trailer != self.mj_trailer) { + // 删除旧的,添加新的 + [self.mj_trailer removeFromSuperview]; + if (mj_trailer) { + [self insertSubview:mj_trailer atIndex:0]; + } + // 存储新的 + objc_setAssociatedObject(self, &MJRefreshTrailerKey, + mj_trailer, OBJC_ASSOCIATION_RETAIN); + } +} + +- (MJRefreshTrailer *)mj_trailer { + return objc_getAssociatedObject(self, &MJRefreshTrailerKey); +} + +#pragma mark - 过期 +- (void)setFooter:(MJRefreshFooter *)footer +{ + self.mj_footer = footer; +} + +- (MJRefreshFooter *)footer +{ + return self.mj_footer; +} + +- (void)setHeader:(MJRefreshHeader *)header +{ + self.mj_header = header; +} + +- (MJRefreshHeader *)header +{ + return self.mj_header; +} + +#pragma mark - other +- (NSInteger)mj_totalDataCount +{ + NSInteger totalCount = 0; + if ([self isKindOfClass:[UITableView class]]) { + UITableView *tableView = (UITableView *)self; + + for (NSInteger section = 0; section < tableView.numberOfSections; section++) { + totalCount += [tableView numberOfRowsInSection:section]; + } + } else if ([self isKindOfClass:[UICollectionView class]]) { + UICollectionView *collectionView = (UICollectionView *)self; + + for (NSInteger section = 0; section < collectionView.numberOfSections; section++) { + totalCount += [collectionView numberOfItemsInSection:section]; + } + } + return totalCount; +} + +@end diff --git a/Pods/MJRefresh/MJRefresh/UIView+MJExtension.h b/Pods/MJRefresh/MJRefresh/UIView+MJExtension.h new file mode 100644 index 0000000..0ac0968 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/UIView+MJExtension.h @@ -0,0 +1,22 @@ +// 代码地址: https://github.com/CoderMJLee/MJRefresh +// UIView+Extension.h +// MJRefresh +// +// Created by MJ Lee on 14-5-28. +// Copyright (c) 2014年 小码哥. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UIView (MJExtension) +@property (assign, nonatomic) CGFloat mj_x; +@property (assign, nonatomic) CGFloat mj_y; +@property (assign, nonatomic) CGFloat mj_w; +@property (assign, nonatomic) CGFloat mj_h; +@property (assign, nonatomic) CGSize mj_size; +@property (assign, nonatomic) CGPoint mj_origin; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MJRefresh/MJRefresh/UIView+MJExtension.m b/Pods/MJRefresh/MJRefresh/UIView+MJExtension.m new file mode 100644 index 0000000..f4dcf44 --- /dev/null +++ b/Pods/MJRefresh/MJRefresh/UIView+MJExtension.m @@ -0,0 +1,83 @@ +// 代码地址: https://github.com/CoderMJLee/MJRefresh +// UIView+Extension.m +// MJRefresh +// +// Created by MJ Lee on 14-5-28. +// Copyright (c) 2014年 小码哥. All rights reserved. +// + +#import "UIView+MJExtension.h" + +@implementation UIView (MJExtension) +- (void)setMj_x:(CGFloat)mj_x +{ + CGRect frame = self.frame; + frame.origin.x = mj_x; + self.frame = frame; +} + +- (CGFloat)mj_x +{ + return self.frame.origin.x; +} + +- (void)setMj_y:(CGFloat)mj_y +{ + CGRect frame = self.frame; + frame.origin.y = mj_y; + self.frame = frame; +} + +- (CGFloat)mj_y +{ + return self.frame.origin.y; +} + +- (void)setMj_w:(CGFloat)mj_w +{ + CGRect frame = self.frame; + frame.size.width = mj_w; + self.frame = frame; +} + +- (CGFloat)mj_w +{ + return self.frame.size.width; +} + +- (void)setMj_h:(CGFloat)mj_h +{ + CGRect frame = self.frame; + frame.size.height = mj_h; + self.frame = frame; +} + +- (CGFloat)mj_h +{ + return self.frame.size.height; +} + +- (void)setMj_size:(CGSize)mj_size +{ + CGRect frame = self.frame; + frame.size = mj_size; + self.frame = frame; +} + +- (CGSize)mj_size +{ + return self.frame.size; +} + +- (void)setMj_origin:(CGPoint)mj_origin +{ + CGRect frame = self.frame; + frame.origin = mj_origin; + self.frame = frame; +} + +- (CGPoint)mj_origin +{ + return self.frame.origin; +} +@end diff --git a/Pods/MJRefresh/README.md b/Pods/MJRefresh/README.md new file mode 100644 index 0000000..0746fbe --- /dev/null +++ b/Pods/MJRefresh/README.md @@ -0,0 +1,457 @@ +## MJRefresh +[![SPM supported](https://img.shields.io/badge/SPM-supported-4BC51D.svg?style=flat)](https://github.com/apple/swift-package-manager) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![podversion](https://img.shields.io/cocoapods/v/MJRefresh.svg)](https://cocoapods.org/pods/MJRefresh) + +* An easy way to use pull-to-refresh + +[📜✍🏻**Release Notes**: more details](https://github.com/CoderMJLee/MJRefresh/releases) + +## Contents + +- New Features + - [Dynamic i18n Switching](#dynamic_i18n_switching) + - [SPM Supported](#spm_supported) + - [Swift Chaining Grammar Supported](#swift_chaining_grammar_supported) + +* Getting Started + * [Features【Support what kinds of controls to refresh】](#Support_what_kinds_of_controls_to_refresh) + * [Installation【How to use MJRefresh】](#How_to_use_MJRefresh) + * [Who's using【More than hundreds of Apps are using MJRefresh】](#More_than_hundreds_of_Apps_are_using_MJRefresh) + * [Classes【The Class Structure Chart of MJRefresh】](#The_Class_Structure_Chart_of_MJRefresh) +* Comment API + * [MJRefreshComponent.h](#MJRefreshComponent.h) + * [MJRefreshHeader.h](#MJRefreshHeader.h) + * [MJRefreshFooter.h](#MJRefreshFooter.h) + * [MJRefreshAutoFooter.h](#MJRefreshAutoFooter.h) + * [MJRefreshTrailer.h](#MJRefreshTrailer.h) +* Examples + * [Reference](#Reference) + * [The drop-down refresh 01-Default](#The_drop-down_refresh_01-Default) + * [The drop-down refresh 02-Animation image](#The_drop-down_refresh_02-Animation_image) + * [The drop-down refresh 03-Hide the time](#The_drop-down_refresh_03-Hide_the_time) + * [The drop-down refresh 04-Hide status and time](#The_drop-down_refresh_04-Hide_status_and_time) + * [The drop-down refresh 05-DIY title](#The_drop-down_refresh_05-DIY_title) + * [The drop-down refresh 06-DIY the control of refresh](#The_drop-down_refresh_06-DIY_the_control_of_refresh) + * [The pull to refresh 01-Default](#The_pull_to_refresh_01-Default) + * [The pull to refresh 02-Animation image](#The_pull_to_refresh_02-Animation_image) + * [The pull to refresh 03-Hide the title of refresh status](#The_pull_to_refresh_03-Hide_the_title_of_refresh_status) + * [The pull to refresh 04-All loaded](#The_pull_to_refresh_04-All_loaded) + * [The pull to refresh 05-DIY title](#The_pull_to_refresh_05-DIY_title) + * [The pull to refresh 06-Hidden After loaded](#The_pull_to_refresh_06-Hidden_After_loaded) + * [The pull to refresh 07-Automatic back of the pull01](#The_pull_to_refresh_07-Automatic_back_of_the_pull01) + * [The pull to refresh 08-Automatic back of the pull02](#The_pull_to_refresh_08-Automatic_back_of_the_pull02) + * [The pull to refresh 09-DIY the control of refresh(Automatic refresh)](#The_pull_to_refresh_09-DIY_the_control_of_refresh(Automatic_refresh)) + * [The pull to refresh 10-DIY the control of refresh(Automatic back)](#The_pull_to_refresh_10-DIY_the_control_of_refresh(Automatic_back)) + * [UICollectionView01-The pull and drop-down refresh](#UICollectionView01-The_pull_and_drop-down_refresh) + * [UICollectionView02-The trailer refresh](#UICollectionView02-The_trailer_refresh) + * [WKWebView01-The drop-down refresh](#WKWebView01-The_drop-down_refresh) +* [Hope](#Hope) + +## New Features +### Dynamic i18n Switching + +Now `MJRefresh components` will be rerendered automatically with `MJRefreshConfig.default.language` setting. + +#### Example + +Go `i18n` folder and see lots of cases. Simulator example is behind `i18n tab` in right-top corner. + +#### Setting language + +```swift +MJRefreshConfig.default.language = "zh-hans" +``` + +#### Setting i18n file name + +```swift +MJRefreshConfig.default.i18nFilename = "i18n File Name(not include type<.strings>)" +``` + +#### Setting i18n language bundle + +```swift +MJRefreshConfig.default.i18nBundle = +``` + +#### Adopting the feature in your DIY component + +1. Just override `i18nDidChange` function and reset texts. + +```swift +// must use this localization methods +Bundle.mj_localizedString(forKey: "") +// or +Bundle.mj_localizedString(forKey: "", value:"") + +override func i18nDidChange() { + // Reset texts function + setupTexts() + // Make sure to call super after resetting texts. It will call placeSubViews for applying new layout. + super.i18nDidChange() +} +``` + +2. Receiving `MJRefreshDidChangeLanguageNotification` notification. + +### SPM Supported + +Released from [`3.7.1`](https://github.com/CoderMJLee/MJRefresh/releases/tag/3.7.1) + +### Swift Chaining Grammar Supported + +```swift + // Example as MJRefreshNormalHeader + func addRefreshHeader() { + MJRefreshNormalHeader { [weak self] in + // load some data + }.autoChangeTransparency(true) + .link(to: tableView) + } +``` + +## Support what kinds of controls to refresh + +* `UIScrollView`、`UITableView`、`UICollectionView`、`WKWebView` + +## How to use MJRefresh +* Installation with CocoaPods:`pod 'MJRefresh'` +* Installation with [Carthage](https://github.com/Carthage/Carthage):`github "CoderMJLee/MJRefresh"` +* Manual import: + * Drag All files in the `MJRefresh` folder to project + * Import the main file:`#import "MJRefresh.h"` + +```objc +Base Custom +MJRefresh.bundle MJRefresh.h +MJRefreshConst.h MJRefreshConst.m +UIScrollView+MJExtension.h UIScrollView+MJExtension.m +UIScrollView+MJRefresh.h UIScrollView+MJRefresh.m +UIView+MJExtension.h UIView+MJExtension.m +``` + +## More than hundreds of Apps are using MJRefresh + +* More information of App can focus on:[M了个J-博客园](http://www.cnblogs.com/mjios/p/4409853.html) + +## The Class Structure Chart of MJRefresh +![](http://images0.cnblogs.com/blog2015/497279/201506/132232456139177.png) +- `The class of red text` in the chart:You can use them directly + - The drop-down refresh control types + - Normal:`MJRefreshNormalHeader` + - Gif:`MJRefreshGifHeader` + - The pull to refresh control types + - Auto refresh + - Normal:`MJRefreshAutoNormalFooter` + - Gif:`MJRefreshAutoGifFooter` + - Auto Back + - Normal:`MJRefreshBackNormalFooter` + - Gif:`MJRefreshBackGifFooter` + +- `The class of non-red text` in the chart:For inheritance,to use DIY the control of refresh + +- About how to DIY the control of refresh,You can refer the Class in below Chart
+ + + +## MJRefreshComponent.h +```objc +/** The Base Class of refresh control */ +@interface MJRefreshComponent : UIView +#pragma mark - Control the state of Refresh + +/** BeginRefreshing */ +- (void)beginRefreshing; +/** EndRefreshing */ +- (void)endRefreshing; +/** IsRefreshing */ +- (BOOL)isRefreshing; + +#pragma mark - Other +/** According to the drag ratio to change alpha automatically */ +@property (assign, nonatomic, getter=isAutomaticallyChangeAlpha) BOOL automaticallyChangeAlpha; +@end +``` + +## MJRefreshHeader.h +```objc +@interface MJRefreshHeader : MJRefreshComponent +/** Creat header */ ++ (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock; +/** Creat header */ ++ (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action; + +/** This key is used to storage the time that the last time of drown-down successfully */ +@property (copy, nonatomic) NSString *lastUpdatedTimeKey; +/** The last time of drown-down successfully */ +@property (strong, nonatomic, readonly) NSDate *lastUpdatedTime; + +/** Ignored scrollView contentInset top */ +@property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetTop; +@end +``` + +## MJRefreshFooter.h +```objc +@interface MJRefreshFooter : MJRefreshComponent +/** Creat footer */ ++ (instancetype)footerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock; +/** Creat footer */ ++ (instancetype)footerWithRefreshingTarget:(id)target refreshingAction:(SEL)action; + +/** NoticeNoMoreData */ +- (void)noticeNoMoreData; +/** ResetNoMoreData(Clear the status of NoMoreData ) */ +- (void)resetNoMoreData; + +/** Ignored scrollView contentInset bottom */ +@property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetBottom; +@end +``` + +## MJRefreshAutoFooter.h +```objc +@interface MJRefreshAutoFooter : MJRefreshFooter +/** Is Automatically Refresh(Default is Yes) */ +@property (assign, nonatomic, getter=isAutomaticallyRefresh) BOOL automaticallyRefresh; + +/** When there is much at the bottom of the control is automatically refresh(Default is 1.0,Is at the bottom of the control appears in full, will refresh automatically) */ +@property (assign, nonatomic) CGFloat triggerAutomaticallyRefreshPercent; +@end +``` + +## MJRefreshTrailer.h +```objc +@interface MJRefreshTrailer : MJRefreshComponent + +/** 创建trailer */ ++ (instancetype)trailerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock; +/** 创建trailer */ ++ (instancetype)trailerWithRefreshingTarget:(id)target refreshingAction:(SEL)action; + +/** 忽略多少scrollView的contentInset的right */ +@property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetRight; + +@end +``` + +## Reference +```objc +* Due to there are more functions of this framework,Don't write specific text describe its usage +* You can directly reference examples MJTableViewController、MJCollectionViewController、MJWebViewController,More intuitive and fast. +``` + + +## The drop-down refresh 01-Default + +```objc +self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{ + //Call this Block When enter the refresh status automatically +}]; +或 +// Set the callback(Once you enter the refresh status,then call the action of target,that is call [self loadNewData]) +self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)]; + +// Enter the refresh status immediately +[self.tableView.mj_header beginRefreshing]; +``` +![(下拉刷新01-普通)](http://images0.cnblogs.com/blog2015/497279/201506/141204343486151.gif) + +## The drop-down refresh 02-Animation image +```objc +// Set the callback(一Once you enter the refresh status,then call the action of target,that is call [self loadNewData]) +MJRefreshGifHeader *header = [MJRefreshGifHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)]; +// Set the ordinary state of animated images +[header setImages:idleImages forState:MJRefreshStateIdle]; +// Set the pulling state of animated images(Enter the status of refreshing as soon as loosen) +[header setImages:pullingImages forState:MJRefreshStatePulling]; +// Set the refreshing state of animated images +[header setImages:refreshingImages forState:MJRefreshStateRefreshing]; +// Set header +self.tableView.mj_header = header; +``` +![(下拉刷新02-动画图片)](http://images0.cnblogs.com/blog2015/497279/201506/141204402238389.gif) + +## The drop-down refresh 03-Hide the time +```objc +// Hide the time +header.lastUpdatedTimeLabel.hidden = YES; +``` +![(下拉刷新03-隐藏时间)](http://images0.cnblogs.com/blog2015/497279/201506/141204456132944.gif) + +## The drop-down refresh 04-Hide status and time +```objc +// Hide the time +header.lastUpdatedTimeLabel.hidden = YES; + +// Hide the status +header.stateLabel.hidden = YES; +``` +![(下拉刷新04-隐藏状态和时间0)](http://images0.cnblogs.com/blog2015/497279/201506/141204508639539.gif) + +## The drop-down refresh 05-DIY title +```objc +// Set title +[header setTitle:@"Pull down to refresh" forState:MJRefreshStateIdle]; +[header setTitle:@"Release to refresh" forState:MJRefreshStatePulling]; +[header setTitle:@"Loading ..." forState:MJRefreshStateRefreshing]; + +// Set font +header.stateLabel.font = [UIFont systemFontOfSize:15]; +header.lastUpdatedTimeLabel.font = [UIFont systemFontOfSize:14]; + +// Set textColor +header.stateLabel.textColor = [UIColor redColor]; +header.lastUpdatedTimeLabel.textColor = [UIColor blueColor]; +``` +![(下拉刷新05-自定义文字)](http://images0.cnblogs.com/blog2015/497279/201506/141204563633593.gif) + +## The drop-down refresh 06-DIY the control of refresh +```objc +self.tableView.mj_header = [MJDIYHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)]; +// Implementation reference to MJDIYHeader.h和MJDIYHeader.m +``` +![(下拉刷新06-自定义刷新控件)](http://images0.cnblogs.com/blog2015/497279/201506/141205019261159.gif) + +## The pull to refresh 01-Default +```objc +self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{ + //Call this Block When enter the refresh status automatically +}]; +或 +// Set the callback(Once you enter the refresh status,then call the action of target,that is call [self loadMoreData]) +self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; +``` +![(上拉刷新01-默认)](http://images0.cnblogs.com/blog2015/497279/201506/141205090047696.gif) + +## The pull to refresh 02-Animation image +```objc +// Set the callback(Once you enter the refresh status,then call the action of target,that is call [self loadMoreData]) +MJRefreshAutoGifFooter *footer = [MJRefreshAutoGifFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; + +// Set the refresh image +[footer setImages:refreshingImages forState:MJRefreshStateRefreshing]; + +// Set footer +self.tableView.mj_footer = footer; +``` +![(上拉刷新02-动画图片)](http://images0.cnblogs.com/blog2015/497279/201506/141205141445793.gif) + +## The pull to refresh 03-Hide the title of refresh status +```objc +// Hide the title of refresh status +footer.refreshingTitleHidden = YES; +// If does have not above method,then use footer.stateLabel.hidden = YES; +``` +![(上拉刷新03-隐藏刷新状态的文字)](http://images0.cnblogs.com/blog2015/497279/201506/141205200985774.gif) + +## The pull to refresh 04-All loaded +```objc +//Become the status of NoMoreData +[footer noticeNoMoreData]; +``` +![(上拉刷新04-全部加载完毕)](http://images0.cnblogs.com/blog2015/497279/201506/141205248634686.gif) + +## The pull to refresh 05-DIY title +```objc +// Set title +[footer setTitle:@"Click or drag up to refresh" forState:MJRefreshStateIdle]; +[footer setTitle:@"Loading more ..." forState:MJRefreshStateRefreshing]; +[footer setTitle:@"No more data" forState:MJRefreshStateNoMoreData]; + +// Set font +footer.stateLabel.font = [UIFont systemFontOfSize:17]; + +// Set textColor +footer.stateLabel.textColor = [UIColor blueColor]; +``` +![(上拉刷新05-自定义文字)](http://images0.cnblogs.com/blog2015/497279/201506/141205295511153.gif) + +## The pull to refresh 06-Hidden After loaded +```objc +//Hidden current control of the pull to refresh +self.tableView.mj_footer.hidden = YES; +``` +![(上拉刷新06-加载后隐藏)](http://images0.cnblogs.com/blog2015/497279/201506/141205343481821.gif) + +## The pull to refresh 07-Automatic back of the pull01 +```objc +self.tableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; +``` +![(上拉刷新07-自动回弹的上拉01)](http://images0.cnblogs.com/blog2015/497279/201506/141205392239231.gif) + +## The pull to refresh 08-Automatic back of the pull02 +```objc +MJRefreshBackGifFooter *footer = [MJRefreshBackGifFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; + +// Set the normal state of the animated image +[footer setImages:idleImages forState:MJRefreshStateIdle]; +// Set the pulling state of animated images(Enter the status of refreshing as soon as loosen) +[footer setImages:pullingImages forState:MJRefreshStatePulling]; +// Set the refreshing state of animated images +[footer setImages:refreshingImages forState:MJRefreshStateRefreshing]; + +// Set footer +self.tableView.mj_footer = footer; +``` +![(上拉刷新07-自动回弹的上拉02)](http://images0.cnblogs.com/blog2015/497279/201506/141205441443628.gif) + +## The pull to refresh 09-DIY the control of refresh(Automatic refresh) +```objc +self.tableView.mj_footer = [MJDIYAutoFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; +// Implementation reference to MJDIYAutoFooter.h和MJDIYAutoFooter.m +``` +![(上拉刷新09-自定义刷新控件(自动刷新))](http://images0.cnblogs.com/blog2015/497279/201506/141205500195866.gif) + +## The pull to refresh 10-DIY the control of refresh(Automatic back) +```objc +self.tableView.mj_footer = [MJDIYBackFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; +// Implementation reference to MJDIYBackFooter.h和MJDIYBackFooter.m +``` +![(上拉刷新10-自定义刷新控件(自动回弹))](http://images0.cnblogs.com/blog2015/497279/201506/141205560666819.gif) + +## UICollectionView01-The pull and drop-down refresh +```objc +// The drop-down refresh +self.collectionView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{ + //Call this Block When enter the refresh status automatically +}]; + +// The pull to refresh +self.collectionView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{ + //Call this Block When enter the refresh status automatically +}]; +``` +![(UICollectionView01-上下拉刷新)](http://images0.cnblogs.com/blog2015/497279/201506/141206021603758.gif) + +## UICollectionView02-The trailer refresh +```objc +// The trailer refresh +self.collectionView.mj_trailer = [MJRefreshNormalTrailer trailerWithRefreshingBlock:^{ + //Call this Block When enter the refresh status automatically +}]; + +``` +![(UICollectionView02-左拉刷新)](Gif/trailer_refresh.gif) + +## WKWebView01-The drop-down refresh +```objc +//Add the control of The drop-down refresh +self.webView.scrollView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{ + //Call this Block When enter the refresh status automatically +}]; +``` +![(UICollectionView01-上下拉刷新)](http://images0.cnblogs.com/blog2015/497279/201506/141206080514524.gif) + +## Remind +* ARC +* iOS>=9.0 +* iPhone \ iPad screen anyway + +## 寻求志同道合的小伙伴 + +- 因本人工作忙,没有太多时间去维护MJRefresh,在此向广大框架使用者说声:非常抱歉!😞 +- 现寻求志同道合的小伙伴一起维护此框架,有兴趣的小伙伴可以[发邮件](mailto:richermj123go@vip.qq.com)给我,非常感谢😊 +- 如果一切OK,我将开放框架维护权限(github、pod等) +- 目前已经找到3位小伙伴(^-^)V diff --git a/Pods/Manifest.lock b/Pods/Manifest.lock new file mode 100644 index 0000000..d2d73bf --- /dev/null +++ b/Pods/Manifest.lock @@ -0,0 +1,38 @@ +PODS: + - AFNetworking (4.0.1): + - AFNetworking/NSURLSession (= 4.0.1) + - AFNetworking/Reachability (= 4.0.1) + - AFNetworking/Security (= 4.0.1) + - AFNetworking/Serialization (= 4.0.1) + - AFNetworking/UIKit (= 4.0.1) + - AFNetworking/NSURLSession (4.0.1): + - AFNetworking/Reachability + - AFNetworking/Security + - AFNetworking/Serialization + - AFNetworking/Reachability (4.0.1) + - AFNetworking/Security (4.0.1) + - AFNetworking/Serialization (4.0.1) + - AFNetworking/UIKit (4.0.1): + - AFNetworking/NSURLSession + - MJExtension (3.4.1) + - MJRefresh (3.7.6) + +DEPENDENCIES: + - AFNetworking (~> 4.0) + - MJExtension + - MJRefresh + +SPEC REPOS: + trunk: + - AFNetworking + - MJExtension + - MJRefresh + +SPEC CHECKSUMS: + AFNetworking: 3bd23d814e976cd148d7d44c3ab78017b744cd58 + MJExtension: 21c5f6f8c4d5d8844b7ae8fbae08fed0b501f961 + MJRefresh: 2fe7fb43a5167ceda20bb7e63f130c04fd1814a5 + +PODFILE CHECKSUM: a62c39409cf3d1c88cf89df3813bc94ce4fb7c02 + +COCOAPODS: 1.16.2 diff --git a/Pods/Pods.xcodeproj/project.pbxproj b/Pods/Pods.xcodeproj/project.pbxproj new file mode 100644 index 0000000..2d4e0a6 --- /dev/null +++ b/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,1267 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 09D41A7EE420D9EF74DA6D16BB1D18AF /* UIButton+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 87B35580C2171881C8098C5F6E2ECCB1 /* UIButton+AFNetworking.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 0AE1DF496E0C62E1C9116F4CA6549E24 /* UIView+MJExtension.h in Headers */ = {isa = PBXBuildFile; fileRef = 9E338B5301869FD897BC137CE237EBDB /* UIView+MJExtension.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 0BCD71E30C7DF98613F1DD5C4F764B38 /* MJRefreshNormalHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 992B6C3F2415D89B541CDA5F86C98096 /* MJRefreshNormalHeader.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 0CD04A66475509E2CCCF1EB3E0E16F9D /* MJRefreshAutoStateFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = 1CD3130D7AF1AF61CA7EAD9C45D31517 /* MJRefreshAutoStateFooter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 0EFE34C85A3BA34F9F28AEAEBE37F584 /* Pods-SDAutoLayoutDemo-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = BA7263AB742A567436F29B19F91CBAA6 /* Pods-SDAutoLayoutDemo-dummy.m */; }; + 156DE14C2B0619B71A332ADBA2191821 /* UICollectionViewLayout+MJRefresh.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EC10D7C1D3A4185A9586E424B67D1A4 /* UICollectionViewLayout+MJRefresh.m */; }; + 197F1A252AC47433212E2BA46203DFBA /* MJProperty.m in Sources */ = {isa = PBXBuildFile; fileRef = FD0EBD2F496968C68128486C3843EC57 /* MJProperty.m */; }; + 1DC85BEB11E6F7A10E4661BC15B5ECEA /* AFURLRequestSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = C22AB92B362E4AE1406C864C0488723B /* AFURLRequestSerialization.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 233DF733DC76A9E25DF3E7648BEC18FD /* AFHTTPSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B5683B1F1ECF46DF138C15AF689142E /* AFHTTPSessionManager.m */; }; + 26200CE7EBEC065B7E2AC8078FBC0986 /* AFNetworkReachabilityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 8677842CED69E5BD58B32AE689AE3A93 /* AFNetworkReachabilityManager.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 2A09213164D9FDC674BA055304AD6279 /* MJRefreshHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 777019FC3CF589E77BD6FEF5404949FB /* MJRefreshHeader.m */; }; + 2E1BE42B88FF31A7165EEE3839109939 /* MJRefreshBackGifFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = 66362DDFF2B2BDDED4B6501B4D47A878 /* MJRefreshBackGifFooter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 31AD36D9206CD116F8D628861DEED443 /* MJRefreshBackNormalFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = A85F0C9FD2E16C9868BF3CE10321A7F0 /* MJRefreshBackNormalFooter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 3305B9CFADF6A27973327DDCF48E22A6 /* UIActivityIndicatorView+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = D2FC9051EDC72B92520B1EBA2051F3A3 /* UIActivityIndicatorView+AFNetworking.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 361F7DCF440C4EC48CD10FFABBDEE743 /* MJRefreshComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = F1CE4E9F615D2A0AB51A40C6A11D0288 /* MJRefreshComponent.m */; }; + 39C2C017D796E4FB119BDCF961D0420D /* MJRefreshStateHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DE44FABAFA5A05AC3677ECD34348FF4 /* MJRefreshStateHeader.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 3EA95093247B6333E5D1E009B2D52225 /* UIView+MJExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 73614FF6A90A282230E3EB1A829047E6 /* UIView+MJExtension.m */; }; + 431F896C247769F1FBFCFBD9F0902BBD /* MJRefreshAutoFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F5920E58AC2AF518A80EBA5B7A50D8F /* MJRefreshAutoFooter.m */; }; + 4361C66728ECD7846FF4AC82BFDC0E49 /* AFURLResponseSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 3FA7AD6CA5C05BE430785BEDF0D5622E /* AFURLResponseSerialization.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 43EC74C3510CCD40EC682CE049D006E7 /* UIScrollView+MJExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = C56E8B2BA7A8667606D848CE43336FA3 /* UIScrollView+MJExtension.m */; }; + 4C8AC7BE0D662BCD3C63D0B6675FFAE5 /* NSObject+MJProperty.h in Headers */ = {isa = PBXBuildFile; fileRef = C13535C1AD9A284F9B3CAB84E14876BA /* NSObject+MJProperty.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 4EAC0312FECAE1F799332014E31CDAB2 /* NSBundle+MJRefresh.h in Headers */ = {isa = PBXBuildFile; fileRef = CB0FB120935FB70E79F7153A3435F181 /* NSBundle+MJRefresh.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 51CC9E2D32B32BFB24C402CEE6F920B7 /* MJExtension.h in Headers */ = {isa = PBXBuildFile; fileRef = 3BBE93C2F1DA1652592F9220C287FD9F /* MJExtension.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 52D2291C1A49AA4224E9249E6D7A4B82 /* MJRefreshNormalHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = E80888AE9294AF6F4049DFC07D1811BC /* MJRefreshNormalHeader.m */; }; + 53352ED6438EAFAB849541F71E3D3B6B /* UIRefreshControl+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 003F2F387F938142757763327115CA40 /* UIRefreshControl+AFNetworking.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 54D67F5495742BC9CC14218097DFFA40 /* MJRefreshComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 5066CEC07501B3456886708503B31774 /* MJRefreshComponent.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 5977DE82E0CF1E6822D635598DBC0134 /* MJPropertyKey.h in Headers */ = {isa = PBXBuildFile; fileRef = CCE4909F6C1EA4DF263F186F96060989 /* MJPropertyKey.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 5A2ED8C55D0F4B1EC69EEEA477C4075F /* MJRefreshStateHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C27763072CD9269C8A4B34FAAFA7A15 /* MJRefreshStateHeader.m */; }; + 5AAB42F4086F8CFC26F5899327212BBB /* MJRefreshBackFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6192E1837ECF5DAAE09F137867DFC342 /* MJRefreshBackFooter.m */; }; + 5AED0F2E944E2DE57022A4583AE9181D /* MJRefreshNormalTrailer.m in Sources */ = {isa = PBXBuildFile; fileRef = 17CF149CA8A57AC0980902FF91136D4E /* MJRefreshNormalTrailer.m */; }; + 5B64FB43132E271390B5522B91D9F7F4 /* NSBundle+MJRefresh.m in Sources */ = {isa = PBXBuildFile; fileRef = DD06A4966B7B99064895273326C1CC9D /* NSBundle+MJRefresh.m */; }; + 5C46F48BC9AB6370CA32A2788D86B3F0 /* WKWebView+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 21869D687A50FF320809D302787BAE73 /* WKWebView+AFNetworking.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 5F292B693B03E8A35BF75CF1F8CD1073 /* NSObject+MJClass.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E4FF8BAB02DA9018EB8793227F25E0A /* NSObject+MJClass.m */; }; + 62C99E03D053C1C36A210BCEBD56A266 /* UIKit+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = B3489BAE48E6C40FBE5C9D46D1875B95 /* UIKit+AFNetworking.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 6314D821A974928C0B83A673140BCBBE /* AFNetworkReachabilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8EFC8DE947D9DF359FB8E6F71057299F /* AFNetworkReachabilityManager.m */; }; + 6479C8BAFC394EE0F28C638381EB5ED0 /* AFURLSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F2921778105F9297AF453ACF545829 /* AFURLSessionManager.m */; }; + 669F0E8A5471E407F975A6969C2FE26F /* MJRefreshTrailer.m in Sources */ = {isa = PBXBuildFile; fileRef = 21952DA0FF6D5581C6D123918FDE64ED /* MJRefreshTrailer.m */; }; + 66D80BDC31AC2884F48E336C734FD559 /* MJFoundation.m in Sources */ = {isa = PBXBuildFile; fileRef = 07BB5F2531885ECD177A92372B19495F /* MJFoundation.m */; }; + 6838AC3B59FDBF1B5563694CAB4E0D03 /* UIScrollView+MJExtension.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B3AB8F9C279B8294EAB5697C87B1AF0 /* UIScrollView+MJExtension.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 69E23641A721D713CB1B2A3630551C71 /* MJRefreshAutoNormalFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = DFA684CF4063385C5AE63334663A36D5 /* MJRefreshAutoNormalFooter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 6ECDC11A63A22C0A44C470DE88DDCBA5 /* UIScrollView+MJRefresh.m in Sources */ = {isa = PBXBuildFile; fileRef = AD44684F6D00504C753C6D0C039CCF42 /* UIScrollView+MJRefresh.m */; }; + 738E895DAE3B29BD045DE8CD80052C2B /* MJRefreshFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = 886FE18C38D25333CE4A088003EF8B86 /* MJRefreshFooter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 777420FB0FDC6639F29030FFDFFB17A3 /* MJRefreshHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 09906815D7E1624A133F7C83EC23FC49 /* MJRefreshHeader.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7B6BBB7188C6D1E096B968D9695738DA /* MJRefreshStateTrailer.h in Headers */ = {isa = PBXBuildFile; fileRef = BA6168C020DD9813153FE6669BB0B5C0 /* MJRefreshStateTrailer.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7BFB64C93924BF38CEB19F11159F2E12 /* MJExtension-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AB26AAB82C123BDFBD9E421B41342AF /* MJExtension-dummy.m */; }; + 7C2726DDF4049D57BDA3AF1A186DD463 /* MJRefreshNormalTrailer.h in Headers */ = {isa = PBXBuildFile; fileRef = 221EB5E49E70EC4BA07C7149AFF1FA61 /* MJRefreshNormalTrailer.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7D9C286227AE8813BF98BB65FB5B88F3 /* AFImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D3236D5161A533931EA01480C3D2A7E /* AFImageDownloader.m */; }; + 80A6240A8ABF4B8CABC03E1DA9C071E5 /* AFAutoPurgingImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = C6D0258E8437CF7AC292D2954428EE52 /* AFAutoPurgingImageCache.m */; }; + 8198BC44FDB13D318FB6564A1EBA5268 /* MJRefreshBackGifFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6182CA42B0D5E4005EB2774927EF98EB /* MJRefreshBackGifFooter.m */; }; + 8467584A123E489CFDDCE25FBA5ED5BD /* MJRefresh.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DF397508F6CF20AB281DFF9C0B4B7B2 /* MJRefresh.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 85CBF33D23E45FEDE3FC54F5C9FA6D98 /* AFURLResponseSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 7C8B5EFFB88C38866EB8B51EC3F6C947 /* AFURLResponseSerialization.m */; }; + 8612D342953F3A933EF8F8D6F6DA45EB /* AFAutoPurgingImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = D517C914ABE68936535F2DA54C361530 /* AFAutoPurgingImageCache.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 89A1B2A98CB89A3DFE3D0683D962AF9B /* MJRefreshAutoStateFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = FCFC703C58DA5FC9FD51C74B30B64A59 /* MJRefreshAutoStateFooter.m */; }; + 8CEFA09CDD8DAB24DEACCD6575182441 /* UIScrollView+MJRefresh.h in Headers */ = {isa = PBXBuildFile; fileRef = 7FCA5179451669FCFA5D96A7D51417B6 /* UIScrollView+MJRefresh.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8D5AB075BC93F3D732E76B622B65CE31 /* AFURLSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = A28AED8678C60F4F74EBD7F071BAB891 /* AFURLSessionManager.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 93D175A192F75135B7FC6ABEF554C0EB /* MJRefreshConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 7039FBE90A674DFBB6127724A893EB84 /* MJRefreshConfig.m */; }; + 941FA59066C026B8AE1C319001603AA6 /* AFCompatibilityMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 77AF93333966DA1CB82BFB8A752B2C03 /* AFCompatibilityMacros.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 998AE7A052BBF04F10810E171DFCDF89 /* NSString+MJExtension.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AE8F4D97CBCE5F87AEE3BF3AC35CFF6 /* NSString+MJExtension.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 9E161F7AFD120CDD9634ECB410955A86 /* MJPropertyType.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E413F4C714F6B080A15AB94B6ABD8F /* MJPropertyType.m */; }; + 9FFBD22E2CA7C2EC43ED70F2FF0302EA /* NSObject+MJCoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 6592E9DDDE203BAFB34346C1D5E0015B /* NSObject+MJCoding.m */; }; + A035ACD7D34FB30B305449A08999B18C /* MJExtensionConst.h in Headers */ = {isa = PBXBuildFile; fileRef = 98DE283983A1DC9ACBB8B957C78F349B /* MJExtensionConst.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A068F6E8B034427D3929E6ED1AD9F817 /* MJRefreshAutoNormalFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = D33E28111D5E38342B64B2270C5A4537 /* MJRefreshAutoNormalFooter.m */; }; + A27022BC20DF22339F2399193568596B /* MJRefreshBackStateFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = 17FEA5607EF7AEC1EB42E6459A0DBD8C /* MJRefreshBackStateFooter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A5A692730C77ACCC9083386CA457EC6A /* UIRefreshControl+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 6C844900CA2AE2525CD1FAA91F110595 /* UIRefreshControl+AFNetworking.m */; }; + A8EF9CBCC1901BCAC02515355BCFC841 /* MJExtensionConst.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C05556D899DD1B0E65C3CD9F87E5F9 /* MJExtensionConst.m */; }; + A96362455716ED1326308EB49D324D1E /* NSObject+MJKeyValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 36C024759BCADFBABF5BA9EDC066F6FC /* NSObject+MJKeyValue.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A9E58B3E2D7A002DD5C679FF71A87FAC /* MJPropertyType.h in Headers */ = {isa = PBXBuildFile; fileRef = 57C5F286C33E193EAF1E1473129A50E8 /* MJPropertyType.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AB0DBB06EBD36910FAE7CB5FC85601B6 /* AFImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 8AD4C86E91439D57D44A32FC99E51246 /* AFImageDownloader.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AB46F742EF940DEB0729F7FAE42C19CF /* NSObject+MJClass.h in Headers */ = {isa = PBXBuildFile; fileRef = A8CE0063148A5BC03AD9D461DDDC7093 /* NSObject+MJClass.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AFE76A2E342556AAD4024E271AE43D08 /* MJRefreshAutoFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = 9272E6ABE583FE100E5D8904A5807E29 /* MJRefreshAutoFooter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B1D7360D931F74AA5112E512EAFEB3F9 /* MJRefreshTrailer.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D4C0F0E83D1130EBCFAA03017D1D4C5 /* MJRefreshTrailer.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B2FC4AAA91F18341302B66A13C831517 /* WKWebView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = CD5768E168641D2DA6FC8355A4D7644A /* WKWebView+AFNetworking.m */; }; + B99C11C03CFBB3B40843D3B5715BA57A /* NSObject+MJCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 9F3571C626F0C6573CB2EFBD907ED644 /* NSObject+MJCoding.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C0A5116438CF1E572A0614633DBF6285 /* MJRefreshAutoGifFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = D445549A62299E72EA9B0762E29BF89A /* MJRefreshAutoGifFooter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C21248B537FFDE05F201F4BF7FD6C4AC /* MJRefreshConst.m in Sources */ = {isa = PBXBuildFile; fileRef = BE0DF68ECE1D24370755A69781A99C0C /* MJRefreshConst.m */; }; + C3B840693F113D51224CD6488059F95A /* MJRefreshConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 977E71F559926D94EC26A6FBC5C0874D /* MJRefreshConfig.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C4339FCB9711A75709762DFEBCF9EFF3 /* MJRefreshFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = F01638DA054BE83D5E3353AB7195D73C /* MJRefreshFooter.m */; }; + C4EF0E376298314E4E4A82ED0CBABA65 /* MJRefreshBackStateFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = A200C575BB233D23BE81C1E687D4B426 /* MJRefreshBackStateFooter.m */; }; + C6BA08C452F5644C1C10C4F736D8ECB5 /* AFNetworkActivityIndicatorManager.m in Sources */ = {isa = PBXBuildFile; fileRef = CB13B029FD083C298ED3820A5D44A72A /* AFNetworkActivityIndicatorManager.m */; }; + C6D3C913D0BFA477E8D8FD295F6BE112 /* MJRefreshBackFooter.h in Headers */ = {isa = PBXBuildFile; fileRef = FE37256586BEA52D4A5C2AD933336C72 /* MJRefreshBackFooter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C8316539D8D2A13C0120613F6CC87E8A /* NSObject+MJKeyValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 6C338D2E182041E0F926C847649D6B7E /* NSObject+MJKeyValue.m */; }; + C86E24CB5791A24236900785B2743CD9 /* UIProgressView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = FE5AA633ACB75CCB46DEAB4D0487E24E /* UIProgressView+AFNetworking.m */; }; + C8926B455D818E7A479AEA923F164BFB /* MJRefreshBackNormalFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = CB520E2272422EE17DA2F88951D5960F /* MJRefreshBackNormalFooter.m */; }; + C8E31E90F50206AE0D7D07CC136FF605 /* MJRefreshGifHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = E41858BC603FB4C0969975AB67240209 /* MJRefreshGifHeader.m */; }; + CAC6DA685CB98BF637741801C621E851 /* MJFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = B324AA03304315595C28314B088F88DC /* MJFoundation.h */; settings = {ATTRIBUTES = (Project, ); }; }; + CD4F72018858093DC10B340CC6E1748F /* MJRefreshStateTrailer.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CC1CA04EF111315E4F22A6AA9FC9F3E /* MJRefreshStateTrailer.m */; }; + CD5EE6ED19DDD6B27E4438AD4B96C71E /* AFSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 3CADE384DAB83778C5D15E434BFA0463 /* AFSecurityPolicy.h */; settings = {ATTRIBUTES = (Project, ); }; }; + CE873195B4A713E8806F7228F853DDB0 /* AFSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = 49DBA7406D13F43CB8E15048E8F399B0 /* AFSecurityPolicy.m */; }; + CF4D75977AB844A37599CBB0C2C9CB8F /* MJRefreshConst.h in Headers */ = {isa = PBXBuildFile; fileRef = 6C8BB4FA14951F831E8F0560E3FFBF22 /* MJRefreshConst.h */; settings = {ATTRIBUTES = (Project, ); }; }; + D0FE7467F4D4E2A2662803EF1AC2EC33 /* AFNetworking-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F348003162523E566FD830DE2DE8E9F /* AFNetworking-dummy.m */; }; + D204022EEE7B262F7AE68D420067B0B1 /* NSString+MJExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = B2395B02DF65894762756796E9C2CAC4 /* NSString+MJExtension.m */; }; + D2BD5F363EAD7054BDAF0D1C2B808444 /* NSObject+MJProperty.m in Sources */ = {isa = PBXBuildFile; fileRef = E55070DC3637DD59DFB8C7568FA21195 /* NSObject+MJProperty.m */; }; + D3B8A3E487D4986375B17E4B711ACF53 /* AFNetworkActivityIndicatorManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F24088189B899F64263198D479125A73 /* AFNetworkActivityIndicatorManager.h */; settings = {ATTRIBUTES = (Project, ); }; }; + D85E13EC2DA7F3189D8630870A5D976C /* AFHTTPSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 2538842296D77F7E7F98A8286060CCDB /* AFHTTPSessionManager.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E3B9C0B78AAE4F7807FEFB75FB4955F3 /* UIImageView+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D7E300AF3EB102959376744B8603986 /* UIImageView+AFNetworking.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E3E3CD02A1D63C1FBA3ECE26BDB1232C /* MJRefresh-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 153054A358EB011630AC18D720F557F8 /* MJRefresh-dummy.m */; }; + E4963C2D2E89735EA6DCFB5D55C9E602 /* UIButton+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = A51423CDCDA9885571EE9D3C7D0B292E /* UIButton+AFNetworking.m */; }; + E67888EAD9EE8BE91ED47958C9CB71D7 /* UIProgressView+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 4440B94FA367D4033A0A0580416E75D7 /* UIProgressView+AFNetworking.h */; settings = {ATTRIBUTES = (Project, ); }; }; + EAEC57FFA3A95DDF2717F6FA603CA091 /* MJRefreshAutoGifFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = 20E95E89CF2E88B05454DA8B4C9E202D /* MJRefreshAutoGifFooter.m */; }; + EE8045B4438C4F1FE89FF4C197939D6B /* UIActivityIndicatorView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 6C83C53EDB66A4FA3A919184A0541296 /* UIActivityIndicatorView+AFNetworking.m */; }; + F2AD5323F4C116CD8E1F5646A63C4A7F /* MJRefreshGifHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 62932E32DB28DD1D6F2A3149BA5E1D69 /* MJRefreshGifHeader.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F5712A4AD44745A2BA6E03C643B71161 /* MJProperty.h in Headers */ = {isa = PBXBuildFile; fileRef = 94C9E232D375BE27F5B3FE26CA27B6AD /* MJProperty.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F5AB2C8AB21989F82AE542617552BF84 /* AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = CCE36A849FABDEDEEEB6B63D483C31FC /* AFNetworking.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F6720F62AA31A8FF4199BEC3FF9668C7 /* UICollectionViewLayout+MJRefresh.h in Headers */ = {isa = PBXBuildFile; fileRef = A5820244084305128DBC4DCBF59BC9A4 /* UICollectionViewLayout+MJRefresh.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F6A19CEF9483C5B5E895D568AF9D4425 /* UIImageView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 74DA9ACDF598AB9C8A8E05DF93D7A966 /* UIImageView+AFNetworking.m */; }; + F77875D5398D97026F2803DD5D424D91 /* MJPropertyKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F3786EEC6C11F36AB2636C4BBCE16D6 /* MJPropertyKey.m */; }; + FF2861FA55C81C274B762AD72595F73C /* AFURLRequestSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = DB19DFD1EF5FB35063DA7BF7AF82B882 /* AFURLRequestSerialization.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 290332588A840352BAD96B38CE160BE8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4D3BA58D0583DF37575CACAB3DDADC85; + remoteInfo = MJExtension; + }; + 3B3C7C1BE7C5D0FB4D78B21B2ADF8E20 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 6868056D761E163D10FDAF8CF1C4D9B8; + remoteInfo = MJRefresh; + }; + 41034314F56511568A806D34371CD4A7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0130B3724283586C0E9D2A112D4F2AA1; + remoteInfo = AFNetworking; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 003F2F387F938142757763327115CA40 /* UIRefreshControl+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIRefreshControl+AFNetworking.h"; path = "UIKit+AFNetworking/UIRefreshControl+AFNetworking.h"; sourceTree = ""; }; + 06675CBD36A1919548FA8B110AE0C9B6 /* MJRefresh.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MJRefresh.debug.xcconfig; sourceTree = ""; }; + 07BB5F2531885ECD177A92372B19495F /* MJFoundation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJFoundation.m; path = MJExtension/MJFoundation.m; sourceTree = ""; }; + 09906815D7E1624A133F7C83EC23FC49 /* MJRefreshHeader.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshHeader.h; path = MJRefresh/Base/MJRefreshHeader.h; sourceTree = ""; }; + 0D3236D5161A533931EA01480C3D2A7E /* AFImageDownloader.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFImageDownloader.m; path = "UIKit+AFNetworking/AFImageDownloader.m"; sourceTree = ""; }; + 0E29D0B31DF146B7ED89B05333256F9A /* MJExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MJExtension.release.xcconfig; sourceTree = ""; }; + 0F3786EEC6C11F36AB2636C4BBCE16D6 /* MJPropertyKey.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJPropertyKey.m; path = MJExtension/MJPropertyKey.m; sourceTree = ""; }; + 153054A358EB011630AC18D720F557F8 /* MJRefresh-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "MJRefresh-dummy.m"; sourceTree = ""; }; + 16611967B6F559837A395D8CDDAA6F42 /* AFNetworking-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "AFNetworking-prefix.pch"; sourceTree = ""; }; + 17CF149CA8A57AC0980902FF91136D4E /* MJRefreshNormalTrailer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshNormalTrailer.m; path = MJRefresh/Custom/Trailer/MJRefreshNormalTrailer.m; sourceTree = ""; }; + 17FEA5607EF7AEC1EB42E6459A0DBD8C /* MJRefreshBackStateFooter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshBackStateFooter.h; path = MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h; sourceTree = ""; }; + 1CD3130D7AF1AF61CA7EAD9C45D31517 /* MJRefreshAutoStateFooter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshAutoStateFooter.h; path = MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h; sourceTree = ""; }; + 20E95E89CF2E88B05454DA8B4C9E202D /* MJRefreshAutoGifFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshAutoGifFooter.m; path = MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.m; sourceTree = ""; }; + 21869D687A50FF320809D302787BAE73 /* WKWebView+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "WKWebView+AFNetworking.h"; path = "UIKit+AFNetworking/WKWebView+AFNetworking.h"; sourceTree = ""; }; + 21952DA0FF6D5581C6D123918FDE64ED /* MJRefreshTrailer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshTrailer.m; path = MJRefresh/Base/MJRefreshTrailer.m; sourceTree = ""; }; + 221EB5E49E70EC4BA07C7149AFF1FA61 /* MJRefreshNormalTrailer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshNormalTrailer.h; path = MJRefresh/Custom/Trailer/MJRefreshNormalTrailer.h; sourceTree = ""; }; + 2538842296D77F7E7F98A8286060CCDB /* AFHTTPSessionManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFHTTPSessionManager.h; path = AFNetworking/AFHTTPSessionManager.h; sourceTree = ""; }; + 29E09F1041B3924E32AB12986CD87C7C /* MJExtension-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MJExtension-prefix.pch"; sourceTree = ""; }; + 2AB26AAB82C123BDFBD9E421B41342AF /* MJExtension-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "MJExtension-dummy.m"; sourceTree = ""; }; + 2B276B0A79173A1D6E83C9B4FB9A4A57 /* MJExtension */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = MJExtension; path = libMJExtension.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 2C27763072CD9269C8A4B34FAAFA7A15 /* MJRefreshStateHeader.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshStateHeader.m; path = MJRefresh/Custom/Header/MJRefreshStateHeader.m; sourceTree = ""; }; + 2D7E300AF3EB102959376744B8603986 /* UIImageView+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImageView+AFNetworking.h"; path = "UIKit+AFNetworking/UIImageView+AFNetworking.h"; sourceTree = ""; }; + 2DE44FABAFA5A05AC3677ECD34348FF4 /* MJRefreshStateHeader.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshStateHeader.h; path = MJRefresh/Custom/Header/MJRefreshStateHeader.h; sourceTree = ""; }; + 30D0AE3AD19B55AAE9EF9CA15E56066D /* MJExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MJExtension.debug.xcconfig; sourceTree = ""; }; + 32C05556D899DD1B0E65C3CD9F87E5F9 /* MJExtensionConst.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJExtensionConst.m; path = MJExtension/MJExtensionConst.m; sourceTree = ""; }; + 35C5E219AA564E19A4371CB45D99D372 /* Pods-SDAutoLayoutDemo-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-SDAutoLayoutDemo-acknowledgements.markdown"; sourceTree = ""; }; + 36C024759BCADFBABF5BA9EDC066F6FC /* NSObject+MJKeyValue.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSObject+MJKeyValue.h"; path = "MJExtension/NSObject+MJKeyValue.h"; sourceTree = ""; }; + 3AE8F4D97CBCE5F87AEE3BF3AC35CFF6 /* NSString+MJExtension.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+MJExtension.h"; path = "MJExtension/NSString+MJExtension.h"; sourceTree = ""; }; + 3B3AB8F9C279B8294EAB5697C87B1AF0 /* UIScrollView+MJExtension.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIScrollView+MJExtension.h"; path = "MJRefresh/UIScrollView+MJExtension.h"; sourceTree = ""; }; + 3BBE93C2F1DA1652592F9220C287FD9F /* MJExtension.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJExtension.h; path = MJExtension/MJExtension.h; sourceTree = ""; }; + 3CADE384DAB83778C5D15E434BFA0463 /* AFSecurityPolicy.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFSecurityPolicy.h; path = AFNetworking/AFSecurityPolicy.h; sourceTree = ""; }; + 3E4FF8BAB02DA9018EB8793227F25E0A /* NSObject+MJClass.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSObject+MJClass.m"; path = "MJExtension/NSObject+MJClass.m"; sourceTree = ""; }; + 3FA7AD6CA5C05BE430785BEDF0D5622E /* AFURLResponseSerialization.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFURLResponseSerialization.h; path = AFNetworking/AFURLResponseSerialization.h; sourceTree = ""; }; + 4440B94FA367D4033A0A0580416E75D7 /* UIProgressView+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIProgressView+AFNetworking.h"; path = "UIKit+AFNetworking/UIProgressView+AFNetworking.h"; sourceTree = ""; }; + 49DBA7406D13F43CB8E15048E8F399B0 /* AFSecurityPolicy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFSecurityPolicy.m; path = AFNetworking/AFSecurityPolicy.m; sourceTree = ""; }; + 4CC1CA04EF111315E4F22A6AA9FC9F3E /* MJRefreshStateTrailer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshStateTrailer.m; path = MJRefresh/Custom/Trailer/MJRefreshStateTrailer.m; sourceTree = ""; }; + 4CD11DCC5DDD0394C88F94E177BD38EF /* AFNetworking.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = AFNetworking.debug.xcconfig; sourceTree = ""; }; + 4D2DE9CE23CC66D7877E5FF66543641C /* Pods-SDAutoLayoutDemo-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-SDAutoLayoutDemo-resources.sh"; sourceTree = ""; }; + 5066CEC07501B3456886708503B31774 /* MJRefreshComponent.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshComponent.h; path = MJRefresh/Base/MJRefreshComponent.h; sourceTree = ""; }; + 56F0BDFE37E17E0EB538DCFCD4C6E1EB /* MJRefresh.bundle */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "wrapper.plug-in"; name = MJRefresh.bundle; path = MJRefresh/MJRefresh.bundle; sourceTree = ""; }; + 57C5F286C33E193EAF1E1473129A50E8 /* MJPropertyType.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJPropertyType.h; path = MJExtension/MJPropertyType.h; sourceTree = ""; }; + 59F2921778105F9297AF453ACF545829 /* AFURLSessionManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFURLSessionManager.m; path = AFNetworking/AFURLSessionManager.m; sourceTree = ""; }; + 5DF397508F6CF20AB281DFF9C0B4B7B2 /* MJRefresh.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefresh.h; path = MJRefresh/MJRefresh.h; sourceTree = ""; }; + 5EC10D7C1D3A4185A9586E424B67D1A4 /* UICollectionViewLayout+MJRefresh.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UICollectionViewLayout+MJRefresh.m"; path = "MJRefresh/UICollectionViewLayout+MJRefresh.m"; sourceTree = ""; }; + 6182CA42B0D5E4005EB2774927EF98EB /* MJRefreshBackGifFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshBackGifFooter.m; path = MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.m; sourceTree = ""; }; + 6192E1837ECF5DAAE09F137867DFC342 /* MJRefreshBackFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshBackFooter.m; path = MJRefresh/Base/MJRefreshBackFooter.m; sourceTree = ""; }; + 62932E32DB28DD1D6F2A3149BA5E1D69 /* MJRefreshGifHeader.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshGifHeader.h; path = MJRefresh/Custom/Header/MJRefreshGifHeader.h; sourceTree = ""; }; + 6592E9DDDE203BAFB34346C1D5E0015B /* NSObject+MJCoding.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSObject+MJCoding.m"; path = "MJExtension/NSObject+MJCoding.m"; sourceTree = ""; }; + 66362DDFF2B2BDDED4B6501B4D47A878 /* MJRefreshBackGifFooter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshBackGifFooter.h; path = MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h; sourceTree = ""; }; + 6B5683B1F1ECF46DF138C15AF689142E /* AFHTTPSessionManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFHTTPSessionManager.m; path = AFNetworking/AFHTTPSessionManager.m; sourceTree = ""; }; + 6C338D2E182041E0F926C847649D6B7E /* NSObject+MJKeyValue.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSObject+MJKeyValue.m"; path = "MJExtension/NSObject+MJKeyValue.m"; sourceTree = ""; }; + 6C83C53EDB66A4FA3A919184A0541296 /* UIActivityIndicatorView+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIActivityIndicatorView+AFNetworking.m"; path = "UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m"; sourceTree = ""; }; + 6C844900CA2AE2525CD1FAA91F110595 /* UIRefreshControl+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIRefreshControl+AFNetworking.m"; path = "UIKit+AFNetworking/UIRefreshControl+AFNetworking.m"; sourceTree = ""; }; + 6C8BB4FA14951F831E8F0560E3FFBF22 /* MJRefreshConst.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshConst.h; path = MJRefresh/MJRefreshConst.h; sourceTree = ""; }; + 7039FBE90A674DFBB6127724A893EB84 /* MJRefreshConfig.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshConfig.m; path = MJRefresh/MJRefreshConfig.m; sourceTree = ""; }; + 73614FF6A90A282230E3EB1A829047E6 /* UIView+MJExtension.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIView+MJExtension.m"; path = "MJRefresh/UIView+MJExtension.m"; sourceTree = ""; }; + 74DA9ACDF598AB9C8A8E05DF93D7A966 /* UIImageView+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImageView+AFNetworking.m"; path = "UIKit+AFNetworking/UIImageView+AFNetworking.m"; sourceTree = ""; }; + 777019FC3CF589E77BD6FEF5404949FB /* MJRefreshHeader.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshHeader.m; path = MJRefresh/Base/MJRefreshHeader.m; sourceTree = ""; }; + 77AF93333966DA1CB82BFB8A752B2C03 /* AFCompatibilityMacros.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFCompatibilityMacros.h; path = AFNetworking/AFCompatibilityMacros.h; sourceTree = ""; }; + 7C8B5EFFB88C38866EB8B51EC3F6C947 /* AFURLResponseSerialization.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFURLResponseSerialization.m; path = AFNetworking/AFURLResponseSerialization.m; sourceTree = ""; }; + 7FCA5179451669FCFA5D96A7D51417B6 /* UIScrollView+MJRefresh.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIScrollView+MJRefresh.h"; path = "MJRefresh/UIScrollView+MJRefresh.h"; sourceTree = ""; }; + 8677842CED69E5BD58B32AE689AE3A93 /* AFNetworkReachabilityManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFNetworkReachabilityManager.h; path = AFNetworking/AFNetworkReachabilityManager.h; sourceTree = ""; }; + 87B35580C2171881C8098C5F6E2ECCB1 /* UIButton+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIButton+AFNetworking.h"; path = "UIKit+AFNetworking/UIButton+AFNetworking.h"; sourceTree = ""; }; + 886F1952967B6D4B179E0FE54FA99E3E /* Pods-SDAutoLayoutDemo-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-SDAutoLayoutDemo-acknowledgements.plist"; sourceTree = ""; }; + 886FE18C38D25333CE4A088003EF8B86 /* MJRefreshFooter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshFooter.h; path = MJRefresh/Base/MJRefreshFooter.h; sourceTree = ""; }; + 8AD4C86E91439D57D44A32FC99E51246 /* AFImageDownloader.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFImageDownloader.h; path = "UIKit+AFNetworking/AFImageDownloader.h"; sourceTree = ""; }; + 8EFC8DE947D9DF359FB8E6F71057299F /* AFNetworkReachabilityManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFNetworkReachabilityManager.m; path = AFNetworking/AFNetworkReachabilityManager.m; sourceTree = ""; }; + 8F5920E58AC2AF518A80EBA5B7A50D8F /* MJRefreshAutoFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshAutoFooter.m; path = MJRefresh/Base/MJRefreshAutoFooter.m; sourceTree = ""; }; + 9272E6ABE583FE100E5D8904A5807E29 /* MJRefreshAutoFooter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshAutoFooter.h; path = MJRefresh/Base/MJRefreshAutoFooter.h; sourceTree = ""; }; + 94C9E232D375BE27F5B3FE26CA27B6AD /* MJProperty.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJProperty.h; path = MJExtension/MJProperty.h; sourceTree = ""; }; + 977E71F559926D94EC26A6FBC5C0874D /* MJRefreshConfig.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshConfig.h; path = MJRefresh/MJRefreshConfig.h; sourceTree = ""; }; + 98A55A7D944AA91E0CBBFE0B7C28178F /* MJRefresh-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MJRefresh-prefix.pch"; sourceTree = ""; }; + 98DE283983A1DC9ACBB8B957C78F349B /* MJExtensionConst.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJExtensionConst.h; path = MJExtension/MJExtensionConst.h; sourceTree = ""; }; + 992B6C3F2415D89B541CDA5F86C98096 /* MJRefreshNormalHeader.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshNormalHeader.h; path = MJRefresh/Custom/Header/MJRefreshNormalHeader.h; sourceTree = ""; }; + 9D4C0F0E83D1130EBCFAA03017D1D4C5 /* MJRefreshTrailer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshTrailer.h; path = MJRefresh/Base/MJRefreshTrailer.h; sourceTree = ""; }; + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 9DA3D9475C63BCB54C71690000C70FA5 /* Pods-SDAutoLayoutDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-SDAutoLayoutDemo.release.xcconfig"; sourceTree = ""; }; + 9E338B5301869FD897BC137CE237EBDB /* UIView+MJExtension.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+MJExtension.h"; path = "MJRefresh/UIView+MJExtension.h"; sourceTree = ""; }; + 9F348003162523E566FD830DE2DE8E9F /* AFNetworking-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "AFNetworking-dummy.m"; sourceTree = ""; }; + 9F3571C626F0C6573CB2EFBD907ED644 /* NSObject+MJCoding.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSObject+MJCoding.h"; path = "MJExtension/NSObject+MJCoding.h"; sourceTree = ""; }; + A056E110BE4A9E1FB6F4F56408B6673E /* Pods-SDAutoLayoutDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-SDAutoLayoutDemo.debug.xcconfig"; sourceTree = ""; }; + A200C575BB233D23BE81C1E687D4B426 /* MJRefreshBackStateFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshBackStateFooter.m; path = MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.m; sourceTree = ""; }; + A28AED8678C60F4F74EBD7F071BAB891 /* AFURLSessionManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFURLSessionManager.h; path = AFNetworking/AFURLSessionManager.h; sourceTree = ""; }; + A4FA15D44DF6BAC7550EDEED10862AA3 /* AFNetworking */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = AFNetworking; path = libAFNetworking.a; sourceTree = BUILT_PRODUCTS_DIR; }; + A51423CDCDA9885571EE9D3C7D0B292E /* UIButton+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIButton+AFNetworking.m"; path = "UIKit+AFNetworking/UIButton+AFNetworking.m"; sourceTree = ""; }; + A5820244084305128DBC4DCBF59BC9A4 /* UICollectionViewLayout+MJRefresh.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UICollectionViewLayout+MJRefresh.h"; path = "MJRefresh/UICollectionViewLayout+MJRefresh.h"; sourceTree = ""; }; + A605EBFA7329DD2A0FE04AE6BFB39E25 /* MJRefresh.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MJRefresh.release.xcconfig; sourceTree = ""; }; + A85F0C9FD2E16C9868BF3CE10321A7F0 /* MJRefreshBackNormalFooter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshBackNormalFooter.h; path = MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h; sourceTree = ""; }; + A8CE0063148A5BC03AD9D461DDDC7093 /* NSObject+MJClass.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSObject+MJClass.h"; path = "MJExtension/NSObject+MJClass.h"; sourceTree = ""; }; + AD44684F6D00504C753C6D0C039CCF42 /* UIScrollView+MJRefresh.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIScrollView+MJRefresh.m"; path = "MJRefresh/UIScrollView+MJRefresh.m"; sourceTree = ""; }; + B2395B02DF65894762756796E9C2CAC4 /* NSString+MJExtension.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSString+MJExtension.m"; path = "MJExtension/NSString+MJExtension.m"; sourceTree = ""; }; + B324AA03304315595C28314B088F88DC /* MJFoundation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJFoundation.h; path = MJExtension/MJFoundation.h; sourceTree = ""; }; + B3489BAE48E6C40FBE5C9D46D1875B95 /* UIKit+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIKit+AFNetworking.h"; path = "UIKit+AFNetworking/UIKit+AFNetworking.h"; sourceTree = ""; }; + BA6168C020DD9813153FE6669BB0B5C0 /* MJRefreshStateTrailer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshStateTrailer.h; path = MJRefresh/Custom/Trailer/MJRefreshStateTrailer.h; sourceTree = ""; }; + BA7263AB742A567436F29B19F91CBAA6 /* Pods-SDAutoLayoutDemo-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-SDAutoLayoutDemo-dummy.m"; sourceTree = ""; }; + BE0DF68ECE1D24370755A69781A99C0C /* MJRefreshConst.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshConst.m; path = MJRefresh/MJRefreshConst.m; sourceTree = ""; }; + C13535C1AD9A284F9B3CAB84E14876BA /* NSObject+MJProperty.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSObject+MJProperty.h"; path = "MJExtension/NSObject+MJProperty.h"; sourceTree = ""; }; + C22AB92B362E4AE1406C864C0488723B /* AFURLRequestSerialization.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFURLRequestSerialization.h; path = AFNetworking/AFURLRequestSerialization.h; sourceTree = ""; }; + C35B9E9E4AFF81919106A9EEE9E2B66E /* Pods-SDAutoLayoutDemo */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "Pods-SDAutoLayoutDemo"; path = "libPods-SDAutoLayoutDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + C3E413F4C714F6B080A15AB94B6ABD8F /* MJPropertyType.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJPropertyType.m; path = MJExtension/MJPropertyType.m; sourceTree = ""; }; + C56E8B2BA7A8667606D848CE43336FA3 /* UIScrollView+MJExtension.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIScrollView+MJExtension.m"; path = "MJRefresh/UIScrollView+MJExtension.m"; sourceTree = ""; }; + C6D0258E8437CF7AC292D2954428EE52 /* AFAutoPurgingImageCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFAutoPurgingImageCache.m; path = "UIKit+AFNetworking/AFAutoPurgingImageCache.m"; sourceTree = ""; }; + CB0FB120935FB70E79F7153A3435F181 /* NSBundle+MJRefresh.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSBundle+MJRefresh.h"; path = "MJRefresh/NSBundle+MJRefresh.h"; sourceTree = ""; }; + CB13B029FD083C298ED3820A5D44A72A /* AFNetworkActivityIndicatorManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFNetworkActivityIndicatorManager.m; path = "UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m"; sourceTree = ""; }; + CB520E2272422EE17DA2F88951D5960F /* MJRefreshBackNormalFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshBackNormalFooter.m; path = MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.m; sourceTree = ""; }; + CCE36A849FABDEDEEEB6B63D483C31FC /* AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFNetworking.h; path = AFNetworking/AFNetworking.h; sourceTree = ""; }; + CCE4909F6C1EA4DF263F186F96060989 /* MJPropertyKey.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJPropertyKey.h; path = MJExtension/MJPropertyKey.h; sourceTree = ""; }; + CD5768E168641D2DA6FC8355A4D7644A /* WKWebView+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "WKWebView+AFNetworking.m"; path = "UIKit+AFNetworking/WKWebView+AFNetworking.m"; sourceTree = ""; }; + CF5A1001D8B8AE656FE6A607016C4A76 /* AFNetworking.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = AFNetworking.release.xcconfig; sourceTree = ""; }; + D2FC9051EDC72B92520B1EBA2051F3A3 /* UIActivityIndicatorView+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIActivityIndicatorView+AFNetworking.h"; path = "UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h"; sourceTree = ""; }; + D33E28111D5E38342B64B2270C5A4537 /* MJRefreshAutoNormalFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshAutoNormalFooter.m; path = MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.m; sourceTree = ""; }; + D445549A62299E72EA9B0762E29BF89A /* MJRefreshAutoGifFooter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshAutoGifFooter.h; path = MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h; sourceTree = ""; }; + D517C914ABE68936535F2DA54C361530 /* AFAutoPurgingImageCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFAutoPurgingImageCache.h; path = "UIKit+AFNetworking/AFAutoPurgingImageCache.h"; sourceTree = ""; }; + DB19DFD1EF5FB35063DA7BF7AF82B882 /* AFURLRequestSerialization.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFURLRequestSerialization.m; path = AFNetworking/AFURLRequestSerialization.m; sourceTree = ""; }; + DD06A4966B7B99064895273326C1CC9D /* NSBundle+MJRefresh.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSBundle+MJRefresh.m"; path = "MJRefresh/NSBundle+MJRefresh.m"; sourceTree = ""; }; + DFA684CF4063385C5AE63334663A36D5 /* MJRefreshAutoNormalFooter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshAutoNormalFooter.h; path = MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h; sourceTree = ""; }; + E41858BC603FB4C0969975AB67240209 /* MJRefreshGifHeader.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshGifHeader.m; path = MJRefresh/Custom/Header/MJRefreshGifHeader.m; sourceTree = ""; }; + E49D6D248DD1CEE584E6776B9164A1B2 /* MJRefresh */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = MJRefresh; path = libMJRefresh.a; sourceTree = BUILT_PRODUCTS_DIR; }; + E55070DC3637DD59DFB8C7568FA21195 /* NSObject+MJProperty.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSObject+MJProperty.m"; path = "MJExtension/NSObject+MJProperty.m"; sourceTree = ""; }; + E80888AE9294AF6F4049DFC07D1811BC /* MJRefreshNormalHeader.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshNormalHeader.m; path = MJRefresh/Custom/Header/MJRefreshNormalHeader.m; sourceTree = ""; }; + F01638DA054BE83D5E3353AB7195D73C /* MJRefreshFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshFooter.m; path = MJRefresh/Base/MJRefreshFooter.m; sourceTree = ""; }; + F1CE4E9F615D2A0AB51A40C6A11D0288 /* MJRefreshComponent.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshComponent.m; path = MJRefresh/Base/MJRefreshComponent.m; sourceTree = ""; }; + F24088189B899F64263198D479125A73 /* AFNetworkActivityIndicatorManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFNetworkActivityIndicatorManager.h; path = "UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h"; sourceTree = ""; }; + FCFC703C58DA5FC9FD51C74B30B64A59 /* MJRefreshAutoStateFooter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJRefreshAutoStateFooter.m; path = MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.m; sourceTree = ""; }; + FD0EBD2F496968C68128486C3843EC57 /* MJProperty.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MJProperty.m; path = MJExtension/MJProperty.m; sourceTree = ""; }; + FE37256586BEA52D4A5C2AD933336C72 /* MJRefreshBackFooter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MJRefreshBackFooter.h; path = MJRefresh/Base/MJRefreshBackFooter.h; sourceTree = ""; }; + FE5AA633ACB75CCB46DEAB4D0487E24E /* UIProgressView+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIProgressView+AFNetworking.m"; path = "UIKit+AFNetworking/UIProgressView+AFNetworking.m"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8330E8FEEAED9A815409C4C81CDE2F37 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A49203409C8C7E2AC1D2E332FDE48A56 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D2403152A4624F56E21460C2F15CB1E3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D39F59C65EF8C6E73DBFB8B6B9821774 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 138FEFE2A54937486DAD0747ECDC29CE /* MJRefresh */ = { + isa = PBXGroup; + children = ( + 5DF397508F6CF20AB281DFF9C0B4B7B2 /* MJRefresh.h */, + 9272E6ABE583FE100E5D8904A5807E29 /* MJRefreshAutoFooter.h */, + 8F5920E58AC2AF518A80EBA5B7A50D8F /* MJRefreshAutoFooter.m */, + D445549A62299E72EA9B0762E29BF89A /* MJRefreshAutoGifFooter.h */, + 20E95E89CF2E88B05454DA8B4C9E202D /* MJRefreshAutoGifFooter.m */, + DFA684CF4063385C5AE63334663A36D5 /* MJRefreshAutoNormalFooter.h */, + D33E28111D5E38342B64B2270C5A4537 /* MJRefreshAutoNormalFooter.m */, + 1CD3130D7AF1AF61CA7EAD9C45D31517 /* MJRefreshAutoStateFooter.h */, + FCFC703C58DA5FC9FD51C74B30B64A59 /* MJRefreshAutoStateFooter.m */, + FE37256586BEA52D4A5C2AD933336C72 /* MJRefreshBackFooter.h */, + 6192E1837ECF5DAAE09F137867DFC342 /* MJRefreshBackFooter.m */, + 66362DDFF2B2BDDED4B6501B4D47A878 /* MJRefreshBackGifFooter.h */, + 6182CA42B0D5E4005EB2774927EF98EB /* MJRefreshBackGifFooter.m */, + A85F0C9FD2E16C9868BF3CE10321A7F0 /* MJRefreshBackNormalFooter.h */, + CB520E2272422EE17DA2F88951D5960F /* MJRefreshBackNormalFooter.m */, + 17FEA5607EF7AEC1EB42E6459A0DBD8C /* MJRefreshBackStateFooter.h */, + A200C575BB233D23BE81C1E687D4B426 /* MJRefreshBackStateFooter.m */, + 5066CEC07501B3456886708503B31774 /* MJRefreshComponent.h */, + F1CE4E9F615D2A0AB51A40C6A11D0288 /* MJRefreshComponent.m */, + 977E71F559926D94EC26A6FBC5C0874D /* MJRefreshConfig.h */, + 7039FBE90A674DFBB6127724A893EB84 /* MJRefreshConfig.m */, + 6C8BB4FA14951F831E8F0560E3FFBF22 /* MJRefreshConst.h */, + BE0DF68ECE1D24370755A69781A99C0C /* MJRefreshConst.m */, + 886FE18C38D25333CE4A088003EF8B86 /* MJRefreshFooter.h */, + F01638DA054BE83D5E3353AB7195D73C /* MJRefreshFooter.m */, + 62932E32DB28DD1D6F2A3149BA5E1D69 /* MJRefreshGifHeader.h */, + E41858BC603FB4C0969975AB67240209 /* MJRefreshGifHeader.m */, + 09906815D7E1624A133F7C83EC23FC49 /* MJRefreshHeader.h */, + 777019FC3CF589E77BD6FEF5404949FB /* MJRefreshHeader.m */, + 992B6C3F2415D89B541CDA5F86C98096 /* MJRefreshNormalHeader.h */, + E80888AE9294AF6F4049DFC07D1811BC /* MJRefreshNormalHeader.m */, + 221EB5E49E70EC4BA07C7149AFF1FA61 /* MJRefreshNormalTrailer.h */, + 17CF149CA8A57AC0980902FF91136D4E /* MJRefreshNormalTrailer.m */, + 2DE44FABAFA5A05AC3677ECD34348FF4 /* MJRefreshStateHeader.h */, + 2C27763072CD9269C8A4B34FAAFA7A15 /* MJRefreshStateHeader.m */, + BA6168C020DD9813153FE6669BB0B5C0 /* MJRefreshStateTrailer.h */, + 4CC1CA04EF111315E4F22A6AA9FC9F3E /* MJRefreshStateTrailer.m */, + 9D4C0F0E83D1130EBCFAA03017D1D4C5 /* MJRefreshTrailer.h */, + 21952DA0FF6D5581C6D123918FDE64ED /* MJRefreshTrailer.m */, + CB0FB120935FB70E79F7153A3435F181 /* NSBundle+MJRefresh.h */, + DD06A4966B7B99064895273326C1CC9D /* NSBundle+MJRefresh.m */, + A5820244084305128DBC4DCBF59BC9A4 /* UICollectionViewLayout+MJRefresh.h */, + 5EC10D7C1D3A4185A9586E424B67D1A4 /* UICollectionViewLayout+MJRefresh.m */, + 3B3AB8F9C279B8294EAB5697C87B1AF0 /* UIScrollView+MJExtension.h */, + C56E8B2BA7A8667606D848CE43336FA3 /* UIScrollView+MJExtension.m */, + 7FCA5179451669FCFA5D96A7D51417B6 /* UIScrollView+MJRefresh.h */, + AD44684F6D00504C753C6D0C039CCF42 /* UIScrollView+MJRefresh.m */, + 9E338B5301869FD897BC137CE237EBDB /* UIView+MJExtension.h */, + 73614FF6A90A282230E3EB1A829047E6 /* UIView+MJExtension.m */, + 9F440F28D1AAF11345E84BF314A87BFE /* Resources */, + 63E1598F0E37FBEF8FB313595034BF40 /* Support Files */, + ); + name = MJRefresh; + path = MJRefresh; + sourceTree = ""; + }; + 258C44439E927D8A3DA55E821B1772DC /* MJExtension */ = { + isa = PBXGroup; + children = ( + 3BBE93C2F1DA1652592F9220C287FD9F /* MJExtension.h */, + 98DE283983A1DC9ACBB8B957C78F349B /* MJExtensionConst.h */, + 32C05556D899DD1B0E65C3CD9F87E5F9 /* MJExtensionConst.m */, + B324AA03304315595C28314B088F88DC /* MJFoundation.h */, + 07BB5F2531885ECD177A92372B19495F /* MJFoundation.m */, + 94C9E232D375BE27F5B3FE26CA27B6AD /* MJProperty.h */, + FD0EBD2F496968C68128486C3843EC57 /* MJProperty.m */, + CCE4909F6C1EA4DF263F186F96060989 /* MJPropertyKey.h */, + 0F3786EEC6C11F36AB2636C4BBCE16D6 /* MJPropertyKey.m */, + 57C5F286C33E193EAF1E1473129A50E8 /* MJPropertyType.h */, + C3E413F4C714F6B080A15AB94B6ABD8F /* MJPropertyType.m */, + A8CE0063148A5BC03AD9D461DDDC7093 /* NSObject+MJClass.h */, + 3E4FF8BAB02DA9018EB8793227F25E0A /* NSObject+MJClass.m */, + 9F3571C626F0C6573CB2EFBD907ED644 /* NSObject+MJCoding.h */, + 6592E9DDDE203BAFB34346C1D5E0015B /* NSObject+MJCoding.m */, + 36C024759BCADFBABF5BA9EDC066F6FC /* NSObject+MJKeyValue.h */, + 6C338D2E182041E0F926C847649D6B7E /* NSObject+MJKeyValue.m */, + C13535C1AD9A284F9B3CAB84E14876BA /* NSObject+MJProperty.h */, + E55070DC3637DD59DFB8C7568FA21195 /* NSObject+MJProperty.m */, + 3AE8F4D97CBCE5F87AEE3BF3AC35CFF6 /* NSString+MJExtension.h */, + B2395B02DF65894762756796E9C2CAC4 /* NSString+MJExtension.m */, + ADB1D5DCB5058460D3E4133FEEB67A80 /* Support Files */, + ); + name = MJExtension; + path = MJExtension; + sourceTree = ""; + }; + 2B56065EA98AEF1967EACA01219EDAC6 /* Support Files */ = { + isa = PBXGroup; + children = ( + 9F348003162523E566FD830DE2DE8E9F /* AFNetworking-dummy.m */, + 16611967B6F559837A395D8CDDAA6F42 /* AFNetworking-prefix.pch */, + 4CD11DCC5DDD0394C88F94E177BD38EF /* AFNetworking.debug.xcconfig */, + CF5A1001D8B8AE656FE6A607016C4A76 /* AFNetworking.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/AFNetworking"; + sourceTree = ""; + }; + 38F1BFD3109B002B7A7561315E090985 /* Security */ = { + isa = PBXGroup; + children = ( + 3CADE384DAB83778C5D15E434BFA0463 /* AFSecurityPolicy.h */, + 49DBA7406D13F43CB8E15048E8F399B0 /* AFSecurityPolicy.m */, + ); + name = Security; + sourceTree = ""; + }; + 4748656837CDD7672A73594C30B62B95 /* UIKit */ = { + isa = PBXGroup; + children = ( + D517C914ABE68936535F2DA54C361530 /* AFAutoPurgingImageCache.h */, + C6D0258E8437CF7AC292D2954428EE52 /* AFAutoPurgingImageCache.m */, + 8AD4C86E91439D57D44A32FC99E51246 /* AFImageDownloader.h */, + 0D3236D5161A533931EA01480C3D2A7E /* AFImageDownloader.m */, + F24088189B899F64263198D479125A73 /* AFNetworkActivityIndicatorManager.h */, + CB13B029FD083C298ED3820A5D44A72A /* AFNetworkActivityIndicatorManager.m */, + D2FC9051EDC72B92520B1EBA2051F3A3 /* UIActivityIndicatorView+AFNetworking.h */, + 6C83C53EDB66A4FA3A919184A0541296 /* UIActivityIndicatorView+AFNetworking.m */, + 87B35580C2171881C8098C5F6E2ECCB1 /* UIButton+AFNetworking.h */, + A51423CDCDA9885571EE9D3C7D0B292E /* UIButton+AFNetworking.m */, + 2D7E300AF3EB102959376744B8603986 /* UIImageView+AFNetworking.h */, + 74DA9ACDF598AB9C8A8E05DF93D7A966 /* UIImageView+AFNetworking.m */, + B3489BAE48E6C40FBE5C9D46D1875B95 /* UIKit+AFNetworking.h */, + 4440B94FA367D4033A0A0580416E75D7 /* UIProgressView+AFNetworking.h */, + FE5AA633ACB75CCB46DEAB4D0487E24E /* UIProgressView+AFNetworking.m */, + 003F2F387F938142757763327115CA40 /* UIRefreshControl+AFNetworking.h */, + 6C844900CA2AE2525CD1FAA91F110595 /* UIRefreshControl+AFNetworking.m */, + 21869D687A50FF320809D302787BAE73 /* WKWebView+AFNetworking.h */, + CD5768E168641D2DA6FC8355A4D7644A /* WKWebView+AFNetworking.m */, + ); + name = UIKit; + sourceTree = ""; + }; + 4C0C9E5CC4C4292C8A45F13EAB50C812 /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + F4073F9EF05ECCB7A1FA27406A3AD428 /* Pods-SDAutoLayoutDemo */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + 63E1598F0E37FBEF8FB313595034BF40 /* Support Files */ = { + isa = PBXGroup; + children = ( + 153054A358EB011630AC18D720F557F8 /* MJRefresh-dummy.m */, + 98A55A7D944AA91E0CBBFE0B7C28178F /* MJRefresh-prefix.pch */, + 06675CBD36A1919548FA8B110AE0C9B6 /* MJRefresh.debug.xcconfig */, + A605EBFA7329DD2A0FE04AE6BFB39E25 /* MJRefresh.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/MJRefresh"; + sourceTree = ""; + }; + 7A57AA62C67335DDD14D0A4F37A81383 /* NSURLSession */ = { + isa = PBXGroup; + children = ( + 77AF93333966DA1CB82BFB8A752B2C03 /* AFCompatibilityMacros.h */, + 2538842296D77F7E7F98A8286060CCDB /* AFHTTPSessionManager.h */, + 6B5683B1F1ECF46DF138C15AF689142E /* AFHTTPSessionManager.m */, + A28AED8678C60F4F74EBD7F071BAB891 /* AFURLSessionManager.h */, + 59F2921778105F9297AF453ACF545829 /* AFURLSessionManager.m */, + ); + name = NSURLSession; + sourceTree = ""; + }; + 9F440F28D1AAF11345E84BF314A87BFE /* Resources */ = { + isa = PBXGroup; + children = ( + 56F0BDFE37E17E0EB538DCFCD4C6E1EB /* MJRefresh.bundle */, + ); + name = Resources; + sourceTree = ""; + }; + ADB1D5DCB5058460D3E4133FEEB67A80 /* Support Files */ = { + isa = PBXGroup; + children = ( + 2AB26AAB82C123BDFBD9E421B41342AF /* MJExtension-dummy.m */, + 29E09F1041B3924E32AB12986CD87C7C /* MJExtension-prefix.pch */, + 30D0AE3AD19B55AAE9EF9CA15E56066D /* MJExtension.debug.xcconfig */, + 0E29D0B31DF146B7ED89B05333256F9A /* MJExtension.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/MJExtension"; + sourceTree = ""; + }; + B860C9AA42E98A13369FF2DB2FAED166 /* Serialization */ = { + isa = PBXGroup; + children = ( + C22AB92B362E4AE1406C864C0488723B /* AFURLRequestSerialization.h */, + DB19DFD1EF5FB35063DA7BF7AF82B882 /* AFURLRequestSerialization.m */, + 3FA7AD6CA5C05BE430785BEDF0D5622E /* AFURLResponseSerialization.h */, + 7C8B5EFFB88C38866EB8B51EC3F6C947 /* AFURLResponseSerialization.m */, + ); + name = Serialization; + sourceTree = ""; + }; + C1F432EA0571173F31BA525C07F05CFB /* AFNetworking */ = { + isa = PBXGroup; + children = ( + CCE36A849FABDEDEEEB6B63D483C31FC /* AFNetworking.h */, + 7A57AA62C67335DDD14D0A4F37A81383 /* NSURLSession */, + DB261EBBA92E8907D84A646994F10495 /* Reachability */, + 38F1BFD3109B002B7A7561315E090985 /* Security */, + B860C9AA42E98A13369FF2DB2FAED166 /* Serialization */, + 2B56065EA98AEF1967EACA01219EDAC6 /* Support Files */, + 4748656837CDD7672A73594C30B62B95 /* UIKit */, + ); + name = AFNetworking; + path = AFNetworking; + sourceTree = ""; + }; + CF1408CF629C7361332E53B88F7BD30C = { + isa = PBXGroup; + children = ( + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, + D89477F20FB1DE18A04690586D7808C4 /* Frameworks */, + DC06350441BD2E63211B41F3211CF4EF /* Pods */, + E15D84178FEECBC822D9247D208A04B1 /* Products */, + 4C0C9E5CC4C4292C8A45F13EAB50C812 /* Targets Support Files */, + ); + sourceTree = ""; + }; + D89477F20FB1DE18A04690586D7808C4 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + DB261EBBA92E8907D84A646994F10495 /* Reachability */ = { + isa = PBXGroup; + children = ( + 8677842CED69E5BD58B32AE689AE3A93 /* AFNetworkReachabilityManager.h */, + 8EFC8DE947D9DF359FB8E6F71057299F /* AFNetworkReachabilityManager.m */, + ); + name = Reachability; + sourceTree = ""; + }; + DC06350441BD2E63211B41F3211CF4EF /* Pods */ = { + isa = PBXGroup; + children = ( + C1F432EA0571173F31BA525C07F05CFB /* AFNetworking */, + 258C44439E927D8A3DA55E821B1772DC /* MJExtension */, + 138FEFE2A54937486DAD0747ECDC29CE /* MJRefresh */, + ); + name = Pods; + sourceTree = ""; + }; + E15D84178FEECBC822D9247D208A04B1 /* Products */ = { + isa = PBXGroup; + children = ( + A4FA15D44DF6BAC7550EDEED10862AA3 /* AFNetworking */, + 2B276B0A79173A1D6E83C9B4FB9A4A57 /* MJExtension */, + E49D6D248DD1CEE584E6776B9164A1B2 /* MJRefresh */, + C35B9E9E4AFF81919106A9EEE9E2B66E /* Pods-SDAutoLayoutDemo */, + ); + name = Products; + sourceTree = ""; + }; + F4073F9EF05ECCB7A1FA27406A3AD428 /* Pods-SDAutoLayoutDemo */ = { + isa = PBXGroup; + children = ( + 35C5E219AA564E19A4371CB45D99D372 /* Pods-SDAutoLayoutDemo-acknowledgements.markdown */, + 886F1952967B6D4B179E0FE54FA99E3E /* Pods-SDAutoLayoutDemo-acknowledgements.plist */, + BA7263AB742A567436F29B19F91CBAA6 /* Pods-SDAutoLayoutDemo-dummy.m */, + 4D2DE9CE23CC66D7877E5FF66543641C /* Pods-SDAutoLayoutDemo-resources.sh */, + A056E110BE4A9E1FB6F4F56408B6673E /* Pods-SDAutoLayoutDemo.debug.xcconfig */, + 9DA3D9475C63BCB54C71690000C70FA5 /* Pods-SDAutoLayoutDemo.release.xcconfig */, + ); + name = "Pods-SDAutoLayoutDemo"; + path = "Target Support Files/Pods-SDAutoLayoutDemo"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 199759A5BFD984F04AE7277DEE60F35C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 8467584A123E489CFDDCE25FBA5ED5BD /* MJRefresh.h in Headers */, + AFE76A2E342556AAD4024E271AE43D08 /* MJRefreshAutoFooter.h in Headers */, + C0A5116438CF1E572A0614633DBF6285 /* MJRefreshAutoGifFooter.h in Headers */, + 69E23641A721D713CB1B2A3630551C71 /* MJRefreshAutoNormalFooter.h in Headers */, + 0CD04A66475509E2CCCF1EB3E0E16F9D /* MJRefreshAutoStateFooter.h in Headers */, + C6D3C913D0BFA477E8D8FD295F6BE112 /* MJRefreshBackFooter.h in Headers */, + 2E1BE42B88FF31A7165EEE3839109939 /* MJRefreshBackGifFooter.h in Headers */, + 31AD36D9206CD116F8D628861DEED443 /* MJRefreshBackNormalFooter.h in Headers */, + A27022BC20DF22339F2399193568596B /* MJRefreshBackStateFooter.h in Headers */, + 54D67F5495742BC9CC14218097DFFA40 /* MJRefreshComponent.h in Headers */, + C3B840693F113D51224CD6488059F95A /* MJRefreshConfig.h in Headers */, + CF4D75977AB844A37599CBB0C2C9CB8F /* MJRefreshConst.h in Headers */, + 738E895DAE3B29BD045DE8CD80052C2B /* MJRefreshFooter.h in Headers */, + F2AD5323F4C116CD8E1F5646A63C4A7F /* MJRefreshGifHeader.h in Headers */, + 777420FB0FDC6639F29030FFDFFB17A3 /* MJRefreshHeader.h in Headers */, + 0BCD71E30C7DF98613F1DD5C4F764B38 /* MJRefreshNormalHeader.h in Headers */, + 7C2726DDF4049D57BDA3AF1A186DD463 /* MJRefreshNormalTrailer.h in Headers */, + 39C2C017D796E4FB119BDCF961D0420D /* MJRefreshStateHeader.h in Headers */, + 7B6BBB7188C6D1E096B968D9695738DA /* MJRefreshStateTrailer.h in Headers */, + B1D7360D931F74AA5112E512EAFEB3F9 /* MJRefreshTrailer.h in Headers */, + 4EAC0312FECAE1F799332014E31CDAB2 /* NSBundle+MJRefresh.h in Headers */, + F6720F62AA31A8FF4199BEC3FF9668C7 /* UICollectionViewLayout+MJRefresh.h in Headers */, + 6838AC3B59FDBF1B5563694CAB4E0D03 /* UIScrollView+MJExtension.h in Headers */, + 8CEFA09CDD8DAB24DEACCD6575182441 /* UIScrollView+MJRefresh.h in Headers */, + 0AE1DF496E0C62E1C9116F4CA6549E24 /* UIView+MJExtension.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 46E7B48FD0BB3A73F2BB5C9DF4FFB38A /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 478FE79D41AF5B22A15943E850B80546 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 8612D342953F3A933EF8F8D6F6DA45EB /* AFAutoPurgingImageCache.h in Headers */, + 941FA59066C026B8AE1C319001603AA6 /* AFCompatibilityMacros.h in Headers */, + D85E13EC2DA7F3189D8630870A5D976C /* AFHTTPSessionManager.h in Headers */, + AB0DBB06EBD36910FAE7CB5FC85601B6 /* AFImageDownloader.h in Headers */, + D3B8A3E487D4986375B17E4B711ACF53 /* AFNetworkActivityIndicatorManager.h in Headers */, + F5AB2C8AB21989F82AE542617552BF84 /* AFNetworking.h in Headers */, + 26200CE7EBEC065B7E2AC8078FBC0986 /* AFNetworkReachabilityManager.h in Headers */, + CD5EE6ED19DDD6B27E4438AD4B96C71E /* AFSecurityPolicy.h in Headers */, + 1DC85BEB11E6F7A10E4661BC15B5ECEA /* AFURLRequestSerialization.h in Headers */, + 4361C66728ECD7846FF4AC82BFDC0E49 /* AFURLResponseSerialization.h in Headers */, + 8D5AB075BC93F3D732E76B622B65CE31 /* AFURLSessionManager.h in Headers */, + 3305B9CFADF6A27973327DDCF48E22A6 /* UIActivityIndicatorView+AFNetworking.h in Headers */, + 09D41A7EE420D9EF74DA6D16BB1D18AF /* UIButton+AFNetworking.h in Headers */, + E3B9C0B78AAE4F7807FEFB75FB4955F3 /* UIImageView+AFNetworking.h in Headers */, + 62C99E03D053C1C36A210BCEBD56A266 /* UIKit+AFNetworking.h in Headers */, + E67888EAD9EE8BE91ED47958C9CB71D7 /* UIProgressView+AFNetworking.h in Headers */, + 53352ED6438EAFAB849541F71E3D3B6B /* UIRefreshControl+AFNetworking.h in Headers */, + 5C46F48BC9AB6370CA32A2788D86B3F0 /* WKWebView+AFNetworking.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FA6DD952364BBE585566957538322200 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 51CC9E2D32B32BFB24C402CEE6F920B7 /* MJExtension.h in Headers */, + A035ACD7D34FB30B305449A08999B18C /* MJExtensionConst.h in Headers */, + CAC6DA685CB98BF637741801C621E851 /* MJFoundation.h in Headers */, + F5712A4AD44745A2BA6E03C643B71161 /* MJProperty.h in Headers */, + 5977DE82E0CF1E6822D635598DBC0134 /* MJPropertyKey.h in Headers */, + A9E58B3E2D7A002DD5C679FF71A87FAC /* MJPropertyType.h in Headers */, + AB46F742EF940DEB0729F7FAE42C19CF /* NSObject+MJClass.h in Headers */, + B99C11C03CFBB3B40843D3B5715BA57A /* NSObject+MJCoding.h in Headers */, + A96362455716ED1326308EB49D324D1E /* NSObject+MJKeyValue.h in Headers */, + 4C8AC7BE0D662BCD3C63D0B6675FFAE5 /* NSObject+MJProperty.h in Headers */, + 998AE7A052BBF04F10810E171DFCDF89 /* NSString+MJExtension.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 0130B3724283586C0E9D2A112D4F2AA1 /* AFNetworking */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6030B64160BACF6FF0C4822489A2B046 /* Build configuration list for PBXNativeTarget "AFNetworking" */; + buildPhases = ( + 478FE79D41AF5B22A15943E850B80546 /* Headers */, + 8477AAE54B57C0840E8177AB71C21006 /* Sources */, + D39F59C65EF8C6E73DBFB8B6B9821774 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AFNetworking; + productName = AFNetworking; + productReference = A4FA15D44DF6BAC7550EDEED10862AA3 /* AFNetworking */; + productType = "com.apple.product-type.library.static"; + }; + 4D3BA58D0583DF37575CACAB3DDADC85 /* MJExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3D31F7327F68CF302624E3DAE493C659 /* Build configuration list for PBXNativeTarget "MJExtension" */; + buildPhases = ( + FA6DD952364BBE585566957538322200 /* Headers */, + CA8F9FF303714731B12265A27D0919FF /* Sources */, + A49203409C8C7E2AC1D2E332FDE48A56 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MJExtension; + productName = MJExtension; + productReference = 2B276B0A79173A1D6E83C9B4FB9A4A57 /* MJExtension */; + productType = "com.apple.product-type.library.static"; + }; + 6868056D761E163D10FDAF8CF1C4D9B8 /* MJRefresh */ = { + isa = PBXNativeTarget; + buildConfigurationList = B9538AEA18585939DEF591DEFEEF4C38 /* Build configuration list for PBXNativeTarget "MJRefresh" */; + buildPhases = ( + 199759A5BFD984F04AE7277DEE60F35C /* Headers */, + 07842D8E208C9CB687817AF7B8A4A84F /* Sources */, + D2403152A4624F56E21460C2F15CB1E3 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MJRefresh; + productName = MJRefresh; + productReference = E49D6D248DD1CEE584E6776B9164A1B2 /* MJRefresh */; + productType = "com.apple.product-type.library.static"; + }; + CEE938C248E565681A188336598B4092 /* Pods-SDAutoLayoutDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7D808A4774E02694CAB64D3EB65E534F /* Build configuration list for PBXNativeTarget "Pods-SDAutoLayoutDemo" */; + buildPhases = ( + 46E7B48FD0BB3A73F2BB5C9DF4FFB38A /* Headers */, + 19618D98CC8B823552E1749845BB19A7 /* Sources */, + 8330E8FEEAED9A815409C4C81CDE2F37 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 6661814D2C802A486A999245A82C51F5 /* PBXTargetDependency */, + 969264F2D2AF3E70C0F30DE982C57BD1 /* PBXTargetDependency */, + F7E681A6233EC93292670AFD064FC048 /* PBXTargetDependency */, + ); + name = "Pods-SDAutoLayoutDemo"; + productName = "Pods-SDAutoLayoutDemo"; + productReference = C35B9E9E4AFF81919106A9EEE9E2B66E /* Pods-SDAutoLayoutDemo */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BFDFE7DC352907FC980B868725387E98 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1600; + LastUpgradeCheck = 1600; + }; + buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + Base, + en, + ); + mainGroup = CF1408CF629C7361332E53B88F7BD30C; + minimizedProjectReferenceProxies = 0; + preferredProjectObjectVersion = 77; + productRefGroup = E15D84178FEECBC822D9247D208A04B1 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 0130B3724283586C0E9D2A112D4F2AA1 /* AFNetworking */, + 4D3BA58D0583DF37575CACAB3DDADC85 /* MJExtension */, + 6868056D761E163D10FDAF8CF1C4D9B8 /* MJRefresh */, + CEE938C248E565681A188336598B4092 /* Pods-SDAutoLayoutDemo */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 07842D8E208C9CB687817AF7B8A4A84F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E3E3CD02A1D63C1FBA3ECE26BDB1232C /* MJRefresh-dummy.m in Sources */, + 431F896C247769F1FBFCFBD9F0902BBD /* MJRefreshAutoFooter.m in Sources */, + EAEC57FFA3A95DDF2717F6FA603CA091 /* MJRefreshAutoGifFooter.m in Sources */, + A068F6E8B034427D3929E6ED1AD9F817 /* MJRefreshAutoNormalFooter.m in Sources */, + 89A1B2A98CB89A3DFE3D0683D962AF9B /* MJRefreshAutoStateFooter.m in Sources */, + 5AAB42F4086F8CFC26F5899327212BBB /* MJRefreshBackFooter.m in Sources */, + 8198BC44FDB13D318FB6564A1EBA5268 /* MJRefreshBackGifFooter.m in Sources */, + C8926B455D818E7A479AEA923F164BFB /* MJRefreshBackNormalFooter.m in Sources */, + C4EF0E376298314E4E4A82ED0CBABA65 /* MJRefreshBackStateFooter.m in Sources */, + 361F7DCF440C4EC48CD10FFABBDEE743 /* MJRefreshComponent.m in Sources */, + 93D175A192F75135B7FC6ABEF554C0EB /* MJRefreshConfig.m in Sources */, + C21248B537FFDE05F201F4BF7FD6C4AC /* MJRefreshConst.m in Sources */, + C4339FCB9711A75709762DFEBCF9EFF3 /* MJRefreshFooter.m in Sources */, + C8E31E90F50206AE0D7D07CC136FF605 /* MJRefreshGifHeader.m in Sources */, + 2A09213164D9FDC674BA055304AD6279 /* MJRefreshHeader.m in Sources */, + 52D2291C1A49AA4224E9249E6D7A4B82 /* MJRefreshNormalHeader.m in Sources */, + 5AED0F2E944E2DE57022A4583AE9181D /* MJRefreshNormalTrailer.m in Sources */, + 5A2ED8C55D0F4B1EC69EEEA477C4075F /* MJRefreshStateHeader.m in Sources */, + CD4F72018858093DC10B340CC6E1748F /* MJRefreshStateTrailer.m in Sources */, + 669F0E8A5471E407F975A6969C2FE26F /* MJRefreshTrailer.m in Sources */, + 5B64FB43132E271390B5522B91D9F7F4 /* NSBundle+MJRefresh.m in Sources */, + 156DE14C2B0619B71A332ADBA2191821 /* UICollectionViewLayout+MJRefresh.m in Sources */, + 43EC74C3510CCD40EC682CE049D006E7 /* UIScrollView+MJExtension.m in Sources */, + 6ECDC11A63A22C0A44C470DE88DDCBA5 /* UIScrollView+MJRefresh.m in Sources */, + 3EA95093247B6333E5D1E009B2D52225 /* UIView+MJExtension.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 19618D98CC8B823552E1749845BB19A7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0EFE34C85A3BA34F9F28AEAEBE37F584 /* Pods-SDAutoLayoutDemo-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8477AAE54B57C0840E8177AB71C21006 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 80A6240A8ABF4B8CABC03E1DA9C071E5 /* AFAutoPurgingImageCache.m in Sources */, + 233DF733DC76A9E25DF3E7648BEC18FD /* AFHTTPSessionManager.m in Sources */, + 7D9C286227AE8813BF98BB65FB5B88F3 /* AFImageDownloader.m in Sources */, + C6BA08C452F5644C1C10C4F736D8ECB5 /* AFNetworkActivityIndicatorManager.m in Sources */, + D0FE7467F4D4E2A2662803EF1AC2EC33 /* AFNetworking-dummy.m in Sources */, + 6314D821A974928C0B83A673140BCBBE /* AFNetworkReachabilityManager.m in Sources */, + CE873195B4A713E8806F7228F853DDB0 /* AFSecurityPolicy.m in Sources */, + FF2861FA55C81C274B762AD72595F73C /* AFURLRequestSerialization.m in Sources */, + 85CBF33D23E45FEDE3FC54F5C9FA6D98 /* AFURLResponseSerialization.m in Sources */, + 6479C8BAFC394EE0F28C638381EB5ED0 /* AFURLSessionManager.m in Sources */, + EE8045B4438C4F1FE89FF4C197939D6B /* UIActivityIndicatorView+AFNetworking.m in Sources */, + E4963C2D2E89735EA6DCFB5D55C9E602 /* UIButton+AFNetworking.m in Sources */, + F6A19CEF9483C5B5E895D568AF9D4425 /* UIImageView+AFNetworking.m in Sources */, + C86E24CB5791A24236900785B2743CD9 /* UIProgressView+AFNetworking.m in Sources */, + A5A692730C77ACCC9083386CA457EC6A /* UIRefreshControl+AFNetworking.m in Sources */, + B2FC4AAA91F18341302B66A13C831517 /* WKWebView+AFNetworking.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CA8F9FF303714731B12265A27D0919FF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7BFB64C93924BF38CEB19F11159F2E12 /* MJExtension-dummy.m in Sources */, + A8EF9CBCC1901BCAC02515355BCFC841 /* MJExtensionConst.m in Sources */, + 66D80BDC31AC2884F48E336C734FD559 /* MJFoundation.m in Sources */, + 197F1A252AC47433212E2BA46203DFBA /* MJProperty.m in Sources */, + F77875D5398D97026F2803DD5D424D91 /* MJPropertyKey.m in Sources */, + 9E161F7AFD120CDD9634ECB410955A86 /* MJPropertyType.m in Sources */, + 5F292B693B03E8A35BF75CF1F8CD1073 /* NSObject+MJClass.m in Sources */, + 9FFBD22E2CA7C2EC43ED70F2FF0302EA /* NSObject+MJCoding.m in Sources */, + C8316539D8D2A13C0120613F6CC87E8A /* NSObject+MJKeyValue.m in Sources */, + D2BD5F363EAD7054BDAF0D1C2B808444 /* NSObject+MJProperty.m in Sources */, + D204022EEE7B262F7AE68D420067B0B1 /* NSString+MJExtension.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 6661814D2C802A486A999245A82C51F5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = AFNetworking; + target = 0130B3724283586C0E9D2A112D4F2AA1 /* AFNetworking */; + targetProxy = 41034314F56511568A806D34371CD4A7 /* PBXContainerItemProxy */; + }; + 969264F2D2AF3E70C0F30DE982C57BD1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = MJExtension; + target = 4D3BA58D0583DF37575CACAB3DDADC85 /* MJExtension */; + targetProxy = 290332588A840352BAD96B38CE160BE8 /* PBXContainerItemProxy */; + }; + F7E681A6233EC93292670AFD064FC048 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = MJRefresh; + target = 6868056D761E163D10FDAF8CF1C4D9B8 /* MJRefresh */; + targetProxy = 3B3C7C1BE7C5D0FB4D78B21B2ADF8E20 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 25AD9454612BF454A1E3DC4CD4FA8C6D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_DEBUG=1", + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; + 2D23FA3B1E43BC918497641F9DF44F53 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A056E110BE4A9E1FB6F4F56408B6673E /* Pods-SDAutoLayoutDemo.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + ENABLE_MODULE_VERIFIER = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MACH_O_TYPE = staticlib; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 371B66617A2BEA932C8BD9DCA3CD9D9D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9DA3D9475C63BCB54C71690000C70FA5 /* Pods-SDAutoLayoutDemo.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + ENABLE_MODULE_VERIFIER = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MACH_O_TYPE = staticlib; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 5BCE4B09B9CEBA9D383D3B8AD0D034B7 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = CF5A1001D8B8AE656FE6A607016C4A76 /* AFNetworking.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + ENABLE_MODULE_VERIFIER = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_PREFIX_HEADER = "Target Support Files/AFNetworking/AFNetworking-prefix.pch"; + GENERATE_INFOPLIST_FILE = NO; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_MODULE_NAME = AFNetworking; + PRODUCT_NAME = AFNetworking; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_INSTALL_OBJC_HEADER = YES; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 616B9BDBEA366047F5244933ECBAEACC /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 06675CBD36A1919548FA8B110AE0C9B6 /* MJRefresh.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + ENABLE_MODULE_VERIFIER = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_PREFIX_HEADER = "Target Support Files/MJRefresh/MJRefresh-prefix.pch"; + GENERATE_INFOPLIST_FILE = NO; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_MODULE_NAME = MJRefresh; + PRODUCT_NAME = MJRefresh; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_INSTALL_OBJC_HEADER = YES; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 6A80F41F107B7575C772C56A3D7797B0 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 30D0AE3AD19B55AAE9EF9CA15E56066D /* MJExtension.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + ENABLE_MODULE_VERIFIER = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_PREFIX_HEADER = "Target Support Files/MJExtension/MJExtension-prefix.pch"; + GENERATE_INFOPLIST_FILE = NO; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_MODULE_NAME = MJExtension; + PRODUCT_NAME = MJExtension; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_INSTALL_OBJC_HEADER = YES; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CA547D2C7E9A8A153DC2B27FBE00B112 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_RELEASE=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Release; + }; + CBC236EE45766765127C24AB10189610 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4CD11DCC5DDD0394C88F94E177BD38EF /* AFNetworking.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + ENABLE_MODULE_VERIFIER = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_PREFIX_HEADER = "Target Support Files/AFNetworking/AFNetworking-prefix.pch"; + GENERATE_INFOPLIST_FILE = NO; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_MODULE_NAME = AFNetworking; + PRODUCT_NAME = AFNetworking; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_INSTALL_OBJC_HEADER = YES; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + E44DC7F2FF744E1EFB667419EE8138A3 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A605EBFA7329DD2A0FE04AE6BFB39E25 /* MJRefresh.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + ENABLE_MODULE_VERIFIER = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_PREFIX_HEADER = "Target Support Files/MJRefresh/MJRefresh-prefix.pch"; + GENERATE_INFOPLIST_FILE = NO; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_MODULE_NAME = MJRefresh; + PRODUCT_NAME = MJRefresh; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_INSTALL_OBJC_HEADER = YES; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + F02A0E9409DC1CFD8DFA25017ED69DBF /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0E29D0B31DF146B7ED89B05333256F9A /* MJExtension.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + ENABLE_MODULE_VERIFIER = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_PREFIX_HEADER = "Target Support Files/MJExtension/MJExtension-prefix.pch"; + GENERATE_INFOPLIST_FILE = NO; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_MODULE_NAME = MJExtension; + PRODUCT_NAME = MJExtension; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_INSTALL_OBJC_HEADER = YES; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 3D31F7327F68CF302624E3DAE493C659 /* Build configuration list for PBXNativeTarget "MJExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6A80F41F107B7575C772C56A3D7797B0 /* Debug */, + F02A0E9409DC1CFD8DFA25017ED69DBF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 25AD9454612BF454A1E3DC4CD4FA8C6D /* Debug */, + CA547D2C7E9A8A153DC2B27FBE00B112 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6030B64160BACF6FF0C4822489A2B046 /* Build configuration list for PBXNativeTarget "AFNetworking" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CBC236EE45766765127C24AB10189610 /* Debug */, + 5BCE4B09B9CEBA9D383D3B8AD0D034B7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7D808A4774E02694CAB64D3EB65E534F /* Build configuration list for PBXNativeTarget "Pods-SDAutoLayoutDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D23FA3B1E43BC918497641F9DF44F53 /* Debug */, + 371B66617A2BEA932C8BD9DCA3CD9D9D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B9538AEA18585939DEF591DEFEEF4C38 /* Build configuration list for PBXNativeTarget "MJRefresh" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 616B9BDBEA366047F5244933ECBAEACC /* Debug */, + E44DC7F2FF744E1EFB667419EE8138A3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; +} diff --git a/Pods/Pods.xcodeproj/xcuserdata/gaoshaodong.xcuserdatad/xcschemes/Pods-SDAutoLayoutDemo.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/gaoshaodong.xcuserdatad/xcschemes/Pods-SDAutoLayoutDemo.xcscheme new file mode 100644 index 0000000..3d67dd5 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/gaoshaodong.xcuserdatad/xcschemes/Pods-SDAutoLayoutDemo.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/gaoshaodong.xcuserdatad/xcschemes/xcschememanagement.plist b/Pods/Pods.xcodeproj/xcuserdata/gaoshaodong.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..ac68cf1 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/gaoshaodong.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,18 @@ + + + + + SchemeUserState + + Pods-SDAutoLayoutDemo.xcscheme + + isShown + + orderHint + 1 + + + SuppressBuildableAutocreation + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/AFNetworking.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/AFNetworking.xcscheme new file mode 100644 index 0000000..bdebd84 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/AFNetworking.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/MJExtension.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/MJExtension.xcscheme new file mode 100644 index 0000000..8d0e3a3 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/MJExtension.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/MJRefresh.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/MJRefresh.xcscheme new file mode 100644 index 0000000..1e1e076 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/MJRefresh.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/Pods-SDAutoLayoutDemo.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/Pods-SDAutoLayoutDemo.xcscheme new file mode 100644 index 0000000..4c1c793 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/Pods-SDAutoLayoutDemo.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/xcschememanagement.plist b/Pods/Pods.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..98df082 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,39 @@ + + + + + SchemeUserState + + AFNetworking.xcscheme + + isShown + + orderHint + 1 + + MJExtension.xcscheme + + isShown + + orderHint + 3 + + MJRefresh.xcscheme + + isShown + + orderHint + 4 + + Pods-SDAutoLayoutDemo.xcscheme + + isShown + + orderHint + 5 + + + SuppressBuildableAutocreation + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/lixiang.xcuserdatad/xcschemes/xcschememanagement.plist b/Pods/Pods.xcodeproj/xcuserdata/lixiang.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..874fa4d --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/lixiang.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,24 @@ + + + + + SchemeUserState + + OOMDetector.xcscheme + + orderHint + 3 + + Pods-SDAutoLayoutDemo.xcscheme + + orderHint + 4 + + fishhook.xcscheme + + orderHint + 2 + + + + diff --git a/Pods/Target Support Files/AFNetworking/AFNetworking-dummy.m b/Pods/Target Support Files/AFNetworking/AFNetworking-dummy.m new file mode 100644 index 0000000..6a29cf8 --- /dev/null +++ b/Pods/Target Support Files/AFNetworking/AFNetworking-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_AFNetworking : NSObject +@end +@implementation PodsDummy_AFNetworking +@end diff --git a/Pods/Target Support Files/AFNetworking/AFNetworking-prefix.pch b/Pods/Target Support Files/AFNetworking/AFNetworking-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/AFNetworking/AFNetworking-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/AFNetworking/AFNetworking.debug.xcconfig b/Pods/Target Support Files/AFNetworking/AFNetworking.debug.xcconfig new file mode 100644 index 0000000..b289e04 --- /dev/null +++ b/Pods/Target Support Files/AFNetworking/AFNetworking.debug.xcconfig @@ -0,0 +1,13 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/AFNetworking" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/AFNetworking +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = com.alamofire.AFNetworking +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/AFNetworking/AFNetworking.release.xcconfig b/Pods/Target Support Files/AFNetworking/AFNetworking.release.xcconfig new file mode 100644 index 0000000..b289e04 --- /dev/null +++ b/Pods/Target Support Files/AFNetworking/AFNetworking.release.xcconfig @@ -0,0 +1,13 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/AFNetworking" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/AFNetworking +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = com.alamofire.AFNetworking +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/MJExtension/MJExtension-dummy.m b/Pods/Target Support Files/MJExtension/MJExtension-dummy.m new file mode 100644 index 0000000..79c234e --- /dev/null +++ b/Pods/Target Support Files/MJExtension/MJExtension-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_MJExtension : NSObject +@end +@implementation PodsDummy_MJExtension +@end diff --git a/Pods/Target Support Files/MJExtension/MJExtension-prefix.pch b/Pods/Target Support Files/MJExtension/MJExtension-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/MJExtension/MJExtension-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/MJExtension/MJExtension.debug.xcconfig b/Pods/Target Support Files/MJExtension/MJExtension.debug.xcconfig new file mode 100644 index 0000000..28ec87a --- /dev/null +++ b/Pods/Target Support Files/MJExtension/MJExtension.debug.xcconfig @@ -0,0 +1,13 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MJExtension +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/MJExtension" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/MJExtension" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/MJExtension +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/MJExtension/MJExtension.release.xcconfig b/Pods/Target Support Files/MJExtension/MJExtension.release.xcconfig new file mode 100644 index 0000000..28ec87a --- /dev/null +++ b/Pods/Target Support Files/MJExtension/MJExtension.release.xcconfig @@ -0,0 +1,13 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MJExtension +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/MJExtension" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/MJExtension" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/MJExtension +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/MJRefresh/MJRefresh-dummy.m b/Pods/Target Support Files/MJRefresh/MJRefresh-dummy.m new file mode 100644 index 0000000..d43259d --- /dev/null +++ b/Pods/Target Support Files/MJRefresh/MJRefresh-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_MJRefresh : NSObject +@end +@implementation PodsDummy_MJRefresh +@end diff --git a/Pods/Target Support Files/MJRefresh/MJRefresh-prefix.pch b/Pods/Target Support Files/MJRefresh/MJRefresh-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/MJRefresh/MJRefresh-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/MJRefresh/MJRefresh.debug.xcconfig b/Pods/Target Support Files/MJRefresh/MJRefresh.debug.xcconfig new file mode 100644 index 0000000..cb46d63 --- /dev/null +++ b/Pods/Target Support Files/MJRefresh/MJRefresh.debug.xcconfig @@ -0,0 +1,13 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/MJRefresh" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/MJRefresh" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/MJRefresh +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/MJRefresh/MJRefresh.release.xcconfig b/Pods/Target Support Files/MJRefresh/MJRefresh.release.xcconfig new file mode 100644 index 0000000..cb46d63 --- /dev/null +++ b/Pods/Target Support Files/MJRefresh/MJRefresh.release.xcconfig @@ -0,0 +1,13 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/MJRefresh" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/MJRefresh" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/MJRefresh +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo-acknowledgements.markdown b/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo-acknowledgements.markdown new file mode 100644 index 0000000..dff7722 --- /dev/null +++ b/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo-acknowledgements.markdown @@ -0,0 +1,72 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## AFNetworking + +Copyright (c) 2011-2020 Alamofire Software Foundation (http://alamofire.org/) + +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. + + +## MJExtension + +Copyright (c) 2013-2019 MJExtension (https://github.com/CoderMJLee/MJExtension) + +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. + + +## MJRefresh + +Copyright (c) 2013-2015 MJRefresh (https://github.com/CoderMJLee/MJRefresh) + +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. + +Generated by CocoaPods - https://cocoapods.org diff --git a/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo-acknowledgements.plist b/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo-acknowledgements.plist new file mode 100644 index 0000000..c5d7169 --- /dev/null +++ b/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo-acknowledgements.plist @@ -0,0 +1,116 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2011-2020 Alamofire Software Foundation (http://alamofire.org/) + +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. + + License + MIT + Title + AFNetworking + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2013-2019 MJExtension (https://github.com/CoderMJLee/MJExtension) + +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. + + License + MIT + Title + MJExtension + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2013-2015 MJRefresh (https://github.com/CoderMJLee/MJRefresh) + +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. + + License + MIT + Title + MJRefresh + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo-dummy.m b/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo-dummy.m new file mode 100644 index 0000000..4f9a9cc --- /dev/null +++ b/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_SDAutoLayoutDemo : NSObject +@end +@implementation PodsDummy_Pods_SDAutoLayoutDemo +@end diff --git a/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo-frameworks.sh b/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo-frameworks.sh new file mode 100755 index 0000000..893c16a --- /dev/null +++ b/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo-frameworks.sh @@ -0,0 +1,84 @@ +#!/bin/sh +set -e + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" + +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink "${source}")" + fi + + # use filter instead of exclude so missing patterns dont' throw errors + echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identitiy + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\"" + /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1" + fi +} + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + # Get architectures for current file + archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" + stripped="" + for arch in $archs; do + if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" || exit 1 + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi +} + diff --git a/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo-resources.sh b/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo-resources.sh new file mode 100755 index 0000000..c75bc1d --- /dev/null +++ b/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo-resources.sh @@ -0,0 +1,129 @@ +#!/bin/sh +set -e +set -u +set -o pipefail + +function on_error { + echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" +} +trap 'on_error $LINENO' ERR + +if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then + # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy + # resources to, so exit 0 (signalling the script phase was successful). + exit 0 +fi + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + +RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt +> "$RESOURCES_TO_COPY" + +XCASSET_FILES=() + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +case "${TARGETED_DEVICE_FAMILY:-}" in + 1,2) + TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" + ;; + 1) + TARGET_DEVICE_ARGS="--target-device iphone" + ;; + 2) + TARGET_DEVICE_ARGS="--target-device ipad" + ;; + 3) + TARGET_DEVICE_ARGS="--target-device tv" + ;; + 4) + TARGET_DEVICE_ARGS="--target-device watch" + ;; + *) + TARGET_DEVICE_ARGS="--target-device mac" + ;; +esac + +install_resource() +{ + if [[ "$1" = /* ]] ; then + RESOURCE_PATH="$1" + else + RESOURCE_PATH="${PODS_ROOT}/$1" + fi + if [[ ! -e "$RESOURCE_PATH" ]] ; then + cat << EOM +error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. +EOM + exit 1 + fi + case $RESOURCE_PATH in + *.storyboard) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true + ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} + ;; + *.xib) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true + ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} + ;; + *.framework) + echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true + mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + ;; + *.xcdatamodel) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" + ;; + *.xcdatamodeld) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" + ;; + *.xcmappingmodel) + echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true + xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" + ;; + *.xcassets) + ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" + XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") + ;; + *) + echo "$RESOURCE_PATH" || true + echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" + ;; + esac +} +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_resource "${PODS_ROOT}/MJRefresh/MJRefresh/MJRefresh.bundle" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_resource "${PODS_ROOT}/MJRefresh/MJRefresh/MJRefresh.bundle" +fi + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then + mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +fi +rm -f "$RESOURCES_TO_COPY" + +if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] +then + # Find all other xcassets (this unfortunately includes those of path pods and other targets). + OTHER_XCASSETS=$(find -L "$PWD" -iname "*.xcassets" -type d) + while read line; do + if [[ $line != "${PODS_ROOT}*" ]]; then + XCASSET_FILES+=("$line") + fi + done <<<"$OTHER_XCASSETS" + + if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then + printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + else + printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" + fi +fi diff --git a/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo.debug.xcconfig b/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo.debug.xcconfig new file mode 100644 index 0000000..cf42be2 --- /dev/null +++ b/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo.debug.xcconfig @@ -0,0 +1,12 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/MJExtension" "${PODS_ROOT}/Headers/Public/MJRefresh" +LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" +OTHER_LDFLAGS = $(inherited) -ObjC -l"AFNetworking" -l"MJExtension" -l"MJRefresh" +OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo.release.xcconfig b/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo.release.xcconfig new file mode 100644 index 0000000..cf42be2 --- /dev/null +++ b/Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo.release.xcconfig @@ -0,0 +1,12 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/MJExtension" "${PODS_ROOT}/Headers/Public/MJRefresh" +LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" +OTHER_LDFLAGS = $(inherited) -ObjC -l"AFNetworking" -l"MJExtension" -l"MJRefresh" +OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Pods-SDAutoLayoutDemoTests/Pods-SDAutoLayoutDemoTests-dummy.m b/Pods/Target Support Files/Pods-SDAutoLayoutDemoTests/Pods-SDAutoLayoutDemoTests-dummy.m new file mode 100644 index 0000000..ae6cb38 --- /dev/null +++ b/Pods/Target Support Files/Pods-SDAutoLayoutDemoTests/Pods-SDAutoLayoutDemoTests-dummy.m @@ -0,0 +1,5 @@ +// CocoaPods generates this file when you run `pod install`. +@interface PodsDummy_Pods_SDAutoLayoutDemoTests : NSObject +@end +@implementation PodsDummy_Pods_SDAutoLayoutDemoTests +@end diff --git a/Pods/Target Support Files/Pods-SDAutoLayoutDemoTests/Pods-SDAutoLayoutDemoTests.debug.xcconfig b/Pods/Target Support Files/Pods-SDAutoLayoutDemoTests/Pods-SDAutoLayoutDemoTests.debug.xcconfig new file mode 100644 index 0000000..9d279e7 --- /dev/null +++ b/Pods/Target Support Files/Pods-SDAutoLayoutDemoTests/Pods-SDAutoLayoutDemoTests.debug.xcconfig @@ -0,0 +1,10 @@ +// Test target uses inherit! :search_paths — headers only, no separate Pods static library. +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO +CLANG_ENABLE_MODULES = YES +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/MJExtension" "${PODS_ROOT}/Headers/Public/MJRefresh" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Pods/Target Support Files/Pods-SDAutoLayoutDemoTests/Pods-SDAutoLayoutDemoTests.release.xcconfig b/Pods/Target Support Files/Pods-SDAutoLayoutDemoTests/Pods-SDAutoLayoutDemoTests.release.xcconfig new file mode 100644 index 0000000..9d279e7 --- /dev/null +++ b/Pods/Target Support Files/Pods-SDAutoLayoutDemoTests/Pods-SDAutoLayoutDemoTests.release.xcconfig @@ -0,0 +1,10 @@ +// Test target uses inherit! :search_paths — headers only, no separate Pods static library. +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO +CLANG_ENABLE_MODULES = YES +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/MJExtension" "${PODS_ROOT}/Headers/Public/MJRefresh" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/README.md b/README.md index 3f58160..e80c5d1 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,110 @@ -# SDAutoLayout - -☆☆ SDAutoLayout 视频教程:http://www.letv.com/ptv/vplay/24038772.html ☆☆ +# SDAutoLayout(一行代码搞定自动布局!) 一行代码搞定自动布局!致力于做最简单易用的Autolayout库。The most easy way for autolayout. +### 公众号 +IMG_9989 -# ☆新增:cell高度自适应 + label文字自适应☆ -![](http://ww2.sinaimg.cn/bmiddle/9b8146edgw1eya1jv951ig208c0etqv5.gif) - -## >>>>> tableview cell 自动高度设置只需要3步 - - 1. >> 设置cell高度自适应: - // cell布局设置好之后调用此方法就可以实现高度自适应(注意:如果用高度自适应则不要再以cell的底边为参照去布局其子view) - [cell setupAutoHeightWithBottomView:_view4 bottomMargin:10]; - - 2. >> 设置 tableview 数据源和代理 - - - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section - { - // 2.1 注册模型cell - [self.tableView startAutoCellHeightWithCellClass:“cell类名” contentViewWidth:“contentview宽度”]; +## 众多公司和个人开发者已经使用本库布局: +SDAutoLayout使用者开发的部分app截图 http://www.jianshu.com/p/9bc04d3effb8 - return _rowCount; - } - - - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath - { - // 2.2 根据模型取得cell高度 - return [self.tableView cellHeightForIndexPath:indexPath model:“model” keyPath:@"model属性名"]; - } +## 技术支持(QQ交流群): + +497140713(1群) 519489682(2群已满) + +## Pod支持: + +支持pod: pod 'SDAutoLayout', '~> 2.1.3' + +## 更新记录: +2018.11.28 -- 修复部分开发者违规调用load的super方法导致自动布局失效的问题 + +2018.07.02 -- 修复了部分布局组合下view居中布局失效bug + +2017.11.11 -- 实现控制富文本可显示行数功能 + +2017.11.11 -- 解决朋友圈demo在iOS11上文字收折或者展开时出现cell跳动问题 + +2017.06.26 -- 解决部分开发者反应因出现“UITableViewCellContentView”而导致应用审核被拒问题 + +2016.08.12 -- 实现在tableview插入新的cell数据时自动缓存管理 + +2016.06.30 -- 增加多参照view的leftSpaceToView和topSpaceToView约束,例:topSpaceToView(@[self.view3, self.view4], 30) + +2016.06.24 -- 修复给button设置约束时在iOS8.x系统中出现的崩溃问题;发布2.1.2版本 + +2016.06.23 -- 实现删除某行cell时自动调整height缓存 + +2016.05.16 -- 修复用xib生成的view出现的部分约束失效问题(发布pod2.0.0版本) + +2016.05.15 -- 增加设置偏移量offset功能 + +2016.04.30 -- 修复之前button作为父视图时内部控件不能自动布局问题 + +2016.04.05 -- 修复宽度自适应label在重用时候偶尔出现的宽度计算不准确的问题(发布pod1.51版本) + +2016.03.23 -- 升级了缓存机制,新版本在tableview滑动cell时候流畅度和性能提升20%以上(发布pod1.50版本) + +2016.01.23 -- 增加label对attributedString的内容自适应 + +2016.01.21 -- 实现tableview局部刷新cell高度缓存的自动管理 + +2016.01.20 -- demo适配在ios7上的屏幕旋转问题 + +2016.01.18 -- 推出“普通简化版”tableview的cell自动高度方法(推荐使用),原来的需2步设置的普通版方法将标记过期 + +2016.01.13 -- 增加在不确定bottom view的情况下的cell高度自适应方法 + +2016.01.07 -- 1.增加 scrollview 横向内容自适应功能;2.增加view宽高相等的功能 + +2016.01.03 -- 增加任何类型对象都可以实现一行代码搞定cell高度自适应;增加文档注释 + +2015.12.08 -- 重大升级:1.支持scrollview内容自适应;2.任意添加或者修改约束不冲突;3.性能提升40%以上;4.添加最大、最小宽高约束 + + +## 视频教程: +☆☆ SDAutoLayout 基础版视频教程:http://www.letv.com/ptv/vplay/24038772.html ☆☆ + +☆☆ SDAutoLayout 进阶版视频教程:http://www.letv.com/ptv/vplay/24381390.html ☆☆ + +☆☆ SDAutoLayout 原理简介视频教程:http://www.iqiyi.com/w_19rt0tec4p.html ☆☆ + +☆☆ SDAutoLayout 朋友圈demo视频教程:http://v.youku.com/v_show/id_XMTYzNzg2NzA0MA==.html ☆☆ + +## 部分SDAutoLayout的DEMO: + +完整微信Demo https://github.com/gsdios/GSD_WeiXin + +![](http://ww3.sinaimg.cn/mw690/9b8146edgw1f1nm3pziawg205u0a0qv5.gif)![](http://ww1.sinaimg.cn/bmiddle/9b8146edgw1f06aoe2umhg206e0b4u0x.gif)![](http://ww4.sinaimg.cn/bmiddle/9b8146edgw1ezal3smihcg206y0ciqv5.gif)![](http://ww3.sinaimg.cn/mw690/9b8146edgw1f1nm3lweg3g207s0dcu0x.gif)![](http://ww2.sinaimg.cn/mw690/9b8146edgw1f23irukb0qg207i0dwu0x.gif)![](http://ww2.sinaimg.cn/bmiddle/9b8146edgw1eya1jv951ig208c0etqv5.gif) + + + +# 用法简介 -# *********** 普通view的自动布局 *********** +## 普通view的自动布局: -## 摒弃复杂累赘的约束,利用运行时Runtime在合适的时机布局视图。 - -## 0.用法示例 - /* 用法一 */ +#### 用法示例 + 用法一 +``` _view.sd_layout .leftSpaceToView(self.view, 10) .topSpaceToView(self.view, 80) .heightIs(130) .widthRatioToView(self.view, 0.4); - - /* 用法二 (一行代码搞定,其实用法一也是一行代码) */ +``` + 用法二 (一行代码搞定,其实用法一也是一行代码) +``` _view.sd_layout.leftSpaceToView(self.view, 10).topSpaceToView(self.view,80).heightIs(130).widthRatioToView(self.view, 0.4); - - - >> UILabel文字自适应: - // autoHeightRatio() 传0则根据文字自动计算高度(传大于0的值则根据此数值设置高度和宽度的比值) - _label.sd_layout.autoHeightRatio(0); +``` + 综合示例 +``` ******************************************************************************* 注意:先把需要自动布局的view加入父view然后在进行自动布局,例: - + UIView *view0 = [UIView new]; UIView *view1 = [UIView new]; [self.view addSubview:view0]; @@ -72,21 +121,67 @@ .topEqualToView(view0) .heightRatioToView(view0, 1) .rightSpaceToView(self.view, 10); + ******************************************************************************* +``` -## 1.用法简析 +## UILabel文字自适应 +``` +// autoHeightRatio() 传0则根据文字自动计算高度(传大于0的值则根据此数值设置高度和宽度的比值) + _label.sd_layout.autoHeightRatio(0); +``` + +## tableview和cell高度自适应: -![](http://ww1.sinaimg.cn/mw690/9b8146edgw1ex4or5ixkjj20k60gw3zg.jpg) +#### 普通(简化)版【推荐使用】:tableview 高度自适应设置只需要2步 +``` + 1. >> 设置cell高度自适应: + // cell布局设置好之后调用此方法就可以实现高度自适应(注意:如果用高度自适应则不要再以cell的底边为参照去布局其子view) + [cell setupAutoHeightWithBottomView:_view4 bottomMargin:10]; +``` +``` + 2. >> 获取自动计算出的cell高度 + - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath + { + id model = self.modelsArray[indexPath.row]; + // 获取cell高度 + return [self.tableView cellHeightForIndexPath:indexPath model:model keyPath:@"model" cellClass:[DemoVC9Cell class] contentViewWidth:cellContentViewWith]; + } +``` + +#### 升级版(适应于cell条数少于100的tableview):tableview 高度自适应设置只需要2步 +``` + 1. >> 设置cell高度自适应: + // cell布局设置好之后调用此方法就可以实现高度自适应(注意:如果用高度自适应则不要再以cell的底边为参照去布局其子view) + + [cell setupAutoHeightWithBottomView:_view4 bottomMargin:10]; +``` +``` + 2. >> 获取自动计算出的cell高度 + + - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath + { + // 获取cell高度 + return [self cellHeightForIndexPath:indexPath cellContentViewWidth:[UIScreen mainScreen].bounds.size.width]; + } +``` + + +#### 自动布局用法简析 + +![](http://ww1.sinaimg.cn/mw690/9b8146edgw1ex4or5ixkjj20k60gw3zg.jpg) + +``` 1.1 > leftSpaceToView(self.view, 10) 方法名中带有“SpaceToView”的方法表示到某个参照view的间距,需要传递2个参数:(UIView)参照view 和 (CGFloat)间距数值 - + 1.2 > widthRatioToView(self.view, 1) 方法名中带有“RatioToView”的方法表示view的宽度或者高度等属性相对于参照view的对应属性值的比例,需要传递2个参数:(UIView)参照view 和 (CGFloat)倍数 - + 1.3 > topEqualToView(view) 方法名中带有“EqualToView”的方法表示view的某一属性等于参照view的对应的属性值,需要传递1个参数:(UIView)参照view @@ -94,12 +189,18 @@ 1.4 > widthIs(100) 方法名中带有“Is”的方法表示view的某一属性值等于参数数值,需要传递1个参数:(CGFloat)数值 +``` # PS -/* 如果您需要布局错误LOG信息提示请打开此宏 */ +// 如果需要用“断言”调试程序请打开此宏(位于UIView+SDAutoLayout.h) + +//#define SDDebugWithAssert -//#define SDAutoLayoutIssueLog ![](http://ww3.sinaimg.cn/bmiddle/9b8146edgw1ex4mukixr6g209g07lhdt.gif) + +![](http://upload-images.jianshu.io/upload_images/1157161-07fa43e0f539ebad.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +![](http://upload-images.jianshu.io/upload_images/1157161-453a5d33d7f3d48d.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/project.pbxproj" "b/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/project.pbxproj" deleted file mode 100644 index 5d48ea2..0000000 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/project.pbxproj" +++ /dev/null @@ -1,676 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 4845116F1C2439830071BE2C /* DemoVC7.m in Sources */ = {isa = PBXBuildFile; fileRef = 484511681C2439830071BE2C /* DemoVC7.m */; }; - 484511701C2439830071BE2C /* DemoVC7Cell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4845116A1C2439830071BE2C /* DemoVC7Cell.m */; }; - 484511711C2439830071BE2C /* DemoVC7Cell2.m in Sources */ = {isa = PBXBuildFile; fileRef = 4845116C1C2439830071BE2C /* DemoVC7Cell2.m */; }; - 484511721C2439830071BE2C /* DemoVC7Model.m in Sources */ = {isa = PBXBuildFile; fileRef = 4845116E1C2439830071BE2C /* DemoVC7Model.m */; }; - 486A382D1C0D852C000D43E5 /* DemoVC6.m in Sources */ = {isa = PBXBuildFile; fileRef = 486A382C1C0D852C000D43E5 /* DemoVC6.m */; }; - 487D3EB71BCB9EC700A66173 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 487D3EB61BCB9EC700A66173 /* main.m */; }; - 487D3EBA1BCB9EC700A66173 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 487D3EB91BCB9EC700A66173 /* AppDelegate.m */; }; - 487D3EBD1BCB9EC700A66173 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 487D3EBC1BCB9EC700A66173 /* ViewController.m */; }; - 487D3EC01BCB9EC700A66173 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 487D3EBE1BCB9EC700A66173 /* Main.storyboard */; }; - 487D3EC21BCB9EC700A66173 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 487D3EC11BCB9EC700A66173 /* Images.xcassets */; }; - 487D3EC51BCB9EC700A66173 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 487D3EC31BCB9EC700A66173 /* LaunchScreen.xib */; }; - 487D3ED11BCB9EC700A66173 /* SDAutoLayout____DemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 487D3ED01BCB9EC700A66173 /* SDAutoLayout____DemoTests.m */; }; - 487D3EE11BCBA55700A66173 /* DemoVC0.m in Sources */ = {isa = PBXBuildFile; fileRef = 487D3EE01BCBA55700A66173 /* DemoVC0.m */; }; - 487D3EE41BCBA6CA00A66173 /* DemoVC1.m in Sources */ = {isa = PBXBuildFile; fileRef = 487D3EE31BCBA6CA00A66173 /* DemoVC1.m */; }; - 487D3EE71BCBA78B00A66173 /* DemoTableViewControler.m in Sources */ = {isa = PBXBuildFile; fileRef = 487D3EE61BCBA78B00A66173 /* DemoTableViewControler.m */; }; - 487D3EEA1BCBACF500A66173 /* DemoVC2.m in Sources */ = {isa = PBXBuildFile; fileRef = 487D3EE91BCBACF500A66173 /* DemoVC2.m */; }; - 487D3EF01BCBB61A00A66173 /* TestCell2.m in Sources */ = {isa = PBXBuildFile; fileRef = 487D3EED1BCBB61A00A66173 /* TestCell2.m */; }; - 487D3EF41BCBB71500A66173 /* DemoVC3.m in Sources */ = {isa = PBXBuildFile; fileRef = 487D3EF31BCBB71500A66173 /* DemoVC3.m */; }; - 48D632331BD0A7B500F86A88 /* DemoVC4.m in Sources */ = {isa = PBXBuildFile; fileRef = 48D632321BD0A7B500F86A88 /* DemoVC4.m */; }; - 48DA2B001BFB529200A771E6 /* UITableView+SDAutoTableViewCellHeight.m in Sources */ = {isa = PBXBuildFile; fileRef = 48DA2AFD1BFB529200A771E6 /* UITableView+SDAutoTableViewCellHeight.m */; }; - 48DA2B011BFB529200A771E6 /* UIView+SDAutoLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 48DA2AFF1BFB529200A771E6 /* UIView+SDAutoLayout.m */; }; - 48DBCD081C01D884002EC476 /* DemoVC5CellTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 48DBCD071C01D884002EC476 /* DemoVC5CellTableViewCell.m */; }; - 48DBCD0B1C01D89C002EC476 /* DemoVC5.m in Sources */ = {isa = PBXBuildFile; fileRef = 48DBCD0A1C01D89C002EC476 /* DemoVC5.m */; }; - 48DBCD0E1C01DA1E002EC476 /* DemoVC5Model.m in Sources */ = {isa = PBXBuildFile; fileRef = 48DBCD0D1C01DA1E002EC476 /* DemoVC5Model.m */; }; - 48DBCD1A1C01E045002EC476 /* icon0.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD101C01E045002EC476 /* icon0.jpg */; }; - 48DBCD1B1C01E045002EC476 /* icon1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD111C01E045002EC476 /* icon1.jpg */; }; - 48DBCD1C1C01E045002EC476 /* icon2.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD121C01E045002EC476 /* icon2.jpg */; }; - 48DBCD1D1C01E045002EC476 /* icon3.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD131C01E045002EC476 /* icon3.jpg */; }; - 48DBCD1E1C01E045002EC476 /* icon4.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD141C01E045002EC476 /* icon4.jpg */; }; - 48DBCD1F1C01E045002EC476 /* pic0.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD151C01E045002EC476 /* pic0.jpg */; }; - 48DBCD201C01E045002EC476 /* pic1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD161C01E045002EC476 /* pic1.jpg */; }; - 48DBCD211C01E045002EC476 /* pic2.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD171C01E045002EC476 /* pic2.jpg */; }; - 48DBCD221C01E045002EC476 /* pic3.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD181C01E045002EC476 /* pic3.jpg */; }; - 48DBCD231C01E045002EC476 /* pic4.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD191C01E045002EC476 /* pic4.jpg */; }; - 99E728A71BE5E22D00771161 /* sdRefeshView_arrow@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 99E7289D1BE5E22D00771161 /* sdRefeshView_arrow@2x.png */; }; - 99E728A81BE5E22D00771161 /* SDRefreshFooterView.m in Sources */ = {isa = PBXBuildFile; fileRef = 99E728A01BE5E22D00771161 /* SDRefreshFooterView.m */; }; - 99E728A91BE5E22D00771161 /* SDRefreshHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 99E728A21BE5E22D00771161 /* SDRefreshHeaderView.m */; }; - 99E728AA1BE5E22D00771161 /* SDRefreshView.m in Sources */ = {isa = PBXBuildFile; fileRef = 99E728A41BE5E22D00771161 /* SDRefreshView.m */; }; - 99E728AB1BE5E22D00771161 /* UIView+SDExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 99E728A61BE5E22D00771161 /* UIView+SDExtension.m */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 487D3ECB1BCB9EC700A66173 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 487D3EA91BCB9EC700A66173 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 487D3EB01BCB9EC700A66173; - remoteInfo = "SDAutoLayout 测试 Demo"; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 484511671C2439830071BE2C /* DemoVC7.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC7.h; sourceTree = ""; }; - 484511681C2439830071BE2C /* DemoVC7.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC7.m; sourceTree = ""; }; - 484511691C2439830071BE2C /* DemoVC7Cell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC7Cell.h; sourceTree = ""; }; - 4845116A1C2439830071BE2C /* DemoVC7Cell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC7Cell.m; sourceTree = ""; }; - 4845116B1C2439830071BE2C /* DemoVC7Cell2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC7Cell2.h; sourceTree = ""; }; - 4845116C1C2439830071BE2C /* DemoVC7Cell2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC7Cell2.m; sourceTree = ""; }; - 4845116D1C2439830071BE2C /* DemoVC7Model.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC7Model.h; sourceTree = ""; }; - 4845116E1C2439830071BE2C /* DemoVC7Model.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC7Model.m; sourceTree = ""; }; - 486A382B1C0D852C000D43E5 /* DemoVC6.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC6.h; sourceTree = ""; }; - 486A382C1C0D852C000D43E5 /* DemoVC6.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC6.m; sourceTree = ""; }; - 487D3EB11BCB9EC700A66173 /* SDAutoLayout 测试 Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SDAutoLayout 测试 Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - 487D3EB51BCB9EC700A66173 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 487D3EB61BCB9EC700A66173 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 487D3EB81BCB9EC700A66173 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 487D3EB91BCB9EC700A66173 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 487D3EBB1BCB9EC700A66173 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; - 487D3EBC1BCB9EC700A66173 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; - 487D3EBF1BCB9EC700A66173 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 487D3EC11BCB9EC700A66173 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; - 487D3EC41BCB9EC700A66173 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; - 487D3ECA1BCB9EC700A66173 /* SDAutoLayout 测试 DemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SDAutoLayout 测试 DemoTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - 487D3ECF1BCB9EC700A66173 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 487D3ED01BCB9EC700A66173 /* SDAutoLayout____DemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SDAutoLayout____DemoTests.m"; sourceTree = ""; }; - 487D3EDF1BCBA55700A66173 /* DemoVC0.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC0.h; sourceTree = ""; }; - 487D3EE01BCBA55700A66173 /* DemoVC0.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC0.m; sourceTree = ""; }; - 487D3EE21BCBA6CA00A66173 /* DemoVC1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC1.h; sourceTree = ""; }; - 487D3EE31BCBA6CA00A66173 /* DemoVC1.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC1.m; sourceTree = ""; }; - 487D3EE51BCBA78B00A66173 /* DemoTableViewControler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoTableViewControler.h; sourceTree = ""; }; - 487D3EE61BCBA78B00A66173 /* DemoTableViewControler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoTableViewControler.m; sourceTree = ""; }; - 487D3EE81BCBACF500A66173 /* DemoVC2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC2.h; sourceTree = ""; }; - 487D3EE91BCBACF500A66173 /* DemoVC2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC2.m; sourceTree = ""; }; - 487D3EEC1BCBB61A00A66173 /* TestCell2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestCell2.h; sourceTree = ""; }; - 487D3EED1BCBB61A00A66173 /* TestCell2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestCell2.m; sourceTree = ""; }; - 487D3EF21BCBB71500A66173 /* DemoVC3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC3.h; sourceTree = ""; }; - 487D3EF31BCBB71500A66173 /* DemoVC3.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC3.m; sourceTree = ""; }; - 48D632311BD0A7B500F86A88 /* DemoVC4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC4.h; sourceTree = ""; }; - 48D632321BD0A7B500F86A88 /* DemoVC4.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC4.m; sourceTree = ""; }; - 48DA2AFC1BFB529200A771E6 /* UITableView+SDAutoTableViewCellHeight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableView+SDAutoTableViewCellHeight.h"; sourceTree = ""; }; - 48DA2AFD1BFB529200A771E6 /* UITableView+SDAutoTableViewCellHeight.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableView+SDAutoTableViewCellHeight.m"; sourceTree = ""; }; - 48DA2AFE1BFB529200A771E6 /* UIView+SDAutoLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+SDAutoLayout.h"; sourceTree = ""; }; - 48DA2AFF1BFB529200A771E6 /* UIView+SDAutoLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+SDAutoLayout.m"; sourceTree = ""; }; - 48DBCD061C01D884002EC476 /* DemoVC5CellTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC5CellTableViewCell.h; sourceTree = ""; }; - 48DBCD071C01D884002EC476 /* DemoVC5CellTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC5CellTableViewCell.m; sourceTree = ""; }; - 48DBCD091C01D89C002EC476 /* DemoVC5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC5.h; sourceTree = ""; }; - 48DBCD0A1C01D89C002EC476 /* DemoVC5.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC5.m; sourceTree = ""; }; - 48DBCD0C1C01DA1E002EC476 /* DemoVC5Model.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC5Model.h; sourceTree = ""; }; - 48DBCD0D1C01DA1E002EC476 /* DemoVC5Model.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC5Model.m; sourceTree = ""; }; - 48DBCD101C01E045002EC476 /* icon0.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = icon0.jpg; sourceTree = ""; }; - 48DBCD111C01E045002EC476 /* icon1.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = icon1.jpg; sourceTree = ""; }; - 48DBCD121C01E045002EC476 /* icon2.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = icon2.jpg; sourceTree = ""; }; - 48DBCD131C01E045002EC476 /* icon3.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = icon3.jpg; sourceTree = ""; }; - 48DBCD141C01E045002EC476 /* icon4.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = icon4.jpg; sourceTree = ""; }; - 48DBCD151C01E045002EC476 /* pic0.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = pic0.jpg; sourceTree = ""; }; - 48DBCD161C01E045002EC476 /* pic1.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = pic1.jpg; sourceTree = ""; }; - 48DBCD171C01E045002EC476 /* pic2.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = pic2.jpg; sourceTree = ""; }; - 48DBCD181C01E045002EC476 /* pic3.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = pic3.jpg; sourceTree = ""; }; - 48DBCD191C01E045002EC476 /* pic4.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = pic4.jpg; sourceTree = ""; }; - 99E7289D1BE5E22D00771161 /* sdRefeshView_arrow@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "sdRefeshView_arrow@2x.png"; sourceTree = ""; }; - 99E7289E1BE5E22D00771161 /* SDRefresh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDRefresh.h; sourceTree = ""; }; - 99E7289F1BE5E22D00771161 /* SDRefreshFooterView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDRefreshFooterView.h; sourceTree = ""; }; - 99E728A01BE5E22D00771161 /* SDRefreshFooterView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDRefreshFooterView.m; sourceTree = ""; }; - 99E728A11BE5E22D00771161 /* SDRefreshHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDRefreshHeaderView.h; sourceTree = ""; }; - 99E728A21BE5E22D00771161 /* SDRefreshHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDRefreshHeaderView.m; sourceTree = ""; }; - 99E728A31BE5E22D00771161 /* SDRefreshView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDRefreshView.h; sourceTree = ""; }; - 99E728A41BE5E22D00771161 /* SDRefreshView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDRefreshView.m; sourceTree = ""; }; - 99E728A51BE5E22D00771161 /* UIView+SDExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+SDExtension.h"; sourceTree = ""; }; - 99E728A61BE5E22D00771161 /* UIView+SDExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+SDExtension.m"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 487D3EAE1BCB9EC700A66173 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 487D3EC71BCB9EC700A66173 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 484511661C24395F0071BE2C /* DemoVC7 */ = { - isa = PBXGroup; - children = ( - 484511671C2439830071BE2C /* DemoVC7.h */, - 484511681C2439830071BE2C /* DemoVC7.m */, - 484511691C2439830071BE2C /* DemoVC7Cell.h */, - 4845116A1C2439830071BE2C /* DemoVC7Cell.m */, - 4845116B1C2439830071BE2C /* DemoVC7Cell2.h */, - 4845116C1C2439830071BE2C /* DemoVC7Cell2.m */, - 4845116D1C2439830071BE2C /* DemoVC7Model.h */, - 4845116E1C2439830071BE2C /* DemoVC7Model.m */, - ); - name = DemoVC7; - sourceTree = ""; - }; - 486A382A1C0D8499000D43E5 /* DemoVC6 */ = { - isa = PBXGroup; - children = ( - 486A382B1C0D852C000D43E5 /* DemoVC6.h */, - 486A382C1C0D852C000D43E5 /* DemoVC6.m */, - ); - name = DemoVC6; - sourceTree = ""; - }; - 487D3EA81BCB9EC700A66173 = { - isa = PBXGroup; - children = ( - 487D3EB31BCB9EC700A66173 /* SDAutoLayout 测试 Demo */, - 487D3ECD1BCB9EC700A66173 /* SDAutoLayout 测试 DemoTests */, - 487D3EB21BCB9EC700A66173 /* Products */, - ); - sourceTree = ""; - }; - 487D3EB21BCB9EC700A66173 /* Products */ = { - isa = PBXGroup; - children = ( - 487D3EB11BCB9EC700A66173 /* SDAutoLayout 测试 Demo.app */, - 487D3ECA1BCB9EC700A66173 /* SDAutoLayout 测试 DemoTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 487D3EB31BCB9EC700A66173 /* SDAutoLayout 测试 Demo */ = { - isa = PBXGroup; - children = ( - 48DA2AFB1BFB529200A771E6 /* SDAutoLayout */, - 99E7289C1BE5E22D00771161 /* SDRefeshView */, - 487D3EDE1BCBA52900A66173 /* DemoVC */, - 487D3EB81BCB9EC700A66173 /* AppDelegate.h */, - 487D3EB91BCB9EC700A66173 /* AppDelegate.m */, - 487D3EE51BCBA78B00A66173 /* DemoTableViewControler.h */, - 487D3EE61BCBA78B00A66173 /* DemoTableViewControler.m */, - 487D3EBE1BCB9EC700A66173 /* Main.storyboard */, - 487D3EC11BCB9EC700A66173 /* Images.xcassets */, - 487D3EC31BCB9EC700A66173 /* LaunchScreen.xib */, - 487D3EB41BCB9EC700A66173 /* Supporting Files */, - ); - path = "SDAutoLayout 测试 Demo"; - sourceTree = ""; - }; - 487D3EB41BCB9EC700A66173 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 48DBCD0F1C01DEB1002EC476 /* Images */, - 487D3EB51BCB9EC700A66173 /* Info.plist */, - 487D3EB61BCB9EC700A66173 /* main.m */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - 487D3ECD1BCB9EC700A66173 /* SDAutoLayout 测试 DemoTests */ = { - isa = PBXGroup; - children = ( - 487D3ED01BCB9EC700A66173 /* SDAutoLayout____DemoTests.m */, - 487D3ECE1BCB9EC700A66173 /* Supporting Files */, - ); - path = "SDAutoLayout 测试 DemoTests"; - sourceTree = ""; - }; - 487D3ECE1BCB9EC700A66173 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 487D3ECF1BCB9EC700A66173 /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - 487D3EDE1BCBA52900A66173 /* DemoVC */ = { - isa = PBXGroup; - children = ( - 484511661C24395F0071BE2C /* DemoVC7 */, - 486A382A1C0D8499000D43E5 /* DemoVC6 */, - 48DBCD051C01D82A002EC476 /* DemoVC5 */, - 487D3EBB1BCB9EC700A66173 /* ViewController.h */, - 487D3EBC1BCB9EC700A66173 /* ViewController.m */, - 487D3EDF1BCBA55700A66173 /* DemoVC0.h */, - 487D3EE01BCBA55700A66173 /* DemoVC0.m */, - 487D3EE21BCBA6CA00A66173 /* DemoVC1.h */, - 487D3EE31BCBA6CA00A66173 /* DemoVC1.m */, - 487D3EE81BCBACF500A66173 /* DemoVC2.h */, - 487D3EE91BCBACF500A66173 /* DemoVC2.m */, - 487D3EEB1BCBB5A400A66173 /* DemoVC3 */, - 48D632311BD0A7B500F86A88 /* DemoVC4.h */, - 48D632321BD0A7B500F86A88 /* DemoVC4.m */, - ); - name = DemoVC; - sourceTree = ""; - }; - 487D3EEB1BCBB5A400A66173 /* DemoVC3 */ = { - isa = PBXGroup; - children = ( - 487D3EEC1BCBB61A00A66173 /* TestCell2.h */, - 487D3EED1BCBB61A00A66173 /* TestCell2.m */, - 487D3EF21BCBB71500A66173 /* DemoVC3.h */, - 487D3EF31BCBB71500A66173 /* DemoVC3.m */, - ); - name = DemoVC3; - sourceTree = ""; - }; - 48DA2AFB1BFB529200A771E6 /* SDAutoLayout */ = { - isa = PBXGroup; - children = ( - 48DA2AFC1BFB529200A771E6 /* UITableView+SDAutoTableViewCellHeight.h */, - 48DA2AFD1BFB529200A771E6 /* UITableView+SDAutoTableViewCellHeight.m */, - 48DA2AFE1BFB529200A771E6 /* UIView+SDAutoLayout.h */, - 48DA2AFF1BFB529200A771E6 /* UIView+SDAutoLayout.m */, - ); - path = SDAutoLayout; - sourceTree = ""; - }; - 48DBCD051C01D82A002EC476 /* DemoVC5 */ = { - isa = PBXGroup; - children = ( - 48DBCD061C01D884002EC476 /* DemoVC5CellTableViewCell.h */, - 48DBCD071C01D884002EC476 /* DemoVC5CellTableViewCell.m */, - 48DBCD091C01D89C002EC476 /* DemoVC5.h */, - 48DBCD0A1C01D89C002EC476 /* DemoVC5.m */, - 48DBCD0C1C01DA1E002EC476 /* DemoVC5Model.h */, - 48DBCD0D1C01DA1E002EC476 /* DemoVC5Model.m */, - ); - name = DemoVC5; - sourceTree = ""; - }; - 48DBCD0F1C01DEB1002EC476 /* Images */ = { - isa = PBXGroup; - children = ( - 48DBCD101C01E045002EC476 /* icon0.jpg */, - 48DBCD111C01E045002EC476 /* icon1.jpg */, - 48DBCD121C01E045002EC476 /* icon2.jpg */, - 48DBCD131C01E045002EC476 /* icon3.jpg */, - 48DBCD141C01E045002EC476 /* icon4.jpg */, - 48DBCD151C01E045002EC476 /* pic0.jpg */, - 48DBCD161C01E045002EC476 /* pic1.jpg */, - 48DBCD171C01E045002EC476 /* pic2.jpg */, - 48DBCD181C01E045002EC476 /* pic3.jpg */, - 48DBCD191C01E045002EC476 /* pic4.jpg */, - ); - path = Images; - sourceTree = ""; - }; - 99E7289C1BE5E22D00771161 /* SDRefeshView */ = { - isa = PBXGroup; - children = ( - 99E7289D1BE5E22D00771161 /* sdRefeshView_arrow@2x.png */, - 99E7289E1BE5E22D00771161 /* SDRefresh.h */, - 99E7289F1BE5E22D00771161 /* SDRefreshFooterView.h */, - 99E728A01BE5E22D00771161 /* SDRefreshFooterView.m */, - 99E728A11BE5E22D00771161 /* SDRefreshHeaderView.h */, - 99E728A21BE5E22D00771161 /* SDRefreshHeaderView.m */, - 99E728A31BE5E22D00771161 /* SDRefreshView.h */, - 99E728A41BE5E22D00771161 /* SDRefreshView.m */, - 99E728A51BE5E22D00771161 /* UIView+SDExtension.h */, - 99E728A61BE5E22D00771161 /* UIView+SDExtension.m */, - ); - path = SDRefeshView; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 487D3EB01BCB9EC700A66173 /* SDAutoLayout 测试 Demo */ = { - isa = PBXNativeTarget; - buildConfigurationList = 487D3ED41BCB9EC700A66173 /* Build configuration list for PBXNativeTarget "SDAutoLayout 测试 Demo" */; - buildPhases = ( - 487D3EAD1BCB9EC700A66173 /* Sources */, - 487D3EAE1BCB9EC700A66173 /* Frameworks */, - 487D3EAF1BCB9EC700A66173 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "SDAutoLayout 测试 Demo"; - productName = "SDAutoLayout 测试 Demo"; - productReference = 487D3EB11BCB9EC700A66173 /* SDAutoLayout 测试 Demo.app */; - productType = "com.apple.product-type.application"; - }; - 487D3EC91BCB9EC700A66173 /* SDAutoLayout 测试 DemoTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 487D3ED71BCB9EC700A66173 /* Build configuration list for PBXNativeTarget "SDAutoLayout 测试 DemoTests" */; - buildPhases = ( - 487D3EC61BCB9EC700A66173 /* Sources */, - 487D3EC71BCB9EC700A66173 /* Frameworks */, - 487D3EC81BCB9EC700A66173 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 487D3ECC1BCB9EC700A66173 /* PBXTargetDependency */, - ); - name = "SDAutoLayout 测试 DemoTests"; - productName = "SDAutoLayout 测试 DemoTests"; - productReference = 487D3ECA1BCB9EC700A66173 /* SDAutoLayout 测试 DemoTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 487D3EA91BCB9EC700A66173 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0640; - ORGANIZATIONNAME = gsd; - TargetAttributes = { - 487D3EB01BCB9EC700A66173 = { - CreatedOnToolsVersion = 6.4; - DevelopmentTeam = V4Q779DR3D; - }; - 487D3EC91BCB9EC700A66173 = { - CreatedOnToolsVersion = 6.4; - TestTargetID = 487D3EB01BCB9EC700A66173; - }; - }; - }; - buildConfigurationList = 487D3EAC1BCB9EC700A66173 /* Build configuration list for PBXProject "SDAutoLayout 测试 Demo" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 487D3EA81BCB9EC700A66173; - productRefGroup = 487D3EB21BCB9EC700A66173 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 487D3EB01BCB9EC700A66173 /* SDAutoLayout 测试 Demo */, - 487D3EC91BCB9EC700A66173 /* SDAutoLayout 测试 DemoTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 487D3EAF1BCB9EC700A66173 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 48DBCD1A1C01E045002EC476 /* icon0.jpg in Resources */, - 48DBCD221C01E045002EC476 /* pic3.jpg in Resources */, - 48DBCD211C01E045002EC476 /* pic2.jpg in Resources */, - 48DBCD231C01E045002EC476 /* pic4.jpg in Resources */, - 48DBCD1F1C01E045002EC476 /* pic0.jpg in Resources */, - 487D3EC01BCB9EC700A66173 /* Main.storyboard in Resources */, - 48DBCD1B1C01E045002EC476 /* icon1.jpg in Resources */, - 48DBCD201C01E045002EC476 /* pic1.jpg in Resources */, - 487D3EC51BCB9EC700A66173 /* LaunchScreen.xib in Resources */, - 48DBCD1D1C01E045002EC476 /* icon3.jpg in Resources */, - 487D3EC21BCB9EC700A66173 /* Images.xcassets in Resources */, - 48DBCD1C1C01E045002EC476 /* icon2.jpg in Resources */, - 99E728A71BE5E22D00771161 /* sdRefeshView_arrow@2x.png in Resources */, - 48DBCD1E1C01E045002EC476 /* icon4.jpg in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 487D3EC81BCB9EC700A66173 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 487D3EAD1BCB9EC700A66173 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 487D3EE71BCBA78B00A66173 /* DemoTableViewControler.m in Sources */, - 487D3EBD1BCB9EC700A66173 /* ViewController.m in Sources */, - 48DA2B011BFB529200A771E6 /* UIView+SDAutoLayout.m in Sources */, - 487D3EF41BCBB71500A66173 /* DemoVC3.m in Sources */, - 486A382D1C0D852C000D43E5 /* DemoVC6.m in Sources */, - 48DBCD0E1C01DA1E002EC476 /* DemoVC5Model.m in Sources */, - 484511711C2439830071BE2C /* DemoVC7Cell2.m in Sources */, - 484511721C2439830071BE2C /* DemoVC7Model.m in Sources */, - 487D3EBA1BCB9EC700A66173 /* AppDelegate.m in Sources */, - 48DBCD0B1C01D89C002EC476 /* DemoVC5.m in Sources */, - 4845116F1C2439830071BE2C /* DemoVC7.m in Sources */, - 484511701C2439830071BE2C /* DemoVC7Cell.m in Sources */, - 48DA2B001BFB529200A771E6 /* UITableView+SDAutoTableViewCellHeight.m in Sources */, - 99E728AA1BE5E22D00771161 /* SDRefreshView.m in Sources */, - 48D632331BD0A7B500F86A88 /* DemoVC4.m in Sources */, - 487D3EE11BCBA55700A66173 /* DemoVC0.m in Sources */, - 99E728AB1BE5E22D00771161 /* UIView+SDExtension.m in Sources */, - 99E728A81BE5E22D00771161 /* SDRefreshFooterView.m in Sources */, - 48DBCD081C01D884002EC476 /* DemoVC5CellTableViewCell.m in Sources */, - 487D3EEA1BCBACF500A66173 /* DemoVC2.m in Sources */, - 99E728A91BE5E22D00771161 /* SDRefreshHeaderView.m in Sources */, - 487D3EF01BCBB61A00A66173 /* TestCell2.m in Sources */, - 487D3EB71BCB9EC700A66173 /* main.m in Sources */, - 487D3EE41BCBA6CA00A66173 /* DemoVC1.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 487D3EC61BCB9EC700A66173 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 487D3ED11BCB9EC700A66173 /* SDAutoLayout____DemoTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 487D3ECC1BCB9EC700A66173 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 487D3EB01BCB9EC700A66173 /* SDAutoLayout 测试 Demo */; - targetProxy = 487D3ECB1BCB9EC700A66173 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 487D3EBE1BCB9EC700A66173 /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 487D3EBF1BCB9EC700A66173 /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 487D3EC31BCB9EC700A66173 /* LaunchScreen.xib */ = { - isa = PBXVariantGroup; - children = ( - 487D3EC41BCB9EC700A66173 /* Base */, - ); - name = LaunchScreen.xib; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 487D3ED21BCB9EC700A66173 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.4; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 487D3ED31BCB9EC700A66173 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.4; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 487D3ED51BCB9EC700A66173 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Developer"; - INFOPLIST_FILE = "SDAutoLayout 测试 Demo/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 6.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 487D3ED61BCB9EC700A66173 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Developer"; - INFOPLIST_FILE = "SDAutoLayout 测试 Demo/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 6.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; - 487D3ED81BCB9EC700A66173 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - INFOPLIST_FILE = "SDAutoLayout 测试 DemoTests/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SDAutoLayout 测试 Demo.app/SDAutoLayout 测试 Demo"; - }; - name = Debug; - }; - 487D3ED91BCB9EC700A66173 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = "SDAutoLayout 测试 DemoTests/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SDAutoLayout 测试 Demo.app/SDAutoLayout 测试 Demo"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 487D3EAC1BCB9EC700A66173 /* Build configuration list for PBXProject "SDAutoLayout 测试 Demo" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 487D3ED21BCB9EC700A66173 /* Debug */, - 487D3ED31BCB9EC700A66173 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 487D3ED41BCB9EC700A66173 /* Build configuration list for PBXNativeTarget "SDAutoLayout 测试 Demo" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 487D3ED51BCB9EC700A66173 /* Debug */, - 487D3ED61BCB9EC700A66173 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 487D3ED71BCB9EC700A66173 /* Build configuration list for PBXNativeTarget "SDAutoLayout 测试 DemoTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 487D3ED81BCB9EC700A66173 /* Debug */, - 487D3ED91BCB9EC700A66173 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 487D3EA91BCB9EC700A66173 /* Project object */; -} diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/project.xcworkspace/xcuserdata/gsd.xcuserdatad/UserInterfaceState.xcuserstate" "b/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/project.xcworkspace/xcuserdata/gsd.xcuserdatad/UserInterfaceState.xcuserstate" deleted file mode 100644 index 62ff8f0..0000000 Binary files "a/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/project.xcworkspace/xcuserdata/gsd.xcuserdatad/UserInterfaceState.xcuserstate" and /dev/null differ diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/xcuserdata/gsd.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist" "b/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/xcuserdata/gsd.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist" deleted file mode 100644 index 8f94031..0000000 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/xcuserdata/gsd.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist" +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoTableViewControler.m" "b/SDAutoLayout \346\265\213\350\257\225 Demo/DemoTableViewControler.m" deleted file mode 100644 index 701d316..0000000 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoTableViewControler.m" +++ /dev/null @@ -1,72 +0,0 @@ -// -// DemoTableViewControler.m -// SDAutoLayout 测试 Demo -// -// Created by gsd on 15/10/12. -// Copyright (c) 2015年 gsd. All rights reserved. -// - -/* - - ********************************************************************************* - * * - * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * - * 帮您解决问题。 * - * QQ : 2689718696(gsdios) * - * Email : gsdios@126.com * - * GitHub: https://github.com/gsdios * - * 新浪微博:GSD_iOS * - * * - ********************************************************************************* - - */ - -#import "DemoTableViewControler.h" - -#import "DemoVC5.h" - -@implementation DemoTableViewControler - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - self.view.backgroundColor = [UIColor whiteColor]; - - UIViewController *vc = [DemoVC5 new]; - vc.title = @"DemoVC5"; - [self.navigationController pushViewController:vc animated:YES]; -} - -#pragma mark - tableview datasourece and delegate - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - return 8; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - static NSString *ID = @"test"; - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; - if (!cell) { - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID]; - } - cell.textLabel.text = [NSString stringWithFormat:@"Demo -- %ld", indexPath.row]; - return cell; -} - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - NSString *demoClassString = [NSString stringWithFormat:@"DemoVC%ld", indexPath.row]; - UIViewController *vc = [NSClassFromString(demoClassString) new]; - vc.title = demoClassString; - [self.navigationController pushViewController:vc animated:YES]; -} - -- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath -{ - return 60; -} - -@end diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC0.m" "b/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC0.m" deleted file mode 100644 index ea15c56..0000000 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC0.m" +++ /dev/null @@ -1,122 +0,0 @@ -// -// DemoVC0.m -// SDAutoLayout 测试 Demo -// -// Created by gsd on 15/10/12. -// Copyright (c) 2015年 gsd. All rights reserved. -// - -/* - - ********************************************************************************* - * * - * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * - * 帮您解决问题。 * - * QQ : 2689718696(gsdios) * - * Email : gsdios@126.com * - * GitHub: https://github.com/gsdios * - * 新浪微博:GSD_iOS * - * * - ********************************************************************************* - - */ - -#import "DemoVC0.h" - -@implementation DemoVC0 - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - self.edgesForExtendedLayout = UIRectEdgeNone; - - self.view0.sd_layout - .leftSpaceToView(self.view, 10) - .topSpaceToView(self.view,80) - .heightIs(130) - .widthRatioToView(self.view, 0.4); - - self.view1.sd_layout - .leftSpaceToView(self.view0, 10) - .topEqualToView(self.view0) - .heightIs(60); - - self.view0.sd_layout - .leftSpaceToView(self.view, 10) - .topSpaceToView(self.view,80) - .heightIs(130) - .widthRatioToView(self.view, 0.4); - - self.view1.sd_layout - .leftSpaceToView(self.view0, 10) - .topEqualToView(self.view0) - .heightIs(60); - - self.view2.sd_layout - .leftSpaceToView(self.view1, 10) - .topEqualToView(self.view1) - .heightRatioToView(self.view1, 1) - .widthRatioToView(self.view1, 1); - - self.view3.sd_layout - .leftEqualToView(self.view1) - .topSpaceToView(self.view1, 10) - .heightRatioToView(self.view1, 1) - .widthRatioToView(self.view1, 1); - - self.view4.sd_layout - .leftEqualToView(self.view2) - .topEqualToView(self.view3) - .heightRatioToView(self.view3, 1) - .widthRatioToView(self.view1, 1); - - - self.view5.sd_layout - .topSpaceToView(self.view0, 20) - .leftEqualToView(self.view0) - .bottomSpaceToView(self.view, 30); - - self.view6.sd_layout - .leftSpaceToView(self.view5, 10) - .topEqualToView(self.view5) - .heightRatioToView(self.view5, 1) - .widthRatioToView(self.view5, 1); -} - - - -/* - Q:这里为什么要在 viewWillLayoutSubviews 里计算宽度? - - A:大多情况下的布局需求是不需要在 viewWillLayoutSubviews 里计算宽度,只需要直接一次设置 - 就可以了(例如其他的demo案例)。 - - 1.为什么这个界面要计算view5的宽度? - - 因为这个界面的布局要求(以view5 和 view6 为例)是:view5 距离屏幕 - 左边10,view6 距离 view5 10, view6 距离屏幕右边10 ,view5 和 view6 宽度相同,这样一来就需要 - 确定 view5 的宽度供 view6 参考,所以要计算下 view5 的宽度。 - - 2.在以上的情形中计算view5宽度理解了,那为什么是要在 viewWillLayoutSubviews 里计算宽度? - - 如果app需要【同时适配横竖屏】,则在横竖屏幕切换时屏幕的宽高对调,那么就需要再计算一次view5的宽度,而切换屏幕时 在viewWillLayoutSubviews 拿到的self.view的宽度才是正确的(因为viewdidload只是在view初始化完成并加 - 载出来时调用一次,以后切换屏幕不再调用),如果app不需要同时适配 - 横竖屏则不需要此步骤。 - - - */ -- (void)viewWillLayoutSubviews -{ - [super viewWillLayoutSubviews]; - - CGFloat view1W = (self.view.width * 0.6 - 10 * 4) / 2; - self.view1.sd_layout.widthIs(view1W); - - CGFloat view5W = (self.view.width - 30) / 2; - self.view5.sd_layout.widthIs(view5W); -} - - -@end - diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC1.m" "b/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC1.m" deleted file mode 100644 index a5c16ca..0000000 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC1.m" +++ /dev/null @@ -1,73 +0,0 @@ -// -// DemoVC1.m -// SDAutoLayout 测试 Demo -// -// Created by gsd on 15/10/12. -// Copyright (c) 2015年 gsd. All rights reserved. -// - -/* - - ********************************************************************************* - * * - * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * - * 帮您解决问题。 * - * QQ : 2689718696(gsdios) * - * Email : gsdios@126.com * - * GitHub: https://github.com/gsdios * - * 新浪微博:GSD_iOS * - * * - ********************************************************************************* - - */ - -#import "DemoVC1.h" - -@implementation DemoVC1 - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - // 暂时用view替代展示cell效果 - - self.view0.sd_layout - .leftSpaceToView(self.view, 10) - .topSpaceToView(self.view, 80) - .widthIs(50) - .heightIs(50); - - self.view1.sd_layout - .leftSpaceToView(self.view0, 10) - .topEqualToView(self.view0) - .rightSpaceToView(self.view, 30) - .heightRatioToView(self.view, 0.2); - - self.view2.sd_layout - .rightSpaceToView(self.view, 10) - .topSpaceToView(self.view1, 20) - .widthRatioToView(self.view0, 1) - .heightRatioToView(self.view0, 1); - - self.view3.sd_layout - .rightSpaceToView(self.view2, 10) - .leftSpaceToView(self.view, 30) - .heightRatioToView(self.view, 0.17) - .topEqualToView(self.view2); - - self.view4.sd_layout - .leftEqualToView(self.view0) - .heightRatioToView(self.view0, 1) - .widthRatioToView(self.view0, 1) - .topSpaceToView(self.view3, 20); - - self.view5.sd_layout - .leftEqualToView(self.view1) - .rightEqualToView(self.view1) - .bottomSpaceToView(self.view, 20) - .topEqualToView(self.view4); - - self.view5.tag = 19999; -} - -@end diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC2.m" "b/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC2.m" deleted file mode 100644 index b5ebd8b..0000000 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC2.m" +++ /dev/null @@ -1,46 +0,0 @@ -// -// DemoVC2.m -// SDAutoLayout 测试 Demo -// -// Created by gsd on 15/10/12. -// Copyright (c) 2015年 gsd. All rights reserved. -// - -/* - - ********************************************************************************* - * * - * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * - * 帮您解决问题。 * - * QQ : 2689718696(gsdios) * - * Email : gsdios@126.com * - * GitHub: https://github.com/gsdios * - * 新浪微博:GSD_iOS * - * * - ********************************************************************************* - - */ - -#import "DemoVC2.h" - -@implementation DemoVC2 - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - [self.view0 addSubview:self.view1]; - [self.view1 addSubview:self.view2]; - - self.view0.sd_layout.leftSpaceToView(self.view, 30).rightSpaceToView(self.view, 30).topSpaceToView(self.view, 100).bottomSpaceToView(self.view, 30); - self.view1.sd_layout.leftSpaceToView(self.view0, 30).topSpaceToView(self.view0, 30).widthRatioToView(self.view0, 0.5).heightRatioToView(self.view0, 0.5); - self.view2.sd_layout.leftSpaceToView(self.view1, 30).topSpaceToView(self.view1, 30).widthRatioToView(self.view1, 0.5).heightRatioToView(self.view1, 0.5); - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.8 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - self.view0.sd_layout.bottomSpaceToView(self.view, 100); - [self.view layoutSubviews]; - }); -} - - -@end diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC4.m" "b/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC4.m" deleted file mode 100644 index daf086f..0000000 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC4.m" +++ /dev/null @@ -1,73 +0,0 @@ -// -// DemoVC4.m -// SDAutoLayout 测试 Demo -// -// Created by gsd on 15/10/16. -// Copyright (c) 2015年 gsd. All rights reserved. -// - - -/* - - ********************************************************************************* - * * - * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * - * 帮您解决问题。 * - * QQ : 2689718696(gsdios) * - * Email : gsdios@126.com * - * GitHub: https://github.com/gsdios * - * 新浪微博:GSD_iOS * - * * - ********************************************************************************* - - */ - -#import "DemoVC4.h" - -@implementation DemoVC4 - - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - self.view0.sd_layout - .widthIs(50) - .heightIs(50) - .topSpaceToView(self.view, 80) - .leftSpaceToView(self.view, 20); - - self.view1.sd_layout - .topEqualToView(self.view0) - .leftSpaceToView(self.view0, 20) - .rightSpaceToView(self.view, 20) - .heightRatioToView(self.view0, 0.4); - - - self.view2.sd_layout - .topSpaceToView(self.view1, 20) - .widthRatioToView(self.view1, 0.45) - .leftEqualToView(self.view1) - .heightIs(80); - - self.view3.sd_layout - .topEqualToView(self.view2) - .leftSpaceToView(self.view2, 10) - .heightRatioToView(self.view2, 1) - .rightEqualToView(self.view1); - - self.view4.sd_layout - .leftEqualToView(self.view2) - .topSpaceToView(self.view2, 20) - .bottomSpaceToView(self.view, 30) - .widthRatioToView(self.view1, 0.7); - - self.view5.sd_layout - .leftSpaceToView(self.view4, 10) - .rightEqualToView(self.view1) - .heightRatioToView(self.view4, 1) - .topEqualToView(self.view4); - -} - -@end diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/Images.xcassets/AppIcon.appiconset/Contents.json" "b/SDAutoLayout \346\265\213\350\257\225 Demo/Images.xcassets/AppIcon.appiconset/Contents.json" deleted file mode 100644 index 36d2c80..0000000 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/Images.xcassets/AppIcon.appiconset/Contents.json" +++ /dev/null @@ -1,68 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/SDAutoLayout/UITableView+SDAutoTableViewCellHeight.h" "b/SDAutoLayout \346\265\213\350\257\225 Demo/SDAutoLayout/UITableView+SDAutoTableViewCellHeight.h" deleted file mode 100644 index bd3d55a..0000000 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/SDAutoLayout/UITableView+SDAutoTableViewCellHeight.h" +++ /dev/null @@ -1,99 +0,0 @@ -// -// UITableView+SDAutoTableViewCellHeight.h -// SDAutoLayout 测试 Demo -// -// Created by aier on 15/11/1. -// Copyright © 2015年 gsd. All rights reserved. -// - -/* - - ********************************************************************************* - * * - * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * - * 帮您解决问题。 * - * QQ : 2689718696(gsdios) * - * Email : gsdios@126.com * - * GitHub: https://github.com/gsdios * - * 新浪微博:GSD_iOS * - * * - ********************************************************************************* - - */ - -#import - -@class SDCellAutoHeightManager; - -#define kSDModelCellTag 199206 - -@interface UITableView (SDAutoTableViewCellHeight) - -- (void)startAutoCellHeightWithCellClass:(Class)cellClass contentViewWidth:(CGFloat)contentViewWidth; - -- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath model:(id)model keyPath:(NSString *)keyPath; - - -// 多cell情景下调用以下方法 - -/* - * cellClassArray 为所有cell的类组成的数组 - */ -- (void)startAutoCellHeightWithCellClasses:(NSArray *)cellClassArray contentViewWidth:(CGFloat)contentViewWidth; - -- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath model:(id)model keyPath:(NSString *)keyPath cellClass:(Class)cellClass; - -@property (nonatomic, strong) SDCellAutoHeightManager *cellAutoHeightManager; - -@end - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// ------------------------------- 以下为库内部使用无须了解 -------------------- - -@interface SDCellAutoHeightManager : NSObject - -@property (nonatomic, assign) CGFloat contentViewWidth; - -@property (nonatomic, assign) Class cellClass; - -@property (nonatomic, assign) CGFloat cellHeight; - -- (void)clearHeightCache; - -- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath model:(id)model keyPath:(NSString *)keyPath; - -- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath model:(id)model keyPath:(NSString *)keyPath cellClass:(Class)cellClass; - -- (instancetype)initWithCellClass:(Class)cellClass; -+ (instancetype)managerWithCellClass:(Class)cellClass; -@end - diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/SDAutoLayout/UITableView+SDAutoTableViewCellHeight.m" "b/SDAutoLayout \346\265\213\350\257\225 Demo/SDAutoLayout/UITableView+SDAutoTableViewCellHeight.m" deleted file mode 100644 index ef8d318..0000000 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/SDAutoLayout/UITableView+SDAutoTableViewCellHeight.m" +++ /dev/null @@ -1,218 +0,0 @@ -// -// UITableView+SDAutoTableViewCellHeight.m -// SDAutoLayout 测试 Demo -// -// Created by aier on 15/11/1. -// Copyright © 2015年 gsd. All rights reserved. -// - -/* - - ********************************************************************************* - * * - * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * - * 帮您解决问题。 * - * QQ : 2689718696(gsdios) * - * Email : gsdios@126.com * - * GitHub: https://github.com/gsdios * - * 新浪微博:GSD_iOS * - * * - ********************************************************************************* - - */ - -#import "UITableView+SDAutoTableViewCellHeight.h" -#import "UIView+SDAutoLayout.h" -#import - -@interface SDCellAutoHeightManager () - -@property (nonatomic, strong) UITableViewCell *modelCell; - -@end - -@implementation SDCellAutoHeightManager -{ - NSMutableDictionary *_cacheDictionary; - UITableView *_modelTableview; -} - -- (instancetype)initWithCellClass:(Class)cellClass -{ - if (self = [super init]) { - _modelTableview = [UITableView new]; - [self registerCellWithCellClass:cellClass]; - _cacheDictionary = [NSMutableDictionary new]; - } - return self; -} - -- (instancetype)initWithCellClasses:(NSArray *)cellClassArray -{ - if (self = [super init]) { - _modelTableview = [UITableView new]; - [cellClassArray enumerateObjectsUsingBlock:^(Class _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { - [self registerCellWithCellClass:obj]; - }]; - _cacheDictionary = [NSMutableDictionary new]; - } - return self; -} - -- (void)registerCellWithCellClass:(Class)cellClass -{ - [_modelTableview registerClass:cellClass forCellReuseIdentifier:NSStringFromClass(cellClass)]; - self.modelCell = [_modelTableview dequeueReusableCellWithIdentifier:NSStringFromClass(cellClass)]; - if (!self.modelCell.contentView.subviews.count) { - self.modelCell = nil; - [_modelTableview registerNib:[UINib nibWithNibName:NSStringFromClass(cellClass) bundle:nil] forCellReuseIdentifier:NSStringFromClass(cellClass)]; - self.modelCell = [_modelTableview dequeueReusableCellWithIdentifier:NSStringFromClass(cellClass)]; - } -} - -+ (instancetype)managerWithCellClass:(Class)cellClass -{ - SDCellAutoHeightManager *manager = [[self alloc] initWithCellClass:cellClass]; - return manager; -} - -- (UITableViewCell *)modelCell -{ - if (_modelCell.tag != kSDModelCellTag) { - _modelCell.contentView.tag = kSDModelCellTag; - } - return _modelCell; -} - -- (void)clearHeightCache -{ - [_cacheDictionary removeAllObjects]; -} - -- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath model:(id)model keyPath:(NSString *)keyPath -{ - NSString *cacheKey = [NSString stringWithFormat:@"%ld%ld", (long)indexPath.section, (long)indexPath.row]; - NSNumber *cacheHeight = [_cacheDictionary objectForKey:cacheKey]; - if (cacheHeight) { - return [cacheHeight floatValue]; - } else { - if (model && keyPath) { - [self.modelCell setValue:model forKey:keyPath]; - } - [self.modelCell.contentView layoutSubviews]; - [_cacheDictionary setObject:@(self.modelCell.autoHeight) forKey:cacheKey]; - return self.modelCell.autoHeight; - } -} - -- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath model:(id)model keyPath:(NSString *)keyPath cellClass:(Class)cellClass -{ - if (![self.modelCell isKindOfClass:cellClass]) { - self.modelCell = nil; - self.modelCell = [_modelTableview dequeueReusableCellWithIdentifier:NSStringFromClass(cellClass)]; - if (!self.modelCell) { - [self registerCellWithCellClass:cellClass]; - } - _modelCell.contentView.tag = kSDModelCellTag; - } - if (self.modelCell.contentView.width != self.contentViewWidth) { - _modelCell.contentView.width = self.contentViewWidth; - } - return [self cellHeightForIndexPath:indexPath model:model keyPath:keyPath]; -} - -- (void)setContentViewWidth:(CGFloat)contentViewWidth -{ - if (_contentViewWidth != contentViewWidth) { - [self clearHeightCache]; - } - _contentViewWidth = contentViewWidth; - - self.modelCell.contentView.width = self.contentViewWidth; -} - -@end - - -@implementation UITableView (SDAutoTableViewCellHeight) - -+ (void)load { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - Method reloadData = class_getInstanceMethod(self, @selector(reloadData)); - Method sd_reloadData = class_getInstanceMethod(self, @selector(sd_reloadData)); - method_exchangeImplementations(reloadData, sd_reloadData); - }); -} - -- (void)sd_reloadData -{ - [self.cellAutoHeightManager clearHeightCache]; - [self sd_reloadData]; -} -/* - * 下一步即将实现的功能 - - - (void)sd_insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation - { - [self sd_insertRowsAtIndexPaths:indexPaths withRowAnimation:animation]; - } - - - (void)sd_deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation - { - [self sd_deleteRowsAtIndexPaths:indexPaths withRowAnimation:animation]; - } - - - (void)sd_reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation - { - [self sd_reloadRowsAtIndexPaths:indexPaths withRowAnimation:animation]; - } - - - (void)sd_moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath - { - [self sd_moveRowAtIndexPath:indexPath toIndexPath:newIndexPath]; - } - - */ - -- (void)startAutoCellHeightWithCellClass:(Class)cellClass contentViewWidth:(CGFloat)contentViewWidth -{ - if (!self.cellAutoHeightManager) { - self.cellAutoHeightManager = [SDCellAutoHeightManager managerWithCellClass:cellClass]; - } - self.cellAutoHeightManager.contentViewWidth = contentViewWidth; -} - -- (void)startAutoCellHeightWithCellClasses:(NSArray *)cellClassArray contentViewWidth:(CGFloat)contentViewWidth -{ - if (!self.cellAutoHeightManager) { - self.cellAutoHeightManager = [[SDCellAutoHeightManager alloc] initWithCellClasses:cellClassArray]; - } - self.cellAutoHeightManager.contentViewWidth = contentViewWidth; -} - -- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath model:(id)model keyPath:(NSString *)keyPath -{ - return [self.cellAutoHeightManager cellHeightForIndexPath:indexPath model:model keyPath:keyPath]; -} - -- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath model:(id)model keyPath:(NSString *)keyPath cellClass:(Class)cellClass -{ - return [self.cellAutoHeightManager cellHeightForIndexPath:indexPath model:model keyPath:keyPath cellClass:cellClass]; -} - -- (SDCellAutoHeightManager *)cellAutoHeightManager -{ - return objc_getAssociatedObject(self, _cmd); -} - -- (void)setCellAutoHeightManager:(SDCellAutoHeightManager *)cellAutoHeightManager -{ - objc_setAssociatedObject(self, @selector(cellAutoHeightManager), cellAutoHeightManager, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -@end - - - - diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/SDAutoLayout/UIView+SDAutoLayout.h" "b/SDAutoLayout \346\265\213\350\257\225 Demo/SDAutoLayout/UIView+SDAutoLayout.h" deleted file mode 100644 index 3446305..0000000 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/SDAutoLayout/UIView+SDAutoLayout.h" +++ /dev/null @@ -1,256 +0,0 @@ -// -// UIView+SDAutoLayout.h -// -// Created by gsd on 15/10/6. -// Copyright (c) 2015年 gsd. All rights reserved. -// - -/* - ************************************************************************* - - --------- INTRODUCTION --------- - - HOW TO USE ? - - MODE 1. >>>>>>>>>>>>>>> You can use it in this way: - - Demo.sd_layout - .topSpaceToView(v1, 100) - .bottomSpaceToView(v3, 100) - .leftSpaceToView(v0, 150) - .rightSpaceToView(v2, 150); - - MODE 2. >>>>>>>>>>>>>>> You can also use it in this way that is more brevity: - - Demo.sd_layout.topSpaceToView(v1, 100).bottomSpaceToView(v3, 100).leftSpaceToView(v0, 150).rightSpaceToView(v2, 150); - - - ************************************************************************* - */ - - -/* - - ********************************************************************************* - * - * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 - * 帮您解决问题。 - * QQ : 2689718696(gsdios) - * Email : gsdios@126.com - * GitHub: https://github.com/gsdios - * 新浪微博:GSD_iOS - * - * 视频教程:http://www.letv.com/ptv/vplay/24038772.html - * 用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md - * - ********************************************************************************* - - */ - -#import - -@class SDAutoLayoutModel; - -typedef SDAutoLayoutModel *(^MarginToView)(UIView *toView, CGFloat value); -typedef SDAutoLayoutModel *(^Margin)(CGFloat value); -typedef SDAutoLayoutModel *(^MarginEqualToView)(UIView *toView); -typedef SDAutoLayoutModel *(^WidthHeight)(CGFloat value); -typedef SDAutoLayoutModel *(^WidthHeightEqualToView)(UIView *toView, CGFloat ratioValue); -typedef SDAutoLayoutModel *(^AutoHeight)(CGFloat ratioValue); -typedef void (^SpaceToSuperView)(UIEdgeInsets insets); - -@interface SDAutoLayoutModel : NSObject - -/* - *************************说明************************ - - 方法名中带有“SpaceToView”的需要传递2个参数:(UIView)参照view 和 (CGFloat)间距数值 - 方法名中带有“RatioToView”的需要传递2个参数:(UIView)参照view 和 (CGFloat)倍数 - 方法名中带有“EqualToView”的需要传递1个参数:(UIView)参照view - 方法名中带有“Is”的需要传递1个参数:(CGFloat)数值 - - ***************************************************** - */ - -/* - * 设置距离其它view的间距 - */ - -@property (nonatomic, copy, readonly) MarginToView leftSpaceToView; -@property (nonatomic, copy, readonly) MarginToView rightSpaceToView; -@property (nonatomic, copy, readonly) MarginToView topSpaceToView; -@property (nonatomic, copy, readonly) MarginToView bottomSpaceToView; - -/* - * 设置x、y、width、height、centerX、centerY 值 - */ - -@property (nonatomic, copy, readonly) Margin xIs; -@property (nonatomic, copy, readonly) Margin yIs; -@property (nonatomic, copy, readonly) Margin centerXIs; -@property (nonatomic, copy, readonly) Margin centerYIs; -@property (nonatomic, copy, readonly) WidthHeight widthIs; -@property (nonatomic, copy, readonly) WidthHeight heightIs; - -/* - * 设置最大宽度和高度、最小宽度和高度 - */ - -@property (nonatomic, copy, readonly) WidthHeight maxWidthIs; -@property (nonatomic, copy, readonly) WidthHeight maxHeightIs; -@property (nonatomic, copy, readonly) WidthHeight minWidthIs; -@property (nonatomic, copy, readonly) WidthHeight minHeightIs; - - -/* - * 设置和哪一个参照view的边距相同 - */ - -@property (nonatomic, copy, readonly) MarginEqualToView leftEqualToView; -@property (nonatomic, copy, readonly) MarginEqualToView rightEqualToView; -@property (nonatomic, copy, readonly) MarginEqualToView topEqualToView; -@property (nonatomic, copy, readonly) MarginEqualToView bottomEqualToView; -@property (nonatomic, copy, readonly) MarginEqualToView centerXEqualToView; -@property (nonatomic, copy, readonly) MarginEqualToView centerYEqualToView; - -/* - * 设置宽度或者高度等于参照view的多少倍 - */ - -@property (nonatomic, copy, readonly) WidthHeightEqualToView widthRatioToView; -@property (nonatomic, copy, readonly) WidthHeightEqualToView heightRatioToView; - -@property (nonatomic, copy, readonly) AutoHeight autoHeightRatio; - -/* - * 填充父view(快捷方法) - */ - -@property (nonatomic, copy, readonly) SpaceToSuperView spaceToSuperView; - -@property (nonatomic, weak) UIView *needsAutoResizeView; - -@end - - - - -@interface UIView (SDAutoHeight) - -@property (nonatomic) CGFloat autoHeight; - -@property (nonatomic) UIView *sd_bottomView; -@property (nonatomic) CGFloat sd_bottomViewBottomMargin; - -// 设置普通view内容自适应 -- (void)setupAutoHeightWithBottomView:(UIView *)bottomView bottomMargin:(CGFloat)bottomMargin; - -@end - -@interface UIView (SDLayoutExtention) - -/* 设置圆角 */ -@property (nonatomic, strong) NSNumber *sd_cornerRadius; -@property (nonatomic, strong) NSNumber *sd_cornerRadiusFromWidthRatio; -@property (nonatomic, strong) NSNumber *sd_cornerRadiusFromHeightRatio; - -// 设置等宽子view -@property (nonatomic, strong) NSArray *sd_equalWidthSubviews; - -@end - - -@interface UIScrollView (SDAutoContentSize) - -// 设置scrollview内容自适应 -- (void)setupAutoContentSizeWithBottomView:(UIView *)bottomView bottomMargin:(CGFloat)bottomMargin; - -@end - - -@interface UILabel (SDLabelAutoResize) - -// 设置单行文本label宽度自适应 -- (void)setSigleLineAutoResizeWithMaxWidth:(CGFloat)maxWidth; - -@end - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// ----------------- 以下为此库内部需要用到的类和方法(可以不用看)---------------- - -@interface SDAutoLayoutModelItem : NSObject - -@property (nonatomic, strong) NSNumber *value; -@property (nonatomic, weak) UIView *refView; - -@end - -@interface UIView (SDAutoLayout) - -- (NSMutableArray *)autoLayoutModelsArray; - -- (SDAutoLayoutModel *)sd_layout; - -- (void)addAutoLayoutModel:(SDAutoLayoutModel *)model; - -@property (nonatomic, strong) NSNumber *fixedWith; -@property (nonatomic, strong) NSNumber *fixedHeight; - -@property (nonatomic, strong) NSNumber *sd_maxWidth; - -@property (nonatomic, strong) NSNumber *autoHeightRatioValue; - -@end - - -@interface UIView (SDChangeFrame) - -@property (nonatomic) CGFloat left; -@property (nonatomic) CGFloat top; -@property (nonatomic) CGFloat right; -@property (nonatomic) CGFloat bottom; -@property (nonatomic) CGFloat centerX; -@property (nonatomic) CGFloat centerY; - -@property (nonatomic) CGFloat width; -@property (nonatomic) CGFloat height; - - -@property (nonatomic) CGPoint origin; -@property (nonatomic) CGSize size; - -@end - diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/SDAutoLayout/UIView+SDAutoLayout.m" "b/SDAutoLayout \346\265\213\350\257\225 Demo/SDAutoLayout/UIView+SDAutoLayout.m" deleted file mode 100644 index 73cd803..0000000 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/SDAutoLayout/UIView+SDAutoLayout.m" +++ /dev/null @@ -1,966 +0,0 @@ -// -// UIView+SDAutoLayout.m -// -// Created by gsd on 15/10/6. -// Copyright (c) 2015年 gsd. All rights reserved. -// - -/* - - ********************************************************************************* - * * - * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * - * 帮您解决问题。 * - * QQ : 2689718696(gsdios) * - * Email : gsdios@126.com * - * GitHub: https://github.com/gsdios * - * 新浪微博:GSD_iOS * - * * - ********************************************************************************* - - */ - -#import "UIView+SDAutoLayout.h" -#import "UITableView+SDAutoTableViewCellHeight.h" - -#import - -@interface SDAutoLayoutModel () - -@property (nonatomic, strong) SDAutoLayoutModelItem *width; -@property (nonatomic, strong) SDAutoLayoutModelItem *height; -@property (nonatomic, strong) SDAutoLayoutModelItem *left; -@property (nonatomic, strong) SDAutoLayoutModelItem *top; -@property (nonatomic, strong) SDAutoLayoutModelItem *right; -@property (nonatomic, strong) SDAutoLayoutModelItem *bottom; -@property (nonatomic, strong) NSNumber *centerX; -@property (nonatomic, strong) NSNumber *centerY; - -@property (nonatomic, strong) NSNumber *maxWidth; -@property (nonatomic, strong) NSNumber *maxHeight; -@property (nonatomic, strong) NSNumber *minWidth; -@property (nonatomic, strong) NSNumber *minHeight; - -@property (nonatomic, strong) SDAutoLayoutModelItem *ratio_width; -@property (nonatomic, strong) SDAutoLayoutModelItem *ratio_height; -@property (nonatomic, strong) SDAutoLayoutModelItem *ratio_left; -@property (nonatomic, strong) SDAutoLayoutModelItem *ratio_top; -@property (nonatomic, strong) SDAutoLayoutModelItem *ratio_right; -@property (nonatomic, strong) SDAutoLayoutModelItem *ratio_bottom; - -@property (nonatomic, strong) SDAutoLayoutModelItem *equalLeft; -@property (nonatomic, strong) SDAutoLayoutModelItem *equalRight; -@property (nonatomic, strong) SDAutoLayoutModelItem *equalTop; -@property (nonatomic, strong) SDAutoLayoutModelItem *equalBottom; -@property (nonatomic, strong) SDAutoLayoutModelItem *equalCenterX; -@property (nonatomic, strong) SDAutoLayoutModelItem *equalCenterY; - -@end - -@implementation SDAutoLayoutModel - -@synthesize leftSpaceToView = _leftSpaceToView; -@synthesize rightSpaceToView = _rightSpaceToView; -@synthesize topSpaceToView = _topSpaceToView; -@synthesize bottomSpaceToView = _bottomSpaceToView; -@synthesize widthIs = _widthIs; -@synthesize heightIs = _heightIs; -@synthesize widthRatioToView = _widthRatioToView; -@synthesize heightRatioToView = _heightRatioToView; -@synthesize leftEqualToView = _leftEqualToView; -@synthesize rightEqualToView = _rightEqualToView; -@synthesize topEqualToView = _topEqualToView; -@synthesize bottomEqualToView = _bottomEqualToView; -@synthesize centerXEqualToView = _centerXEqualToView; -@synthesize centerYEqualToView = _centerYEqualToView; -@synthesize xIs = _xIs; -@synthesize yIs = _yIs; -@synthesize centerXIs = _centerXIs; -@synthesize centerYIs = _centerYIs; -@synthesize autoHeightRatio = _autoHeightRatio; -@synthesize spaceToSuperView = _spaceToSuperView; -@synthesize maxWidthIs = _maxWidthIs; -@synthesize maxHeightIs = _maxHeightIs; -@synthesize minWidthIs = _minWidthIs; -@synthesize minHeightIs = _minHeightIs; - -- (MarginToView)leftSpaceToView -{ - if (!_leftSpaceToView) { - _leftSpaceToView = [self marginToViewblockWithKey:@"left"]; - } - return _leftSpaceToView; -} - -- (MarginToView)rightSpaceToView -{ - if (!_rightSpaceToView) { - _rightSpaceToView = [self marginToViewblockWithKey:@"right"]; - } - return _rightSpaceToView; -} - -- (MarginToView)topSpaceToView -{ - if (!_topSpaceToView) { - _topSpaceToView = [self marginToViewblockWithKey:@"top"]; - } - return _topSpaceToView; -} - -- (MarginToView)bottomSpaceToView -{ - if (!_bottomSpaceToView) { - _bottomSpaceToView = [self marginToViewblockWithKey:@"bottom"]; - } - return _bottomSpaceToView; -} - -- (MarginToView)marginToViewblockWithKey:(NSString *)key -{ - __weak typeof(self) weakSelf = self; - return ^(UIView *view, CGFloat value) { - SDAutoLayoutModelItem *item = [SDAutoLayoutModelItem new]; - item.value = @(value); - item.refView = view; - [weakSelf setValue:item forKey:key]; - return weakSelf; - }; -} - -- (WidthHeight)widthIs -{ - if (!_widthIs) { - __weak typeof(self) weakSelf = self; - _widthIs = ^(CGFloat value) { - weakSelf.needsAutoResizeView.width = value; - weakSelf.needsAutoResizeView.fixedWith = @(value); - return weakSelf; - }; - } - return _widthIs; -} - -- (WidthHeight)heightIs -{ - if (!_heightIs) { - __weak typeof(self) weakSelf = self; - _heightIs = ^(CGFloat value) { - weakSelf.needsAutoResizeView.height = value; - weakSelf.needsAutoResizeView.fixedHeight = @(value); - return weakSelf; - }; - } - return _heightIs; -} - -- (WidthHeightEqualToView)widthRatioToView -{ - if (!_widthRatioToView) { - __weak typeof(self) weakSelf = self; - _widthRatioToView = ^(UIView *view, CGFloat value) { - weakSelf.ratio_width = [SDAutoLayoutModelItem new]; - weakSelf.ratio_width.value = @(value); - weakSelf.ratio_width.refView = view; - return weakSelf; - }; - } - return _widthRatioToView; -} - -- (WidthHeightEqualToView)heightRatioToView -{ - if (!_heightRatioToView) { - __weak typeof(self) weakSelf = self; - _heightRatioToView = ^(UIView *view, CGFloat value) { - weakSelf.ratio_height = [SDAutoLayoutModelItem new]; - weakSelf.ratio_height.refView = view; - weakSelf.ratio_height.value = @(value); - return weakSelf; - }; - } - return _heightRatioToView; -} - -- (WidthHeight)maxWidthIs -{ - if (!_maxWidthIs) { - _maxWidthIs = [self limitingWidthHeightWithKey:@"maxWidth"]; - } - return _maxWidthIs; -} - -- (WidthHeight)maxHeightIs -{ - if (!_maxHeightIs) { - _maxHeightIs = [self limitingWidthHeightWithKey:@"maxHeight"]; - } - return _maxHeightIs; -} - -- (WidthHeight)minWidthIs -{ - if (!_minWidthIs) { - _minWidthIs = [self limitingWidthHeightWithKey:@"minWidth"]; - } - return _minWidthIs; -} - -- (WidthHeight)minHeightIs -{ - if (!_minHeightIs) { - _minHeightIs = [self limitingWidthHeightWithKey:@"minHeight"]; - } - return _minHeightIs; -} - - -- (WidthHeight)limitingWidthHeightWithKey:(NSString *)key -{ - __weak typeof(self) weakSelf = self; - - return ^(CGFloat value) { - [weakSelf setValue:@(value) forKey:key]; - - return weakSelf; - }; -} - - -- (MarginEqualToView)marginEqualToViewBlockWithKey:(NSString *)key -{ - __weak typeof(self) weakSelf = self; - - return ^(UIView *view) { - SDAutoLayoutModelItem *item = [SDAutoLayoutModelItem new]; - item.refView = view; - [weakSelf setValue:item forKey:key]; - return weakSelf; - }; -} - -- (MarginEqualToView)leftEqualToView -{ - if (!_leftEqualToView) { - _leftEqualToView = [self marginEqualToViewBlockWithKey:@"equalLeft"]; - } - return _leftEqualToView; -} - -- (MarginEqualToView)rightEqualToView -{ - if (!_rightEqualToView) { - _rightEqualToView = [self marginEqualToViewBlockWithKey:@"equalRight"]; - } - return _rightEqualToView; -} - -- (MarginEqualToView)topEqualToView -{ - if (!_topEqualToView) { - _topEqualToView = [self marginEqualToViewBlockWithKey:@"equalTop"]; - } - return _topEqualToView; -} - -- (MarginEqualToView)bottomEqualToView -{ - if (!_bottomEqualToView) { - _bottomEqualToView = [self marginEqualToViewBlockWithKey:@"equalBottom"]; - } - return _bottomEqualToView; -} - -- (MarginEqualToView)centerXEqualToView -{ - if (!_centerXEqualToView) { - _centerXEqualToView = [self marginEqualToViewBlockWithKey:@"equalCenterX"]; - } - return _centerXEqualToView; -} - -- (MarginEqualToView)centerYEqualToView -{ - if (!_centerYEqualToView) { - _centerYEqualToView = [self marginEqualToViewBlockWithKey:@"equalCenterY"]; - } - return _centerYEqualToView; -} - - -- (Margin)marginBlockWithKey:(NSString *)key -{ - __weak typeof(self) weakSelf = self; - - return ^(CGFloat value) { - - if ([key isEqualToString:@"x"]) { - weakSelf.needsAutoResizeView.left = value; - } else if ([key isEqualToString:@"y"]) { - weakSelf.needsAutoResizeView.top = value; - } else if ([key isEqualToString:@"centerX"]) { - weakSelf.centerX = @(value); - } else if ([key isEqualToString:@"centerY"]) { - weakSelf.centerY = @(value); - } - - return weakSelf; - }; -} - -- (Margin)xIs -{ - if (!_xIs) { - _xIs = [self marginBlockWithKey:@"x"]; - } - return _xIs; -} - -- (Margin)yIs -{ - if (!_yIs) { - _yIs = [self marginBlockWithKey:@"y"]; - } - return _yIs; -} - -- (Margin)centerXIs -{ - if (!_centerXIs) { - _centerXIs = [self marginBlockWithKey:@"centerX"]; - } - return _centerXIs; -} - -- (Margin)centerYIs -{ - if (!_centerYIs) { - _centerYIs = [self marginBlockWithKey:@"centerY"]; - } - return _centerYIs; -} - -- (AutoHeight)autoHeightRatio -{ - __weak typeof(self) weakSelf = self; - - if (!_autoHeightRatio) { - _autoHeightRatio = ^(CGFloat ratioaValue) { - weakSelf.needsAutoResizeView.autoHeightRatioValue = @(ratioaValue); - return weakSelf; - }; - } - return _autoHeightRatio; -} - -- (SpaceToSuperView)spaceToSuperView -{ - __weak typeof(self) weakSelf = self; - - if (!_spaceToSuperView) { - _spaceToSuperView = ^(UIEdgeInsets insets) { - UIView *superView = weakSelf.needsAutoResizeView.superview; - if (superView) { - weakSelf.needsAutoResizeView.sd_layout - .leftSpaceToView(superView, insets.left) - .topSpaceToView(superView, insets.top) - .rightSpaceToView(superView, insets.right) - .bottomSpaceToView(superView, insets.bottom); - } - }; - } - return _spaceToSuperView; -} - -@end - - -@implementation UIView (SDAutoHeight) - -- (void)setupAutoHeightWithBottomView:(UIView *)bottomView bottomMargin:(CGFloat)bottomMargin -{ - self.sd_bottomView = bottomView; - self.sd_bottomViewBottomMargin = bottomMargin; -} - -- (CGFloat)autoHeight -{ - return [objc_getAssociatedObject(self, _cmd) floatValue]; -} - -- (void)setAutoHeight:(CGFloat)autoHeight -{ - objc_setAssociatedObject(self, @selector(autoHeight), @(autoHeight), OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (UIView *)sd_bottomView -{ - return objc_getAssociatedObject(self, _cmd); -} - -- (void)setSd_bottomView:(UIView *)sd_bottomView -{ - objc_setAssociatedObject(self, @selector(sd_bottomView), sd_bottomView, OBJC_ASSOCIATION_ASSIGN); -} - -- (CGFloat)sd_bottomViewBottomMargin -{ - return [objc_getAssociatedObject(self, _cmd) floatValue]; -} - -- (void)setSd_bottomViewBottomMargin:(CGFloat)sd_bottomViewBottomMargin -{ - objc_setAssociatedObject(self, @selector(sd_bottomViewBottomMargin), @(sd_bottomViewBottomMargin), OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -@end - -@implementation UIView (SDLayoutExtention) - -- (NSNumber *)sd_cornerRadius -{ - return objc_getAssociatedObject(self, _cmd); -} - -- (void)setSd_cornerRadius:(NSNumber *)sd_cornerRadius -{ - objc_setAssociatedObject(self, @selector(sd_cornerRadius), sd_cornerRadius, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - - -- (NSNumber *)sd_cornerRadiusFromWidthRatio -{ - return objc_getAssociatedObject(self, _cmd); -} - -- (void)setSd_cornerRadiusFromWidthRatio:(NSNumber *)sd_cornerRadiusFromWidthRatio -{ - objc_setAssociatedObject(self, @selector(sd_cornerRadiusFromWidthRatio), sd_cornerRadiusFromWidthRatio, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - - -- (NSNumber *)sd_cornerRadiusFromHeightRatio -{ - return objc_getAssociatedObject(self, _cmd); -} - -- (void)setSd_cornerRadiusFromHeightRatio:(NSNumber *)sd_cornerRadiusFromHeightRatio -{ - objc_setAssociatedObject(self, @selector(sd_cornerRadiusFromHeightRatio), sd_cornerRadiusFromHeightRatio, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (NSArray *)sd_equalWidthSubviews -{ - return objc_getAssociatedObject(self, _cmd); -} - -- (void)setSd_equalWidthSubviews:(NSArray *)sd_equalWidthSubviews -{ - objc_setAssociatedObject(self, @selector(sd_equalWidthSubviews), sd_equalWidthSubviews, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -@end - -@implementation UIScrollView (SDAutoContentSize) - -- (void)setupAutoContentSizeWithBottomView:(UIView *)bottomView bottomMargin:(CGFloat)bottomMargin -{ - [self setupAutoHeightWithBottomView:bottomView bottomMargin:bottomMargin]; -} - -@end - -@implementation UILabel (SDLabelAutoResize) - -- (void)setSigleLineAutoResizeWithMaxWidth:(CGFloat)maxWidth -{ - self.sd_maxWidth = @(maxWidth); -} - -@end - - -@implementation SDAutoLayoutModelItem - -@end - - -@implementation UIView (SDAutoLayout) - -+ (void)load -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - Method layoutSubviews = class_getInstanceMethod(self, @selector(layoutSubviews)); - Method sd_autolayout = class_getInstanceMethod(self, @selector(sd_autolayout)); - method_exchangeImplementations(layoutSubviews, sd_autolayout); - }); -} - -#pragma mark - properties - -- (NSMutableArray *)autoLayoutModelsArray -{ - if (!objc_getAssociatedObject(self, _cmd)) { - objc_setAssociatedObject(self, _cmd, [NSMutableArray array], OBJC_ASSOCIATION_RETAIN_NONATOMIC); - } - return objc_getAssociatedObject(self, _cmd); -} - -- (NSNumber *)fixedWith -{ - return objc_getAssociatedObject(self, _cmd); -} - -- (void)setFixedWith:(NSNumber *)fixedWith -{ - objc_setAssociatedObject(self, @selector(fixedWith), fixedWith, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (NSNumber *)fixedHeight -{ - return objc_getAssociatedObject(self, _cmd); -} - -- (void)setFixedHeight:(NSNumber *)fixedHeight -{ - objc_setAssociatedObject(self, @selector(fixedHeight), fixedHeight, OBJC_ASSOCIATION_RETAIN); -} - -- (NSNumber *)autoHeightRatioValue -{ - return objc_getAssociatedObject(self, _cmd); -} - -- (void)setAutoHeightRatioValue:(NSNumber *)autoHeightRatioValue -{ - objc_setAssociatedObject(self, @selector(autoHeightRatioValue), autoHeightRatioValue, OBJC_ASSOCIATION_RETAIN); -} - -- (NSNumber *)sd_maxWidth -{ - return objc_getAssociatedObject(self, _cmd); -} - -- (void)setSd_maxWidth:(NSNumber *)sd_maxWidth -{ - objc_setAssociatedObject(self, @selector(sd_maxWidth), sd_maxWidth, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (SDAutoLayoutModel *)ownLayoutModel -{ - return objc_getAssociatedObject(self, _cmd); -} - -- (void)setOwnLayoutModel:(SDAutoLayoutModel *)ownLayoutModel -{ - objc_setAssociatedObject(self, @selector(ownLayoutModel), ownLayoutModel, OBJC_ASSOCIATION_RETAIN); -} - -- (SDAutoLayoutModel *)sd_layout -{ - SDAutoLayoutModel *model = [self ownLayoutModel]; - if (!model) { - model = [SDAutoLayoutModel new]; - model.needsAutoResizeView = self; - [self setOwnLayoutModel:model]; - [self.superview.autoLayoutModelsArray addObject:model]; - } - - return model; -} - -- (void)sd_autolayout -{ - [self sd_autolayout]; - - if (self.sd_equalWidthSubviews.count) { - __block CGFloat totalMargin = 0; - [self.sd_equalWidthSubviews enumerateObjectsUsingBlock:^(UIView * _Nonnull view, NSUInteger idx, BOOL * _Nonnull stop) { - SDAutoLayoutModel *model = view.sd_layout; - totalMargin += ([model.left.value floatValue] + [model.right.value floatValue]); - }]; - CGFloat averageWidth = (self.width - totalMargin) / self.sd_equalWidthSubviews.count; - [self.sd_equalWidthSubviews enumerateObjectsUsingBlock:^(UIView * _Nonnull view, NSUInteger idx, BOOL * _Nonnull stop) { - view.width = averageWidth; - view.fixedWith = @(averageWidth); - }]; - } - - if (self.autoLayoutModelsArray.count) { - [self.autoLayoutModelsArray enumerateObjectsUsingBlock:^(SDAutoLayoutModel *model, NSUInteger idx, BOOL *stop) { - [self sd_resizeWithModel:model]; - }]; - } - - if (self.tag == kSDModelCellTag && [self isKindOfClass:NSClassFromString(@"UITableViewCellContentView")]) { - UITableViewCell *cell = (UITableViewCell *)(self.superview); - if ([cell isKindOfClass:NSClassFromString(@"UITableViewCellScrollView")]) { - cell = (UITableViewCell *)cell.superview; - } - if ([cell isKindOfClass:[UITableViewCell class]]) { - cell.autoHeight = cell.sd_bottomView.bottom + cell.sd_bottomViewBottomMargin; - } - } else if (![self isKindOfClass:[UITableViewCell class]] && self.sd_bottomView) { - if ([self isKindOfClass:[UIScrollView class]]) { - CGFloat contentHeight = self.sd_bottomView.bottom + self.sd_bottomViewBottomMargin; - UIScrollView *scrollView = (UIScrollView *)self; - CGSize contentSize = scrollView.contentSize; - contentSize.height = contentHeight; - if (contentSize.width <= 0) { - contentSize.width = scrollView.width; - } - scrollView.contentSize = contentSize; - } - } -} - -- (void)sd_resizeWithModel:(SDAutoLayoutModel *)model -{ - UIView *view = model.needsAutoResizeView; - - if (view.sd_maxWidth && (model.rightSpaceToView || model.rightEqualToView)) { // 靠右布局前提设置 - if ([view isKindOfClass:[UILabel class]]) { - UILabel *label = (UILabel *)view; - CGFloat width = [view.sd_maxWidth floatValue] > 0 ? [view.sd_maxWidth floatValue] : MAXFLOAT; - label.numberOfLines = 1; - if (label.text.length) { - CGRect rect = [label.text boundingRectWithSize:CGSizeMake(width, label.height) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName : label.font} context:nil]; - label.width = rect.size.width; - } else { - label.width = 0; - } - } - } - - if (view.autoHeightRatioValue && view.width > 0 && (model.bottomEqualToView || model.bottomSpaceToView)) { // 底部布局前提设置 - if ([view.autoHeightRatioValue floatValue] > 0) { - view.height = view.width * [view.autoHeightRatioValue floatValue]; - } else { - if ([view isKindOfClass:[UILabel class]]) { - UILabel *label = (UILabel *)view; - label.numberOfLines = 0; - if (label.text.length) { - CGRect rect = [label.text boundingRectWithSize:CGSizeMake(label.width, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName : label.font} context:nil]; - label.height = rect.size.height; - } else { - label.height = 0; - } - } else { - view.height = 0; - } - } - } - - if (model.width) { - view.width = [model.width.value floatValue]; - view.fixedWith = @(view.width); - } else if (model.ratio_width) { - view.width = model.ratio_width.refView.width * [model.ratio_width.value floatValue]; - view.fixedWith = @(view.width); - } - - if (model.height) { - view.height = [model.height.value floatValue]; - view.fixedHeight = @(view.height); - } else if (model.ratio_height) { - view.height = [model.ratio_height.value floatValue] * model.ratio_height.refView.height; - view.fixedHeight = @(view.height); - } - - if (model.left) { - if (view.superview == model.left.refView) { - if (!view.fixedWith) { // view.autoLeft && view.autoRight - view.width = view.right - [model.left.value floatValue]; - } - view.left = [model.left.value floatValue]; - } else { - if (!view.fixedWith) { // view.autoLeft && view.autoRight - view.width = view.right - model.left.refView.right - [model.left.value floatValue]; - } - view.left = model.left.refView.right + [model.left.value floatValue]; - } - - } else if (model.equalLeft) { - if (!view.fixedWith) { - view.width = view.right - model.equalLeft.refView.left; - } - if (view.superview == model.equalLeft.refView) { - view.left = 0; - } else { - view.left = model.equalLeft.refView.left; - } - } else if (model.equalCenterX) { - if (view.superview == model.equalCenterX.refView) { - view.centerX = model.equalCenterX.refView.width * 0.5; - } else { - view.centerX = model.equalCenterX.refView.centerX; - } - } else if (model.centerX) { - view.centerX = [model.centerX floatValue]; - } - - if (model.right) { - if (view.superview == model.right.refView) { - if (!view.fixedWith) { // view.autoLeft && view.autoRight - view.width = model.right.refView.width - view.left - [model.right.value floatValue]; - } - view.right = model.right.refView.width - [model.right.value floatValue]; - } else { - if (!view.fixedWith) { // view.autoLeft && view.autoRight - view.width = model.right.refView.left - view.left - [model.right.value floatValue]; - } - view.right = model.right.refView.left - [model.right.value floatValue]; - } - } else if (model.equalRight) { - if (!view.fixedWith) { - view.width = model.equalRight.refView.right - view.left; - } - - view.right = model.equalRight.refView.right; - if (view.superview == model.equalRight.refView) { - view.right = model.equalRight.refView.width; - } - - } - - if (model.top) { - if (view.superview == model.top.refView) { - if (!view.fixedHeight) { // view.autoTop && view.autoBottom && view.bottom - view.height = view.bottom - [model.top.value floatValue]; - } - view.top = [model.top.value floatValue]; - } else { - if (!view.fixedHeight) { // view.autoTop && view.autoBottom && view.bottom - view.height = view.bottom - model.top.refView.bottom - [model.top.value floatValue]; - } - view.top = model.top.refView.bottom + [model.top.value floatValue]; - } - } else if (model.equalTop) { - if (view.superview == model.equalTop.refView) { - if (!view.fixedHeight) { - view.height = view.bottom; - } - view.top = 0; - } else { - if (!view.fixedHeight) { - view.height = view.bottom - model.equalTop.refView.top; - } - view.top = model.equalTop.refView.top; - } - } else if (model.equalCenterY) { - if (view.superview == model.equalCenterY.refView) { - view.centerY = model.equalCenterY.refView.height * 0.5; - } else { - view.centerY = model.equalCenterY.refView.centerY; - } - } else if (model.centerY) { - view.centerY = [model.centerY floatValue]; - } - - if (model.bottom) { - if (view.superview == model.bottom.refView) { - if (!view.fixedHeight) { - view.height = view.superview.height - view.top - [model.bottom.value floatValue]; - } - view.bottom = model.bottom.refView.height - [model.bottom.value floatValue]; - } else { - if (!view.fixedHeight) { - view.height = model.bottom.refView.top - view.top - [model.bottom.value floatValue]; - } - view.bottom = model.bottom.refView.top - [model.bottom.value floatValue]; - } - - } else if (model.equalBottom) { - if (view.superview == model.equalBottom.refView) { - if (!view.fixedHeight) { - view.height = view.superview.height - view.top; - } - view.bottom = model.equalBottom.refView.height; - } else { - if (!view.fixedHeight) { - view.height = model.equalBottom.refView.bottom - view.top; - } - view.bottom = model.equalBottom.refView.bottom; - } - } - - if (view.autoHeightRatioValue && view.width > 0) { - if ([view.autoHeightRatioValue floatValue] > 0) { - view.height = view.width * [view.autoHeightRatioValue floatValue]; - } else { - if ([view isKindOfClass:[UILabel class]]) { - UILabel *label = (UILabel *)view; - label.numberOfLines = 0; - if (label.text.length) { - CGRect rect = [label.text boundingRectWithSize:CGSizeMake(label.width, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName : label.font} context:nil]; - label.height = rect.size.height; - } else { - label.height = 0; - } - } else { - view.height = 0; - } - } - } - - if (view.sd_maxWidth) { - if ([view isKindOfClass:[UILabel class]]) { - UILabel *label = (UILabel *)view; - CGFloat width = [view.sd_maxWidth floatValue] > 0 ? [view.sd_maxWidth floatValue] : MAXFLOAT; - label.numberOfLines = 1; - if (label.text.length) { - CGRect rect = [label.text boundingRectWithSize:CGSizeMake(width, label.height) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName : label.font} context:nil]; - label.width = rect.size.width; - } else { - label.width = 0; - } - } - } - - if (model.maxWidth && [model.maxWidth floatValue] < view.width) { - view.width = [model.maxWidth floatValue]; - } - - if (model.minWidth && [model.minWidth floatValue] > view.width) { - view.width = [model.minWidth floatValue]; - } - - if (model.maxHeight && [model.maxHeight floatValue] < view.height) { - view.height = [model.maxHeight floatValue]; - } - - if (model.minHeight && [model.minHeight floatValue] > view.height) { - view.height = [model.minHeight floatValue]; - } - - CGFloat cornerRadius = view.layer.cornerRadius; - CGFloat newCornerRadius = 0; - - if (view.sd_cornerRadius && (cornerRadius != [view.sd_cornerRadius floatValue])) { - newCornerRadius = [view.sd_cornerRadius floatValue]; - } else if (view.sd_cornerRadiusFromWidthRatio && (cornerRadius != [view.sd_cornerRadiusFromWidthRatio floatValue] * view.width)) { - newCornerRadius = view.width * [view.sd_cornerRadiusFromWidthRatio floatValue]; - } else if (view.sd_cornerRadiusFromHeightRatio && (cornerRadius != view.height * [view.sd_cornerRadiusFromHeightRatio floatValue])) { - newCornerRadius = view.height * [view.sd_cornerRadiusFromHeightRatio floatValue]; - } - - if (newCornerRadius > 0) { - view.layer.cornerRadius = newCornerRadius; - view.clipsToBounds = YES; - } - -} - -- (void)addAutoLayoutModel:(SDAutoLayoutModel *)model -{ - [self.autoLayoutModelsArray addObject:model]; -} - -@end - - -@implementation UIView (SDChangeFrame) - -- (CGFloat)left { - return self.frame.origin.x; -} - -- (void)setLeft:(CGFloat)x { - CGRect frame = self.frame; - frame.origin.x = x; - self.frame = frame; -} - -- (CGFloat)top { - return self.frame.origin.y; -} - -- (void)setTop:(CGFloat)y { - CGRect frame = self.frame; - frame.origin.y = y; - self.frame = frame; -} - -- (CGFloat)right { - return self.frame.origin.x + self.frame.size.width; -} - -- (void)setRight:(CGFloat)right { - CGRect frame = self.frame; - frame.origin.x = right - frame.size.width; - self.frame = frame; -} - -- (CGFloat)bottom { - return self.frame.origin.y + self.frame.size.height; -} - -- (void)setBottom:(CGFloat)bottom { - CGRect frame = self.frame; - frame.origin.y = bottom - frame.size.height; - self.frame = frame; -} - -- (CGFloat)centerX -{ - return self.left + self.width * 0.5; -} - -- (void)setCenterX:(CGFloat)centerX -{ - self.left = centerX - self.width * 0.5; -} - -- (CGFloat)centerY -{ - return self.top + self.height * 0.5; -} - -- (void)setCenterY:(CGFloat)centerY -{ - self.top = centerY - self.height * 0.5; -} - -- (CGFloat)width { - return self.frame.size.width; -} - -- (void)setWidth:(CGFloat)width { - CGRect frame = self.frame; - frame.size.width = width; - self.frame = frame; -} - -- (CGFloat)height { - return self.frame.size.height; -} - -- (void)setHeight:(CGFloat)height { - CGRect frame = self.frame; - frame.size.height = height; - self.frame = frame; -} - -- (CGPoint)origin { - return self.frame.origin; -} - -- (void)setOrigin:(CGPoint)origin { - CGRect frame = self.frame; - frame.origin = origin; - self.frame = frame; -} - -- (CGSize)size { - return self.frame.size; -} - -- (void)setSize:(CGSize)size { - CGRect frame = self.frame; - frame.size = size; - self.frame = frame; -} - -@end - diff --git "a/SDAutoLayout \346\265\213\350\257\225 DemoTests/SDAutoLayout____DemoTests.m" "b/SDAutoLayout \346\265\213\350\257\225 DemoTests/SDAutoLayout____DemoTests.m" deleted file mode 100644 index c0ea95a..0000000 --- "a/SDAutoLayout \346\265\213\350\257\225 DemoTests/SDAutoLayout____DemoTests.m" +++ /dev/null @@ -1,40 +0,0 @@ -// -// SDAutoLayout____DemoTests.m -// SDAutoLayout 测试 DemoTests -// -// Created by gsd on 15/10/12. -// Copyright (c) 2015年 gsd. All rights reserved. -// - -#import -#import - -@interface SDAutoLayout____DemoTests : XCTestCase - -@end - -@implementation SDAutoLayout____DemoTests - -- (void)setUp { - [super setUp]; - // Put setup code here. This method is called before the invocation of each test method in the class. -} - -- (void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; -} - -- (void)testExample { - // This is an example of a functional test case. - XCTAssert(YES, @"Pass"); -} - -- (void)testPerformanceExample { - // This is an example of a performance test case. - [self measureBlock:^{ - // Put the code you want to measure the time of here. - }]; -} - -@end diff --git a/SDAutoLayout.podspec b/SDAutoLayout.podspec index 9df7993..8ae8842 100644 --- a/SDAutoLayout.podspec +++ b/SDAutoLayout.podspec @@ -1,126 +1,28 @@ -# -# Be sure to run `pod spec lint SDAutoLayout.podspec' to ensure this is a -# valid spec and to remove all comments including this before submitting the spec. -# -# To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html -# To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ -# - Pod::Spec.new do |s| - # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # These will help people to find your library, and whilst it - # can feel like a chore to fill in it's definitely to your advantage. The - # summary should be tweet-length, and the description more in depth. - # - s.name = "SDAutoLayout" - s.version = "1.1" - s.summary = "一行代码搞定自动布局!致力于做最简单易用的Autolayout库。The most easy way for autolayout." + s.version = "2.2.0" + s.summary = "The most easy way for autoLayout. 2.2.0版本更新内容:解决部分开发者反应因出现“UITableViewCellContentView”而导致应用审核被拒问题" s.homepage = "https://github.com/gsdios/SDAutoLayout" # s.screenshots = "https://camo.githubusercontent.com/5d9e879c7006297b3d6e12c20c6cd1e15bf83016/687474703a2f2f7777332e73696e61696d672e636e2f626d6964646c652f39623831343665646777316578346d756b69787236673230396730376c6864742e676966" - - # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # Licensing your code is important. See http://choosealicense.com for more info. - # CocoaPods will detect a license file if there is a named LICENSE* - # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. - # - s.license = "MIT" - # s.license = { :type => "MIT", :file => "FILE_LICENSE" } - - - # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # Specify the authors of the library, with email addresses. Email addresses - # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also - # accepts just a name if you'd rather not provide an email address. - # - # Specify a social_media_url where others can refer to, for example a twitter - # profile URL. - # s.author = { "GSD_iOS" => "gsdios@126.com" } - # Or just: s.author = "GSD_iOS" - # s.authors = { "GSD_iOS" => "gsdios@126.com" } - # s.social_media_url = "http://twitter.com/GSD_iOS" - - # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # If this Pod runs only on iOS or OS X, then specify the platform and - # the deployment target. You can optionally include the target after the platform. - # s.platform = :ios s.platform = :ios, "7.0" - # When using multiple platforms - # s.ios.deployment_target = "5.0" - # s.osx.deployment_target = "10.7" - - - # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # Specify the location from where the source should be retrieved. - # Supports git, hg, bzr, svn and HTTP. - # - - s.source = { :git => "https://github.com/gsdios/SDAutoLayout.git", :tag => "1.1"} - + s.source = { :git => "https://github.com/gsdios/SDAutoLayout.git", :tag => "2.2.0"} - # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # CocoaPods is smart about how it includes source code. For source files - # giving a folder will include any swift, h, m, mm, c & cpp files. - # For header files it will include any header in the folder. - # Not including the public_header_files will make all headers public. - # - - s.source_files = "SDAutoLayout 测试 Demo/SDAutoLayout/**/*.{h,m}" + s.source_files = "SDAutoLayout/**/*.{h,m}" # s.public_header_files = "Classes/**/*.h" - # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # A list of resources included with the Pod. These are copied into the - # target bundle with a build phase script. Anything else will be cleaned. - # You can preserve files from being cleaned, please don't preserve - # non-essential files like tests, examples and documentation. - # - - # s.resource = "icon.png" - # s.resources = "Resources/*.png" - - # s.preserve_paths = "FilesToSave", "MoreFilesToSave" - - - # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # Link your library with frameworks, or libraries. Libraries do not include - # the lib prefix of their name. - # - - # s.framework = "SomeFramework" - # s.frameworks = "SomeFramework", "AnotherFramework" - - # s.library = "iconv" - # s.libraries = "iconv", "xml2" - - - # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # If your library depends on compiler flags you can set them in the xcconfig hash - # where they will only apply to your library. If you depend on other Podspecs - # you can include multiple dependencies to ensure it works. - s.requires_arc = true - # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } - # s.dependency "JSONKit", "~> 1.4" + # s.dependency "JSONKit", "~> 1.4" end diff --git a/SDAutoLayout/SDAutoLayout.h b/SDAutoLayout/SDAutoLayout.h new file mode 100644 index 0000000..6c5c5f1 --- /dev/null +++ b/SDAutoLayout/SDAutoLayout.h @@ -0,0 +1,24 @@ +// +// SDAutoLayout.h +// SDAutoLayoutDemo +// +// Created by gsd on 16/6/27. +// Copyright © 2016年 gsd. All rights reserved. +// + + +/* + + SDAutoLayout + 版本:2.1.7 + 发布:2016.08.12 + + */ + +#ifndef SDAutoLayout_h +#define SDAutoLayout_h + +#import "UIView+SDAutoLayout.h" +#import "UITableView+SDAutoTableViewCellHeight.h" + +#endif diff --git a/SDAutoLayout/UITableView+SDAutoTableViewCellHeight.h b/SDAutoLayout/UITableView+SDAutoTableViewCellHeight.h new file mode 100644 index 0000000..4307d2e --- /dev/null +++ b/SDAutoLayout/UITableView+SDAutoTableViewCellHeight.h @@ -0,0 +1,169 @@ +// +// UITableView+SDAutoTableViewCellHeight.h +// SDAutoLayout 测试 Demo +// +// Created by aier on 15/11/1. +// Copyright © 2015年 gsd. All rights reserved. +// + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * QQ : 2689718696(gsdios) * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * * + ********************************************************************************* + + */ + + + + +/* + PS:cell高度自适应前提>>应该调用cell的“- (void)setupAutoHeightWithBottomView:(UIView *)bottomView bottomMargin:(CGFloat)bottomMargin”方法进行cell的自动高度设置 + */ + +#import + +#import "UIView+SDAutoLayout.h" + +@class SDCellAutoHeightManager; + +typedef void (^AutoCellHeightDataSettingBlock)(UITableViewCell * _Nonnull cell); + +#define kSDModelCellTag 199206 + + + +#pragma mark - UITableView 方法,返回自动计算出的cell高度 + +@interface UITableView (SDAutoTableViewCellHeight) + +@property (nonatomic, strong) SDCellAutoHeightManager * _Nullable cellAutoHeightManager; + + +/** + * 返回计算出的cell高度(普通简化版方法,同样只需一步设置即可完成)(用法:单cell详见demo5,多cell详见demo7) + * model : cell的数据模型实例 + * keyPath : cell的数据模型属性的属性名字符串(即kvc原理中的key) + * cellClass : 当前的indexPath对应的cell的class + * contentViewWidth : cell的contentView的宽度 + */ +- (CGFloat)cellHeightForIndexPath:(NSIndexPath *_Nonnull)indexPath model:(id _Nullable )model keyPath:(NSString *_Nullable)keyPath cellClass:(Class _Nullable )cellClass contentViewWidth:(CGFloat)contentViewWidth; + +/** + * 返回计算出的cell高度(普通简化版方法,同样只需一步设置即可完成)(用法:见DemoVC14) + * cellClass : 当前的indexPath对应的cell的class + * contentViewWidth : cell的contentView的宽度 + * cellDataSetting : 设置cell数据的block + */ +- (CGFloat)cellHeightForIndexPath:(NSIndexPath *_Nonnull)indexPath cellClass:(Class _Nonnull )cellClass cellContentViewWidth:(CGFloat)width cellDataSetting:(AutoCellHeightDataSettingBlock _Nonnull )cellDataSetting; + +/** 刷新tableView但不清空之前已经计算好的高度缓存,用于直接将新数据拼接在旧数据之后的tableView刷新 */ +- (void)reloadDataWithExistedHeightCache; + +/** 刷新tableView同时调整已经计算好的高度缓存,用于直接将新数据插在旧数据前面的tableView的刷新 */ +- (void)reloadDataWithInsertingDataAtTheBeginingOfSection:(NSInteger)section newDataCount:(NSInteger)count; + +/** + * 刷新tableView同时调整已经计算好的高度缓存,用于直接将新数据插在旧数据前面的tableView的刷新(用于刷新多个section) + * sectionNumsArray : 要刷新的所有section序号组成的数组, 例@[@(0), @(1)] + * dataCountsArray : 每个section的数据条数组成的数组, 例@[@(20), @(10)] + */ +- (void)reloadDataWithInsertingDataAtTheBeginingOfSections:(NSArray *_Nonnull)sectionNumsArray newDataCounts:(NSArray *_Nonnull)dataCountsArray; + +/** 返回所有cell的高度总和 */ +- (CGFloat)cellsTotalHeight; + +@property (nonatomic, copy) AutoCellHeightDataSettingBlock _Nonnull cellDataSetting; + +@end + + + + +#pragma mark - UITableViewController 方法,返回自动计算出的cell高度 + +@interface UITableViewController (SDTableViewControllerAutoCellHeight) + +/** (UITableViewController方法)升级版!一行代码(一步设置)搞定tableview的cell高度自适应,同时适用于单cell和多cell,性能比普通版稍微差一些,不建议在数据量大的tableview中使用 */ +- (CGFloat)cellHeightForIndexPath:(NSIndexPath *_Nonnull)indexPath cellContentViewWidth:(CGFloat)width; + +@end + + + +#pragma mark - NSObject 方法,返回自动计算出的cell高度 + +@interface NSObject (SDAnyObjectAutoCellHeight) + +/** (NSObject方法)升级版!一行代码(一步设置)搞定tableview的cell高度自适应,同时适用于单cell和多cell,性能比普通版稍微差一些,不建议在数据量大的tableview中使用 */ +- (CGFloat)cellHeightForIndexPath:(NSIndexPath *_Nonnull)indexPath cellContentViewWidth:(CGFloat)width tableView:(UITableView *_Nonnull)tableView; + +@end + + + + + + + + + + + + + + + + + +// ------------------------------- 以下为库内部使用无须了解 -------------------- + +@interface SDCellAutoHeightManager : NSObject + +@property (nonatomic, assign) BOOL shouldKeepHeightCacheWhenReloadingData; + +@property (nonatomic, assign) CGFloat contentViewWidth; + +@property (nonatomic, assign) Class _Nullable cellClass; + +@property (nonatomic, assign) CGFloat cellHeight; + +@property (nonatomic, strong) UITableViewCell * _Nullable modelCell; + +@property (nonatomic, strong) NSMutableDictionary * _Nullable subviewFrameCacheDict; + +@property (nonatomic, strong, readonly) NSDictionary * _Nullable heightCacheDict; + +@property (nonatomic, copy) AutoCellHeightDataSettingBlock _Nullable cellDataSetting; + +- (void)clearHeightCache; + +- (void)clearHeightCacheOfIndexPaths:(NSArray *_Nonnull)indexPaths; + +- (void)deleteThenResetHeightCache:(NSIndexPath *_Nonnull)indexPathToDelete; + +- (void)insertNewDataAtTheBeginingOfSection:(NSInteger)section newDataCount:(NSInteger)count; + +- (void)insertNewDataAtIndexPaths:(NSArray *_Nonnull)indexPaths; + +- (NSNumber *_Nonnull)heightCacheForIndexPath:(NSIndexPath *_Nonnull)indexPath; + +- (CGFloat)cellHeightForIndexPath:(NSIndexPath *_Nonnull)indexPath model:(id _Nullable )model keyPath:(NSString *_Nullable)keyPath; + +- (CGFloat)cellHeightForIndexPath:(NSIndexPath *_Nonnull)indexPath model:(id _Nonnull )model keyPath:(NSString *_Nonnull)keyPath cellClass:(Class _Nonnull )cellClass; + + +- (NSMutableArray *_Nullable)subviewFrameCachesWithIndexPath:(NSIndexPath *_Nonnull)indexPath;; +- (void)setSubviewFrameCache:(CGRect)rect WithIndexPath:(NSIndexPath *_Nonnull)indexPath; + +- (instancetype _Nonnull )initWithCellClass:(Class _Nonnull )cellClass tableView:(UITableView *_Nonnull)tableView; ++ (instancetype _Nonnull )managerWithCellClass:(Class _Nonnull )cellClass tableView:(UITableView *_Nonnull)tableView; +@end + diff --git a/SDAutoLayout/UITableView+SDAutoTableViewCellHeight.m b/SDAutoLayout/UITableView+SDAutoTableViewCellHeight.m new file mode 100644 index 0000000..5b5b5a8 --- /dev/null +++ b/SDAutoLayout/UITableView+SDAutoTableViewCellHeight.m @@ -0,0 +1,537 @@ +// +// UITableView+SDAutoTableViewCellHeight.m +// SDAutoLayout 测试 Demo +// +// Created by aier on 15/11/1. +// Copyright © 2015年 gsd. All rights reserved. +// + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * QQ : 2689718696(gsdios) * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * * + ********************************************************************************* + + */ + +#import "UITableView+SDAutoTableViewCellHeight.h" +#import + +@interface SDCellAutoHeightManager () + +@property (nonatomic, weak) UITableView *modelTableview; + +@end + +@implementation SDCellAutoHeightManager +{ + NSMutableDictionary *_cacheDictionary; + NSMutableDictionary *_modelCellsDict; +} + +- (instancetype)init +{ + if (self = [super init]) { + [self setup]; + } + return self; +} + +- (instancetype)initWithCellClass:(Class)cellClass tableView:(UITableView *)tableView +{ + if (self = [super init]) { + [self setup]; + self.modelTableview = tableView; + [self registerCellWithCellClass:cellClass]; + } + return self; +} + +- (instancetype)initWithCellClasses:(NSArray *)cellClassArray tableView:(UITableView *)tableView +{ + if (self = [super init]) { + [self setup]; + self.modelTableview = tableView; + [cellClassArray enumerateObjectsUsingBlock:^(Class obj, NSUInteger idx, BOOL *stop) { + [self registerCellWithCellClass:obj]; + }]; + } + return self; +} + +- (void)setup +{ + _cacheDictionary = [NSMutableDictionary new]; + _modelCellsDict = [NSMutableDictionary new]; +} + +- (void)registerCellWithCellClass:(Class)cellClass +{ + [_modelTableview registerClass:cellClass forCellReuseIdentifier:NSStringFromClass(cellClass)]; + self.modelCell = [_modelTableview dequeueReusableCellWithIdentifier:NSStringFromClass(cellClass)]; + + if (!self.modelCell.contentView.subviews.count) { + NSString *path = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%@.nib", NSStringFromClass(cellClass)] ofType:nil]; + if (path) { + self.modelCell = nil; + [_modelTableview registerNib:[UINib nibWithNibName:NSStringFromClass(cellClass) bundle:nil] forCellReuseIdentifier:NSStringFromClass(cellClass)]; + self.modelCell = [_modelTableview dequeueReusableCellWithIdentifier:NSStringFromClass(cellClass)]; + } + } + if (self.modelCell) { + [_modelCellsDict setObject:self.modelCell forKey:NSStringFromClass(cellClass)]; + } +} + ++ (instancetype)managerWithCellClass:(Class)cellClass tableView:(UITableView *)tableView +{ + SDCellAutoHeightManager *manager = [[self alloc] initWithCellClass:cellClass tableView:tableView]; + return manager; +} + +- (UITableViewCell *)modelCell +{ + if (_modelCell.contentView.tag != kSDModelCellTag) { + _modelCell.contentView.tag = kSDModelCellTag; + } + return _modelCell; +} + +- (NSDictionary *)heightCacheDict +{ + return _cacheDictionary; +} + +- (void)clearHeightCache +{ + [_cacheDictionary removeAllObjects]; + [_subviewFrameCacheDict removeAllObjects]; +} + +- (NSString *)cacheKeyForIndexPath:(NSIndexPath *)indexPath +{ + return [NSString stringWithFormat:@"%ld-%ld", (long)indexPath.section, (long)indexPath.row]; +} + +- (void)clearHeightCacheOfIndexPaths:(NSArray *)indexPaths +{ + [indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) { + NSString *cacheKey = [self cacheKeyForIndexPath:indexPath]; + [self->_cacheDictionary removeObjectForKey:cacheKey]; + [self->_subviewFrameCacheDict removeObjectForKey:cacheKey]; + }]; +} + +- (void)deleteThenResetHeightCache:(NSIndexPath *)indexPathToDelete +{ + + NSString *cacheKey = [self cacheKeyForIndexPath:indexPathToDelete]; + [_cacheDictionary removeObjectForKey:cacheKey]; + [_subviewFrameCacheDict removeObjectForKey:cacheKey]; + + long sectionOfToDeleteItem = indexPathToDelete.section; + long rowOfToDeleteItem = indexPathToDelete.row; + NSMutableDictionary *tempHeightCacheDict = [NSMutableDictionary new]; + NSMutableDictionary *tempFrameCacheDict = [NSMutableDictionary new]; + for (NSString *key in _cacheDictionary.allKeys) { + NSArray *res = [key componentsSeparatedByString:@"-"]; + long section = [res.firstObject integerValue]; + long row = [res.lastObject integerValue]; + if (section == sectionOfToDeleteItem && row > rowOfToDeleteItem) { + NSNumber *heightCache = _cacheDictionary[key]; + NSArray *frameCache = _subviewFrameCacheDict[key]; + NSString *newKey = [NSString stringWithFormat:@"%ld-%ld", section, (row - 1)]; + [tempHeightCacheDict setValue:heightCache forKey:newKey]; + [tempFrameCacheDict setValue:frameCache forKey:newKey]; + [_cacheDictionary removeObjectForKey:key]; + [_subviewFrameCacheDict removeObjectForKey:key]; + } + } + [_cacheDictionary addEntriesFromDictionary:tempHeightCacheDict]; + [_subviewFrameCacheDict addEntriesFromDictionary:tempFrameCacheDict]; + +} + +- (void)insertNewDataAtTheBeginingOfSection:(NSInteger)section newDataCount:(NSInteger)count +{ + NSMutableDictionary *tempHeightCacheDict = [NSMutableDictionary new]; + NSMutableDictionary *tempFrameCacheDict = [NSMutableDictionary new]; + for (NSString *key in _cacheDictionary.allKeys) { + NSArray *res = [key componentsSeparatedByString:@"-"]; + long originalSection = [res.firstObject integerValue]; + long row = [res.lastObject integerValue]; + if (originalSection == section) { + NSNumber *heightCache = _cacheDictionary[key]; + NSArray *frameCache = _subviewFrameCacheDict[key]; + NSString *newKey = [NSString stringWithFormat:@"%ld-%ld", originalSection, (row + count)]; + [tempHeightCacheDict setValue:heightCache forKey:newKey]; + [tempFrameCacheDict setValue:frameCache forKey:newKey]; + [_cacheDictionary removeObjectForKey:key]; + [_subviewFrameCacheDict removeObjectForKey:key]; + } + } + [_cacheDictionary addEntriesFromDictionary:tempHeightCacheDict]; + [_subviewFrameCacheDict addEntriesFromDictionary:tempFrameCacheDict]; +} + +- (void)insertNewDataAtIndexPaths:(NSArray *)indexPaths +{ + NSMutableDictionary *sectionsdict = [NSMutableDictionary new]; + for (NSIndexPath *indexPath in indexPaths) { + NSString *sectionkey = [@(indexPath.section) stringValue]; + if (![sectionsdict objectForKey:sectionkey]) { + [sectionsdict setValue:[NSMutableArray new] forKey:sectionkey]; + } + NSMutableArray *arr = sectionsdict[sectionkey]; + [arr addObject:indexPath]; + } + for (NSString *sectionkey in sectionsdict.allKeys) { + NSMutableArray *tempHeightCaches = [NSMutableArray new]; + NSMutableArray *tempFrameCaches = [NSMutableArray new]; + NSInteger section = [sectionkey integerValue]; + NSInteger rowCount = [self.modelTableview numberOfRowsInSection:section]; + if (rowCount <= 0) { + continue; + } else { + for (int i = 0; i < rowCount; i++) { + [tempHeightCaches addObject:[NSNull null]]; + [tempFrameCaches addObject:[NSNull null]]; + } + } + + for (NSString *key in _cacheDictionary.allKeys) { + NSArray *res = [key componentsSeparatedByString:@"-"]; + long originalSection = [res.firstObject integerValue]; + long row = [res.lastObject integerValue]; + if (originalSection == section) { + NSNumber *heightCache = _cacheDictionary[key]; + NSArray *frameCache = _subviewFrameCacheDict[key]; + [tempHeightCaches setObject:heightCache atIndexedSubscript:row]; + [tempFrameCaches setObject:frameCache atIndexedSubscript:row]; + [_cacheDictionary removeObjectForKey:key]; + [_subviewFrameCacheDict removeObjectForKey:key]; + } + } + NSMutableArray *objsToInsert = [NSMutableArray new]; + NSMutableIndexSet *indexSet = [NSMutableIndexSet new]; + NSArray *indexPaths = sectionsdict[sectionkey]; + [indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *obj, NSUInteger idx, BOOL *stop) { + [objsToInsert addObject:[NSNull null]]; + [indexSet addIndex:obj.row]; + }]; + [tempHeightCaches insertObjects:objsToInsert atIndexes:indexSet]; + [tempFrameCaches insertObjects:objsToInsert atIndexes:indexSet]; + [tempHeightCaches enumerateObjectsUsingBlock:^(NSNumber *heightCache, NSUInteger idx, BOOL *stop) { + if (![heightCache isKindOfClass:[NSNull class]]) { + NSString *key = [NSString stringWithFormat:@"%zd-%zd", section, idx]; + [self->_cacheDictionary setValue:heightCache forKey:key]; + [self->_subviewFrameCacheDict setValue:[tempFrameCaches objectAtIndex:idx] forKey:key]; + } + }]; + } +} + +- (NSNumber *)heightCacheForIndexPath:(NSIndexPath *)indexPath +{ + /* + 如果程序卡在了这里很可能是由于你用了“dequeueReusableCellWithIdentifier:forIndexPath:”方法来重用cell,换成““dequeueReusableCellWithIdentifier:”(不带IndexPath)方法即可解决 + */ + NSString *cacheKey = [self cacheKeyForIndexPath:indexPath]; + return (NSNumber *)[_cacheDictionary objectForKey:cacheKey]; +} + +- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath model:(id)model keyPath:(NSString *)keyPath +{ + + NSNumber *cacheHeight = [self heightCacheForIndexPath:indexPath]; + if (cacheHeight) { + return [cacheHeight floatValue]; + } else { + if (!self.modelCell) { + return 0; + } + + if (self.modelTableview && self.modelTableview != self.modelCell.sd_tableView) { + self.modelCell.sd_tableView = self.modelTableview; + } + self.modelCell.sd_indexPath = indexPath; + + if (model && keyPath) { + [self.modelCell setValue:model forKey:keyPath]; + } else if (self.cellDataSetting) { + self.cellDataSetting(self.modelCell); + } + + +#ifdef SDDebugWithAssert + /* + 如果程序卡在了这里说明你的cell还没有调用“setupAutoHeightWithBottomView:(UIView *)bottomView bottomMargin:(CGFloat)bottomMargin”方法或者你传递的bottomView为nil,请检查并修改。例: + + //注意:bottomView不能为nil + [cell setupAutoHeightWithBottomView:bottomView bottomMargin:bottomMargin]; + */ + NSAssert(self.modelCell.sd_bottomViewsArray.count, @">>>>>> 你的cell还没有调用“setupAutoHeightWithBottomView:(UIView *)bottomView bottomMargin:(CGFloat)bottomMargin”方法或者你传递的bottomView为nil,请检查并修改"); + +#endif + + [self.modelCell.contentView layoutSubviews]; + NSString *cacheKey = [self cacheKeyForIndexPath:indexPath]; + [_cacheDictionary setObject:@(self.modelCell.autoHeight) forKey:cacheKey]; + + + if (self.modelCell.sd_indexPath && self.modelCell.sd_tableView) { + if (self.modelCell.contentView.shouldReadjustFrameBeforeStoreCache) { + self.modelCell.contentView.height_sd = self.modelCell.autoHeight; + [self.modelCell.contentView layoutSubviews]; + } + [self.modelCell.contentView.autoLayoutModelsArray enumerateObjectsUsingBlock:^(SDAutoLayoutModel *model, NSUInteger idx, BOOL *stop) { + [self.modelTableview.cellAutoHeightManager setSubviewFrameCache:model.needsAutoResizeView.frame WithIndexPath:self.modelCell.sd_indexPath]; + }]; + } + + + return self.modelCell.autoHeight; + } +} + +- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath model:(id)model keyPath:(NSString *)keyPath cellClass:(Class)cellClass +{ + if (![self.modelCell isKindOfClass:cellClass]) { + self.modelCell = nil; + self.modelCell = [_modelCellsDict objectForKey:NSStringFromClass(cellClass)]; + if (!self.modelCell) { + [self registerCellWithCellClass:cellClass]; + } + _modelCell.contentView.tag = kSDModelCellTag; + } + if (self.modelCell.contentView.width_sd != self.contentViewWidth) { + _modelCell.contentView.width_sd = self.contentViewWidth; + } + return [self cellHeightForIndexPath:indexPath model:model keyPath:keyPath]; +} + +- (void)setContentViewWidth:(CGFloat)contentViewWidth +{ + if (_contentViewWidth == contentViewWidth) return; + + CGFloat lastContentViewWidth = _contentViewWidth; + _contentViewWidth = contentViewWidth; + + self.modelCell.contentView.width_sd = self.contentViewWidth; + + if (lastContentViewWidth > 0) { + [_subviewFrameCacheDict removeAllObjects]; + dispatch_async(dispatch_get_main_queue(), ^{ + [self clearHeightCache]; + [self.modelTableview reloadData]; + }); + } +} + + +- (void)setSubviewFrameCache:(CGRect)rect WithIndexPath:(NSIndexPath *)indexPath +{ + if (!self.subviewFrameCacheDict) { + self.subviewFrameCacheDict = [NSMutableDictionary new]; + } + NSString *cacheKey = [self cacheKeyForIndexPath:indexPath]; + NSMutableArray *caches = [self.subviewFrameCacheDict objectForKey:cacheKey]; + if (!caches) { + caches = [NSMutableArray new]; + [self.subviewFrameCacheDict setValue:caches forKey:cacheKey]; + } + [caches addObject:[NSValue valueWithCGRect:rect]]; +} + +- (NSMutableArray *)subviewFrameCachesWithIndexPath:(NSIndexPath *)indexPath +{ + NSString *cacheKey = [self cacheKeyForIndexPath:indexPath]; + return [self.subviewFrameCacheDict valueForKey:cacheKey]; +} + +@end + + +@implementation UITableView (SDAutoTableViewCellHeight) + ++ (void)load { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + + NSArray *selStringsArray = @[@"reloadData", @"reloadRowsAtIndexPaths:withRowAnimation:", @"deleteRowsAtIndexPaths:withRowAnimation:", @"insertRowsAtIndexPaths:withRowAnimation:"]; + + [selStringsArray enumerateObjectsUsingBlock:^(NSString *selString, NSUInteger idx, BOOL *stop) { + NSString *mySelString = [@"sd_" stringByAppendingString:selString]; + + Method originalMethod = class_getInstanceMethod(self, NSSelectorFromString(selString)); + Method myMethod = class_getInstanceMethod(self, NSSelectorFromString(mySelString)); + method_exchangeImplementations(originalMethod, myMethod); + }]; + }); +} + +- (void)sd_reloadData +{ + if (!self.cellAutoHeightManager.shouldKeepHeightCacheWhenReloadingData) { + [self.cellAutoHeightManager clearHeightCache]; + } + [self sd_reloadData]; + self.cellAutoHeightManager.shouldKeepHeightCacheWhenReloadingData = NO; +} + +- (void)sd_reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation +{ + [self.cellAutoHeightManager clearHeightCacheOfIndexPaths:indexPaths]; + [self sd_reloadRowsAtIndexPaths:indexPaths withRowAnimation:animation]; +} + +- (void)sd_deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation +{ + for (NSIndexPath *indexPath in indexPaths) { + [self.cellAutoHeightManager deleteThenResetHeightCache:indexPath]; + } + [self sd_deleteRowsAtIndexPaths:indexPaths withRowAnimation:animation]; +} + + +- (void)sd_insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation +{ + [self.cellAutoHeightManager insertNewDataAtIndexPaths:indexPaths]; + [self sd_insertRowsAtIndexPaths:indexPaths withRowAnimation:animation]; +} + +/* + * 下一步即将实现的功能 + + - (void)sd_moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath + { + [self sd_moveRowAtIndexPath:indexPath toIndexPath:newIndexPath]; + } + + */ + +- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath model:(id)model keyPath:(NSString *)keyPath cellClass:(Class)cellClass contentViewWidth:(CGFloat)contentViewWidth +{ + self.cellAutoHeightManager.modelTableview = self; + + self.cellAutoHeightManager.contentViewWidth = contentViewWidth; + + return [self.cellAutoHeightManager cellHeightForIndexPath:indexPath model:model keyPath:keyPath cellClass:cellClass]; +} + +- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath cellClass:(__unsafe_unretained Class)cellClass cellContentViewWidth:(CGFloat)width cellDataSetting:(AutoCellHeightDataSettingBlock)cellDataSetting +{ + + self.cellDataSetting = cellDataSetting; + + return [self cellHeightForIndexPath:indexPath model:nil keyPath:nil cellClass:cellClass contentViewWidth:width]; +} + +- (void)reloadDataWithExistedHeightCache +{ + self.cellAutoHeightManager.shouldKeepHeightCacheWhenReloadingData = YES; + [self reloadData]; +} + +- (void)reloadDataWithInsertingDataAtTheBeginingOfSection:(NSInteger)section newDataCount:(NSInteger)count +{ + self.cellAutoHeightManager.shouldKeepHeightCacheWhenReloadingData = YES; + [self.cellAutoHeightManager insertNewDataAtTheBeginingOfSection:section newDataCount:count]; + [self reloadData]; +} + +- (void)reloadDataWithInsertingDataAtTheBeginingOfSections:(NSArray *)sectionNumsArray newDataCounts:(NSArray *)dataCountsArray +{ + self.cellAutoHeightManager.shouldKeepHeightCacheWhenReloadingData = YES; + [sectionNumsArray enumerateObjectsUsingBlock:^(NSNumber *num, NSUInteger idx, BOOL *stop) { + int section = [num intValue]; + int dataCountForSection = [dataCountsArray[idx] intValue]; + [self.cellAutoHeightManager insertNewDataAtTheBeginingOfSection:section newDataCount:dataCountForSection]; + }]; + [self reloadData]; +} + +- (CGFloat)cellsTotalHeight +{ + CGFloat h = 0; + if (!self.cellAutoHeightManager.heightCacheDict.count) { + [self reloadData]; + } + NSArray *values = [self.cellAutoHeightManager.heightCacheDict allValues]; + for (NSNumber *number in values) { + h += [number floatValue]; + } + return h; +} + +- (SDCellAutoHeightManager *)cellAutoHeightManager +{ + + SDCellAutoHeightManager *cellAutoHeightManager = objc_getAssociatedObject(self, _cmd); + + if (!cellAutoHeightManager) { + + cellAutoHeightManager = [[SDCellAutoHeightManager alloc] init]; + + [self setCellAutoHeightManager:cellAutoHeightManager]; + } + + return cellAutoHeightManager; +} + +- (void)setCellAutoHeightManager:(SDCellAutoHeightManager *)cellAutoHeightManager +{ + objc_setAssociatedObject(self, @selector(cellAutoHeightManager), cellAutoHeightManager, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (void)setCellDataSetting:(AutoCellHeightDataSettingBlock)cellDataSetting +{ + self.cellAutoHeightManager.cellDataSetting = cellDataSetting; +} + +- (AutoCellHeightDataSettingBlock)cellDataSetting +{ + return self.cellAutoHeightManager.cellDataSetting; +} + +@end + + +@implementation UITableViewController (SDTableViewControllerAutoCellHeight) + +- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath cellContentViewWidth:(CGFloat)width +{ + return [self cellHeightForIndexPath:indexPath cellContentViewWidth:width tableView:self.tableView]; +} + +@end + +@implementation NSObject (SDAnyObjectAutoCellHeight) + +- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath cellContentViewWidth:(CGFloat)width tableView:(UITableView *)tableView +{ + tableView.cellAutoHeightManager.modelTableview = tableView; + + if (tableView.cellAutoHeightManager.contentViewWidth != width) { + tableView.cellAutoHeightManager.contentViewWidth = width; + } + if ([tableView.cellAutoHeightManager heightCacheForIndexPath:indexPath]) { + return [[tableView.cellAutoHeightManager heightCacheForIndexPath:indexPath] floatValue]; + } + UITableViewCell *cell = [tableView.dataSource tableView:tableView cellForRowAtIndexPath:indexPath]; + tableView.cellAutoHeightManager.modelCell = cell; + if (cell.contentView.width_sd != width) { + cell.contentView.width_sd = width; + } + return [[tableView cellAutoHeightManager] cellHeightForIndexPath:indexPath model:nil keyPath:nil]; +} + +@end + diff --git a/SDAutoLayout/UIView+SDAutoLayout.h b/SDAutoLayout/UIView+SDAutoLayout.h new file mode 100755 index 0000000..cdc4285 --- /dev/null +++ b/SDAutoLayout/UIView+SDAutoLayout.h @@ -0,0 +1,484 @@ +// +// UIView+SDAutoLayout.h +// +// Created by gsd on 15/10/6. +// Copyright (c) 2015年 gsd. All rights reserved. +// + +/* + ************************************************************************* + + --------- INTRODUCTION --------- + + USAGE: + + MODE 1. >>>>>>>>>>>>>>> You can use it in this way: + + Demo.sd_layout + .topSpaceToView(v1, 100) + .bottomSpaceToView(v3, 100) + .leftSpaceToView(v0, 150) + .rightSpaceToView(v2, 150); + + MODE 2. >>>>>>>>>>>>>>> You can also use it in this way that is more brevity: + + Demo.sd_layout.topSpaceToView(v1, 100).bottomSpaceToView(v3, 100).leftSpaceToView(v0, 150).rightSpaceToView(v2, 150); + + + ************************************************************************* + */ + + +/* + + ********************************************************************************* + * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 + * 帮您解决问题。 + * QQ : 2689718696(gsdios) + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios + * 新浪微博:GSD_iOS + * + * 视频教程:http://www.letv.com/ptv/vplay/24038772.html + * 用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + + SDAutoLayout + 版本:2.1.7 + 发布:2016.08.12 + + */ + + +// 如果需要用“断言”调试程序请打开此宏 + +//#define SDDebugWithAssert + +#import + +@class SDAutoLayoutModel, SDUIViewCategoryManager; + +typedef SDAutoLayoutModel * _Nonnull (^MarginToView)(id _Nonnull viewOrViewsArray, CGFloat value); +typedef SDAutoLayoutModel * _Nonnull (^Margin)(CGFloat value); +typedef SDAutoLayoutModel * _Nonnull (^MarginEqualToView)(UIView * _Nonnull toView); +typedef SDAutoLayoutModel * _Nonnull (^WidthHeight)(CGFloat value); +typedef SDAutoLayoutModel * _Nonnull (^WidthHeightEqualToView)(UIView * _Nonnull toView, CGFloat ratioValue); +typedef SDAutoLayoutModel * _Nonnull (^AutoHeightWidth)(CGFloat ratioValue); +typedef SDAutoLayoutModel * _Nonnull (^SameWidthHeight)(void); +typedef SDAutoLayoutModel * _Nonnull (^Offset)(CGFloat value); +typedef void (^SpaceToSuperView)(UIEdgeInsets insets); + +@interface SDAutoLayoutModel : NSObject + +/* + *************************说明************************ + + 方法名中带有“SpaceToView”的需要传递2个参数:(UIView)参照view 和 (CGFloat)间距数值 + 方法名中带有“RatioToView”的需要传递2个参数:(UIView)参照view 和 (CGFloat)倍数 + 方法名中带有“EqualToView”的需要传递1个参数:(UIView)参照view + 方法名中带有“Is”的需要传递1个参数:(CGFloat)数值 + + ***************************************************** + */ + + +/* 设置距离其它view的间距 */ + +/** 左边到其参照view之间的间距,参数为“(View 或者 view数组, CGFloat)” */ +@property (nonatomic, copy, readonly) MarginToView _Nonnull leftSpaceToView; +/** 右边到其参照view之间的间距,参数为“(View, CGFloat)” */ +@property (nonatomic, copy, readonly) MarginToView _Nonnull rightSpaceToView; +/** 顶部到其参照view之间的间距,参数为“(View 或者 view数组, CGFloat)” */ +@property (nonatomic, copy, readonly) MarginToView _Nonnull topSpaceToView; +/** 底部到其参照view之间的间距,参数为“(View, CGFloat)” */ +@property (nonatomic, copy, readonly) MarginToView _Nonnull bottomSpaceToView; + + + +/* 设置x、y、width、height、centerX、centerY 值 */ + +/** x值,参数为“(CGFloat)” */ +@property (nonatomic, copy, readonly) Margin _Nonnull xIs; +/** y值,参数为“(CGFloat)” */ +@property (nonatomic, copy, readonly) Margin _Nonnull yIs; +/** centerX值,参数为“(CGFloat)” */ +@property (nonatomic, copy, readonly) Margin _Nonnull centerXIs; +/** centerY值,参数为“(CGFloat)” */ +@property (nonatomic, copy, readonly) Margin _Nonnull centerYIs; +/** 宽度值,参数为“(CGFloat)” */ +@property (nonatomic, copy, readonly) WidthHeight _Nonnull widthIs; +/** 高度值,参数为“(CGFloat)” */ +@property (nonatomic, copy, readonly) WidthHeight _Nonnull heightIs; + + + +/* 设置最大宽度和高度、最小宽度和高度 */ + +/** 最大宽度值,参数为“(CGFloat)” */ +@property (nonatomic, copy, readonly) WidthHeight _Nonnull maxWidthIs; +/** 最大高度值,参数为“(CGFloat)” */ +@property (nonatomic, copy, readonly) WidthHeight _Nonnull maxHeightIs; +/** 最小宽度值,参数为“(CGFloat)” */ +@property (nonatomic, copy, readonly) WidthHeight _Nonnull minWidthIs; +/** 最小高度值,参数为“(CGFloat)” */ +@property (nonatomic, copy, readonly) WidthHeight _Nonnull minHeightIs; + + + +/* 设置和某个参照view的边距相同 */ + +/** 左间距与参照view相同,参数为“(View)” */ +@property (nonatomic, copy, readonly) MarginEqualToView _Nonnull leftEqualToView; +/** 右间距与参照view相同,参数为“(View)” */ +@property (nonatomic, copy, readonly) MarginEqualToView _Nonnull rightEqualToView; +/** 顶部间距与参照view相同,参数为“(View)” */ +@property (nonatomic, copy, readonly) MarginEqualToView _Nonnull topEqualToView; +/** 底部间距与参照view相同,参数为“(View)” */ +@property (nonatomic, copy, readonly) MarginEqualToView _Nonnull bottomEqualToView; +/** centerX与参照view相同,参数为“(View)” */ +@property (nonatomic, copy, readonly) MarginEqualToView _Nonnull centerXEqualToView; +/** centerY与参照view相同,参数为“(View)” */ +@property (nonatomic, copy, readonly) MarginEqualToView _Nonnull centerYEqualToView; + + + +/* 设置宽度或者高度等于参照view的多少倍 */ + +/** 宽度是参照view宽度的多少倍,参数为“(View, CGFloat)” */ +@property (nonatomic, copy, readonly) WidthHeightEqualToView _Nonnull widthRatioToView; +/** 高度是参照view高度的多少倍,参数为“(View, CGFloat)” */ +@property (nonatomic, copy, readonly) WidthHeightEqualToView _Nonnull heightRatioToView; +/** 设置一个view的宽度和它的高度相同,参数为空“()” */ +@property (nonatomic, copy, readonly) SameWidthHeight _Nonnull widthEqualToHeight; +/** 设置一个view的高度和它的宽度相同,参数为空“()” */ +@property (nonatomic, copy, readonly) SameWidthHeight _Nonnull heightEqualToWidth; +/** 自适应高度,传入高宽比值,label可以传0实现文字高度自适应 */ +@property (nonatomic, copy, readonly) AutoHeightWidth _Nonnull autoHeightRatio; + +/** 自适应宽度,参数为宽高比值 */ +@property (nonatomic, copy, readonly) AutoHeightWidth _Nonnull autoWidthRatio; + + + +/* 填充父view(快捷方法) */ + +/** 传入UIEdgeInsetsMake(top, left, bottom, right),可以快捷设置view到其父view上左下右的间距 */ +@property (nonatomic, copy, readonly) SpaceToSuperView _Nonnull spaceToSuperView; + +/** 设置偏移量,参数为“(CGFloat value),目前只有带有equalToView的方法可以设置offset” */ +@property (nonatomic, copy, readonly) Offset _Nonnull offset; + +@property (nonatomic, weak) UIView * _Nullable needsAutoResizeView; + +@end + + + +#pragma mark - UIView 高度、宽度自适应相关方法 + +@interface UIView (SDAutoHeightWidth) + +/** 设置Cell的高度自适应,也可用于设置普通view内容高度自适应 */ +- (void)setupAutoHeightWithBottomView:(UIView *_Nonnull)bottomView bottomMargin:(CGFloat)bottomMargin; + +/** 用于设置普通view内容宽度自适应 */ +- (void)setupAutoWidthWithRightView:(UIView *_Nonnull)rightView rightMargin:(CGFloat)rightMargin; + +/** 设置Cell的高度自适应,也可用于设置普通view内容自适应(应用于当你不确定哪个view在自动布局之后会排布在最下方最为bottomView的时候可以调用次方法将所有可能在最下方的view都传过去) */ +- (void)setupAutoHeightWithBottomViewsArray:(NSArray *_Nonnull)bottomViewsArray bottomMargin:(CGFloat)bottomMargin; + +/** 更新布局(主动刷新布局,如果你需要设置完布局代码就获得view的frame请调用此方法) */ +- (void)updateLayout; + +/** 更新cell内部的控件的布局(cell内部控件专属的更新约束方法,如果启用了cell frame缓存则会自动清除缓存再更新约束) */ +- (void)updateLayoutWithCellContentView:(UIView *_Nonnull)cellContentView; + +/** 清空高度自适应设置 */ +- (void)clearAutoHeigtSettings; + +/** 清空宽度自适应设置 */ +- (void)clearAutoWidthSettings; + +@property (nonatomic) CGFloat autoHeight; + +@property (nonatomic, readonly) SDUIViewCategoryManager * _Nullable sd_categoryManager; + +@property (nonatomic, readonly) NSMutableArray * _Nullable sd_bottomViewsArray; +@property (nonatomic) CGFloat sd_bottomViewBottomMargin; + +@property (nonatomic) NSArray * _Nullable sd_rightViewsArray; +@property (nonatomic) CGFloat sd_rightViewRightMargin; + +@end + + + +#pragma mark - UIView 设置圆角半径、自动布局回调block等相关方法 + +@interface UIView (SDLayoutExtention) + +/** 自动布局完成后的回调block,可以在这里获取到view的真实frame */ +@property (nonatomic) void (^ _Nullable didFinishAutoLayoutBlock)(CGRect frame); + +/** 添加一组子view */ +- (void)sd_addSubviews:(NSArray *_Nullable)subviews; + +/* 设置圆角 */ + +/** 设置圆角半径值 */ +@property (nonatomic, strong) NSNumber * _Nullable sd_cornerRadius; +/** 设置圆角半径值为view宽度的多少倍 */ +@property (nonatomic, strong) NSNumber * _Nullable sd_cornerRadiusFromWidthRatio; +/** 设置圆角半径值为view高度的多少倍 */ +@property (nonatomic, strong) NSNumber * _Nullable sd_cornerRadiusFromHeightRatio; + +/** 设置等宽子view(子view需要在同一水平方向) */ +@property (nonatomic, strong) NSArray * _Nullable sd_equalWidthSubviews; + +@end + + + +#pragma mark - UIView 九宫格浮动布局效果 + +@interface UIView (SDAutoFlowItems) + +/** + * 设置类似collectionView效果的固定间距自动宽度浮动子view + * viewsArray : 需要浮动布局的所有视图 + * perRowItemsCount : 每行显示的视图个数 + * verticalMargin : 视图之间的垂直间距 + * horizontalMargin : 视图之间的水平间距 + * vInset : 上下缩进值 + * hInset : 左右缩进值 + */ +- (void)setupAutoWidthFlowItems:(NSArray *_Nullable)viewsArray withPerRowItemsCount:(NSInteger)perRowItemsCount verticalMargin:(CGFloat)verticalMargin horizontalMargin:(CGFloat)horizontalMagin verticalEdgeInset:(CGFloat)vInset horizontalEdgeInset:(CGFloat)hInset; + +/** 清除固定间距自动宽度浮动子view设置 */ +- (void)clearAutoWidthFlowItemsSettings; + +/** + * 设置类似collectionView效果的固定宽带自动间距浮动子view + * viewsArray : 需要浮动布局的所有视图 + * perRowItemsCount : 每行显示的视图个数 + * verticalMargin : 视图之间的垂直间距 + * vInset : 上下缩进值 + * hInset : 左右缩进值 + */ +- (void)setupAutoMarginFlowItems:(NSArray *_Nullable)viewsArray withPerRowItemsCount:(NSInteger)perRowItemsCount itemWidth:(CGFloat)itemWidth verticalMargin:(CGFloat)verticalMargin verticalEdgeInset:(CGFloat)vInset horizontalEdgeInset:(CGFloat)hInset; + +/** 清除固定宽带自动间距浮动子view设置 */ +- (void)clearAutoMarginFlowItemsSettings; + +@end + + + +#pragma mark - UIView 设置约束、更新约束、清空约束、从父view移除并清空约束、开启cell的frame缓存等相关方法 + +@interface UIView (SDAutoLayout) + +/** 开始自动布局 */ +- (nonnull SDAutoLayoutModel *)sd_layout; + +/** 清空之前的自动布局设置,重新开始自动布局(重新生成布局约束并使其在父view的布局序列数组中位置保持不变) */ +- (nonnull SDAutoLayoutModel *)sd_resetLayout; + +/** 清空之前的自动布局设置,重新开始自动布局(重新生成布局约束并添加到父view布局序列数组中的最后一个位置) */ +- (nonnull SDAutoLayoutModel *)sd_resetNewLayout; + +/** 是否关闭自动布局 */ +@property (nonatomic, getter = sd_isClosingAutoLayout) BOOL sd_closeAutoLayout; + +/** 从父view移除并清空约束 */ +- (void)removeFromSuperviewAndClearAutoLayoutSettings; + +/** 清空之前的自动布局设置 */ +- (void)sd_clearAutoLayoutSettings; + +/** 将自身frame清零(一般在cell内部控件重用前调用) */ +- (void)sd_clearViewFrameCache; + +/** 将自己的需要自动布局的subviews的frame(或者frame缓存)清零 */ +- (void)sd_clearSubviewsAutoLayoutFrameCaches; + +/** 设置固定宽度保证宽度不在自动布局过程再做中调整 */ +@property (nonatomic, strong) NSNumber * _Nullable fixedWidth; + +/** 设置固定高度保证高度不在自动布局过程中再做调整 */ +@property (nonatomic, strong) NSNumber * _Nullable fixedHeight; + +/** 启用cell frame缓存(可以提高cell滚动的流畅度, 目前为cell专用方法,后期会扩展到其他view) */ +- (void)useCellFrameCacheWithIndexPath:(NSIndexPath *_Nonnull)indexPath tableView:(UITableView *_Nonnull)tableview; + +/** 所属tableview(目前为cell专用属性,后期会扩展到其他view) */ +@property (nonatomic) UITableView * _Nullable sd_tableView; + +/** cell的indexPath(目前为cell专用属性,后期会扩展到cell的其他子view) */ +@property (nonatomic) NSIndexPath * _Nullable sd_indexPath; + +- (NSMutableArray *_Nullable)autoLayoutModelsArray; +- (void)addAutoLayoutModel:(SDAutoLayoutModel *_Nullable)model; +@property (nonatomic) SDAutoLayoutModel * _Nullable ownLayoutModel; +@property (nonatomic, strong) NSNumber * _Nullable sd_maxWidth; +@property (nonatomic, strong) NSNumber * _Nullable autoHeightRatioValue; +@property (nonatomic, strong) NSNumber * _Nullable autoWidthRatioValue; + +@end + + + +#pragma mark - UIScrollView 内容竖向自适应、内容横向自适应方法 + +@interface UIScrollView (SDAutoContentSize) + +/** 设置scrollview内容竖向自适应 */ +- (void)setupAutoContentSizeWithBottomView:(UIView *_Nonnull)bottomView bottomMargin:(CGFloat)bottomMargin; + +/** 设置scrollview内容横向自适应 */ +- (void)setupAutoContentSizeWithRightView:(UIView *_Nonnull)rightView rightMargin:(CGFloat)rightMargin; + +@end + + + +#pragma mark - UILabel 开启富文本布局、设置单行文本label宽度自适应、 设置label最多可以显示的行数 + +@interface UILabel (SDLabelAutoResize) + +/** 是否是attributedString */ +@property (nonatomic) BOOL isAttributedContent; + +/** 设置单行文本label宽度自适应 */ +- (void)setSingleLineAutoResizeWithMaxWidth:(CGFloat)maxWidth; + +/** 设置label最多可以显示多少行,如果传0则显示所有行文字 */ +- (void)setMaxNumberOfLinesToShow:(NSInteger)lineCount; + +@end + + + +#pragma mark - UIButton 设置button根据单行文字自适应 + +@interface UIButton (SDExtention) + +/* + * 设置button根据单行文字自适应 + * hPadding:左右边距 + */ +- (void)setupAutoSizeWithHorizontalPadding:(CGFloat)hPadding buttonHeight:(CGFloat)buttonHeight; + +@end + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#pragma mark - 其他方法(如果有需要可以自己利用以下接口拓展更多功能) + +@interface SDAutoLayoutModelItem : NSObject + +@property (nonatomic, strong) NSNumber * _Nullable value; +@property (nonatomic, weak) UIView * _Nullable refView; +@property (nonatomic, assign) CGFloat offset; +@property (nonatomic, strong) NSArray * _Nullable refViewsArray; + +@end + + +@interface UIView (SDChangeFrame) + +@property (nonatomic) BOOL shouldReadjustFrameBeforeStoreCache; + +@property (nonatomic) CGFloat left_sd; +@property (nonatomic) CGFloat top_sd; +@property (nonatomic) CGFloat right_sd; +@property (nonatomic) CGFloat bottom_sd; +@property (nonatomic) CGFloat centerX_sd; +@property (nonatomic) CGFloat centerY_sd; + +@property (nonatomic) CGFloat width_sd; +@property (nonatomic) CGFloat height_sd; + + +@property (nonatomic) CGPoint origin_sd; +@property (nonatomic) CGSize size_sd; + + +// 兼容旧版本 +@property (nonatomic) CGFloat left; +@property (nonatomic) CGFloat top; +@property (nonatomic) CGFloat right; +@property (nonatomic) CGFloat bottom; +@property (nonatomic) CGFloat centerX; +@property (nonatomic) CGFloat centerY; +@property (nonatomic) CGFloat width; +@property (nonatomic) CGFloat height; +@property (nonatomic) CGPoint origin; +@property (nonatomic) CGSize size; + +@end + + +@interface SDUIViewCategoryManager : NSObject + +@property (nonatomic, strong) NSArray * _Nullable rightViewsArray; +@property (nonatomic, assign) CGFloat rightViewRightMargin; + +@property (nonatomic, weak) UITableView * _Nullable sd_tableView; +@property (nonatomic, strong) NSIndexPath * _Nullable sd_indexPath; + +@property (nonatomic, assign) BOOL hasSetFrameWithCache; + +@property (nonatomic) BOOL shouldReadjustFrameBeforeStoreCache; + +@property (nonatomic, assign, getter = sd_isClosingAutoLayout) BOOL sd_closeAutoLayout; + + +/** 设置类似collectionView效果的固定间距自动宽度浮动子view */ + +@property (nonatomic, strong) NSArray * _Nullable flowItems; +@property (nonatomic, assign) CGFloat verticalMargin; +@property (nonatomic, assign) CGFloat horizontalMargin; +@property (nonatomic, assign) NSInteger perRowItemsCount; +@property (nonatomic, assign) CGFloat lastWidth; + + +/** 设置类似collectionView效果的固定宽带自动间距浮动子view */ + +@property (nonatomic, assign) CGFloat flowItemWidth; +@property (nonatomic, assign) BOOL shouldShowAsAutoMarginViews; + + +@property (nonatomic) CGFloat horizontalEdgeInset; +@property (nonatomic) CGFloat verticalEdgeInset; + +@end + diff --git a/SDAutoLayout/UIView+SDAutoLayout.m b/SDAutoLayout/UIView+SDAutoLayout.m new file mode 100755 index 0000000..6c41425 --- /dev/null +++ b/SDAutoLayout/UIView+SDAutoLayout.m @@ -0,0 +1,1849 @@ +// +// UIView+SDAutoLayout.m +// +// Created by gsd on 15/10/6. +// Copyright (c) 2015年 gsd. All rights reserved. +// + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * QQ : 2689718696(gsdios) * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * * + ********************************************************************************* + + */ + +#import "UIView+SDAutoLayout.h" +#import "UITableView+SDAutoTableViewCellHeight.h" + +#import + +Class cellContVClass() +{ + // 为了应付SB审核的SB条款 The use of non-public APIs is not permitted on the App Store because it can lead to a poor user experience should these APIs change. + static UITableViewCell *tempCell; + + if (!tempCell) { + tempCell = [UITableViewCell new]; + } + return [tempCell.contentView class]; +} + +@interface NSObject (SDALSwizzling) + ++ (void)sdal_exchengeMethods:(NSArray *)selectorStingArr prefix:(NSString *)prefix; + +@end + +@implementation NSObject (SDALSwizzling) + ++ (void)sdal_exchengeMethods:(NSArray *)selectorStingArr prefix:(NSString *)prefix { + if (!prefix) { + prefix = @"sd_"; + } + [selectorStingArr enumerateObjectsUsingBlock:^(NSString *selString, NSUInteger idx, BOOL *stop) { + NSString *mySelString = [prefix stringByAppendingString:selString]; + Method originalMethod = class_getInstanceMethod(self, NSSelectorFromString(selString)); + Method myMethod = class_getInstanceMethod(self, NSSelectorFromString(mySelString)); + + const char *types = method_getTypeEncoding(originalMethod); + if (class_addMethod(self, NSSelectorFromString(selString), method_getImplementation(myMethod), types)) { + class_replaceMethod(self, NSSelectorFromString(mySelString), method_getImplementation(originalMethod), types); + } else { + method_exchangeImplementations(originalMethod, myMethod); + } + }]; +} + +@end + +@interface SDAutoLayoutModel () + +@property (nonatomic, strong) SDAutoLayoutModelItem *width; +@property (nonatomic, strong) SDAutoLayoutModelItem *height; +@property (nonatomic, strong) SDAutoLayoutModelItem *left; +@property (nonatomic, strong) SDAutoLayoutModelItem *top; +@property (nonatomic, strong) SDAutoLayoutModelItem *right; +@property (nonatomic, strong) SDAutoLayoutModelItem *bottom; +@property (nonatomic, strong) NSNumber *centerX; +@property (nonatomic, strong) NSNumber *centerY; + +@property (nonatomic, strong) NSNumber *maxWidth; +@property (nonatomic, strong) NSNumber *maxHeight; +@property (nonatomic, strong) NSNumber *minWidth; +@property (nonatomic, strong) NSNumber *minHeight; + +@property (nonatomic, strong) SDAutoLayoutModelItem *ratio_width; +@property (nonatomic, strong) SDAutoLayoutModelItem *ratio_height; +@property (nonatomic, strong) SDAutoLayoutModelItem *ratio_left; +@property (nonatomic, strong) SDAutoLayoutModelItem *ratio_top; +@property (nonatomic, strong) SDAutoLayoutModelItem *ratio_right; +@property (nonatomic, strong) SDAutoLayoutModelItem *ratio_bottom; + +@property (nonatomic, strong) SDAutoLayoutModelItem *equalLeft; +@property (nonatomic, strong) SDAutoLayoutModelItem *equalRight; +@property (nonatomic, strong) SDAutoLayoutModelItem *equalTop; +@property (nonatomic, strong) SDAutoLayoutModelItem *equalBottom; +@property (nonatomic, strong) SDAutoLayoutModelItem *equalCenterX; +@property (nonatomic, strong) SDAutoLayoutModelItem *equalCenterY; + +@property (nonatomic, strong) SDAutoLayoutModelItem *widthEqualHeight; +@property (nonatomic, strong) SDAutoLayoutModelItem *heightEqualWidth; + +@property (nonatomic, strong) SDAutoLayoutModelItem *lastModelItem; + +@end + +@implementation SDAutoLayoutModel + +@synthesize leftSpaceToView = _leftSpaceToView; +@synthesize rightSpaceToView = _rightSpaceToView; +@synthesize topSpaceToView = _topSpaceToView; +@synthesize bottomSpaceToView = _bottomSpaceToView; +@synthesize widthIs = _widthIs; +@synthesize heightIs = _heightIs; +@synthesize widthRatioToView = _widthRatioToView; +@synthesize heightRatioToView = _heightRatioToView; +@synthesize leftEqualToView = _leftEqualToView; +@synthesize rightEqualToView = _rightEqualToView; +@synthesize topEqualToView = _topEqualToView; +@synthesize bottomEqualToView = _bottomEqualToView; +@synthesize centerXEqualToView = _centerXEqualToView; +@synthesize centerYEqualToView = _centerYEqualToView; +@synthesize xIs = _xIs; +@synthesize yIs = _yIs; +@synthesize centerXIs = _centerXIs; +@synthesize centerYIs = _centerYIs; +@synthesize autoHeightRatio = _autoHeightRatio; +@synthesize autoWidthRatio = _autoWidthRatio; +@synthesize spaceToSuperView = _spaceToSuperView; +@synthesize maxWidthIs = _maxWidthIs; +@synthesize maxHeightIs = _maxHeightIs; +@synthesize minWidthIs = _minWidthIs; +@synthesize minHeightIs = _minHeightIs; +@synthesize widthEqualToHeight = _widthEqualToHeight; +@synthesize heightEqualToWidth = _heightEqualToWidth; +@synthesize offset = _offset; + + +- (MarginToView)leftSpaceToView +{ + if (!_leftSpaceToView) { + _leftSpaceToView = [self marginToViewblockWithKey:@"left"]; + } + return _leftSpaceToView; +} + +- (MarginToView)rightSpaceToView +{ + if (!_rightSpaceToView) { + _rightSpaceToView = [self marginToViewblockWithKey:@"right"]; + } + return _rightSpaceToView; +} + +- (MarginToView)topSpaceToView +{ + if (!_topSpaceToView) { + _topSpaceToView = [self marginToViewblockWithKey:@"top"]; + } + return _topSpaceToView; +} + +- (MarginToView)bottomSpaceToView +{ + if (!_bottomSpaceToView) { + _bottomSpaceToView = [self marginToViewblockWithKey:@"bottom"]; + } + return _bottomSpaceToView; +} + +- (MarginToView)marginToViewblockWithKey:(NSString *)key +{ + __weak typeof(self) weakSelf = self; + return ^(id viewOrViewsArray, CGFloat value) { + SDAutoLayoutModelItem *item = [SDAutoLayoutModelItem new]; + item.value = @(value); + if ([viewOrViewsArray isKindOfClass:[UIView class]]) { + item.refView = viewOrViewsArray; + } else if ([viewOrViewsArray isKindOfClass:[NSArray class]]) { + item.refViewsArray = [viewOrViewsArray copy]; + } + [weakSelf setValue:item forKey:key]; + return weakSelf; + }; +} + +- (WidthHeight)widthIs +{ + if (!_widthIs) { + __weak typeof(self) weakSelf = self; + _widthIs = ^(CGFloat value) { + weakSelf.needsAutoResizeView.fixedWidth = @(value); + SDAutoLayoutModelItem *widthItem = [SDAutoLayoutModelItem new]; + widthItem.value = @(value); + weakSelf.width = widthItem; + return weakSelf; + }; + } + return _widthIs; +} + +- (WidthHeight)heightIs +{ + if (!_heightIs) { + __weak typeof(self) weakSelf = self; + _heightIs = ^(CGFloat value) { + weakSelf.needsAutoResizeView.fixedHeight = @(value); + SDAutoLayoutModelItem *heightItem = [SDAutoLayoutModelItem new]; + heightItem.value = @(value); + weakSelf.height = heightItem; + return weakSelf; + }; + } + return _heightIs; +} + +- (WidthHeightEqualToView)widthRatioToView +{ + if (!_widthRatioToView) { + __weak typeof(self) weakSelf = self; + _widthRatioToView = ^(UIView *view, CGFloat value) { + weakSelf.ratio_width = [SDAutoLayoutModelItem new]; + weakSelf.ratio_width.value = @(value); + weakSelf.ratio_width.refView = view; + return weakSelf; + }; + } + return _widthRatioToView; +} + +- (WidthHeightEqualToView)heightRatioToView +{ + if (!_heightRatioToView) { + __weak typeof(self) weakSelf = self; + _heightRatioToView = ^(UIView *view, CGFloat value) { + weakSelf.ratio_height = [SDAutoLayoutModelItem new]; + weakSelf.ratio_height.refView = view; + weakSelf.ratio_height.value = @(value); + return weakSelf; + }; + } + return _heightRatioToView; +} + +- (WidthHeight)maxWidthIs +{ + if (!_maxWidthIs) { + _maxWidthIs = [self limitingWidthHeightWithKey:@"maxWidth"]; + } + return _maxWidthIs; +} + +- (WidthHeight)maxHeightIs +{ + if (!_maxHeightIs) { + _maxHeightIs = [self limitingWidthHeightWithKey:@"maxHeight"]; + } + return _maxHeightIs; +} + +- (WidthHeight)minWidthIs +{ + if (!_minWidthIs) { + _minWidthIs = [self limitingWidthHeightWithKey:@"minWidth"]; + } + return _minWidthIs; +} + +- (WidthHeight)minHeightIs +{ + if (!_minHeightIs) { + _minHeightIs = [self limitingWidthHeightWithKey:@"minHeight"]; + } + return _minHeightIs; +} + + +- (WidthHeight)limitingWidthHeightWithKey:(NSString *)key +{ + __weak typeof(self) weakSelf = self; + + return ^(CGFloat value) { + [weakSelf setValue:@(value) forKey:key]; + + return weakSelf; + }; +} + + +- (MarginEqualToView)marginEqualToViewBlockWithKey:(NSString *)key +{ + __weak typeof(self) weakSelf = self; + + return ^(UIView *view) { + SDAutoLayoutModelItem *item = [SDAutoLayoutModelItem new]; + item.refView = view; + [weakSelf setValue:item forKey:key]; + weakSelf.lastModelItem = item; + if ([view isKindOfClass:cellContVClass()] && ([key isEqualToString:@"equalCenterY"] || [key isEqualToString:@"equalBottom"])) { + view.shouldReadjustFrameBeforeStoreCache = YES; + } + return weakSelf; + }; +} + +- (MarginEqualToView)leftEqualToView +{ + if (!_leftEqualToView) { + _leftEqualToView = [self marginEqualToViewBlockWithKey:@"equalLeft"]; + } + return _leftEqualToView; +} + +- (MarginEqualToView)rightEqualToView +{ + if (!_rightEqualToView) { + _rightEqualToView = [self marginEqualToViewBlockWithKey:@"equalRight"]; + } + return _rightEqualToView; +} + +- (MarginEqualToView)topEqualToView +{ + if (!_topEqualToView) { + _topEqualToView = [self marginEqualToViewBlockWithKey:@"equalTop"]; + } + return _topEqualToView; +} + +- (MarginEqualToView)bottomEqualToView +{ + if (!_bottomEqualToView) { + _bottomEqualToView = [self marginEqualToViewBlockWithKey:@"equalBottom"]; + } + return _bottomEqualToView; +} + +- (MarginEqualToView)centerXEqualToView +{ + if (!_centerXEqualToView) { + _centerXEqualToView = [self marginEqualToViewBlockWithKey:@"equalCenterX"]; + } + return _centerXEqualToView; +} + +- (MarginEqualToView)centerYEqualToView +{ + if (!_centerYEqualToView) { + _centerYEqualToView = [self marginEqualToViewBlockWithKey:@"equalCenterY"]; + } + return _centerYEqualToView; +} + + +- (Margin)marginBlockWithKey:(NSString *)key +{ + __weak typeof(self) weakSelf = self; + + return ^(CGFloat value) { + + if ([key isEqualToString:@"x"]) { + weakSelf.needsAutoResizeView.left_sd = value; + } else if ([key isEqualToString:@"y"]) { + weakSelf.needsAutoResizeView.top_sd = value; + } else if ([key isEqualToString:@"centerX"]) { + weakSelf.centerX = @(value); + } else if ([key isEqualToString:@"centerY"]) { + weakSelf.centerY = @(value); + } + + return weakSelf; + }; +} + +- (Margin)xIs +{ + if (!_xIs) { + _xIs = [self marginBlockWithKey:@"x"]; + } + return _xIs; +} + +- (Margin)yIs +{ + if (!_yIs) { + _yIs = [self marginBlockWithKey:@"y"]; + } + return _yIs; +} + +- (Margin)centerXIs +{ + if (!_centerXIs) { + _centerXIs = [self marginBlockWithKey:@"centerX"]; + } + return _centerXIs; +} + +- (Margin)centerYIs +{ + if (!_centerYIs) { + _centerYIs = [self marginBlockWithKey:@"centerY"]; + } + return _centerYIs; +} + +- (AutoHeightWidth)autoHeightRatio +{ + __weak typeof(self) weakSelf = self; + + if (!_autoHeightRatio) { + _autoHeightRatio = ^(CGFloat ratioaValue) { + weakSelf.needsAutoResizeView.autoHeightRatioValue = @(ratioaValue); + return weakSelf; + }; + } + return _autoHeightRatio; +} + +- (AutoHeightWidth)autoWidthRatio +{ + __weak typeof(self) weakSelf = self; + + if (!_autoWidthRatio) { + _autoWidthRatio = ^(CGFloat ratioaValue) { + weakSelf.needsAutoResizeView.autoWidthRatioValue = @(ratioaValue); + return weakSelf; + }; + } + return _autoWidthRatio; +} + +- (SpaceToSuperView)spaceToSuperView +{ + __weak typeof(self) weakSelf = self; + + if (!_spaceToSuperView) { + _spaceToSuperView = ^(UIEdgeInsets insets) { + UIView *superView = weakSelf.needsAutoResizeView.superview; + if (superView) { + weakSelf.needsAutoResizeView.sd_layout + .leftSpaceToView(superView, insets.left) + .topSpaceToView(superView, insets.top) + .rightSpaceToView(superView, insets.right) + .bottomSpaceToView(superView, insets.bottom); + } + }; + } + return _spaceToSuperView; +} + +- (SameWidthHeight)widthEqualToHeight +{ + __weak typeof(self) weakSelf = self; + + if (!_widthEqualToHeight) { + _widthEqualToHeight = ^() { + weakSelf.widthEqualHeight = [SDAutoLayoutModelItem new]; + weakSelf.lastModelItem = weakSelf.widthEqualHeight; + // 主动触发一次赋值操作 + weakSelf.needsAutoResizeView.height_sd = weakSelf.needsAutoResizeView.height_sd; + return weakSelf; + }; + } + return _widthEqualToHeight; +} + +- (SameWidthHeight)heightEqualToWidth +{ + __weak typeof(self) weakSelf = self; + + if (!_heightEqualToWidth) { + _heightEqualToWidth = ^() { + weakSelf.heightEqualWidth = [SDAutoLayoutModelItem new]; + weakSelf.lastModelItem = weakSelf.heightEqualWidth; + // 主动触发一次赋值操作 + weakSelf.needsAutoResizeView.width_sd = weakSelf.needsAutoResizeView.width_sd; + return weakSelf; + }; + } + return _heightEqualToWidth; +} + +- (SDAutoLayoutModel *(^)(CGFloat))offset +{ + __weak typeof(self) weakSelf = self; + if (!_offset) { + _offset = ^(CGFloat offset) { + weakSelf.lastModelItem.offset = offset; + return weakSelf; + }; + } + return _offset; +} + +@end + + +@implementation UIView (SDAutoHeightWidth) + +- (SDUIViewCategoryManager *)sd_categoryManager +{ + SDUIViewCategoryManager *manager = objc_getAssociatedObject(self, _cmd); + if (!manager) { + objc_setAssociatedObject(self, _cmd, [SDUIViewCategoryManager new], OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setupAutoHeightWithBottomView:(UIView *)bottomView bottomMargin:(CGFloat)bottomMargin +{ + if (!bottomView) return; + + [self setupAutoHeightWithBottomViewsArray:@[bottomView] bottomMargin:bottomMargin]; +} + +- (void)setupAutoWidthWithRightView:(UIView *)rightView rightMargin:(CGFloat)rightMargin +{ + if (!rightView) return; + + self.sd_rightViewsArray = @[rightView]; + self.sd_rightViewRightMargin = rightMargin; +} + +- (void)setupAutoHeightWithBottomViewsArray:(NSArray *)bottomViewsArray bottomMargin:(CGFloat)bottomMargin +{ + if (!bottomViewsArray) return; + + // 清空之前的view + [self.sd_bottomViewsArray removeAllObjects]; + [self.sd_bottomViewsArray addObjectsFromArray:bottomViewsArray]; + self.sd_bottomViewBottomMargin = bottomMargin; +} + +- (void)clearAutoHeigtSettings +{ + [self.sd_bottomViewsArray removeAllObjects]; +} + +- (void)clearAutoWidthSettings +{ + self.sd_rightViewsArray = nil; +} + +- (void)updateLayout +{ + [self.superview layoutSubviews]; +} + +- (void)updateLayoutWithCellContentView:(UIView *)cellContentView +{ + if (cellContentView.sd_indexPath) { + [cellContentView sd_clearSubviewsAutoLayoutFrameCaches]; + } + [self updateLayout]; +} + +- (CGFloat)autoHeight +{ + return [objc_getAssociatedObject(self, _cmd) floatValue]; +} + +- (void)setAutoHeight:(CGFloat)autoHeight +{ + objc_setAssociatedObject(self, @selector(autoHeight), @(autoHeight), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSMutableArray *)sd_bottomViewsArray +{ + NSMutableArray *array = objc_getAssociatedObject(self, _cmd); + if (!array) { + objc_setAssociatedObject(self, _cmd, [NSMutableArray new], OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return objc_getAssociatedObject(self, _cmd); +} + +- (NSArray *)sd_rightViewsArray +{ + return [[self sd_categoryManager] rightViewsArray]; +} + +- (void)setSd_rightViewsArray:(NSArray *)sd_rightViewsArray +{ + [[self sd_categoryManager] setRightViewsArray:sd_rightViewsArray]; +} + +- (CGFloat)sd_bottomViewBottomMargin +{ + return [objc_getAssociatedObject(self, _cmd) floatValue]; +} + +- (void)setSd_bottomViewBottomMargin:(CGFloat)sd_bottomViewBottomMargin +{ + objc_setAssociatedObject(self, @selector(sd_bottomViewBottomMargin), @(sd_bottomViewBottomMargin), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (void)setSd_rightViewRightMargin:(CGFloat)sd_rightViewRightMargin +{ + [[self sd_categoryManager] setRightViewRightMargin:sd_rightViewRightMargin]; +} + +- (CGFloat)sd_rightViewRightMargin +{ + return [[self sd_categoryManager] rightViewRightMargin]; +} + +@end + +@implementation UIView (SDLayoutExtention) + +- (void (^)(CGRect))didFinishAutoLayoutBlock +{ + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setDidFinishAutoLayoutBlock:(void (^)(CGRect))didFinishAutoLayoutBlock +{ + objc_setAssociatedObject(self, @selector(didFinishAutoLayoutBlock), didFinishAutoLayoutBlock, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + +- (NSNumber *)sd_cornerRadius +{ + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setSd_cornerRadius:(NSNumber *)sd_cornerRadius +{ + objc_setAssociatedObject(self, @selector(sd_cornerRadius), sd_cornerRadius, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + + +- (NSNumber *)sd_cornerRadiusFromWidthRatio +{ + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setSd_cornerRadiusFromWidthRatio:(NSNumber *)sd_cornerRadiusFromWidthRatio +{ + objc_setAssociatedObject(self, @selector(sd_cornerRadiusFromWidthRatio), sd_cornerRadiusFromWidthRatio, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + + +- (NSNumber *)sd_cornerRadiusFromHeightRatio +{ + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setSd_cornerRadiusFromHeightRatio:(NSNumber *)sd_cornerRadiusFromHeightRatio +{ + objc_setAssociatedObject(self, @selector(sd_cornerRadiusFromHeightRatio), sd_cornerRadiusFromHeightRatio, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSArray *)sd_equalWidthSubviews +{ + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setSd_equalWidthSubviews:(NSArray *)sd_equalWidthSubviews +{ + objc_setAssociatedObject(self, @selector(sd_equalWidthSubviews), sd_equalWidthSubviews, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (void)sd_addSubviews:(NSArray *)subviews +{ + [subviews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) { + if ([view isKindOfClass:[UIView class]]) { + [self addSubview:view]; + } + }]; +} + +@end + +@implementation UIView (SDAutoFlowItems) + +- (void)setupAutoWidthFlowItems:(NSArray *)viewsArray withPerRowItemsCount:(NSInteger)perRowItemsCount verticalMargin:(CGFloat)verticalMargin horizontalMargin:(CGFloat)horizontalMagin verticalEdgeInset:(CGFloat)vInset horizontalEdgeInset:(CGFloat)hInset +{ + self.sd_categoryManager.flowItems = viewsArray; + self.sd_categoryManager.perRowItemsCount = perRowItemsCount; + self.sd_categoryManager.verticalMargin = verticalMargin; + self.sd_categoryManager.horizontalMargin = horizontalMagin; + self.verticalEdgeInset = vInset; + self.horizontalEdgeInset = hInset; + + self.sd_categoryManager.lastWidth = 0; + + if (viewsArray.count) { + [self setupAutoHeightWithBottomView:viewsArray.lastObject bottomMargin:vInset]; + } else { + [self clearAutoHeigtSettings]; + } +} + +- (void)clearAutoWidthFlowItemsSettings +{ + [self setupAutoWidthFlowItems:nil withPerRowItemsCount:0 verticalMargin:0 horizontalMargin:0 verticalEdgeInset:0 horizontalEdgeInset:0]; +} + +- (void)setupAutoMarginFlowItems:(NSArray *)viewsArray withPerRowItemsCount:(NSInteger)perRowItemsCount itemWidth:(CGFloat)itemWidth verticalMargin:(CGFloat)verticalMargin verticalEdgeInset:(CGFloat)vInset horizontalEdgeInset:(CGFloat)hInset +{ + self.sd_categoryManager.shouldShowAsAutoMarginViews = YES; + self.sd_categoryManager.flowItemWidth = itemWidth; + [self setupAutoWidthFlowItems:viewsArray withPerRowItemsCount:perRowItemsCount verticalMargin:verticalMargin horizontalMargin:0 verticalEdgeInset:vInset horizontalEdgeInset:hInset]; +} + +- (void)clearAutoMarginFlowItemsSettings +{ + [self setupAutoMarginFlowItems:nil withPerRowItemsCount:0 itemWidth:0 verticalMargin:0 verticalEdgeInset:0 horizontalEdgeInset:0]; +} + +- (void)setHorizontalEdgeInset:(CGFloat)horizontalEdgeInset +{ + self.sd_categoryManager.horizontalEdgeInset = horizontalEdgeInset; +} + +- (CGFloat)horizontalEdgeInset +{ + return self.sd_categoryManager.horizontalEdgeInset; +} + +- (void)setVerticalEdgeInset:(CGFloat)verticalEdgeInset +{ + self.sd_categoryManager.verticalEdgeInset = verticalEdgeInset; +} + +- (CGFloat)verticalEdgeInset +{ + return self.sd_categoryManager.verticalEdgeInset; +} + +@end + +@implementation UIScrollView (SDAutoContentSize) + +- (void)setupAutoContentSizeWithBottomView:(UIView *)bottomView bottomMargin:(CGFloat)bottomMargin +{ + [self setupAutoHeightWithBottomView:bottomView bottomMargin:bottomMargin]; +} + +- (void)setupAutoContentSizeWithRightView:(UIView *)rightView rightMargin:(CGFloat)rightMargin +{ + if (!rightView) return; + + self.sd_rightViewsArray = @[rightView]; + self.sd_rightViewRightMargin = rightMargin; +} + +@end + +@implementation UILabel (SDLabelAutoResize) + ++ (void)load +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + [self sdal_exchengeMethods:@[@"setText:"] prefix:nil]; + }); +} + +- (void)sd_setText:(NSString *)text +{ + // 如果程序崩溃在这行代码说明是你的label在执行“setText”方法时出了问题而不是在此自动布局库内部出现了问题,请检查你的“setText”方法 + [self sd_setText:text]; + + + if (self.sd_maxWidth) { + [self sizeToFit]; + } else if (self.autoHeightRatioValue) { + self.size_sd = CGSizeZero; + } +} + +- (BOOL)isAttributedContent +{ + return [objc_getAssociatedObject(self, _cmd) boolValue]; +} + +- (void)setIsAttributedContent:(BOOL)isAttributedContent +{ + objc_setAssociatedObject(self, @selector(isAttributedContent), @(isAttributedContent), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (void)setSingleLineAutoResizeWithMaxWidth:(CGFloat)maxWidth +{ + self.sd_maxWidth = @(maxWidth); +} + +- (void)setMaxNumberOfLinesToShow:(NSInteger)lineCount +{ + NSAssert(self.ownLayoutModel, @"请在布局完成之后再做此步设置!"); + if (lineCount > 0) { + if (self.isAttributedContent) { + NSDictionary *attrs = [self.attributedText attributesAtIndex:0 effectiveRange:nil]; + NSMutableParagraphStyle *paragraphStyle = attrs[NSParagraphStyleAttributeName]; + self.sd_layout.maxHeightIs((self.font.lineHeight) * lineCount + paragraphStyle.lineSpacing * (lineCount - 1) + 0.1); + } else { + self.sd_layout.maxHeightIs(self.font.lineHeight * lineCount + 0.1); + } + } else { + self.sd_layout.maxHeightIs(MAXFLOAT); + } +} + +@end + +@implementation UIButton (SDExtention) + +- (void)setupAutoSizeWithHorizontalPadding:(CGFloat)hPadding buttonHeight:(CGFloat)buttonHeight +{ + self.fixedHeight = @(buttonHeight); + + self.titleLabel.sd_layout + .leftSpaceToView(self, hPadding) + .topEqualToView(self) + .heightIs(buttonHeight); + + [self.titleLabel setSingleLineAutoResizeWithMaxWidth:MAXFLOAT]; + [self setupAutoWidthWithRightView:self.titleLabel rightMargin:hPadding]; +} + +@end + + +@implementation SDAutoLayoutModelItem + +- (instancetype)init +{ + if (self = [super init]) { + _offset = 0; + } + return self; +} + +@end + + +@implementation UIView (SDAutoLayout) + ++ (void)load +{ + if (self == [UIView class]) { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + [self sdal_exchengeMethods:@[@"layoutSubviews"] prefix:nil]; + }); + } +} + +#pragma mark - properties + +- (NSMutableArray *)autoLayoutModelsArray +{ + if (!objc_getAssociatedObject(self, _cmd)) { + objc_setAssociatedObject(self, _cmd, [NSMutableArray array], OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return objc_getAssociatedObject(self, _cmd); +} + +- (NSNumber *)fixedWidth +{ + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setFixedWidth:(NSNumber *)fixedWidth +{ + if (fixedWidth) { + self.width_sd = [fixedWidth floatValue]; + } + objc_setAssociatedObject(self, @selector(fixedWidth), fixedWidth, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSNumber *)fixedHeight +{ + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setFixedHeight:(NSNumber *)fixedHeight +{ + if (fixedHeight) { + self.height_sd = [fixedHeight floatValue]; + } + objc_setAssociatedObject(self, @selector(fixedHeight), fixedHeight, OBJC_ASSOCIATION_RETAIN); +} + +- (NSNumber *)autoHeightRatioValue +{ + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setAutoHeightRatioValue:(NSNumber *)autoHeightRatioValue +{ + objc_setAssociatedObject(self, @selector(autoHeightRatioValue), autoHeightRatioValue, OBJC_ASSOCIATION_RETAIN); +} + +- (NSNumber *)autoWidthRatioValue +{ + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setAutoWidthRatioValue:(NSNumber *)autoWidthRatioValue +{ + objc_setAssociatedObject(self, @selector(autoWidthRatioValue), autoWidthRatioValue, OBJC_ASSOCIATION_RETAIN); +} + +- (NSNumber *)sd_maxWidth +{ + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setSd_maxWidth:(NSNumber *)sd_maxWidth +{ + objc_setAssociatedObject(self, @selector(sd_maxWidth), sd_maxWidth, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (void)useCellFrameCacheWithIndexPath:(NSIndexPath *)indexPath tableView:(UITableView *)tableview +{ + self.sd_indexPath = indexPath; + self.sd_tableView = tableview; +} + +- (UITableView *)sd_tableView +{ + return self.sd_categoryManager.sd_tableView; +} + +- (void)setSd_tableView:(UITableView *)sd_tableView +{ + if ([self isKindOfClass:[UITableViewCell class]]) { + [(UITableViewCell *)self contentView].sd_tableView = sd_tableView; + } + self.sd_categoryManager.sd_tableView = sd_tableView; +} + +- (NSIndexPath *)sd_indexPath +{ + return self.sd_categoryManager.sd_indexPath; +} + +- (void)setSd_indexPath:(NSIndexPath *)sd_indexPath +{ + if ([self isKindOfClass:[UITableViewCell class]]) { + [(UITableViewCell *)self contentView].sd_indexPath = sd_indexPath; + } + self.sd_categoryManager.sd_indexPath = sd_indexPath; +} + +- (SDAutoLayoutModel *)ownLayoutModel +{ + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setOwnLayoutModel:(SDAutoLayoutModel *)ownLayoutModel +{ + objc_setAssociatedObject(self, @selector(ownLayoutModel), ownLayoutModel, OBJC_ASSOCIATION_RETAIN); +} + +- (SDAutoLayoutModel *)sd_layout +{ + +#ifdef SDDebugWithAssert + /* + 卡在这里说明你的要自动布局的view在没有添加到父view的情况下就开始设置布局,你需要这样: + 1. UIView *view = [UIView new]; + 2. [superView addSubview:view]; + 3. view.sd_layout + .leftEqualToView()... + */ + NSAssert(self.superview, @">>>>>>>>>在加入父view之后才可以做自动布局设置"); + +#endif + + SDAutoLayoutModel *model = [self ownLayoutModel]; + if (!model) { + model = [SDAutoLayoutModel new]; + model.needsAutoResizeView = self; + [self setOwnLayoutModel:model]; + [self.superview.autoLayoutModelsArray addObject:model]; + } + + return model; +} + +- (SDAutoLayoutModel *)sd_resetLayout +{ + /* + * 方案待定 + [self sd_clearAutoLayoutSettings]; + return [self sd_layout]; + */ + + SDAutoLayoutModel *model = [self ownLayoutModel]; + SDAutoLayoutModel *newModel = [SDAutoLayoutModel new]; + newModel.needsAutoResizeView = self; + [self sd_clearViewFrameCache]; + NSInteger index = 0; + if (model) { + index = [self.superview.autoLayoutModelsArray indexOfObject:model]; + [self.superview.autoLayoutModelsArray replaceObjectAtIndex:index withObject:newModel]; + } else { + [self.superview.autoLayoutModelsArray addObject:newModel]; + } + [self setOwnLayoutModel:newModel]; + [self sd_clearExtraAutoLayoutItems]; + return newModel; +} + +- (SDAutoLayoutModel *)sd_resetNewLayout +{ + [self sd_clearAutoLayoutSettings]; + [self sd_clearExtraAutoLayoutItems]; + return [self sd_layout]; +} + +- (BOOL)sd_isClosingAutoLayout +{ + return self.sd_categoryManager.sd_isClosingAutoLayout; +} + +- (void)setSd_closeAutoLayout:(BOOL)sd_closeAutoLayout +{ + self.sd_categoryManager.sd_closeAutoLayout = sd_closeAutoLayout; +} + +- (void)removeFromSuperviewAndClearAutoLayoutSettings +{ + [self sd_clearAutoLayoutSettings]; + [self removeFromSuperview]; +} + +- (void)sd_clearAutoLayoutSettings +{ + SDAutoLayoutModel *model = [self ownLayoutModel]; + if (model) { + [self.superview.autoLayoutModelsArray removeObject:model]; + [self setOwnLayoutModel:nil]; + } + [self sd_clearExtraAutoLayoutItems]; +} + +- (void)sd_clearExtraAutoLayoutItems +{ + if (self.autoHeightRatioValue) { + self.autoHeightRatioValue = nil; + } + self.fixedHeight = nil; + self.fixedWidth = nil; +} + +- (void)sd_clearViewFrameCache +{ + self.frame = CGRectZero; +} + +- (void)sd_clearSubviewsAutoLayoutFrameCaches +{ + if (self.sd_tableView && self.sd_indexPath) { + [self.sd_tableView.cellAutoHeightManager clearHeightCacheOfIndexPaths:@[self.sd_indexPath]]; + return; + } + + if (self.autoLayoutModelsArray.count == 0) return; + + [self.autoLayoutModelsArray enumerateObjectsUsingBlock:^(SDAutoLayoutModel *model, NSUInteger idx, BOOL *stop) { + model.needsAutoResizeView.frame = CGRectZero; + }]; +} + +- (void)sd_layoutSubviews +{ + // 如果程序崩溃在这行代码说明是你的view在执行“layoutSubvies”方法时出了问题而不是在此自动布局库内部出现了问题,请检查你的“layoutSubvies”方法 + [self sd_layoutSubviews]; + + [self sd_layoutSubviewsHandle]; +} + +- (void)sd_layoutSubviewsHandle{ + + if (self.sd_equalWidthSubviews.count) { + __block CGFloat totalMargin = 0; + [self.sd_equalWidthSubviews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) { + SDAutoLayoutModel *model = view.sd_layout; + CGFloat left = model.left ? [model.left.value floatValue] : model.needsAutoResizeView.left_sd; + totalMargin += (left + [model.right.value floatValue]); + }]; + CGFloat averageWidth = (self.width_sd - totalMargin) / self.sd_equalWidthSubviews.count; + [self.sd_equalWidthSubviews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) { + view.width_sd = averageWidth; + view.fixedWidth = @(averageWidth); + }]; + } + + if (self.sd_categoryManager.flowItems.count && (self.sd_categoryManager.lastWidth != self.width_sd)) { + + self.sd_categoryManager.lastWidth = self.width_sd; + + NSInteger perRowItemsCount = self.sd_categoryManager.perRowItemsCount; + CGFloat horizontalMargin = 0; + CGFloat w = 0; + if (self.sd_categoryManager.shouldShowAsAutoMarginViews) { + w = self.sd_categoryManager.flowItemWidth; + long itemsCount = self.sd_categoryManager.perRowItemsCount; + if (itemsCount > 1) { + horizontalMargin = (self.width_sd - (self.horizontalEdgeInset * 2) - itemsCount * w) / (itemsCount - 1); + } + } else { + horizontalMargin = self.sd_categoryManager.horizontalMargin; + w = (self.width_sd - (self.horizontalEdgeInset * 2) - (perRowItemsCount - 1) * horizontalMargin) / perRowItemsCount; + } + CGFloat verticalMargin = self.sd_categoryManager.verticalMargin; + + __block UIView *referencedView = self; + [self.sd_categoryManager.flowItems enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) { + if (idx < perRowItemsCount) { + if (idx == 0) { + /* 保留 + BOOL shouldShowAsAutoMarginViews = self.sd_categoryManager.shouldShowAsAutoMarginViews; + */ + view.sd_layout + .leftSpaceToView(referencedView, self.horizontalEdgeInset) + .topSpaceToView(referencedView, self.verticalEdgeInset) + .widthIs(w); + } else { + view.sd_layout + .leftSpaceToView(referencedView, horizontalMargin) + .topEqualToView(referencedView) + .widthIs(w); + } + referencedView = view; + } else { + referencedView = self.sd_categoryManager.flowItems[idx - perRowItemsCount]; + view.sd_layout + .leftEqualToView(referencedView) + .widthIs(w) + .topSpaceToView(referencedView, verticalMargin); + } + }]; + } + + if (self.autoLayoutModelsArray.count) { + + NSMutableArray *caches = nil; + + if ([self isKindOfClass:cellContVClass()] && self.sd_tableView) { + caches = [self.sd_tableView.cellAutoHeightManager subviewFrameCachesWithIndexPath:self.sd_indexPath]; + } + + [self.autoLayoutModelsArray enumerateObjectsUsingBlock:^(SDAutoLayoutModel *model, NSUInteger idx, BOOL *stop) { + if (idx < caches.count) { + CGRect originalFrame = model.needsAutoResizeView.frame; + CGRect newFrame = [[caches objectAtIndex:idx] CGRectValue]; + if (CGRectEqualToRect(originalFrame, newFrame)) { + [model.needsAutoResizeView setNeedsLayout]; + } else { + model.needsAutoResizeView.frame = newFrame; + } + [self setupCornerRadiusWithView:model.needsAutoResizeView model:model]; + model.needsAutoResizeView.sd_categoryManager.hasSetFrameWithCache = YES; + } else { + if (model.needsAutoResizeView.sd_categoryManager.hasSetFrameWithCache) { + model.needsAutoResizeView.sd_categoryManager.hasSetFrameWithCache = NO; + } + [self sd_resizeWithModel:model]; + } + }]; + } + + if (self.tag == kSDModelCellTag && [self isKindOfClass:cellContVClass()]) { + UITableViewCell *cell = (UITableViewCell *)(self.superview); + + while (cell && ![cell isKindOfClass:[UITableViewCell class]]) { + cell = (UITableViewCell *)cell.superview; + } + + if ([cell isKindOfClass:[UITableViewCell class]]) { + CGFloat height = 0; + for (UIView *view in cell.sd_bottomViewsArray) { + height = MAX(height, view.bottom_sd); + } + cell.autoHeight = height + cell.sd_bottomViewBottomMargin; + } + } else if (![self isKindOfClass:[UITableViewCell class]] && (self.sd_bottomViewsArray.count || self.sd_rightViewsArray.count)) { + if (self.sd_categoryManager.hasSetFrameWithCache) { + self.sd_categoryManager.hasSetFrameWithCache = NO; + return; + } + CGFloat contentHeight = 0; + CGFloat contentWidth = 0; + if (self.sd_bottomViewsArray) { + CGFloat height = 0; + for (UIView *view in self.sd_bottomViewsArray) { + height = MAX(height, view.bottom_sd); + } + contentHeight = height + self.sd_bottomViewBottomMargin; + } + if (self.sd_rightViewsArray) { + CGFloat width = 0; + for (UIView *view in self.sd_rightViewsArray) { + width = MAX(width, view.right_sd); + } + contentWidth = width + self.sd_rightViewRightMargin; + } + if ([self isKindOfClass:[UIScrollView class]]) { + UIScrollView *scrollView = (UIScrollView *)self; + CGSize contentSize = scrollView.contentSize; + if (contentHeight > 0) { + contentSize.height = contentHeight; + } + if (contentWidth > 0) { + contentSize.width = contentWidth; + } + if (contentSize.width <= 0) { + contentSize.width = scrollView.width_sd; + } + if (!CGSizeEqualToSize(contentSize, scrollView.contentSize)) { + scrollView.contentSize = contentSize; + } + } else { + // 如果这里出现循环调用情况请把demo发送到gsdios@126.com,谢谢配合。 + if (self.sd_bottomViewsArray.count && (floorf(contentHeight) != floorf(self.height_sd))) { + self.height_sd = contentHeight; + self.fixedHeight = @(self.height_sd); + } + + if (self.sd_rightViewsArray.count && (floorf(contentWidth) != floorf(self.width_sd))) { + self.width_sd = contentWidth; + self.fixedWidth = @(self.width_sd); + } + } + + SDAutoLayoutModel *model = self.ownLayoutModel; + + if (![self isKindOfClass:[UIScrollView class]] && self.sd_rightViewsArray.count && (model.right || model.equalRight || model.centerX || model.equalCenterX)) { + self.fixedWidth = @(self.width); + if (model.right || model.equalRight) { + [self layoutRightWithView:self model:model]; + } else { + [self layoutLeftWithView:self model:model]; + } + self.fixedWidth = nil; + } + + if (![self isKindOfClass:[UIScrollView class]] && self.sd_bottomViewsArray.count && (model.bottom || model.equalBottom || model.centerY || model.equalCenterY)) { + self.fixedHeight = @(self.height); + if (model.bottom || model.equalBottom) { + [self layoutBottomWithView:self model:model]; + } else { + [self layoutTopWithView:self model:model]; + } + self.fixedHeight = nil; + } + + if (self.didFinishAutoLayoutBlock) { + self.didFinishAutoLayoutBlock(self.frame); + } + } +} + +- (void)sd_resizeWithModel:(SDAutoLayoutModel *)model +{ + UIView *view = model.needsAutoResizeView; + + if (!view || view.sd_isClosingAutoLayout) return; + + if (view.sd_maxWidth && (model.rightSpaceToView || model.rightEqualToView)) { // 靠右布局前提设置 + [self layoutAutoWidthWidthView:view model:model]; + view.fixedWidth = @(view.width_sd); + } + + [self layoutWidthWithView:view model:model]; + + [self layoutHeightWithView:view model:model]; + + [self layoutLeftWithView:view model:model]; + + [self layoutRightWithView:view model:model]; + + if (view.autoHeightRatioValue && view.width_sd > 0 && (model.bottomEqualToView || model.bottomSpaceToView)) { // 底部布局前提设置 + [self layoutAutoHeightWidthView:view model:model]; + view.fixedHeight = @(view.height_sd); + } + + if (view.autoWidthRatioValue) { + view.fixedWidth = @(view.height_sd * [view.autoWidthRatioValue floatValue]); + } + + + [self layoutTopWithView:view model:model]; + + [self layoutBottomWithView:view model:model]; + + if ((model.centerX || model.equalCenterX) && !view.fixedWidth) { + [self layoutLeftWithView:view model:model]; + } + + if ((model.centerY || model.equalCenterY) && !view.fixedHeight) { + [self layoutTopWithView:view model:model]; + } + + if (view.sd_maxWidth) { + [self layoutAutoWidthWidthView:view model:model]; + } + + if (model.maxWidth && [model.maxWidth floatValue] < view.width_sd) { + view.width_sd = [model.maxWidth floatValue]; + } + + if (model.minWidth && [model.minWidth floatValue] > view.width_sd) { + view.width_sd = [model.minWidth floatValue]; + } + + if (view.autoHeightRatioValue && view.width_sd > 0) { + [self layoutAutoHeightWidthView:view model:model]; + } + + if (model.maxHeight && [model.maxHeight floatValue] < view.height_sd) { + view.height_sd = [model.maxHeight floatValue]; + } + + if (model.minHeight && [model.minHeight floatValue] > view.height_sd) { + view.height_sd = [model.minHeight floatValue]; + } + + if (model.widthEqualHeight) { + view.width_sd = view.height_sd; + } + + if (model.heightEqualWidth) { + view.height_sd = view.width_sd; + } + + if (view.didFinishAutoLayoutBlock) { + view.didFinishAutoLayoutBlock(view.frame); + } + + if (view.sd_bottomViewsArray.count || view.sd_rightViewsArray.count) { + [view layoutSubviews]; + } + + [self setupCornerRadiusWithView:view model:model]; +} + +- (void)layoutAutoHeightWidthView:(UIView *)view model:(SDAutoLayoutModel *)model +{ + if ([view.autoHeightRatioValue floatValue] > 0) { + view.height_sd = view.width_sd * [view.autoHeightRatioValue floatValue]; + } else { + if ([view isKindOfClass:[UILabel class]]) { + UILabel *label = (UILabel *)view; + label.numberOfLines = 0; + if (label.text.length) { + if (!label.isAttributedContent) { + CGRect rect = [label.text boundingRectWithSize:CGSizeMake(label.width_sd, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName : label.font} context:nil]; + label.height_sd = rect.size.height + 0.1; + } else { + [label sizeToFit]; + if (label.sd_maxWidth && label.width_sd > [label.sd_maxWidth floatValue]) { + label.width_sd = [label.sd_maxWidth floatValue]; + } + } + } else { + label.height_sd = 0; + } + } else { + view.height_sd = 0; + } + } +} + +- (void)layoutAutoWidthWidthView:(UIView *)view model:(SDAutoLayoutModel *)model +{ + if ([view isKindOfClass:[UILabel class]]) { + UILabel *label = (UILabel *)view; + CGFloat width = [view.sd_maxWidth floatValue] > 0 ? [view.sd_maxWidth floatValue] : MAXFLOAT; + label.numberOfLines = 1; + if (label.text.length) { + if (!label.isAttributedContent) { + CGRect rect = [label.text boundingRectWithSize:CGSizeMake(MAXFLOAT, label.height_sd) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName : label.font} context:nil]; + if (rect.size.width > width) { + rect.size.width = width; + } + label.width_sd = rect.size.width + 0.1; + } else{ + [label sizeToFit]; + if (label.width_sd > width) { + label.width_sd = width; + } + } + } else { + label.size_sd = CGSizeZero; + } + } +} + +- (void)layoutWidthWithView:(UIView *)view model:(SDAutoLayoutModel *)model +{ + if (model.width) { + view.width_sd = [model.width.value floatValue]; + view.fixedWidth = @(view.width_sd); + } else if (model.ratio_width) { + view.width_sd = model.ratio_width.refView.width_sd * [model.ratio_width.value floatValue]; + view.fixedWidth = @(view.width_sd); + } +} + +- (void)layoutHeightWithView:(UIView *)view model:(SDAutoLayoutModel *)model +{ + if (model.height) { + view.height_sd = [model.height.value floatValue]; + view.fixedHeight = @(view.height_sd); + } else if (model.ratio_height) { + view.height_sd = [model.ratio_height.value floatValue] * model.ratio_height.refView.height_sd; + view.fixedHeight = @(view.height_sd); + } +} + +- (void)layoutLeftWithView:(UIView *)view model:(SDAutoLayoutModel *)model +{ + if (model.left) { + if (view.superview == model.left.refView) { + if (!view.fixedWidth) { // view.autoLeft && view.autoRight + view.width_sd = view.right_sd - [model.left.value floatValue]; + } + view.left_sd = [model.left.value floatValue]; + } else { + if (model.left.refViewsArray.count) { + CGFloat lastRefRight = 0; + for (UIView *ref in model.left.refViewsArray) { + if ([ref isKindOfClass:[UIView class]] && ref.right_sd > lastRefRight) { + model.left.refView = ref; + lastRefRight = ref.right_sd; + } + } + } + if (!view.fixedWidth) { // view.autoLeft && view.autoRight + view.width_sd = view.right_sd - model.left.refView.right_sd - [model.left.value floatValue]; + } + view.left_sd = model.left.refView.right_sd + [model.left.value floatValue]; + } + + } else if (model.equalLeft) { + if (!view.fixedWidth) { + if (model.needsAutoResizeView == view.superview) { + view.width_sd = view.right_sd - (0 + model.equalLeft.offset); + } else { + view.width_sd = view.right_sd - (model.equalLeft.refView.left_sd + model.equalLeft.offset); + } + } + if (view.superview == model.equalLeft.refView) { + view.left_sd = 0 + model.equalLeft.offset; + } else { + view.left_sd = model.equalLeft.refView.left_sd + model.equalLeft.offset; + } + } else if (model.equalCenterX) { + if (view.superview == model.equalCenterX.refView) { + view.centerX_sd = model.equalCenterX.refView.width_sd * 0.5 + model.equalCenterX.offset; + } else { + view.centerX_sd = model.equalCenterX.refView.centerX_sd + model.equalCenterX.offset; + } + } else if (model.centerX) { + view.centerX_sd = [model.centerX floatValue]; + } +} + +- (void)layoutRightWithView:(UIView *)view model:(SDAutoLayoutModel *)model +{ + if (model.right) { + if (view.superview == model.right.refView) { + if (!view.fixedWidth) { // view.autoLeft && view.autoRight + view.width_sd = model.right.refView.width_sd - view.left_sd - [model.right.value floatValue]; + } + view.right_sd = model.right.refView.width_sd - [model.right.value floatValue]; + } else { + if (!view.fixedWidth) { // view.autoLeft && view.autoRight + view.width_sd = model.right.refView.left_sd - view.left_sd - [model.right.value floatValue]; + } + view.right_sd = model.right.refView.left_sd - [model.right.value floatValue]; + } + } else if (model.equalRight) { + if (!view.fixedWidth) { + if (model.equalRight.refView == view.superview) { + view.width_sd = model.equalRight.refView.width_sd - view.left_sd + model.equalRight.offset; + } else { + view.width_sd = model.equalRight.refView.right_sd - view.left_sd + model.equalRight.offset; + } + } + + view.right_sd = model.equalRight.refView.right_sd + model.equalRight.offset; + if (view.superview == model.equalRight.refView) { + view.right_sd = model.equalRight.refView.width_sd + model.equalRight.offset; + } + + } +} + +- (void)layoutTopWithView:(UIView *)view model:(SDAutoLayoutModel *)model +{ + if (model.top) { + if (view.superview == model.top.refView) { + if (!view.fixedHeight) { // view.autoTop && view.autoBottom && view.bottom + view.height_sd = view.bottom_sd - [model.top.value floatValue]; + } + view.top_sd = [model.top.value floatValue]; + } else { + if (model.top.refViewsArray.count) { + CGFloat lastRefBottom = 0; + for (UIView *ref in model.top.refViewsArray) { + if ([ref isKindOfClass:[UIView class]] && ref.bottom_sd > lastRefBottom) { + model.top.refView = ref; + lastRefBottom = ref.bottom_sd; + } + } + } + if (!view.fixedHeight) { // view.autoTop && view.autoBottom && view.bottom + view.height_sd = view.bottom_sd - model.top.refView.bottom_sd - [model.top.value floatValue]; + } + view.top_sd = model.top.refView.bottom_sd + [model.top.value floatValue]; + } + } else if (model.equalTop) { + if (view.superview == model.equalTop.refView) { + if (!view.fixedHeight) { + view.height_sd = view.bottom_sd - model.equalTop.offset; + } + view.top_sd = 0 + model.equalTop.offset; + } else { + if (!view.fixedHeight) { + view.height_sd = view.bottom_sd - (model.equalTop.refView.top_sd + model.equalTop.offset); + } + view.top_sd = model.equalTop.refView.top_sd + model.equalTop.offset; + } + } else if (model.equalCenterY) { + if (view.superview == model.equalCenterY.refView) { + view.centerY_sd = model.equalCenterY.refView.height_sd * 0.5 + model.equalCenterY.offset; + } else { + view.centerY_sd = model.equalCenterY.refView.centerY_sd + model.equalCenterY.offset; + } + } else if (model.centerY) { + view.centerY_sd = [model.centerY floatValue]; + } +} + +- (void)layoutBottomWithView:(UIView *)view model:(SDAutoLayoutModel *)model +{ + if (model.bottom) { + if (view.superview == model.bottom.refView) { + if (!view.fixedHeight) { + view.height_sd = view.superview.height_sd - view.top_sd - [model.bottom.value floatValue]; + } + view.bottom_sd = model.bottom.refView.height_sd - [model.bottom.value floatValue]; + } else { + if (!view.fixedHeight) { + view.height_sd = model.bottom.refView.top_sd - view.top_sd - [model.bottom.value floatValue]; + } + view.bottom_sd = model.bottom.refView.top_sd - [model.bottom.value floatValue]; + } + + } else if (model.equalBottom) { + if (view.superview == model.equalBottom.refView) { + if (!view.fixedHeight) { + view.height_sd = view.superview.height_sd - view.top_sd + model.equalBottom.offset; + } + view.bottom_sd = model.equalBottom.refView.height_sd + model.equalBottom.offset; + } else { + if (!view.fixedHeight) { + view.height_sd = model.equalBottom.refView.bottom_sd - view.top_sd + model.equalBottom.offset; + } + view.bottom_sd = model.equalBottom.refView.bottom_sd + model.equalBottom.offset; + } + } + if (model.widthEqualHeight && !view.fixedHeight) { + [self layoutRightWithView:view model:model]; + } +} + + +- (void)setupCornerRadiusWithView:(UIView *)view model:(SDAutoLayoutModel *)model +{ + CGFloat cornerRadius = view.layer.cornerRadius; + CGFloat newCornerRadius = 0; + + if (view.sd_cornerRadius && (cornerRadius != [view.sd_cornerRadius floatValue])) { + newCornerRadius = [view.sd_cornerRadius floatValue]; + } else if (view.sd_cornerRadiusFromWidthRatio && (cornerRadius != [view.sd_cornerRadiusFromWidthRatio floatValue] * view.width_sd)) { + newCornerRadius = view.width_sd * [view.sd_cornerRadiusFromWidthRatio floatValue]; + } else if (view.sd_cornerRadiusFromHeightRatio && (cornerRadius != view.height_sd * [view.sd_cornerRadiusFromHeightRatio floatValue])) { + newCornerRadius = view.height_sd * [view.sd_cornerRadiusFromHeightRatio floatValue]; + } + + if (newCornerRadius > 0) { + view.layer.cornerRadius = newCornerRadius; + view.clipsToBounds = YES; + } +} + +- (void)addAutoLayoutModel:(SDAutoLayoutModel *)model +{ + [self.autoLayoutModelsArray addObject:model]; +} + +@end + +@implementation UIButton (SDAutoLayoutButton) + ++ (void)load +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + [self sdal_exchengeMethods:@[@"layoutSubviews"] prefix:@"sd_button_"]; + }); +} + +- (void)sd_button_layoutSubviews +{ + // 如果程序崩溃在这行代码说明是你的view在执行“layoutSubvies”方法时出了问题而不是在此自动布局库内部出现了问题,请检查你的“layoutSubvies”方法 + [self sd_button_layoutSubviews]; + + [self sd_layoutSubviewsHandle]; + +} + +@end + + +@implementation UIView (SDChangeFrame) + +- (BOOL)shouldReadjustFrameBeforeStoreCache +{ + return self.sd_categoryManager.shouldReadjustFrameBeforeStoreCache; +} + +- (void)setShouldReadjustFrameBeforeStoreCache:(BOOL)shouldReadjustFrameBeforeStoreCache +{ + self.sd_categoryManager.shouldReadjustFrameBeforeStoreCache = shouldReadjustFrameBeforeStoreCache; +} + +- (CGFloat)left_sd { + return self.frame.origin.x; +} + +- (void)setLeft_sd:(CGFloat)x_sd { + CGRect frame = self.frame; + frame.origin.x = x_sd; + self.frame = frame; +} + +- (CGFloat)top_sd { + return self.frame.origin.y; +} + +- (void)setTop_sd:(CGFloat)y_sd { + CGRect frame = self.frame; + frame.origin.y = y_sd; + self.frame = frame; +} + +- (CGFloat)right_sd { + return self.frame.origin.x + self.frame.size.width; +} + +- (void)setRight_sd:(CGFloat)right_sd { + CGRect frame = self.frame; + frame.origin.x = right_sd - frame.size.width; + self.frame = frame; +} + +- (CGFloat)bottom_sd { + return self.frame.origin.y + self.frame.size.height; +} + +- (void)setBottom_sd:(CGFloat)bottom_sd { + CGRect frame = self.frame; + frame.origin.y = bottom_sd - frame.size.height; + self.frame = frame; +} + +- (CGFloat)centerX_sd +{ + return self.left_sd + self.width_sd * 0.5; +} + +- (void)setCenterX_sd:(CGFloat)centerX_sd +{ + self.left_sd = centerX_sd - self.width_sd * 0.5; +} + +- (CGFloat)centerY_sd +{ + return self.top_sd + self.height_sd * 0.5; +} + +- (void)setCenterY_sd:(CGFloat)centerY_sd +{ + self.top_sd = centerY_sd - self.height_sd * 0.5; +} + +- (CGFloat)width_sd { + return self.frame.size.width; +} + +- (void)setWidth_sd:(CGFloat)width_sd { + if (self.ownLayoutModel.widthEqualHeight) { + if (width_sd != self.height_sd) return; + } + [self setWidth:width_sd]; + if (self.ownLayoutModel.heightEqualWidth) { + self.height_sd = width_sd; + } +} + +- (CGFloat)height_sd { + return self.frame.size.height; +} + +- (void)setHeight_sd:(CGFloat)height_sd { + if (self.ownLayoutModel.heightEqualWidth) { + if (height_sd != self.width_sd) return; + } + [self setHeight:height_sd]; + if (self.ownLayoutModel.widthEqualHeight) { + self.width_sd = height_sd; + } +} + +- (CGPoint)origin_sd { + return self.frame.origin; +} + +- (void)setOrigin_sd:(CGPoint)origin_sd { + CGRect frame = self.frame; + frame.origin = origin_sd; + self.frame = frame; +} + +- (CGSize)size_sd { + return self.frame.size; +} + +- (void)setSize_sd:(CGSize)size_sd { + [self setSize:size_sd]; +} + +- (void)setWidth:(CGFloat)width +{ + CGRect frame = self.frame; + frame.size.width = width; + self.frame = frame; +} + +- (void)setHeight:(CGFloat)height { + CGRect frame = self.frame; + frame.size.height = height; + self.frame = frame; +} + +- (void)setSize:(CGSize)size { + CGRect frame = self.frame; + frame.size = size; + self.frame = frame; +} + +// 兼容旧版本 + +- (CGFloat)left +{ + return self.left_sd; +} + +- (void)setLeft:(CGFloat)left +{ + self.left_sd = left; +} + +- (CGFloat)right +{ + return self.right_sd; +} + +- (void)setRight:(CGFloat)right +{ + self.right_sd = right; +} + +- (CGFloat)width +{ + return self.width_sd; +} + +- (CGFloat)height +{ + return self.height_sd; +} + +- (CGFloat)top +{ + return self.top_sd; +} + +- (void)setTop:(CGFloat)top +{ + self.top_sd = top; +} + +- (CGFloat)bottom +{ + return self.bottom_sd; +} + +- (void)setBottom:(CGFloat)bottom +{ + self.bottom_sd = bottom; +} + +- (CGFloat)centerX +{ + return self.centerX_sd; +} + +- (void)setCenterX:(CGFloat)centerX +{ + self.centerX_sd = centerX; +} + +- (CGFloat)centerY +{ + return self.centerY_sd; +} + +- (void)setCenterY:(CGFloat)centerY +{ + self.centerY_sd = centerY; +} + +- (CGPoint)origin +{ + return self.origin_sd; +} + +- (void)setOrigin:(CGPoint)origin +{ + self.origin_sd = origin; +} + +- (CGSize)size +{ + return self.size_sd; +} + +@end + +@implementation SDUIViewCategoryManager + +@end + diff --git a/SDAutoLayoutDemo.xcodeproj/project.pbxproj b/SDAutoLayoutDemo.xcodeproj/project.pbxproj new file mode 100644 index 0000000..8e3d3ad --- /dev/null +++ b/SDAutoLayoutDemo.xcodeproj/project.pbxproj @@ -0,0 +1,2282 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 38B700E467BDE4C37C6ADD5C /* libPods-SDAutoLayoutDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B9BAE9A3DD9811F84DDDECB2 /* libPods-SDAutoLayoutDemo.a */; }; + 4808F3941CE4ABFB00242779 /* DemoVC14.m in Sources */ = {isa = PBXBuildFile; fileRef = 4808F3931CE4ABFB00242779 /* DemoVC14.m */; }; + 4808F3981CE4AC1700242779 /* DemoVC14Cell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4808F3961CE4AC1700242779 /* DemoVC14Cell.m */; }; + 4841F5E91C2ABC60004979F4 /* pic5.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4841F5E51C2ABC60004979F4 /* pic5.jpg */; }; + 4841F5EA1C2ABC60004979F4 /* pic6.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4841F5E61C2ABC60004979F4 /* pic6.jpg */; }; + 4841F5EB1C2ABC60004979F4 /* pic7.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4841F5E71C2ABC60004979F4 /* pic7.jpg */; }; + 4841F5EC1C2ABC60004979F4 /* pic8.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4841F5E81C2ABC60004979F4 /* pic8.jpg */; }; + 4841F5F21C2ACC2B004979F4 /* pbg.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4841F5F01C2ACC2B004979F4 /* pbg.jpg */; }; + 4841F5F31C2ACC2B004979F4 /* picon.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4841F5F11C2ACC2B004979F4 /* picon.jpg */; }; + 485302CC1C3F6490004361A7 /* SDBrowserImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302991C3F6490004361A7 /* SDBrowserImageView.m */; }; + 485302CD1C3F6490004361A7 /* SDPhotoBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = 4853029B1C3F6490004361A7 /* SDPhotoBrowser.m */; }; + 485302CE1C3F6490004361A7 /* SDWaitingView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4853029E1C3F6490004361A7 /* SDWaitingView.m */; }; + 485302CF1C3F6490004361A7 /* sdRefeshView_arrow@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 485302A01C3F6490004361A7 /* sdRefeshView_arrow@2x.png */; }; + 485302D01C3F6490004361A7 /* SDRefreshFooterView.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302A31C3F6490004361A7 /* SDRefreshFooterView.m */; }; + 485302D11C3F6490004361A7 /* SDRefreshHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302A51C3F6490004361A7 /* SDRefreshHeaderView.m */; }; + 485302D21C3F6490004361A7 /* SDRefreshView.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302A71C3F6490004361A7 /* SDRefreshView.m */; }; + 485302D31C3F6490004361A7 /* UIView+SDExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302A91C3F6490004361A7 /* UIView+SDExtension.m */; }; + 485302D41C3F6490004361A7 /* MKAnnotationView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302AC1C3F6490004361A7 /* MKAnnotationView+WebCache.m */; }; + 485302D51C3F6490004361A7 /* NSData+ImageContentType.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302AE1C3F6490004361A7 /* NSData+ImageContentType.m */; }; + 485302D61C3F6490004361A7 /* SDImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302B01C3F6490004361A7 /* SDImageCache.m */; }; + 485302D71C3F6490004361A7 /* SDWebImageCompat.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302B21C3F6490004361A7 /* SDWebImageCompat.m */; }; + 485302D81C3F6490004361A7 /* SDWebImageDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302B41C3F6490004361A7 /* SDWebImageDecoder.m */; }; + 485302D91C3F6490004361A7 /* SDWebImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302B61C3F6490004361A7 /* SDWebImageDownloader.m */; }; + 485302DA1C3F6490004361A7 /* SDWebImageDownloaderOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302B81C3F6490004361A7 /* SDWebImageDownloaderOperation.m */; }; + 485302DB1C3F6490004361A7 /* SDWebImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302BA1C3F6490004361A7 /* SDWebImageManager.m */; }; + 485302DC1C3F6490004361A7 /* SDWebImagePrefetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302BD1C3F6490004361A7 /* SDWebImagePrefetcher.m */; }; + 485302DD1C3F6490004361A7 /* UIButton+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302BF1C3F6490004361A7 /* UIButton+WebCache.m */; }; + 485302DE1C3F6490004361A7 /* UIImage+GIF.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302C11C3F6490004361A7 /* UIImage+GIF.m */; }; + 485302DF1C3F6490004361A7 /* UIImage+MultiFormat.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302C31C3F6490004361A7 /* UIImage+MultiFormat.m */; }; + 485302E01C3F6490004361A7 /* UIImage+WebP.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302C51C3F6490004361A7 /* UIImage+WebP.m */; }; + 485302E11C3F6490004361A7 /* UIImageView+HighlightedWebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302C71C3F6490004361A7 /* UIImageView+HighlightedWebCache.m */; }; + 485302E21C3F6490004361A7 /* UIImageView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302C91C3F6490004361A7 /* UIImageView+WebCache.m */; }; + 485302E31C3F6490004361A7 /* UIView+WebCacheOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302CB1C3F6490004361A7 /* UIView+WebCacheOperation.m */; }; + 485302F41C3F65EE004361A7 /* TAAbstractDotView.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302E71C3F65EE004361A7 /* TAAbstractDotView.m */; }; + 485302F51C3F65EE004361A7 /* TAAnimatedDotView.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302E91C3F65EE004361A7 /* TAAnimatedDotView.m */; }; + 485302F61C3F65EE004361A7 /* TADotView.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302EB1C3F65EE004361A7 /* TADotView.m */; }; + 485302F71C3F65EE004361A7 /* TAPageControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302ED1C3F65EE004361A7 /* TAPageControl.m */; }; + 485302F81C3F65EE004361A7 /* SDCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302EF1C3F65EE004361A7 /* SDCollectionViewCell.m */; }; + 485302F91C3F65EE004361A7 /* SDCycleScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302F11C3F65EE004361A7 /* SDCycleScrollView.m */; }; + 485302FA1C3F65EE004361A7 /* UIView+SDExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 485302F31C3F65EE004361A7 /* UIView+SDExtension.m */; }; + 485303351C3F977A004361A7 /* DemoVC0.m in Sources */ = {isa = PBXBuildFile; fileRef = 485303051C3F977A004361A7 /* DemoVC0.m */; }; + 485303361C3F977A004361A7 /* DemoVC1.m in Sources */ = {isa = PBXBuildFile; fileRef = 485303081C3F977A004361A7 /* DemoVC1.m */; }; + 485303371C3F977A004361A7 /* DemoVC2.m in Sources */ = {isa = PBXBuildFile; fileRef = 4853030B1C3F977A004361A7 /* DemoVC2.m */; }; + 485303381C3F977A004361A7 /* DemoVC3.m in Sources */ = {isa = PBXBuildFile; fileRef = 4853030E1C3F977A004361A7 /* DemoVC3.m */; }; + 485303391C3F977A004361A7 /* TestCell2.m in Sources */ = {isa = PBXBuildFile; fileRef = 485303101C3F977A004361A7 /* TestCell2.m */; }; + 4853033A1C3F977A004361A7 /* DemoVC4.m in Sources */ = {isa = PBXBuildFile; fileRef = 485303131C3F977A004361A7 /* DemoVC4.m */; }; + 4853033B1C3F977A004361A7 /* DemoVC5.m in Sources */ = {isa = PBXBuildFile; fileRef = 485303161C3F977A004361A7 /* DemoVC5.m */; }; + 4853033C1C3F977A004361A7 /* DemoVC5CellTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 485303181C3F977A004361A7 /* DemoVC5CellTableViewCell.m */; }; + 4853033D1C3F977A004361A7 /* DemoVC5Model.m in Sources */ = {isa = PBXBuildFile; fileRef = 4853031A1C3F977A004361A7 /* DemoVC5Model.m */; }; + 4853033E1C3F977A004361A7 /* DemoVC6.m in Sources */ = {isa = PBXBuildFile; fileRef = 4853031D1C3F977A004361A7 /* DemoVC6.m */; }; + 4853033F1C3F977A004361A7 /* DemoVC7.m in Sources */ = {isa = PBXBuildFile; fileRef = 485303201C3F977A004361A7 /* DemoVC7.m */; }; + 485303401C3F977A004361A7 /* DemoVC7Cell.m in Sources */ = {isa = PBXBuildFile; fileRef = 485303221C3F977A004361A7 /* DemoVC7Cell.m */; }; + 485303411C3F977A004361A7 /* DemoVC7Cell2.m in Sources */ = {isa = PBXBuildFile; fileRef = 485303241C3F977A004361A7 /* DemoVC7Cell2.m */; }; + 485303421C3F977A004361A7 /* DemoVC7Model.m in Sources */ = {isa = PBXBuildFile; fileRef = 485303261C3F977A004361A7 /* DemoVC7Model.m */; }; + 485303431C3F977A004361A7 /* DemoVC8.m in Sources */ = {isa = PBXBuildFile; fileRef = 485303291C3F977A004361A7 /* DemoVC8.m */; }; + 485303451C3F977A004361A7 /* DemoVC9.m in Sources */ = {isa = PBXBuildFile; fileRef = 4853032E1C3F977A004361A7 /* DemoVC9.m */; }; + 485303501C3F97AA004361A7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4853034B1C3F97AA004361A7 /* AppDelegate.m */; }; + 485303511C3F97AA004361A7 /* DemoTableViewControler.m in Sources */ = {isa = PBXBuildFile; fileRef = 4853034D1C3F97AA004361A7 /* DemoTableViewControler.m */; }; + 485303521C3F97AA004361A7 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4853034F1C3F97AA004361A7 /* ViewController.m */; }; + 485303551C3F97E6004361A7 /* DemoCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 485303541C3F97E6004361A7 /* DemoCell.m */; }; + 487D3EB71BCB9EC700A66173 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 487D3EB61BCB9EC700A66173 /* main.m */; }; + 487D3EC01BCB9EC700A66173 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 487D3EBE1BCB9EC700A66173 /* Main.storyboard */; }; + 487D3EC21BCB9EC700A66173 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 487D3EC11BCB9EC700A66173 /* Images.xcassets */; }; + 487D3EC51BCB9EC700A66173 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 487D3EC31BCB9EC700A66173 /* LaunchScreen.xib */; }; + 489489091CE5FA1700C9BB71 /* PhotosContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 489489081CE5FA1700C9BB71 /* PhotosContainerView.m */; }; + 48DBCD1A1C01E045002EC476 /* icon0.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD101C01E045002EC476 /* icon0.jpg */; }; + 48DBCD1B1C01E045002EC476 /* icon1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD111C01E045002EC476 /* icon1.jpg */; }; + 48DBCD1C1C01E045002EC476 /* icon2.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD121C01E045002EC476 /* icon2.jpg */; }; + 48DBCD1D1C01E045002EC476 /* icon3.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD131C01E045002EC476 /* icon3.jpg */; }; + 48DBCD1E1C01E045002EC476 /* icon4.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD141C01E045002EC476 /* icon4.jpg */; }; + 48DBCD1F1C01E045002EC476 /* pic0.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD151C01E045002EC476 /* pic0.jpg */; }; + 48DBCD201C01E045002EC476 /* pic1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD161C01E045002EC476 /* pic1.jpg */; }; + 48DBCD211C01E045002EC476 /* pic2.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD171C01E045002EC476 /* pic2.jpg */; }; + 48DBCD221C01E045002EC476 /* pic3.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD181C01E045002EC476 /* pic3.jpg */; }; + 48DBCD231C01E045002EC476 /* pic4.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48DBCD191C01E045002EC476 /* pic4.jpg */; }; + 48EB72EF1C748D6C003A818E /* SDWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 48EB72E31C748D6C003A818E /* SDWebViewController.m */; }; + 48EB72F01C748D6C003A818E /* SDChatModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 48EB72E71C748D6C003A818E /* SDChatModel.m */; }; + 48EB72F21C748D6C003A818E /* SDChatTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 48EB72EC1C748D6C003A818E /* SDChatTableViewCell.m */; }; + 48EB72FC1C748DF5003A818E /* MLEmoji_Expression.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 48EB72F41C748DF5003A818E /* MLEmoji_Expression.bundle */; }; + 48EB72FD1C748DF5003A818E /* MLEmoji_Expression.plist in Resources */ = {isa = PBXBuildFile; fileRef = 48EB72F51C748DF5003A818E /* MLEmoji_Expression.plist */; }; + 48EB72FE1C748DF5003A818E /* MLEmoji_ExpressionImage.plist in Resources */ = {isa = PBXBuildFile; fileRef = 48EB72F61C748DF5003A818E /* MLEmoji_ExpressionImage.plist */; }; + 48EB72FF1C748DF5003A818E /* MLEmojiLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 48EB72F81C748DF5003A818E /* MLEmojiLabel.m */; }; + 48EB73001C748DF5003A818E /* TTTAttributedLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 48EB72FB1C748DF5003A818E /* TTTAttributedLabel.m */; }; + 48EB73041C748E1A003A818E /* SDAnalogDataGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 48EB73031C748E1A003A818E /* SDAnalogDataGenerator.m */; }; + 48EB73241C748EDE003A818E /* test0.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB73061C748EDE003A818E /* test0.jpg */; }; + 48EB73251C748EDE003A818E /* test1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB73071C748EDE003A818E /* test1.jpg */; }; + 48EB73261C748EDE003A818E /* test2.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB73081C748EDE003A818E /* test2.jpg */; }; + 48EB73271C748EDE003A818E /* test3.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB73091C748EDE003A818E /* test3.jpg */; }; + 48EB73281C748EDE003A818E /* test4.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB730A1C748EDE003A818E /* test4.jpg */; }; + 48EB73291C748EDE003A818E /* 0.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB730C1C748EDE003A818E /* 0.jpg */; }; + 48EB732A1C748EDE003A818E /* 1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB730D1C748EDE003A818E /* 1.jpg */; }; + 48EB732B1C748EDE003A818E /* 10.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB730E1C748EDE003A818E /* 10.jpg */; }; + 48EB732C1C748EDE003A818E /* 11.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB730F1C748EDE003A818E /* 11.jpg */; }; + 48EB732D1C748EDE003A818E /* 12.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB73101C748EDE003A818E /* 12.jpg */; }; + 48EB732E1C748EDE003A818E /* 13.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB73111C748EDE003A818E /* 13.jpg */; }; + 48EB732F1C748EDE003A818E /* 14.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB73121C748EDE003A818E /* 14.jpg */; }; + 48EB73301C748EDE003A818E /* 15.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB73131C748EDE003A818E /* 15.jpg */; }; + 48EB73311C748EDE003A818E /* 16.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB73141C748EDE003A818E /* 16.jpg */; }; + 48EB73321C748EDE003A818E /* 17.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB73151C748EDE003A818E /* 17.jpg */; }; + 48EB73331C748EDE003A818E /* 18.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB73161C748EDE003A818E /* 18.jpg */; }; + 48EB73341C748EDE003A818E /* 19.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB73171C748EDE003A818E /* 19.jpg */; }; + 48EB73351C748EDE003A818E /* 2.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB73181C748EDE003A818E /* 2.jpg */; }; + 48EB73361C748EDE003A818E /* 20.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB73191C748EDE003A818E /* 20.jpg */; }; + 48EB73371C748EDE003A818E /* 21.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB731A1C748EDE003A818E /* 21.jpg */; }; + 48EB73381C748EDE003A818E /* 22.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB731B1C748EDE003A818E /* 22.jpg */; }; + 48EB73391C748EDE003A818E /* 23.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB731C1C748EDE003A818E /* 23.jpg */; }; + 48EB733A1C748EDE003A818E /* 3.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB731D1C748EDE003A818E /* 3.jpg */; }; + 48EB733B1C748EDE003A818E /* 4.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB731E1C748EDE003A818E /* 4.jpg */; }; + 48EB733C1C748EDE003A818E /* 5.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB731F1C748EDE003A818E /* 5.jpg */; }; + 48EB733D1C748EDE003A818E /* 6.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB73201C748EDE003A818E /* 6.jpg */; }; + 48EB733E1C748EDE003A818E /* 7.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB73211C748EDE003A818E /* 7.jpg */; }; + 48EB733F1C748EDE003A818E /* 8.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB73221C748EDE003A818E /* 8.jpg */; }; + 48EB73401C748EDE003A818E /* 9.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 48EB73231C748EDE003A818E /* 9.jpg */; }; + 48EB73431C748F26003A818E /* DemoVC11.m in Sources */ = {isa = PBXBuildFile; fileRef = 48EB73421C748F26003A818E /* DemoVC11.m */; }; + 48EB73451C749212003A818E /* Home.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 48EB73441C749212003A818E /* Home.xcassets */; }; + 48EEAD6C1C9C25F4001138FE /* DemoVC12.m in Sources */ = {isa = PBXBuildFile; fileRef = 48EEAD6B1C9C25F4001138FE /* DemoVC12.m */; }; + 48EEAD701C9C3006001138FE /* DemoVC13.m in Sources */ = {isa = PBXBuildFile; fileRef = 48EEAD6F1C9C3006001138FE /* DemoVC13.m */; }; + 48FC0E161C4FB3A8004098B8 /* Ads.m in Sources */ = {isa = PBXBuildFile; fileRef = 48FC0E041C4FB3A8004098B8 /* Ads.m */; }; + 48FC0E171C4FB3A8004098B8 /* Editor.m in Sources */ = {isa = PBXBuildFile; fileRef = 48FC0E061C4FB3A8004098B8 /* Editor.m */; }; + 48FC0E181C4FB3A8004098B8 /* Imgextra.m in Sources */ = {isa = PBXBuildFile; fileRef = 48FC0E081C4FB3A8004098B8 /* Imgextra.m */; }; + 48FC0E191C4FB3A8004098B8 /* ThreeModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 48FC0E0A1C4FB3A8004098B8 /* ThreeModel.m */; }; + 48FC0E1A1C4FB3A8004098B8 /* ThreeBaseCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 48FC0E0D1C4FB3A8004098B8 /* ThreeBaseCell.m */; }; + 48FC0E1B1C4FB3A8004098B8 /* ThreeFirstCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 48FC0E0F1C4FB3A8004098B8 /* ThreeFirstCell.m */; }; + 48FC0E1C1C4FB3A8004098B8 /* ThreeFourthCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 48FC0E111C4FB3A8004098B8 /* ThreeFourthCell.m */; }; + 48FC0E1D1C4FB3A8004098B8 /* ThreeSecondCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 48FC0E131C4FB3A8004098B8 /* ThreeSecondCell.m */; }; + 48FC0E1E1C4FB3A8004098B8 /* ThreeThirdCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 48FC0E151C4FB3A8004098B8 /* ThreeThirdCell.m */; }; + 48FC0E211C4FB3EA004098B8 /* DemoVC10.m in Sources */ = {isa = PBXBuildFile; fileRef = 48FC0E201C4FB3EA004098B8 /* DemoVC10.m */; }; + 48FC0ECB1C4FB7CA004098B8 /* XYString.m in Sources */ = {isa = PBXBuildFile; fileRef = 48FC0ECA1C4FB7CA004098B8 /* XYString.m */; }; + 6B42D97E2087825A009D5BEC /* DemoVC14Cell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6B42D97D2087825A009D5BEC /* DemoVC14Cell.xib */; }; + 9997FB651CB2C26800A6FF62 /* SDTimeLineCellOperationMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 9997FB641CB2C26800A6FF62 /* SDTimeLineCellOperationMenu.m */; }; + 99D694B51C8C54D000DA97E1 /* SDTimeLineCellModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694A01C8C54D000DA97E1 /* SDTimeLineCellModel.m */; }; + 99D694B61C8C54D100DA97E1 /* SDTimeLineTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694A31C8C54D000DA97E1 /* SDTimeLineTableViewController.m */; }; + 99D694B71C8C54D100DA97E1 /* SDTimeLineCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694A71C8C54D000DA97E1 /* SDTimeLineCell.m */; }; + 99D694B81C8C54D100DA97E1 /* SDTimeLineCellCommentView.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694AA1C8C54D000DA97E1 /* SDTimeLineCellCommentView.m */; }; + 99D694B91C8C54D100DA97E1 /* SDTimeLineTableHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694AD1C8C54D000DA97E1 /* SDTimeLineTableHeaderView.m */; }; + 99D694BA1C8C54D100DA97E1 /* SDWeiXinPhotoContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694B01C8C54D000DA97E1 /* SDWeiXinPhotoContainerView.m */; }; + 99D694BB1C8C54D100DA97E1 /* SDTimeLineRefreshHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694B41C8C54D000DA97E1 /* SDTimeLineRefreshHeader.m */; }; + 99D694D81C8C553500DA97E1 /* NSAttributedString+MLLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694C01C8C553500DA97E1 /* NSAttributedString+MLLabel.m */; }; + 99D694D91C8C553500DA97E1 /* NSMutableAttributedString+MLLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694C21C8C553500DA97E1 /* NSMutableAttributedString+MLLabel.m */; }; + 99D694DA1C8C553500DA97E1 /* NSString+MLLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694C41C8C553500DA97E1 /* NSString+MLLabel.m */; }; + 99D694DB1C8C553500DA97E1 /* MLExpressionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694C71C8C553500DA97E1 /* MLExpressionManager.m */; }; + 99D694DC1C8C553500DA97E1 /* MLTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694C91C8C553500DA97E1 /* MLTextAttachment.m */; }; + 99D694DD1C8C553500DA97E1 /* NSAttributedString+MLExpression.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694CB1C8C553500DA97E1 /* NSAttributedString+MLExpression.m */; }; + 99D694DE1C8C553500DA97E1 /* NSString+MLExpression.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694CD1C8C553500DA97E1 /* NSString+MLExpression.m */; }; + 99D694DF1C8C553500DA97E1 /* MLLabelLayoutManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694D11C8C553500DA97E1 /* MLLabelLayoutManager.m */; }; + 99D694E01C8C553500DA97E1 /* MLLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694D31C8C553500DA97E1 /* MLLabel.m */; }; + 99D694E11C8C553500DA97E1 /* MLLinkLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694D51C8C553500DA97E1 /* MLLinkLabel.m */; }; + 99D694E31C8C553500DA97E1 /* README.md in Sources */ = {isa = PBXBuildFile; fileRef = 99D694D71C8C553500DA97E1 /* README.md */; }; + 99D694EE1C8C567A00DA97E1 /* SDBaseRefreshView.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694E91C8C567A00DA97E1 /* SDBaseRefreshView.m */; }; + 99D694EF1C8C567A00DA97E1 /* SDBaseNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694EB1C8C567A00DA97E1 /* SDBaseNavigationController.m */; }; + 99D694F01C8C567A00DA97E1 /* SDBaseTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694ED1C8C567A00DA97E1 /* SDBaseTableViewController.m */; }; + 99D694F21C8C585300DA97E1 /* Discover.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 99D694F11C8C585200DA97E1 /* Discover.xcassets */; }; + 99D694F81C8C7F7600DA97E1 /* SDTimeLineRefreshFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = 99D694F71C8C7F7600DA97E1 /* SDTimeLineRefreshFooter.m */; }; + 9B2AF0F51D1166690007A130 /* theme_day_json.json in Resources */ = {isa = PBXBuildFile; fileRef = 9B2AF0F31D1166690007A130 /* theme_day_json.json */; }; + 9B2AF0F61D1166690007A130 /* theme_night_json.json in Resources */ = {isa = PBXBuildFile; fileRef = 9B2AF0F41D1166690007A130 /* theme_night_json.json */; }; + A1B2C3D11BCB9EC700A66173 /* SDAutoLayoutDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D01BCB9EC700A66173 /* SDAutoLayoutDemoTests.m */; }; + B2C3D4EC1BCB9EC700A66173 /* SDAutoLayoutDemoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2C3D4EB1BCB9EC700A66173 /* SDAutoLayoutDemoUITests.swift */; }; + B2C3D4F71BCB9EC700A66173 /* LayoutAssertionHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2C3D4F51BCB9EC700A66173 /* LayoutAssertionHelper.swift */; }; + B2C3D4F81BCB9EC700A66173 /* DemoLayoutCatalog.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2C3D4F61BCB9EC700A66173 /* DemoLayoutCatalog.swift */; }; + B2C3D4F91BCB9EC700A66173 /* DemoLayoutContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2C3D4FA1BCB9EC700A66173 /* DemoLayoutContext.swift */; }; + B2C3D4FB1BCB9EC700A66173 /* DemoVC1LayoutAssertions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2C3D4FC1BCB9EC700A66173 /* DemoVC1LayoutAssertions.swift */; }; + B2C3D4FD1BCB9EC700A66173 /* DemoScreenLayoutAssertions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2C3D4FE1BCB9EC700A66173 /* DemoScreenLayoutAssertions.swift */; }; + A1B2C3D31BCB9EC700A66173 /* news_sample.json in Resources */ = {isa = PBXBuildFile; fileRef = A1B2C3D21BCB9EC700A66173 /* news_sample.json */; }; + B1B944B220BCFB44008F658A /* SDAutolayout+extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B944B120BCFB44008F658A /* SDAutolayout+extension.swift */; }; + C94A1348215F572E00A92F35 /* LEETheme.m in Sources */ = {isa = PBXBuildFile; fileRef = C94A1346215F572E00A92F35 /* LEETheme.m */; }; + C94A1350215F591A00A92F35 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C94A134F215F591A00A92F35 /* AppDelegate.swift */; }; + C94A1352215F591A00A92F35 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C94A1351215F591A00A92F35 /* ViewController.swift */; }; + C94A1355215F591A00A92F35 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C94A1353215F591A00A92F35 /* Main.storyboard */; }; + C94A1357215F591C00A92F35 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C94A1356215F591C00A92F35 /* Assets.xcassets */; }; + C94A135A215F591C00A92F35 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C94A1358215F591C00A92F35 /* LaunchScreen.storyboard */; }; + C96D0594215F5BED0050BA9F /* UIView+SDAutoLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = C96D058F215F5BED0050BA9F /* UIView+SDAutoLayout.m */; }; + C96D0595215F5BED0050BA9F /* UITableView+SDAutoTableViewCellHeight.m in Sources */ = {isa = PBXBuildFile; fileRef = C96D0592215F5BED0050BA9F /* UITableView+SDAutoTableViewCellHeight.m */; }; + C96D059C215F5C040050BA9F /* UIView+SDAutoLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = C96D0597215F5C040050BA9F /* UIView+SDAutoLayout.m */; }; + C96D059D215F5C040050BA9F /* UITableView+SDAutoTableViewCellHeight.m in Sources */ = {isa = PBXBuildFile; fileRef = C96D059A215F5C040050BA9F /* UITableView+SDAutoTableViewCellHeight.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 487D3ED11BCB9EC700A66173 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 487D3EA91BCB9EC700A66173 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 487D3EB01BCB9EC700A66173; + remoteInfo = SDAutoLayoutDemo; + }; + B2C3D4E91BCB9EC700A66173 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 487D3EA91BCB9EC700A66173 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 487D3EB01BCB9EC700A66173; + remoteInfo = SDAutoLayoutDemo; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 4808F3921CE4ABFB00242779 /* DemoVC14.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC14.h; sourceTree = ""; }; + 4808F3931CE4ABFB00242779 /* DemoVC14.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC14.m; sourceTree = ""; }; + 4808F3951CE4AC1700242779 /* DemoVC14Cell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC14Cell.h; sourceTree = ""; }; + 4808F3961CE4AC1700242779 /* DemoVC14Cell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC14Cell.m; sourceTree = ""; }; + 4841F5E51C2ABC60004979F4 /* pic5.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = pic5.jpg; sourceTree = ""; }; + 4841F5E61C2ABC60004979F4 /* pic6.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = pic6.jpg; sourceTree = ""; }; + 4841F5E71C2ABC60004979F4 /* pic7.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = pic7.jpg; sourceTree = ""; }; + 4841F5E81C2ABC60004979F4 /* pic8.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = pic8.jpg; sourceTree = ""; }; + 4841F5F01C2ACC2B004979F4 /* pbg.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = pbg.jpg; sourceTree = ""; }; + 4841F5F11C2ACC2B004979F4 /* picon.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = picon.jpg; sourceTree = ""; }; + 485302981C3F648F004361A7 /* SDBrowserImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDBrowserImageView.h; sourceTree = ""; }; + 485302991C3F6490004361A7 /* SDBrowserImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDBrowserImageView.m; sourceTree = ""; }; + 4853029A1C3F6490004361A7 /* SDPhotoBrowser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDPhotoBrowser.h; sourceTree = ""; }; + 4853029B1C3F6490004361A7 /* SDPhotoBrowser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDPhotoBrowser.m; sourceTree = ""; }; + 4853029C1C3F6490004361A7 /* SDPhotoBrowserConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDPhotoBrowserConfig.h; sourceTree = ""; }; + 4853029D1C3F6490004361A7 /* SDWaitingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWaitingView.h; sourceTree = ""; }; + 4853029E1C3F6490004361A7 /* SDWaitingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWaitingView.m; sourceTree = ""; }; + 485302A01C3F6490004361A7 /* sdRefeshView_arrow@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "sdRefeshView_arrow@2x.png"; sourceTree = ""; }; + 485302A11C3F6490004361A7 /* SDRefresh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDRefresh.h; sourceTree = ""; }; + 485302A21C3F6490004361A7 /* SDRefreshFooterView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDRefreshFooterView.h; sourceTree = ""; }; + 485302A31C3F6490004361A7 /* SDRefreshFooterView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDRefreshFooterView.m; sourceTree = ""; }; + 485302A41C3F6490004361A7 /* SDRefreshHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDRefreshHeaderView.h; sourceTree = ""; }; + 485302A51C3F6490004361A7 /* SDRefreshHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDRefreshHeaderView.m; sourceTree = ""; }; + 485302A61C3F6490004361A7 /* SDRefreshView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDRefreshView.h; sourceTree = ""; }; + 485302A71C3F6490004361A7 /* SDRefreshView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDRefreshView.m; sourceTree = ""; }; + 485302A81C3F6490004361A7 /* UIView+SDExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+SDExtension.h"; sourceTree = ""; }; + 485302A91C3F6490004361A7 /* UIView+SDExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+SDExtension.m"; sourceTree = ""; }; + 485302AB1C3F6490004361A7 /* MKAnnotationView+WebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MKAnnotationView+WebCache.h"; sourceTree = ""; }; + 485302AC1C3F6490004361A7 /* MKAnnotationView+WebCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MKAnnotationView+WebCache.m"; sourceTree = ""; }; + 485302AD1C3F6490004361A7 /* NSData+ImageContentType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+ImageContentType.h"; sourceTree = ""; }; + 485302AE1C3F6490004361A7 /* NSData+ImageContentType.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+ImageContentType.m"; sourceTree = ""; }; + 485302AF1C3F6490004361A7 /* SDImageCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDImageCache.h; sourceTree = ""; }; + 485302B01C3F6490004361A7 /* SDImageCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDImageCache.m; sourceTree = ""; }; + 485302B11C3F6490004361A7 /* SDWebImageCompat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebImageCompat.h; sourceTree = ""; }; + 485302B21C3F6490004361A7 /* SDWebImageCompat.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageCompat.m; sourceTree = ""; }; + 485302B31C3F6490004361A7 /* SDWebImageDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebImageDecoder.h; sourceTree = ""; }; + 485302B41C3F6490004361A7 /* SDWebImageDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageDecoder.m; sourceTree = ""; }; + 485302B51C3F6490004361A7 /* SDWebImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebImageDownloader.h; sourceTree = ""; }; + 485302B61C3F6490004361A7 /* SDWebImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageDownloader.m; sourceTree = ""; }; + 485302B71C3F6490004361A7 /* SDWebImageDownloaderOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebImageDownloaderOperation.h; sourceTree = ""; }; + 485302B81C3F6490004361A7 /* SDWebImageDownloaderOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageDownloaderOperation.m; sourceTree = ""; }; + 485302B91C3F6490004361A7 /* SDWebImageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebImageManager.h; sourceTree = ""; }; + 485302BA1C3F6490004361A7 /* SDWebImageManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageManager.m; sourceTree = ""; }; + 485302BB1C3F6490004361A7 /* SDWebImageOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebImageOperation.h; sourceTree = ""; }; + 485302BC1C3F6490004361A7 /* SDWebImagePrefetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebImagePrefetcher.h; sourceTree = ""; }; + 485302BD1C3F6490004361A7 /* SDWebImagePrefetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImagePrefetcher.m; sourceTree = ""; }; + 485302BE1C3F6490004361A7 /* UIButton+WebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIButton+WebCache.h"; sourceTree = ""; }; + 485302BF1C3F6490004361A7 /* UIButton+WebCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIButton+WebCache.m"; sourceTree = ""; }; + 485302C01C3F6490004361A7 /* UIImage+GIF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+GIF.h"; sourceTree = ""; }; + 485302C11C3F6490004361A7 /* UIImage+GIF.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+GIF.m"; sourceTree = ""; }; + 485302C21C3F6490004361A7 /* UIImage+MultiFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+MultiFormat.h"; sourceTree = ""; }; + 485302C31C3F6490004361A7 /* UIImage+MultiFormat.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+MultiFormat.m"; sourceTree = ""; }; + 485302C41C3F6490004361A7 /* UIImage+WebP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+WebP.h"; sourceTree = ""; }; + 485302C51C3F6490004361A7 /* UIImage+WebP.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+WebP.m"; sourceTree = ""; }; + 485302C61C3F6490004361A7 /* UIImageView+HighlightedWebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImageView+HighlightedWebCache.h"; sourceTree = ""; }; + 485302C71C3F6490004361A7 /* UIImageView+HighlightedWebCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImageView+HighlightedWebCache.m"; sourceTree = ""; }; + 485302C81C3F6490004361A7 /* UIImageView+WebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImageView+WebCache.h"; sourceTree = ""; }; + 485302C91C3F6490004361A7 /* UIImageView+WebCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImageView+WebCache.m"; sourceTree = ""; }; + 485302CA1C3F6490004361A7 /* UIView+WebCacheOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+WebCacheOperation.h"; sourceTree = ""; }; + 485302CB1C3F6490004361A7 /* UIView+WebCacheOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+WebCacheOperation.m"; sourceTree = ""; }; + 485302E61C3F65EE004361A7 /* TAAbstractDotView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TAAbstractDotView.h; sourceTree = ""; }; + 485302E71C3F65EE004361A7 /* TAAbstractDotView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TAAbstractDotView.m; sourceTree = ""; }; + 485302E81C3F65EE004361A7 /* TAAnimatedDotView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TAAnimatedDotView.h; sourceTree = ""; }; + 485302E91C3F65EE004361A7 /* TAAnimatedDotView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TAAnimatedDotView.m; sourceTree = ""; }; + 485302EA1C3F65EE004361A7 /* TADotView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TADotView.h; sourceTree = ""; }; + 485302EB1C3F65EE004361A7 /* TADotView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TADotView.m; sourceTree = ""; }; + 485302EC1C3F65EE004361A7 /* TAPageControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TAPageControl.h; sourceTree = ""; }; + 485302ED1C3F65EE004361A7 /* TAPageControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TAPageControl.m; sourceTree = ""; }; + 485302EE1C3F65EE004361A7 /* SDCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDCollectionViewCell.h; sourceTree = ""; }; + 485302EF1C3F65EE004361A7 /* SDCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDCollectionViewCell.m; sourceTree = ""; }; + 485302F01C3F65EE004361A7 /* SDCycleScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDCycleScrollView.h; sourceTree = ""; }; + 485302F11C3F65EE004361A7 /* SDCycleScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDCycleScrollView.m; sourceTree = ""; }; + 485302F21C3F65EE004361A7 /* UIView+SDExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+SDExtension.h"; sourceTree = ""; }; + 485302F31C3F65EE004361A7 /* UIView+SDExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+SDExtension.m"; sourceTree = ""; }; + 485303041C3F977A004361A7 /* DemoVC0.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC0.h; sourceTree = ""; }; + 485303051C3F977A004361A7 /* DemoVC0.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC0.m; sourceTree = ""; }; + 485303071C3F977A004361A7 /* DemoVC1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC1.h; sourceTree = ""; }; + 485303081C3F977A004361A7 /* DemoVC1.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC1.m; sourceTree = ""; }; + 4853030A1C3F977A004361A7 /* DemoVC2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC2.h; sourceTree = ""; }; + 4853030B1C3F977A004361A7 /* DemoVC2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC2.m; sourceTree = ""; }; + 4853030D1C3F977A004361A7 /* DemoVC3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC3.h; sourceTree = ""; }; + 4853030E1C3F977A004361A7 /* DemoVC3.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC3.m; sourceTree = ""; }; + 4853030F1C3F977A004361A7 /* TestCell2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestCell2.h; sourceTree = ""; }; + 485303101C3F977A004361A7 /* TestCell2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestCell2.m; sourceTree = ""; }; + 485303121C3F977A004361A7 /* DemoVC4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC4.h; sourceTree = ""; }; + 485303131C3F977A004361A7 /* DemoVC4.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC4.m; sourceTree = ""; }; + 485303151C3F977A004361A7 /* DemoVC5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC5.h; sourceTree = ""; }; + 485303161C3F977A004361A7 /* DemoVC5.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC5.m; sourceTree = ""; }; + 485303171C3F977A004361A7 /* DemoVC5CellTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC5CellTableViewCell.h; sourceTree = ""; }; + 485303181C3F977A004361A7 /* DemoVC5CellTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC5CellTableViewCell.m; sourceTree = ""; }; + 485303191C3F977A004361A7 /* DemoVC5Model.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC5Model.h; sourceTree = ""; }; + 4853031A1C3F977A004361A7 /* DemoVC5Model.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC5Model.m; sourceTree = ""; }; + 4853031C1C3F977A004361A7 /* DemoVC6.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC6.h; sourceTree = ""; }; + 4853031D1C3F977A004361A7 /* DemoVC6.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC6.m; sourceTree = ""; }; + 4853031F1C3F977A004361A7 /* DemoVC7.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC7.h; sourceTree = ""; }; + 485303201C3F977A004361A7 /* DemoVC7.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC7.m; sourceTree = ""; }; + 485303211C3F977A004361A7 /* DemoVC7Cell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC7Cell.h; sourceTree = ""; }; + 485303221C3F977A004361A7 /* DemoVC7Cell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC7Cell.m; sourceTree = ""; }; + 485303231C3F977A004361A7 /* DemoVC7Cell2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC7Cell2.h; sourceTree = ""; }; + 485303241C3F977A004361A7 /* DemoVC7Cell2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC7Cell2.m; sourceTree = ""; }; + 485303251C3F977A004361A7 /* DemoVC7Model.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC7Model.h; sourceTree = ""; }; + 485303261C3F977A004361A7 /* DemoVC7Model.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC7Model.m; sourceTree = ""; }; + 485303281C3F977A004361A7 /* DemoVC8.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC8.h; sourceTree = ""; }; + 485303291C3F977A004361A7 /* DemoVC8.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = DemoVC8.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 4853032D1C3F977A004361A7 /* DemoVC9.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC9.h; sourceTree = ""; }; + 4853032E1C3F977A004361A7 /* DemoVC9.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = DemoVC9.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 4853034A1C3F97AA004361A7 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 4853034B1C3F97AA004361A7 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 4853034C1C3F97AA004361A7 /* DemoTableViewControler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoTableViewControler.h; sourceTree = ""; }; + 4853034D1C3F97AA004361A7 /* DemoTableViewControler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoTableViewControler.m; sourceTree = ""; }; + 4853034E1C3F97AA004361A7 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 4853034F1C3F97AA004361A7 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 485303531C3F97E6004361A7 /* DemoCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoCell.h; sourceTree = ""; }; + 485303541C3F97E6004361A7 /* DemoCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoCell.m; sourceTree = ""; }; + 487D3EB11BCB9EC700A66173 /* SDAutoLayoutDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SDAutoLayoutDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 487D3EB51BCB9EC700A66173 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 487D3EB61BCB9EC700A66173 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 487D3EBF1BCB9EC700A66173 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 487D3EC11BCB9EC700A66173 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 487D3EC41BCB9EC700A66173 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; + 487D3EC71BCB9EC700A66173 /* SDAutoLayoutDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SDAutoLayoutDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + B2C3D4E01BCB9EC700A66173 /* SDAutoLayoutDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SDAutoLayoutDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + B2C3D4EB1BCB9EC700A66173 /* SDAutoLayoutDemoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDAutoLayoutDemoUITests.swift; sourceTree = ""; }; + B2C3D4ED1BCB9EC700A66173 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B2C3D4F51BCB9EC700A66173 /* LayoutAssertionHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutAssertionHelper.swift; sourceTree = ""; }; + B2C3D4F61BCB9EC700A66173 /* DemoLayoutCatalog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoLayoutCatalog.swift; sourceTree = ""; }; + B2C3D4FA1BCB9EC700A66173 /* DemoLayoutContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoLayoutContext.swift; sourceTree = ""; }; + B2C3D4FC1BCB9EC700A66173 /* DemoVC1LayoutAssertions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoVC1LayoutAssertions.swift; sourceTree = ""; }; + B2C3D4FE1BCB9EC700A66173 /* DemoScreenLayoutAssertions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoScreenLayoutAssertions.swift; sourceTree = ""; }; + 489489071CE5FA1700C9BB71 /* PhotosContainerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PhotosContainerView.h; sourceTree = ""; }; + 489489081CE5FA1700C9BB71 /* PhotosContainerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PhotosContainerView.m; sourceTree = ""; }; + 48DBCD101C01E045002EC476 /* icon0.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = icon0.jpg; sourceTree = ""; }; + 48DBCD111C01E045002EC476 /* icon1.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = icon1.jpg; sourceTree = ""; }; + 48DBCD121C01E045002EC476 /* icon2.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = icon2.jpg; sourceTree = ""; }; + 48DBCD131C01E045002EC476 /* icon3.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = icon3.jpg; sourceTree = ""; }; + 48DBCD141C01E045002EC476 /* icon4.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = icon4.jpg; sourceTree = ""; }; + 48DBCD151C01E045002EC476 /* pic0.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = pic0.jpg; sourceTree = ""; }; + 48DBCD161C01E045002EC476 /* pic1.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = pic1.jpg; sourceTree = ""; }; + 48DBCD171C01E045002EC476 /* pic2.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = pic2.jpg; sourceTree = ""; }; + 48DBCD181C01E045002EC476 /* pic3.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = pic3.jpg; sourceTree = ""; }; + 48DBCD191C01E045002EC476 /* pic4.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = pic4.jpg; sourceTree = ""; }; + 48EB72E21C748D6C003A818E /* SDWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebViewController.h; sourceTree = ""; }; + 48EB72E31C748D6C003A818E /* SDWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebViewController.m; sourceTree = ""; }; + 48EB72E61C748D6C003A818E /* SDChatModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDChatModel.h; sourceTree = ""; }; + 48EB72E71C748D6C003A818E /* SDChatModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDChatModel.m; sourceTree = ""; }; + 48EB72EB1C748D6C003A818E /* SDChatTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDChatTableViewCell.h; sourceTree = ""; }; + 48EB72EC1C748D6C003A818E /* SDChatTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDChatTableViewCell.m; sourceTree = ""; }; + 48EB72F41C748DF5003A818E /* MLEmoji_Expression.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = MLEmoji_Expression.bundle; sourceTree = ""; }; + 48EB72F51C748DF5003A818E /* MLEmoji_Expression.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = MLEmoji_Expression.plist; sourceTree = ""; }; + 48EB72F61C748DF5003A818E /* MLEmoji_ExpressionImage.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = MLEmoji_ExpressionImage.plist; sourceTree = ""; }; + 48EB72F71C748DF5003A818E /* MLEmojiLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLEmojiLabel.h; sourceTree = ""; }; + 48EB72F81C748DF5003A818E /* MLEmojiLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MLEmojiLabel.m; sourceTree = ""; }; + 48EB72FA1C748DF5003A818E /* TTTAttributedLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTTAttributedLabel.h; sourceTree = ""; }; + 48EB72FB1C748DF5003A818E /* TTTAttributedLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTTAttributedLabel.m; sourceTree = ""; }; + 48EB73021C748E1A003A818E /* SDAnalogDataGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDAnalogDataGenerator.h; sourceTree = ""; }; + 48EB73031C748E1A003A818E /* SDAnalogDataGenerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDAnalogDataGenerator.m; sourceTree = ""; }; + 48EB73061C748EDE003A818E /* test0.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test0.jpg; sourceTree = ""; }; + 48EB73071C748EDE003A818E /* test1.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test1.jpg; sourceTree = ""; }; + 48EB73081C748EDE003A818E /* test2.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test2.jpg; sourceTree = ""; }; + 48EB73091C748EDE003A818E /* test3.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test3.jpg; sourceTree = ""; }; + 48EB730A1C748EDE003A818E /* test4.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test4.jpg; sourceTree = ""; }; + 48EB730C1C748EDE003A818E /* 0.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 0.jpg; sourceTree = ""; }; + 48EB730D1C748EDE003A818E /* 1.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 1.jpg; sourceTree = ""; }; + 48EB730E1C748EDE003A818E /* 10.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 10.jpg; sourceTree = ""; }; + 48EB730F1C748EDE003A818E /* 11.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 11.jpg; sourceTree = ""; }; + 48EB73101C748EDE003A818E /* 12.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 12.jpg; sourceTree = ""; }; + 48EB73111C748EDE003A818E /* 13.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 13.jpg; sourceTree = ""; }; + 48EB73121C748EDE003A818E /* 14.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 14.jpg; sourceTree = ""; }; + 48EB73131C748EDE003A818E /* 15.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 15.jpg; sourceTree = ""; }; + 48EB73141C748EDE003A818E /* 16.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 16.jpg; sourceTree = ""; }; + 48EB73151C748EDE003A818E /* 17.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 17.jpg; sourceTree = ""; }; + 48EB73161C748EDE003A818E /* 18.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 18.jpg; sourceTree = ""; }; + 48EB73171C748EDE003A818E /* 19.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 19.jpg; sourceTree = ""; }; + 48EB73181C748EDE003A818E /* 2.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 2.jpg; sourceTree = ""; }; + 48EB73191C748EDE003A818E /* 20.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 20.jpg; sourceTree = ""; }; + 48EB731A1C748EDE003A818E /* 21.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 21.jpg; sourceTree = ""; }; + 48EB731B1C748EDE003A818E /* 22.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 22.jpg; sourceTree = ""; }; + 48EB731C1C748EDE003A818E /* 23.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 23.jpg; sourceTree = ""; }; + 48EB731D1C748EDE003A818E /* 3.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 3.jpg; sourceTree = ""; }; + 48EB731E1C748EDE003A818E /* 4.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 4.jpg; sourceTree = ""; }; + 48EB731F1C748EDE003A818E /* 5.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 5.jpg; sourceTree = ""; }; + 48EB73201C748EDE003A818E /* 6.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 6.jpg; sourceTree = ""; }; + 48EB73211C748EDE003A818E /* 7.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 7.jpg; sourceTree = ""; }; + 48EB73221C748EDE003A818E /* 8.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 8.jpg; sourceTree = ""; }; + 48EB73231C748EDE003A818E /* 9.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 9.jpg; sourceTree = ""; }; + 48EB73411C748F26003A818E /* DemoVC11.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC11.h; sourceTree = ""; }; + 48EB73421C748F26003A818E /* DemoVC11.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC11.m; sourceTree = ""; }; + 48EB73441C749212003A818E /* Home.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Home.xcassets; sourceTree = ""; }; + 48EEAD6A1C9C25F4001138FE /* DemoVC12.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC12.h; sourceTree = ""; }; + 48EEAD6B1C9C25F4001138FE /* DemoVC12.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC12.m; sourceTree = ""; }; + 48EEAD6E1C9C3006001138FE /* DemoVC13.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC13.h; sourceTree = ""; }; + 48EEAD6F1C9C3006001138FE /* DemoVC13.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC13.m; sourceTree = ""; }; + 48FC0E031C4FB3A8004098B8 /* Ads.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Ads.h; sourceTree = ""; }; + 48FC0E041C4FB3A8004098B8 /* Ads.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Ads.m; sourceTree = ""; }; + 48FC0E051C4FB3A8004098B8 /* Editor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Editor.h; sourceTree = ""; }; + 48FC0E061C4FB3A8004098B8 /* Editor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Editor.m; sourceTree = ""; }; + 48FC0E071C4FB3A8004098B8 /* Imgextra.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Imgextra.h; sourceTree = ""; }; + 48FC0E081C4FB3A8004098B8 /* Imgextra.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Imgextra.m; sourceTree = ""; }; + 48FC0E091C4FB3A8004098B8 /* ThreeModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreeModel.h; sourceTree = ""; }; + 48FC0E0A1C4FB3A8004098B8 /* ThreeModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreeModel.m; sourceTree = ""; }; + 48FC0E0C1C4FB3A8004098B8 /* ThreeBaseCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreeBaseCell.h; sourceTree = ""; }; + 48FC0E0D1C4FB3A8004098B8 /* ThreeBaseCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreeBaseCell.m; sourceTree = ""; }; + 48FC0E0E1C4FB3A8004098B8 /* ThreeFirstCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreeFirstCell.h; sourceTree = ""; }; + 48FC0E0F1C4FB3A8004098B8 /* ThreeFirstCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreeFirstCell.m; sourceTree = ""; }; + 48FC0E101C4FB3A8004098B8 /* ThreeFourthCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreeFourthCell.h; sourceTree = ""; }; + 48FC0E111C4FB3A8004098B8 /* ThreeFourthCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreeFourthCell.m; sourceTree = ""; }; + 48FC0E121C4FB3A8004098B8 /* ThreeSecondCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreeSecondCell.h; sourceTree = ""; }; + 48FC0E131C4FB3A8004098B8 /* ThreeSecondCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreeSecondCell.m; sourceTree = ""; }; + 48FC0E141C4FB3A8004098B8 /* ThreeThirdCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreeThirdCell.h; sourceTree = ""; }; + 48FC0E151C4FB3A8004098B8 /* ThreeThirdCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreeThirdCell.m; sourceTree = ""; }; + 48FC0E1F1C4FB3EA004098B8 /* DemoVC10.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoVC10.h; sourceTree = ""; }; + 48FC0E201C4FB3EA004098B8 /* DemoVC10.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoVC10.m; sourceTree = ""; }; + 48FC0EC91C4FB7CA004098B8 /* XYString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XYString.h; sourceTree = ""; }; + 48FC0ECA1C4FB7CA004098B8 /* XYString.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XYString.m; sourceTree = ""; }; + 59F464B58FAB1B71D18C24E1 /* Pods-SDAutoLayoutDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDAutoLayoutDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo.debug.xcconfig"; sourceTree = ""; }; + 6B42D97D2087825A009D5BEC /* DemoVC14Cell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DemoVC14Cell.xib; sourceTree = ""; }; + 9997FB631CB2C26800A6FF62 /* SDTimeLineCellOperationMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDTimeLineCellOperationMenu.h; sourceTree = ""; }; + 9997FB641CB2C26800A6FF62 /* SDTimeLineCellOperationMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDTimeLineCellOperationMenu.m; sourceTree = ""; }; + 99D6949F1C8C54D000DA97E1 /* SDTimeLineCellModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDTimeLineCellModel.h; sourceTree = ""; }; + 99D694A01C8C54D000DA97E1 /* SDTimeLineCellModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDTimeLineCellModel.m; sourceTree = ""; }; + 99D694A21C8C54D000DA97E1 /* SDTimeLineTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDTimeLineTableViewController.h; sourceTree = ""; }; + 99D694A31C8C54D000DA97E1 /* SDTimeLineTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDTimeLineTableViewController.m; sourceTree = ""; }; + 99D694A61C8C54D000DA97E1 /* SDTimeLineCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDTimeLineCell.h; sourceTree = ""; }; + 99D694A71C8C54D000DA97E1 /* SDTimeLineCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDTimeLineCell.m; sourceTree = ""; }; + 99D694A91C8C54D000DA97E1 /* SDTimeLineCellCommentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDTimeLineCellCommentView.h; sourceTree = ""; }; + 99D694AA1C8C54D000DA97E1 /* SDTimeLineCellCommentView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDTimeLineCellCommentView.m; sourceTree = ""; }; + 99D694AC1C8C54D000DA97E1 /* SDTimeLineTableHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDTimeLineTableHeaderView.h; sourceTree = ""; }; + 99D694AD1C8C54D000DA97E1 /* SDTimeLineTableHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDTimeLineTableHeaderView.m; sourceTree = ""; }; + 99D694AF1C8C54D000DA97E1 /* SDWeiXinPhotoContainerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWeiXinPhotoContainerView.h; sourceTree = ""; }; + 99D694B01C8C54D000DA97E1 /* SDWeiXinPhotoContainerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWeiXinPhotoContainerView.m; sourceTree = ""; }; + 99D694B31C8C54D000DA97E1 /* SDTimeLineRefreshHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDTimeLineRefreshHeader.h; sourceTree = ""; }; + 99D694B41C8C54D000DA97E1 /* SDTimeLineRefreshHeader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDTimeLineRefreshHeader.m; sourceTree = ""; }; + 99D694BF1C8C553500DA97E1 /* NSAttributedString+MLLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSAttributedString+MLLabel.h"; sourceTree = ""; }; + 99D694C01C8C553500DA97E1 /* NSAttributedString+MLLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSAttributedString+MLLabel.m"; sourceTree = ""; }; + 99D694C11C8C553500DA97E1 /* NSMutableAttributedString+MLLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableAttributedString+MLLabel.h"; sourceTree = ""; }; + 99D694C21C8C553500DA97E1 /* NSMutableAttributedString+MLLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableAttributedString+MLLabel.m"; sourceTree = ""; }; + 99D694C31C8C553500DA97E1 /* NSString+MLLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+MLLabel.h"; sourceTree = ""; }; + 99D694C41C8C553500DA97E1 /* NSString+MLLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+MLLabel.m"; sourceTree = ""; }; + 99D694C61C8C553500DA97E1 /* MLExpressionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLExpressionManager.h; sourceTree = ""; }; + 99D694C71C8C553500DA97E1 /* MLExpressionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MLExpressionManager.m; sourceTree = ""; }; + 99D694C81C8C553500DA97E1 /* MLTextAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLTextAttachment.h; sourceTree = ""; }; + 99D694C91C8C553500DA97E1 /* MLTextAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MLTextAttachment.m; sourceTree = ""; }; + 99D694CA1C8C553500DA97E1 /* NSAttributedString+MLExpression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSAttributedString+MLExpression.h"; sourceTree = ""; }; + 99D694CB1C8C553500DA97E1 /* NSAttributedString+MLExpression.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSAttributedString+MLExpression.m"; sourceTree = ""; }; + 99D694CC1C8C553500DA97E1 /* NSString+MLExpression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+MLExpression.h"; sourceTree = ""; }; + 99D694CD1C8C553500DA97E1 /* NSString+MLExpression.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+MLExpression.m"; sourceTree = ""; }; + 99D694CF1C8C553500DA97E1 /* MLLabel+Override.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MLLabel+Override.h"; sourceTree = ""; }; + 99D694D01C8C553500DA97E1 /* MLLabelLayoutManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLLabelLayoutManager.h; sourceTree = ""; }; + 99D694D11C8C553500DA97E1 /* MLLabelLayoutManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MLLabelLayoutManager.m; sourceTree = ""; }; + 99D694D21C8C553500DA97E1 /* MLLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLLabel.h; sourceTree = ""; }; + 99D694D31C8C553500DA97E1 /* MLLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MLLabel.m; sourceTree = ""; }; + 99D694D41C8C553500DA97E1 /* MLLinkLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLLinkLabel.h; sourceTree = ""; }; + 99D694D51C8C553500DA97E1 /* MLLinkLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MLLinkLabel.m; sourceTree = ""; }; + 99D694D71C8C553500DA97E1 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + 99D694E51C8C556B00DA97E1 /* GlobalDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GlobalDefines.h; sourceTree = ""; }; + 99D694E81C8C567A00DA97E1 /* SDBaseRefreshView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDBaseRefreshView.h; sourceTree = ""; }; + 99D694E91C8C567A00DA97E1 /* SDBaseRefreshView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDBaseRefreshView.m; sourceTree = ""; }; + 99D694EA1C8C567A00DA97E1 /* SDBaseNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDBaseNavigationController.h; sourceTree = ""; }; + 99D694EB1C8C567A00DA97E1 /* SDBaseNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDBaseNavigationController.m; sourceTree = ""; }; + 99D694EC1C8C567A00DA97E1 /* SDBaseTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDBaseTableViewController.h; sourceTree = ""; }; + 99D694ED1C8C567A00DA97E1 /* SDBaseTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDBaseTableViewController.m; sourceTree = ""; }; + 99D694F11C8C585200DA97E1 /* Discover.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Discover.xcassets; sourceTree = ""; }; + 99D694F61C8C7F7600DA97E1 /* SDTimeLineRefreshFooter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDTimeLineRefreshFooter.h; sourceTree = ""; }; + 99D694F71C8C7F7600DA97E1 /* SDTimeLineRefreshFooter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDTimeLineRefreshFooter.m; sourceTree = ""; }; + 9B2AF0F31D1166690007A130 /* theme_day_json.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = theme_day_json.json; sourceTree = ""; }; + 9B2AF0F41D1166690007A130 /* theme_night_json.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = theme_night_json.json; sourceTree = ""; }; + A1B2C3D01BCB9EC700A66173 /* SDAutoLayoutDemoTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDAutoLayoutDemoTests.m; sourceTree = ""; }; + A1B2C3D21BCB9EC700A66173 /* news_sample.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = news_sample.json; sourceTree = ""; }; + A1B2C3D41BCB9EC700A66173 /* libPods-SDAutoLayoutDemoTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SDAutoLayoutDemoTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + A1B2C3D61BCB9EC700A66173 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A1B2C3D71BCB9EC700A66173 /* Pods-SDAutoLayoutDemoTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDAutoLayoutDemoTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SDAutoLayoutDemoTests/Pods-SDAutoLayoutDemoTests.debug.xcconfig"; sourceTree = ""; }; + A1B2C3D81BCB9EC700A66173 /* Pods-SDAutoLayoutDemoTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDAutoLayoutDemoTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-SDAutoLayoutDemoTests/Pods-SDAutoLayoutDemoTests.release.xcconfig"; sourceTree = ""; }; + B1B944B020BCFB44008F658A /* SDAutoLayoutDemo-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SDAutoLayoutDemo-Bridging-Header.h"; sourceTree = ""; }; + B1B944B120BCFB44008F658A /* SDAutolayout+extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SDAutolayout+extension.swift"; sourceTree = ""; }; + B9BAE9A3DD9811F84DDDECB2 /* libPods-SDAutoLayoutDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SDAutoLayoutDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + C94A1345215F572E00A92F35 /* LEETheme.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LEETheme.h; sourceTree = ""; }; + C94A1346215F572E00A92F35 /* LEETheme.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LEETheme.m; sourceTree = ""; }; + C94A1347215F572E00A92F35 /* LEEThemeHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LEEThemeHelper.h; sourceTree = ""; }; + C94A134D215F591A00A92F35 /* SDAutoLayoutSwiftDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; name = SDAutoLayoutSwiftDemo.app; path = SDAutoLayoutDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + C94A134F215F591A00A92F35 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + C94A1351215F591A00A92F35 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + C94A1354215F591A00A92F35 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + C94A1356215F591C00A92F35 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + C94A1359215F591C00A92F35 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + C94A135B215F591C00A92F35 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C96D058F215F5BED0050BA9F /* UIView+SDAutoLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+SDAutoLayout.m"; sourceTree = ""; }; + C96D0590215F5BED0050BA9F /* UITableView+SDAutoTableViewCellHeight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableView+SDAutoTableViewCellHeight.h"; sourceTree = ""; }; + C96D0591215F5BED0050BA9F /* SDAutoLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDAutoLayout.h; sourceTree = ""; }; + C96D0592215F5BED0050BA9F /* UITableView+SDAutoTableViewCellHeight.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableView+SDAutoTableViewCellHeight.m"; sourceTree = ""; }; + C96D0593215F5BED0050BA9F /* UIView+SDAutoLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+SDAutoLayout.h"; sourceTree = ""; }; + C96D0597215F5C040050BA9F /* UIView+SDAutoLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+SDAutoLayout.m"; sourceTree = ""; }; + C96D0598215F5C040050BA9F /* UITableView+SDAutoTableViewCellHeight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableView+SDAutoTableViewCellHeight.h"; sourceTree = ""; }; + C96D0599215F5C040050BA9F /* SDAutoLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDAutoLayout.h; sourceTree = ""; }; + C96D059A215F5C040050BA9F /* UITableView+SDAutoTableViewCellHeight.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableView+SDAutoTableViewCellHeight.m"; sourceTree = ""; }; + C96D059B215F5C040050BA9F /* UIView+SDAutoLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+SDAutoLayout.h"; sourceTree = ""; }; + C96D059E215F5C980050BA9F /* Bridge-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Bridge-Header.h"; sourceTree = ""; }; + EF70C3084D41C4217B66DF2B /* Pods-SDAutoLayoutDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDAutoLayoutDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 487D3EAE1BCB9EC700A66173 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 38B700E467BDE4C37C6ADD5C /* libPods-SDAutoLayoutDemo.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 487D3ECB1BCB9EC700A66173 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B2C3D4E31BCB9EC700A66173 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C94A134A215F591A00A92F35 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4808F3911CE4ABB500242779 /* DemoVC14 */ = { + isa = PBXGroup; + children = ( + 4808F3921CE4ABFB00242779 /* DemoVC14.h */, + 4808F3931CE4ABFB00242779 /* DemoVC14.m */, + 4808F3951CE4AC1700242779 /* DemoVC14Cell.h */, + 4808F3961CE4AC1700242779 /* DemoVC14Cell.m */, + 489489071CE5FA1700C9BB71 /* PhotosContainerView.h */, + 489489081CE5FA1700C9BB71 /* PhotosContainerView.m */, + 6B42D97D2087825A009D5BEC /* DemoVC14Cell.xib */, + ); + path = DemoVC14; + sourceTree = ""; + }; + 485302961C3F648F004361A7 /* Vender */ = { + isa = PBXGroup; + children = ( + 48F4867B1D1010BA003B7418 /* LEETheme */, + 48EB72F31C748DF5003A818E /* MLEmojiLabel */, + 485302E41C3F65EE004361A7 /* SDCycleScrollView */, + 485302971C3F648F004361A7 /* SDPhotoBrowser */, + 4853029F1C3F6490004361A7 /* SDRefeshView */, + 485302AA1C3F6490004361A7 /* SDWebImage */, + ); + path = Vender; + sourceTree = ""; + }; + 485302971C3F648F004361A7 /* SDPhotoBrowser */ = { + isa = PBXGroup; + children = ( + 485302981C3F648F004361A7 /* SDBrowserImageView.h */, + 485302991C3F6490004361A7 /* SDBrowserImageView.m */, + 4853029A1C3F6490004361A7 /* SDPhotoBrowser.h */, + 4853029B1C3F6490004361A7 /* SDPhotoBrowser.m */, + 4853029C1C3F6490004361A7 /* SDPhotoBrowserConfig.h */, + 4853029D1C3F6490004361A7 /* SDWaitingView.h */, + 4853029E1C3F6490004361A7 /* SDWaitingView.m */, + ); + path = SDPhotoBrowser; + sourceTree = ""; + }; + 4853029F1C3F6490004361A7 /* SDRefeshView */ = { + isa = PBXGroup; + children = ( + 485302A01C3F6490004361A7 /* sdRefeshView_arrow@2x.png */, + 485302A11C3F6490004361A7 /* SDRefresh.h */, + 485302A21C3F6490004361A7 /* SDRefreshFooterView.h */, + 485302A31C3F6490004361A7 /* SDRefreshFooterView.m */, + 485302A41C3F6490004361A7 /* SDRefreshHeaderView.h */, + 485302A51C3F6490004361A7 /* SDRefreshHeaderView.m */, + 485302A61C3F6490004361A7 /* SDRefreshView.h */, + 485302A71C3F6490004361A7 /* SDRefreshView.m */, + 485302A81C3F6490004361A7 /* UIView+SDExtension.h */, + 485302A91C3F6490004361A7 /* UIView+SDExtension.m */, + ); + path = SDRefeshView; + sourceTree = ""; + }; + 485302AA1C3F6490004361A7 /* SDWebImage */ = { + isa = PBXGroup; + children = ( + 485302AB1C3F6490004361A7 /* MKAnnotationView+WebCache.h */, + 485302AC1C3F6490004361A7 /* MKAnnotationView+WebCache.m */, + 485302AD1C3F6490004361A7 /* NSData+ImageContentType.h */, + 485302AE1C3F6490004361A7 /* NSData+ImageContentType.m */, + 485302AF1C3F6490004361A7 /* SDImageCache.h */, + 485302B01C3F6490004361A7 /* SDImageCache.m */, + 485302B11C3F6490004361A7 /* SDWebImageCompat.h */, + 485302B21C3F6490004361A7 /* SDWebImageCompat.m */, + 485302B31C3F6490004361A7 /* SDWebImageDecoder.h */, + 485302B41C3F6490004361A7 /* SDWebImageDecoder.m */, + 485302B51C3F6490004361A7 /* SDWebImageDownloader.h */, + 485302B61C3F6490004361A7 /* SDWebImageDownloader.m */, + 485302B71C3F6490004361A7 /* SDWebImageDownloaderOperation.h */, + 485302B81C3F6490004361A7 /* SDWebImageDownloaderOperation.m */, + 485302B91C3F6490004361A7 /* SDWebImageManager.h */, + 485302BA1C3F6490004361A7 /* SDWebImageManager.m */, + 485302BB1C3F6490004361A7 /* SDWebImageOperation.h */, + 485302BC1C3F6490004361A7 /* SDWebImagePrefetcher.h */, + 485302BD1C3F6490004361A7 /* SDWebImagePrefetcher.m */, + 485302BE1C3F6490004361A7 /* UIButton+WebCache.h */, + 485302BF1C3F6490004361A7 /* UIButton+WebCache.m */, + 485302C01C3F6490004361A7 /* UIImage+GIF.h */, + 485302C11C3F6490004361A7 /* UIImage+GIF.m */, + 485302C21C3F6490004361A7 /* UIImage+MultiFormat.h */, + 485302C31C3F6490004361A7 /* UIImage+MultiFormat.m */, + 485302C41C3F6490004361A7 /* UIImage+WebP.h */, + 485302C51C3F6490004361A7 /* UIImage+WebP.m */, + 485302C61C3F6490004361A7 /* UIImageView+HighlightedWebCache.h */, + 485302C71C3F6490004361A7 /* UIImageView+HighlightedWebCache.m */, + 485302C81C3F6490004361A7 /* UIImageView+WebCache.h */, + 485302C91C3F6490004361A7 /* UIImageView+WebCache.m */, + 485302CA1C3F6490004361A7 /* UIView+WebCacheOperation.h */, + 485302CB1C3F6490004361A7 /* UIView+WebCacheOperation.m */, + ); + path = SDWebImage; + sourceTree = ""; + }; + 485302E41C3F65EE004361A7 /* SDCycleScrollView */ = { + isa = PBXGroup; + children = ( + 485302E51C3F65EE004361A7 /* PageControl */, + 485302EE1C3F65EE004361A7 /* SDCollectionViewCell.h */, + 485302EF1C3F65EE004361A7 /* SDCollectionViewCell.m */, + 485302F01C3F65EE004361A7 /* SDCycleScrollView.h */, + 485302F11C3F65EE004361A7 /* SDCycleScrollView.m */, + 485302F21C3F65EE004361A7 /* UIView+SDExtension.h */, + 485302F31C3F65EE004361A7 /* UIView+SDExtension.m */, + ); + path = SDCycleScrollView; + sourceTree = ""; + }; + 485302E51C3F65EE004361A7 /* PageControl */ = { + isa = PBXGroup; + children = ( + 485302E61C3F65EE004361A7 /* TAAbstractDotView.h */, + 485302E71C3F65EE004361A7 /* TAAbstractDotView.m */, + 485302E81C3F65EE004361A7 /* TAAnimatedDotView.h */, + 485302E91C3F65EE004361A7 /* TAAnimatedDotView.m */, + 485302EA1C3F65EE004361A7 /* TADotView.h */, + 485302EB1C3F65EE004361A7 /* TADotView.m */, + 485302EC1C3F65EE004361A7 /* TAPageControl.h */, + 485302ED1C3F65EE004361A7 /* TAPageControl.m */, + ); + path = PageControl; + sourceTree = ""; + }; + 485303021C3F977A004361A7 /* DemoVC */ = { + isa = PBXGroup; + children = ( + 4808F3911CE4ABB500242779 /* DemoVC14 */, + 48EEAD6D1C9C2FE4001138FE /* DemoVC13 */, + 48EEAD691C9C25DB001138FE /* DemoVC12 */, + 48EB72E01C748D40003A818E /* DemoVC11 */, + 48FC0E011C4FB399004098B8 /* DemoVC10 */, + 485303031C3F977A004361A7 /* DemoVC0 */, + 485303061C3F977A004361A7 /* DemoVC1 */, + 485303091C3F977A004361A7 /* DemoVC2 */, + 4853030C1C3F977A004361A7 /* DemoVC3 */, + 485303111C3F977A004361A7 /* DemoVC4 */, + 485303141C3F977A004361A7 /* DemoVC5 */, + 4853031B1C3F977A004361A7 /* DemoVC6 */, + 4853031E1C3F977A004361A7 /* DemoVC7 */, + 485303271C3F977A004361A7 /* DemoVC8 */, + 4853032A1C3F977A004361A7 /* DemoVC9 */, + ); + path = DemoVC; + sourceTree = ""; + }; + 485303031C3F977A004361A7 /* DemoVC0 */ = { + isa = PBXGroup; + children = ( + 485303041C3F977A004361A7 /* DemoVC0.h */, + 485303051C3F977A004361A7 /* DemoVC0.m */, + ); + path = DemoVC0; + sourceTree = ""; + }; + 485303061C3F977A004361A7 /* DemoVC1 */ = { + isa = PBXGroup; + children = ( + 485303071C3F977A004361A7 /* DemoVC1.h */, + 485303081C3F977A004361A7 /* DemoVC1.m */, + ); + path = DemoVC1; + sourceTree = ""; + }; + 485303091C3F977A004361A7 /* DemoVC2 */ = { + isa = PBXGroup; + children = ( + 4853030A1C3F977A004361A7 /* DemoVC2.h */, + 4853030B1C3F977A004361A7 /* DemoVC2.m */, + ); + path = DemoVC2; + sourceTree = ""; + }; + 4853030C1C3F977A004361A7 /* DemoVC3 */ = { + isa = PBXGroup; + children = ( + 4853030D1C3F977A004361A7 /* DemoVC3.h */, + 4853030E1C3F977A004361A7 /* DemoVC3.m */, + 4853030F1C3F977A004361A7 /* TestCell2.h */, + 485303101C3F977A004361A7 /* TestCell2.m */, + ); + path = DemoVC3; + sourceTree = ""; + }; + 485303111C3F977A004361A7 /* DemoVC4 */ = { + isa = PBXGroup; + children = ( + 485303121C3F977A004361A7 /* DemoVC4.h */, + 485303131C3F977A004361A7 /* DemoVC4.m */, + ); + path = DemoVC4; + sourceTree = ""; + }; + 485303141C3F977A004361A7 /* DemoVC5 */ = { + isa = PBXGroup; + children = ( + 485303151C3F977A004361A7 /* DemoVC5.h */, + 485303161C3F977A004361A7 /* DemoVC5.m */, + 485303171C3F977A004361A7 /* DemoVC5CellTableViewCell.h */, + 485303181C3F977A004361A7 /* DemoVC5CellTableViewCell.m */, + 485303191C3F977A004361A7 /* DemoVC5Model.h */, + 4853031A1C3F977A004361A7 /* DemoVC5Model.m */, + ); + path = DemoVC5; + sourceTree = ""; + }; + 4853031B1C3F977A004361A7 /* DemoVC6 */ = { + isa = PBXGroup; + children = ( + 4853031C1C3F977A004361A7 /* DemoVC6.h */, + 4853031D1C3F977A004361A7 /* DemoVC6.m */, + ); + path = DemoVC6; + sourceTree = ""; + }; + 4853031E1C3F977A004361A7 /* DemoVC7 */ = { + isa = PBXGroup; + children = ( + 4853031F1C3F977A004361A7 /* DemoVC7.h */, + 485303201C3F977A004361A7 /* DemoVC7.m */, + 485303211C3F977A004361A7 /* DemoVC7Cell.h */, + 485303221C3F977A004361A7 /* DemoVC7Cell.m */, + 485303231C3F977A004361A7 /* DemoVC7Cell2.h */, + 485303241C3F977A004361A7 /* DemoVC7Cell2.m */, + 485303251C3F977A004361A7 /* DemoVC7Model.h */, + 485303261C3F977A004361A7 /* DemoVC7Model.m */, + ); + path = DemoVC7; + sourceTree = ""; + }; + 485303271C3F977A004361A7 /* DemoVC8 */ = { + isa = PBXGroup; + children = ( + 485303281C3F977A004361A7 /* DemoVC8.h */, + 485303291C3F977A004361A7 /* DemoVC8.m */, + ); + path = DemoVC8; + sourceTree = ""; + }; + 4853032A1C3F977A004361A7 /* DemoVC9 */ = { + isa = PBXGroup; + children = ( + 99D6949D1C8C54D000DA97E1 /* TimeLine */, + 4853032D1C3F977A004361A7 /* DemoVC9.h */, + 4853032E1C3F977A004361A7 /* DemoVC9.m */, + ); + path = DemoVC9; + sourceTree = ""; + }; + 485303491C3F97AA004361A7 /* Others */ = { + isa = PBXGroup; + children = ( + 4853034A1C3F97AA004361A7 /* AppDelegate.h */, + 4853034B1C3F97AA004361A7 /* AppDelegate.m */, + 4853034C1C3F97AA004361A7 /* DemoTableViewControler.h */, + 4853034D1C3F97AA004361A7 /* DemoTableViewControler.m */, + 4853034E1C3F97AA004361A7 /* ViewController.h */, + 4853034F1C3F97AA004361A7 /* ViewController.m */, + 485303531C3F97E6004361A7 /* DemoCell.h */, + 485303541C3F97E6004361A7 /* DemoCell.m */, + 9B2AF0F21D1166690007A130 /* Theme */, + ); + path = Others; + sourceTree = ""; + }; + 487D3EA81BCB9EC700A66173 = { + isa = PBXGroup; + children = ( + 487D3EB31BCB9EC700A66173 /* SDAutoLayoutDemo */, + A1B2C3D91BCB9EC700A66173 /* SDAutoLayoutDemoTests */, + B2C3D4EA1BCB9EC700A66173 /* SDAutoLayoutDemoUITests */, + C94A134E215F591A00A92F35 /* SDAutoLayoutSwiftDemo */, + 487D3EB21BCB9EC700A66173 /* Products */, + 8692F3CE0736DACAE5C8D22B /* Pods */, + DFED35631060760095A64CE0 /* Frameworks */, + ); + sourceTree = ""; + }; + 487D3EB21BCB9EC700A66173 /* Products */ = { + isa = PBXGroup; + children = ( + 487D3EB11BCB9EC700A66173 /* SDAutoLayoutDemo.app */, + 487D3EC71BCB9EC700A66173 /* SDAutoLayoutDemoTests.xctest */, + B2C3D4E01BCB9EC700A66173 /* SDAutoLayoutDemoUITests.xctest */, + C94A134D215F591A00A92F35 /* SDAutoLayoutSwiftDemo.app */, + ); + name = Products; + sourceTree = ""; + }; + 487D3EB31BCB9EC700A66173 /* SDAutoLayoutDemo */ = { + isa = PBXGroup; + children = ( + C96D058E215F5BED0050BA9F /* SDAutoLayout */, + B1B944AF20BCFAAE008F658A /* SwiftExtension */, + 485303491C3F97AA004361A7 /* Others */, + 485303021C3F977A004361A7 /* DemoVC */, + 485302961C3F648F004361A7 /* Vender */, + 487D3EBE1BCB9EC700A66173 /* Main.storyboard */, + 487D3EC11BCB9EC700A66173 /* Images.xcassets */, + 487D3EC31BCB9EC700A66173 /* LaunchScreen.xib */, + 487D3EB41BCB9EC700A66173 /* Supporting Files */, + ); + path = SDAutoLayoutDemo; + sourceTree = ""; + }; + 487D3EB41BCB9EC700A66173 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 48DBCD0F1C01DEB1002EC476 /* Images */, + 487D3EB51BCB9EC700A66173 /* Info.plist */, + 487D3EB61BCB9EC700A66173 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 48DBCD0F1C01DEB1002EC476 /* Images */ = { + isa = PBXGroup; + children = ( + 99D694F11C8C585200DA97E1 /* Discover.xcassets */, + 48EB73441C749212003A818E /* Home.xcassets */, + 48EB73051C748EDE003A818E /* test */, + 48EB730B1C748EDE003A818E /* cell_icons */, + 4841F5F01C2ACC2B004979F4 /* pbg.jpg */, + 4841F5F11C2ACC2B004979F4 /* picon.jpg */, + 4841F5E51C2ABC60004979F4 /* pic5.jpg */, + 4841F5E61C2ABC60004979F4 /* pic6.jpg */, + 4841F5E71C2ABC60004979F4 /* pic7.jpg */, + 4841F5E81C2ABC60004979F4 /* pic8.jpg */, + 48DBCD101C01E045002EC476 /* icon0.jpg */, + 48DBCD111C01E045002EC476 /* icon1.jpg */, + 48DBCD121C01E045002EC476 /* icon2.jpg */, + 48DBCD131C01E045002EC476 /* icon3.jpg */, + 48DBCD141C01E045002EC476 /* icon4.jpg */, + 48DBCD151C01E045002EC476 /* pic0.jpg */, + 48DBCD161C01E045002EC476 /* pic1.jpg */, + 48DBCD171C01E045002EC476 /* pic2.jpg */, + 48DBCD181C01E045002EC476 /* pic3.jpg */, + 48DBCD191C01E045002EC476 /* pic4.jpg */, + ); + path = Images; + sourceTree = ""; + }; + 48EB72E01C748D40003A818E /* DemoVC11 */ = { + isa = PBXGroup; + children = ( + 48EB73011C748E1A003A818E /* AnalogDataGenerator */, + 48EB72E11C748D6C003A818E /* WebViewController */, + 48EB72E41C748D6C003A818E /* ChatController */, + ); + path = DemoVC11; + sourceTree = ""; + }; + 48EB72E11C748D6C003A818E /* WebViewController */ = { + isa = PBXGroup; + children = ( + 48EB72E21C748D6C003A818E /* SDWebViewController.h */, + 48EB72E31C748D6C003A818E /* SDWebViewController.m */, + ); + path = WebViewController; + sourceTree = ""; + }; + 48EB72E41C748D6C003A818E /* ChatController */ = { + isa = PBXGroup; + children = ( + 48EB72E51C748D6C003A818E /* Models */, + 48EB72EA1C748D6C003A818E /* Views */, + 48EB73411C748F26003A818E /* DemoVC11.h */, + 48EB73421C748F26003A818E /* DemoVC11.m */, + ); + path = ChatController; + sourceTree = ""; + }; + 48EB72E51C748D6C003A818E /* Models */ = { + isa = PBXGroup; + children = ( + 48EB72E61C748D6C003A818E /* SDChatModel.h */, + 48EB72E71C748D6C003A818E /* SDChatModel.m */, + ); + path = Models; + sourceTree = ""; + }; + 48EB72EA1C748D6C003A818E /* Views */ = { + isa = PBXGroup; + children = ( + 48EB72EB1C748D6C003A818E /* SDChatTableViewCell.h */, + 48EB72EC1C748D6C003A818E /* SDChatTableViewCell.m */, + ); + path = Views; + sourceTree = ""; + }; + 48EB72F31C748DF5003A818E /* MLEmojiLabel */ = { + isa = PBXGroup; + children = ( + 99D694BC1C8C553500DA97E1 /* MLLabel */, + 48EB72F41C748DF5003A818E /* MLEmoji_Expression.bundle */, + 48EB72F51C748DF5003A818E /* MLEmoji_Expression.plist */, + 48EB72F61C748DF5003A818E /* MLEmoji_ExpressionImage.plist */, + 48EB72F71C748DF5003A818E /* MLEmojiLabel.h */, + 48EB72F81C748DF5003A818E /* MLEmojiLabel.m */, + 48EB72F91C748DF5003A818E /* TTTAttributedLabel */, + ); + path = MLEmojiLabel; + sourceTree = ""; + }; + 48EB72F91C748DF5003A818E /* TTTAttributedLabel */ = { + isa = PBXGroup; + children = ( + 48EB72FA1C748DF5003A818E /* TTTAttributedLabel.h */, + 48EB72FB1C748DF5003A818E /* TTTAttributedLabel.m */, + ); + path = TTTAttributedLabel; + sourceTree = ""; + }; + 48EB73011C748E1A003A818E /* AnalogDataGenerator */ = { + isa = PBXGroup; + children = ( + 48EB73021C748E1A003A818E /* SDAnalogDataGenerator.h */, + 48EB73031C748E1A003A818E /* SDAnalogDataGenerator.m */, + ); + path = AnalogDataGenerator; + sourceTree = ""; + }; + 48EB73051C748EDE003A818E /* test */ = { + isa = PBXGroup; + children = ( + 48EB73061C748EDE003A818E /* test0.jpg */, + 48EB73071C748EDE003A818E /* test1.jpg */, + 48EB73081C748EDE003A818E /* test2.jpg */, + 48EB73091C748EDE003A818E /* test3.jpg */, + 48EB730A1C748EDE003A818E /* test4.jpg */, + ); + path = test; + sourceTree = ""; + }; + 48EB730B1C748EDE003A818E /* cell_icons */ = { + isa = PBXGroup; + children = ( + 48EB730C1C748EDE003A818E /* 0.jpg */, + 48EB730D1C748EDE003A818E /* 1.jpg */, + 48EB730E1C748EDE003A818E /* 10.jpg */, + 48EB730F1C748EDE003A818E /* 11.jpg */, + 48EB73101C748EDE003A818E /* 12.jpg */, + 48EB73111C748EDE003A818E /* 13.jpg */, + 48EB73121C748EDE003A818E /* 14.jpg */, + 48EB73131C748EDE003A818E /* 15.jpg */, + 48EB73141C748EDE003A818E /* 16.jpg */, + 48EB73151C748EDE003A818E /* 17.jpg */, + 48EB73161C748EDE003A818E /* 18.jpg */, + 48EB73171C748EDE003A818E /* 19.jpg */, + 48EB73181C748EDE003A818E /* 2.jpg */, + 48EB73191C748EDE003A818E /* 20.jpg */, + 48EB731A1C748EDE003A818E /* 21.jpg */, + 48EB731B1C748EDE003A818E /* 22.jpg */, + 48EB731C1C748EDE003A818E /* 23.jpg */, + 48EB731D1C748EDE003A818E /* 3.jpg */, + 48EB731E1C748EDE003A818E /* 4.jpg */, + 48EB731F1C748EDE003A818E /* 5.jpg */, + 48EB73201C748EDE003A818E /* 6.jpg */, + 48EB73211C748EDE003A818E /* 7.jpg */, + 48EB73221C748EDE003A818E /* 8.jpg */, + 48EB73231C748EDE003A818E /* 9.jpg */, + ); + path = cell_icons; + sourceTree = ""; + }; + 48EEAD691C9C25DB001138FE /* DemoVC12 */ = { + isa = PBXGroup; + children = ( + 48EEAD6A1C9C25F4001138FE /* DemoVC12.h */, + 48EEAD6B1C9C25F4001138FE /* DemoVC12.m */, + ); + path = DemoVC12; + sourceTree = ""; + }; + 48EEAD6D1C9C2FE4001138FE /* DemoVC13 */ = { + isa = PBXGroup; + children = ( + 48EEAD6E1C9C3006001138FE /* DemoVC13.h */, + 48EEAD6F1C9C3006001138FE /* DemoVC13.m */, + ); + path = DemoVC13; + sourceTree = ""; + }; + 48F4867B1D1010BA003B7418 /* LEETheme */ = { + isa = PBXGroup; + children = ( + C94A1345215F572E00A92F35 /* LEETheme.h */, + C94A1346215F572E00A92F35 /* LEETheme.m */, + C94A1347215F572E00A92F35 /* LEEThemeHelper.h */, + ); + path = LEETheme; + sourceTree = ""; + }; + 48FC0E011C4FB399004098B8 /* DemoVC10 */ = { + isa = PBXGroup; + children = ( + 48FC0E221C4FB5F6004098B8 /* Lib */, + 48FC0E021C4FB3A8004098B8 /* model */, + 48FC0E0B1C4FB3A8004098B8 /* view */, + 48FC0E1F1C4FB3EA004098B8 /* DemoVC10.h */, + 48FC0E201C4FB3EA004098B8 /* DemoVC10.m */, + ); + path = DemoVC10; + sourceTree = ""; + }; + 48FC0E021C4FB3A8004098B8 /* model */ = { + isa = PBXGroup; + children = ( + 48FC0E031C4FB3A8004098B8 /* Ads.h */, + 48FC0E041C4FB3A8004098B8 /* Ads.m */, + 48FC0E051C4FB3A8004098B8 /* Editor.h */, + 48FC0E061C4FB3A8004098B8 /* Editor.m */, + 48FC0E071C4FB3A8004098B8 /* Imgextra.h */, + 48FC0E081C4FB3A8004098B8 /* Imgextra.m */, + 48FC0E091C4FB3A8004098B8 /* ThreeModel.h */, + 48FC0E0A1C4FB3A8004098B8 /* ThreeModel.m */, + ); + path = model; + sourceTree = ""; + }; + 48FC0E0B1C4FB3A8004098B8 /* view */ = { + isa = PBXGroup; + children = ( + 48FC0E0C1C4FB3A8004098B8 /* ThreeBaseCell.h */, + 48FC0E0D1C4FB3A8004098B8 /* ThreeBaseCell.m */, + 48FC0E0E1C4FB3A8004098B8 /* ThreeFirstCell.h */, + 48FC0E0F1C4FB3A8004098B8 /* ThreeFirstCell.m */, + 48FC0E101C4FB3A8004098B8 /* ThreeFourthCell.h */, + 48FC0E111C4FB3A8004098B8 /* ThreeFourthCell.m */, + 48FC0E121C4FB3A8004098B8 /* ThreeSecondCell.h */, + 48FC0E131C4FB3A8004098B8 /* ThreeSecondCell.m */, + 48FC0E141C4FB3A8004098B8 /* ThreeThirdCell.h */, + 48FC0E151C4FB3A8004098B8 /* ThreeThirdCell.m */, + ); + path = view; + sourceTree = ""; + }; + 48FC0E221C4FB5F6004098B8 /* Lib */ = { + isa = PBXGroup; + children = ( + 48FC0EC81C4FB7BD004098B8 /* XYString */, + ); + path = Lib; + sourceTree = ""; + }; + 48FC0EC81C4FB7BD004098B8 /* XYString */ = { + isa = PBXGroup; + children = ( + 48FC0EC91C4FB7CA004098B8 /* XYString.h */, + 48FC0ECA1C4FB7CA004098B8 /* XYString.m */, + ); + path = XYString; + sourceTree = ""; + }; + 8692F3CE0736DACAE5C8D22B /* Pods */ = { + isa = PBXGroup; + children = ( + 59F464B58FAB1B71D18C24E1 /* Pods-SDAutoLayoutDemo.debug.xcconfig */, + EF70C3084D41C4217B66DF2B /* Pods-SDAutoLayoutDemo.release.xcconfig */, + A1B2C3D71BCB9EC700A66173 /* Pods-SDAutoLayoutDemoTests.debug.xcconfig */, + A1B2C3D81BCB9EC700A66173 /* Pods-SDAutoLayoutDemoTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 9997FB621CB2C26800A6FF62 /* OperationMenu */ = { + isa = PBXGroup; + children = ( + 9997FB631CB2C26800A6FF62 /* SDTimeLineCellOperationMenu.h */, + 9997FB641CB2C26800A6FF62 /* SDTimeLineCellOperationMenu.m */, + ); + path = OperationMenu; + sourceTree = ""; + }; + 99D6949D1C8C54D000DA97E1 /* TimeLine */ = { + isa = PBXGroup; + children = ( + 99D694E61C8C567A00DA97E1 /* BaseClasses */, + 99D694E41C8C556B00DA97E1 /* GloabalDefines */, + 99D6949E1C8C54D000DA97E1 /* Model */, + 99D694A11C8C54D000DA97E1 /* TimeLineController */, + 99D694A41C8C54D000DA97E1 /* Views */, + ); + path = TimeLine; + sourceTree = ""; + }; + 99D6949E1C8C54D000DA97E1 /* Model */ = { + isa = PBXGroup; + children = ( + 99D6949F1C8C54D000DA97E1 /* SDTimeLineCellModel.h */, + 99D694A01C8C54D000DA97E1 /* SDTimeLineCellModel.m */, + ); + path = Model; + sourceTree = ""; + }; + 99D694A11C8C54D000DA97E1 /* TimeLineController */ = { + isa = PBXGroup; + children = ( + 99D694A21C8C54D000DA97E1 /* SDTimeLineTableViewController.h */, + 99D694A31C8C54D000DA97E1 /* SDTimeLineTableViewController.m */, + ); + path = TimeLineController; + sourceTree = ""; + }; + 99D694A41C8C54D000DA97E1 /* Views */ = { + isa = PBXGroup; + children = ( + 9997FB621CB2C26800A6FF62 /* OperationMenu */, + 99D694A51C8C54D000DA97E1 /* Cell */, + 99D694A81C8C54D000DA97E1 /* CommentView */, + 99D694AB1C8C54D000DA97E1 /* HeaderView */, + 99D694AE1C8C54D000DA97E1 /* PhotoContainer */, + 99D694B11C8C54D000DA97E1 /* TimeLineRefreshFooter */, + 99D694B21C8C54D000DA97E1 /* TimeLineRefreshHeader */, + ); + path = Views; + sourceTree = ""; + }; + 99D694A51C8C54D000DA97E1 /* Cell */ = { + isa = PBXGroup; + children = ( + 99D694A61C8C54D000DA97E1 /* SDTimeLineCell.h */, + 99D694A71C8C54D000DA97E1 /* SDTimeLineCell.m */, + ); + path = Cell; + sourceTree = ""; + }; + 99D694A81C8C54D000DA97E1 /* CommentView */ = { + isa = PBXGroup; + children = ( + 99D694A91C8C54D000DA97E1 /* SDTimeLineCellCommentView.h */, + 99D694AA1C8C54D000DA97E1 /* SDTimeLineCellCommentView.m */, + ); + path = CommentView; + sourceTree = ""; + }; + 99D694AB1C8C54D000DA97E1 /* HeaderView */ = { + isa = PBXGroup; + children = ( + 99D694AC1C8C54D000DA97E1 /* SDTimeLineTableHeaderView.h */, + 99D694AD1C8C54D000DA97E1 /* SDTimeLineTableHeaderView.m */, + ); + path = HeaderView; + sourceTree = ""; + }; + 99D694AE1C8C54D000DA97E1 /* PhotoContainer */ = { + isa = PBXGroup; + children = ( + 99D694AF1C8C54D000DA97E1 /* SDWeiXinPhotoContainerView.h */, + 99D694B01C8C54D000DA97E1 /* SDWeiXinPhotoContainerView.m */, + ); + path = PhotoContainer; + sourceTree = ""; + }; + 99D694B11C8C54D000DA97E1 /* TimeLineRefreshFooter */ = { + isa = PBXGroup; + children = ( + 99D694F61C8C7F7600DA97E1 /* SDTimeLineRefreshFooter.h */, + 99D694F71C8C7F7600DA97E1 /* SDTimeLineRefreshFooter.m */, + ); + path = TimeLineRefreshFooter; + sourceTree = ""; + }; + 99D694B21C8C54D000DA97E1 /* TimeLineRefreshHeader */ = { + isa = PBXGroup; + children = ( + 99D694B31C8C54D000DA97E1 /* SDTimeLineRefreshHeader.h */, + 99D694B41C8C54D000DA97E1 /* SDTimeLineRefreshHeader.m */, + ); + path = TimeLineRefreshHeader; + sourceTree = ""; + }; + 99D694BC1C8C553500DA97E1 /* MLLabel */ = { + isa = PBXGroup; + children = ( + 99D694BD1C8C553500DA97E1 /* Classes */, + 99D694D71C8C553500DA97E1 /* README.md */, + ); + path = MLLabel; + sourceTree = ""; + }; + 99D694BD1C8C553500DA97E1 /* Classes */ = { + isa = PBXGroup; + children = ( + 99D694BE1C8C553500DA97E1 /* Category */, + 99D694C51C8C553500DA97E1 /* Expression */, + 99D694CE1C8C553500DA97E1 /* Helper */, + 99D694D21C8C553500DA97E1 /* MLLabel.h */, + 99D694D31C8C553500DA97E1 /* MLLabel.m */, + 99D694D41C8C553500DA97E1 /* MLLinkLabel.h */, + 99D694D51C8C553500DA97E1 /* MLLinkLabel.m */, + ); + path = Classes; + sourceTree = ""; + }; + 99D694BE1C8C553500DA97E1 /* Category */ = { + isa = PBXGroup; + children = ( + 99D694BF1C8C553500DA97E1 /* NSAttributedString+MLLabel.h */, + 99D694C01C8C553500DA97E1 /* NSAttributedString+MLLabel.m */, + 99D694C11C8C553500DA97E1 /* NSMutableAttributedString+MLLabel.h */, + 99D694C21C8C553500DA97E1 /* NSMutableAttributedString+MLLabel.m */, + 99D694C31C8C553500DA97E1 /* NSString+MLLabel.h */, + 99D694C41C8C553500DA97E1 /* NSString+MLLabel.m */, + ); + path = Category; + sourceTree = ""; + }; + 99D694C51C8C553500DA97E1 /* Expression */ = { + isa = PBXGroup; + children = ( + 99D694C61C8C553500DA97E1 /* MLExpressionManager.h */, + 99D694C71C8C553500DA97E1 /* MLExpressionManager.m */, + 99D694C81C8C553500DA97E1 /* MLTextAttachment.h */, + 99D694C91C8C553500DA97E1 /* MLTextAttachment.m */, + 99D694CA1C8C553500DA97E1 /* NSAttributedString+MLExpression.h */, + 99D694CB1C8C553500DA97E1 /* NSAttributedString+MLExpression.m */, + 99D694CC1C8C553500DA97E1 /* NSString+MLExpression.h */, + 99D694CD1C8C553500DA97E1 /* NSString+MLExpression.m */, + ); + path = Expression; + sourceTree = ""; + }; + 99D694CE1C8C553500DA97E1 /* Helper */ = { + isa = PBXGroup; + children = ( + 99D694CF1C8C553500DA97E1 /* MLLabel+Override.h */, + 99D694D01C8C553500DA97E1 /* MLLabelLayoutManager.h */, + 99D694D11C8C553500DA97E1 /* MLLabelLayoutManager.m */, + ); + path = Helper; + sourceTree = ""; + }; + 99D694E41C8C556B00DA97E1 /* GloabalDefines */ = { + isa = PBXGroup; + children = ( + 99D694E51C8C556B00DA97E1 /* GlobalDefines.h */, + ); + path = GloabalDefines; + sourceTree = ""; + }; + 99D694E61C8C567A00DA97E1 /* BaseClasses */ = { + isa = PBXGroup; + children = ( + 99D694E71C8C567A00DA97E1 /* BaseRefreshView */, + 99D694EA1C8C567A00DA97E1 /* SDBaseNavigationController.h */, + 99D694EB1C8C567A00DA97E1 /* SDBaseNavigationController.m */, + 99D694EC1C8C567A00DA97E1 /* SDBaseTableViewController.h */, + 99D694ED1C8C567A00DA97E1 /* SDBaseTableViewController.m */, + ); + path = BaseClasses; + sourceTree = ""; + }; + 99D694E71C8C567A00DA97E1 /* BaseRefreshView */ = { + isa = PBXGroup; + children = ( + 99D694E81C8C567A00DA97E1 /* SDBaseRefreshView.h */, + 99D694E91C8C567A00DA97E1 /* SDBaseRefreshView.m */, + ); + path = BaseRefreshView; + sourceTree = ""; + }; + 9B2AF0F21D1166690007A130 /* Theme */ = { + isa = PBXGroup; + children = ( + 9B2AF0F31D1166690007A130 /* theme_day_json.json */, + 9B2AF0F41D1166690007A130 /* theme_night_json.json */, + ); + path = Theme; + sourceTree = ""; + }; + A1B2C3D91BCB9EC700A66173 /* SDAutoLayoutDemoTests */ = { + isa = PBXGroup; + children = ( + A1B2C3D01BCB9EC700A66173 /* SDAutoLayoutDemoTests.m */, + A1B2C3DA1BCB9EC700A66173 /* Fixtures */, + A1B2C3D61BCB9EC700A66173 /* Info.plist */, + ); + path = SDAutoLayoutDemoTests; + sourceTree = ""; + }; + B2C3D4EA1BCB9EC700A66173 /* SDAutoLayoutDemoUITests */ = { + isa = PBXGroup; + children = ( + B2C3D4EB1BCB9EC700A66173 /* SDAutoLayoutDemoUITests.swift */, + B2C3D4F51BCB9EC700A66173 /* LayoutAssertionHelper.swift */, + B2C3D4F61BCB9EC700A66173 /* DemoLayoutCatalog.swift */, + B2C3D4FA1BCB9EC700A66173 /* DemoLayoutContext.swift */, + B2C3D4FC1BCB9EC700A66173 /* DemoVC1LayoutAssertions.swift */, + B2C3D4FE1BCB9EC700A66173 /* DemoScreenLayoutAssertions.swift */, + B2C3D4ED1BCB9EC700A66173 /* Info.plist */, + ); + path = SDAutoLayoutDemoUITests; + sourceTree = ""; + }; + A1B2C3DA1BCB9EC700A66173 /* Fixtures */ = { + isa = PBXGroup; + children = ( + A1B2C3D21BCB9EC700A66173 /* news_sample.json */, + ); + path = Fixtures; + sourceTree = ""; + }; + B1B944AF20BCFAAE008F658A /* SwiftExtension */ = { + isa = PBXGroup; + children = ( + B1B944B120BCFB44008F658A /* SDAutolayout+extension.swift */, + B1B944B020BCFB44008F658A /* SDAutoLayoutDemo-Bridging-Header.h */, + ); + path = SwiftExtension; + sourceTree = ""; + }; + C94A134E215F591A00A92F35 /* SDAutoLayoutSwiftDemo */ = { + isa = PBXGroup; + children = ( + C96D0596215F5C040050BA9F /* SDAutoLayout */, + C94A134F215F591A00A92F35 /* AppDelegate.swift */, + C94A1351215F591A00A92F35 /* ViewController.swift */, + C94A1353215F591A00A92F35 /* Main.storyboard */, + C94A1356215F591C00A92F35 /* Assets.xcassets */, + C94A1358215F591C00A92F35 /* LaunchScreen.storyboard */, + C96D059E215F5C980050BA9F /* Bridge-Header.h */, + C94A135B215F591C00A92F35 /* Info.plist */, + ); + path = SDAutoLayoutSwiftDemo; + sourceTree = ""; + }; + C96D058E215F5BED0050BA9F /* SDAutoLayout */ = { + isa = PBXGroup; + children = ( + C96D058F215F5BED0050BA9F /* UIView+SDAutoLayout.m */, + C96D0590215F5BED0050BA9F /* UITableView+SDAutoTableViewCellHeight.h */, + C96D0591215F5BED0050BA9F /* SDAutoLayout.h */, + C96D0592215F5BED0050BA9F /* UITableView+SDAutoTableViewCellHeight.m */, + C96D0593215F5BED0050BA9F /* UIView+SDAutoLayout.h */, + ); + path = SDAutoLayout; + sourceTree = SOURCE_ROOT; + }; + C96D0596215F5C040050BA9F /* SDAutoLayout */ = { + isa = PBXGroup; + children = ( + C96D0597215F5C040050BA9F /* UIView+SDAutoLayout.m */, + C96D0598215F5C040050BA9F /* UITableView+SDAutoTableViewCellHeight.h */, + C96D0599215F5C040050BA9F /* SDAutoLayout.h */, + C96D059A215F5C040050BA9F /* UITableView+SDAutoTableViewCellHeight.m */, + C96D059B215F5C040050BA9F /* UIView+SDAutoLayout.h */, + ); + path = SDAutoLayout; + sourceTree = SOURCE_ROOT; + }; + DFED35631060760095A64CE0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + B9BAE9A3DD9811F84DDDECB2 /* libPods-SDAutoLayoutDemo.a */, + A1B2C3D41BCB9EC700A66173 /* libPods-SDAutoLayoutDemoTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 487D3EB01BCB9EC700A66173 /* SDAutoLayoutDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 487D3ED41BCB9EC700A66173 /* Build configuration list for PBXNativeTarget "SDAutoLayoutDemo" */; + buildPhases = ( + 382CBFBAD727003877850AE8 /* [CP] Check Pods Manifest.lock */, + 487D3EAD1BCB9EC700A66173 /* Sources */, + 487D3EAE1BCB9EC700A66173 /* Frameworks */, + 487D3EAF1BCB9EC700A66173 /* Resources */, + 1B12CB532F5C34EB3E2E1922 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SDAutoLayoutDemo; + productName = "SDAutoLayout 测试 Demo"; + productReference = 487D3EB11BCB9EC700A66173 /* SDAutoLayoutDemo.app */; + productType = "com.apple.product-type.application"; + }; + 487D3EC91BCB9EC700A66173 /* SDAutoLayoutDemoTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 487D3ECC1BCB9EC700A66173 /* Build configuration list for PBXNativeTarget "SDAutoLayoutDemoTests" */; + buildPhases = ( + A1B2C3DB1BCB9EC700A66173 /* [CP] Check Pods Manifest.lock */, + 487D3ECA1BCB9EC700A66173 /* Sources */, + 487D3ECB1BCB9EC700A66173 /* Frameworks */, + 487D3ECF1BCB9EC700A66173 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 487D3ED01BCB9EC700A66173 /* PBXTargetDependency */, + ); + name = SDAutoLayoutDemoTests; + productName = SDAutoLayoutDemoTests; + productReference = 487D3EC71BCB9EC700A66173 /* SDAutoLayoutDemoTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + B2C3D4E11BCB9EC700A66173 /* SDAutoLayoutDemoUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = B2C3D4E51BCB9EC700A66173 /* Build configuration list for PBXNativeTarget "SDAutoLayoutDemoUITests" */; + buildPhases = ( + B2C3D4E21BCB9EC700A66173 /* Sources */, + B2C3D4E31BCB9EC700A66173 /* Frameworks */, + B2C3D4E41BCB9EC700A66173 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + B2C3D4E81BCB9EC700A66173 /* PBXTargetDependency */, + ); + name = SDAutoLayoutDemoUITests; + productName = SDAutoLayoutDemoUITests; + productReference = B2C3D4E01BCB9EC700A66173 /* SDAutoLayoutDemoUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; + C94A134C215F591A00A92F35 /* SDAutoLayoutSwiftDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = C94A135C215F591C00A92F35 /* Build configuration list for PBXNativeTarget "SDAutoLayoutSwiftDemo" */; + buildPhases = ( + C94A1349215F591A00A92F35 /* Sources */, + C94A134A215F591A00A92F35 /* Frameworks */, + C94A134B215F591A00A92F35 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SDAutoLayoutSwiftDemo; + productName = SDAutoLayoutSwiftDemo; + productReference = C94A134D215F591A00A92F35 /* SDAutoLayoutSwiftDemo.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 487D3EA91BCB9EC700A66173 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1000; + LastUpgradeCheck = 1000; + ORGANIZATIONNAME = gsd; + TargetAttributes = { + 487D3EB01BCB9EC700A66173 = { + CreatedOnToolsVersion = 6.4; + DevelopmentTeam = 3GNXU2LRUU; + LastSwiftMigration = 0930; + }; + 487D3EC91BCB9EC700A66173 = { + CreatedOnToolsVersion = 6.4; + DevelopmentTeam = 8D5EHT9KTL; + TestTargetID = 487D3EB01BCB9EC700A66173; + }; + B2C3D4E11BCB9EC700A66173 = { + CreatedOnToolsVersion = 16.0; + DevelopmentTeam = 8D5EHT9KTL; + TestTargetID = 487D3EB01BCB9EC700A66173; + }; + C94A134C215F591A00A92F35 = { + CreatedOnToolsVersion = 10.0; + DevelopmentTeam = 3GNXU2LRUU; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 487D3EAC1BCB9EC700A66173 /* Build configuration list for PBXProject "SDAutoLayoutDemo" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + English, + en, + Base, + ); + mainGroup = 487D3EA81BCB9EC700A66173; + productRefGroup = 487D3EB21BCB9EC700A66173 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 487D3EB01BCB9EC700A66173 /* SDAutoLayoutDemo */, + 487D3EC91BCB9EC700A66173 /* SDAutoLayoutDemoTests */, + B2C3D4E11BCB9EC700A66173 /* SDAutoLayoutDemoUITests */, + C94A134C215F591A00A92F35 /* SDAutoLayoutSwiftDemo */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 487D3EAF1BCB9EC700A66173 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 48EB732A1C748EDE003A818E /* 1.jpg in Resources */, + 48EB73281C748EDE003A818E /* test4.jpg in Resources */, + 48EB733D1C748EDE003A818E /* 6.jpg in Resources */, + 48DBCD1A1C01E045002EC476 /* icon0.jpg in Resources */, + 4841F5E91C2ABC60004979F4 /* pic5.jpg in Resources */, + 48EB72FE1C748DF5003A818E /* MLEmoji_ExpressionImage.plist in Resources */, + 9B2AF0F51D1166690007A130 /* theme_day_json.json in Resources */, + 48EB73381C748EDE003A818E /* 22.jpg in Resources */, + 48EB732C1C748EDE003A818E /* 11.jpg in Resources */, + 48EB73341C748EDE003A818E /* 19.jpg in Resources */, + 48EB73291C748EDE003A818E /* 0.jpg in Resources */, + 48EB732E1C748EDE003A818E /* 13.jpg in Resources */, + 48EB73261C748EDE003A818E /* test2.jpg in Resources */, + 4841F5EB1C2ABC60004979F4 /* pic7.jpg in Resources */, + 48EB73251C748EDE003A818E /* test1.jpg in Resources */, + 99D694F21C8C585300DA97E1 /* Discover.xcassets in Resources */, + 48DBCD221C01E045002EC476 /* pic3.jpg in Resources */, + 48DBCD211C01E045002EC476 /* pic2.jpg in Resources */, + 48EB73451C749212003A818E /* Home.xcassets in Resources */, + 48EB73241C748EDE003A818E /* test0.jpg in Resources */, + 48DBCD231C01E045002EC476 /* pic4.jpg in Resources */, + 48DBCD1F1C01E045002EC476 /* pic0.jpg in Resources */, + 485302CF1C3F6490004361A7 /* sdRefeshView_arrow@2x.png in Resources */, + 48EB72FD1C748DF5003A818E /* MLEmoji_Expression.plist in Resources */, + 48EB73311C748EDE003A818E /* 16.jpg in Resources */, + 48EB73351C748EDE003A818E /* 2.jpg in Resources */, + 4841F5F21C2ACC2B004979F4 /* pbg.jpg in Resources */, + 48EB732D1C748EDE003A818E /* 12.jpg in Resources */, + 48EB73391C748EDE003A818E /* 23.jpg in Resources */, + 487D3EC01BCB9EC700A66173 /* Main.storyboard in Resources */, + 4841F5F31C2ACC2B004979F4 /* picon.jpg in Resources */, + 48EB73321C748EDE003A818E /* 17.jpg in Resources */, + 48EB73331C748EDE003A818E /* 18.jpg in Resources */, + 48EB72FC1C748DF5003A818E /* MLEmoji_Expression.bundle in Resources */, + 48EB73401C748EDE003A818E /* 9.jpg in Resources */, + 48DBCD1B1C01E045002EC476 /* icon1.jpg in Resources */, + 48EB73301C748EDE003A818E /* 15.jpg in Resources */, + 48DBCD201C01E045002EC476 /* pic1.jpg in Resources */, + 4841F5EA1C2ABC60004979F4 /* pic6.jpg in Resources */, + 487D3EC51BCB9EC700A66173 /* LaunchScreen.xib in Resources */, + 48EB73361C748EDE003A818E /* 20.jpg in Resources */, + 48EB733B1C748EDE003A818E /* 4.jpg in Resources */, + 9B2AF0F61D1166690007A130 /* theme_night_json.json in Resources */, + 48EB73371C748EDE003A818E /* 21.jpg in Resources */, + 48DBCD1D1C01E045002EC476 /* icon3.jpg in Resources */, + 48EB733C1C748EDE003A818E /* 5.jpg in Resources */, + 4841F5EC1C2ABC60004979F4 /* pic8.jpg in Resources */, + 487D3EC21BCB9EC700A66173 /* Images.xcassets in Resources */, + 6B42D97E2087825A009D5BEC /* DemoVC14Cell.xib in Resources */, + 48EB732B1C748EDE003A818E /* 10.jpg in Resources */, + 48EB732F1C748EDE003A818E /* 14.jpg in Resources */, + 48EB733F1C748EDE003A818E /* 8.jpg in Resources */, + 48DBCD1C1C01E045002EC476 /* icon2.jpg in Resources */, + 48EB733E1C748EDE003A818E /* 7.jpg in Resources */, + 48DBCD1E1C01E045002EC476 /* icon4.jpg in Resources */, + 48EB733A1C748EDE003A818E /* 3.jpg in Resources */, + 48EB73271C748EDE003A818E /* test3.jpg in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 487D3ECF1BCB9EC700A66173 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A1B2C3D31BCB9EC700A66173 /* news_sample.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B2C3D4E41BCB9EC700A66173 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C94A134B215F591A00A92F35 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C94A135A215F591C00A92F35 /* LaunchScreen.storyboard in Resources */, + C94A1357215F591C00A92F35 /* Assets.xcassets in Resources */, + C94A1355215F591A00A92F35 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 1B12CB532F5C34EB3E2E1922 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo-resources.sh", + "${PODS_ROOT}/MJRefresh/MJRefresh/MJRefresh.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MJRefresh.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SDAutoLayoutDemo/Pods-SDAutoLayoutDemo-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 382CBFBAD727003877850AE8 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-SDAutoLayoutDemo-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + A1B2C3DB1BCB9EC700A66173 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-SDAutoLayoutDemoTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 487D3EAD1BCB9EC700A66173 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 485303411C3F977A004361A7 /* DemoVC7Cell2.m in Sources */, + 4853033C1C3F977A004361A7 /* DemoVC5CellTableViewCell.m in Sources */, + 485303501C3F97AA004361A7 /* AppDelegate.m in Sources */, + 485302F61C3F65EE004361A7 /* TADotView.m in Sources */, + 99D694E11C8C553500DA97E1 /* MLLinkLabel.m in Sources */, + 485302DB1C3F6490004361A7 /* SDWebImageManager.m in Sources */, + 48EB73001C748DF5003A818E /* TTTAttributedLabel.m in Sources */, + 48EB73041C748E1A003A818E /* SDAnalogDataGenerator.m in Sources */, + 485302F51C3F65EE004361A7 /* TAAnimatedDotView.m in Sources */, + 9997FB651CB2C26800A6FF62 /* SDTimeLineCellOperationMenu.m in Sources */, + 485302FA1C3F65EE004361A7 /* UIView+SDExtension.m in Sources */, + 485302D31C3F6490004361A7 /* UIView+SDExtension.m in Sources */, + 485303431C3F977A004361A7 /* DemoVC8.m in Sources */, + 48FC0E1D1C4FB3A8004098B8 /* ThreeSecondCell.m in Sources */, + 485303421C3F977A004361A7 /* DemoVC7Model.m in Sources */, + 485302CE1C3F6490004361A7 /* SDWaitingView.m in Sources */, + 485302DF1C3F6490004361A7 /* UIImage+MultiFormat.m in Sources */, + 485302D91C3F6490004361A7 /* SDWebImageDownloader.m in Sources */, + 485303361C3F977A004361A7 /* DemoVC1.m in Sources */, + C96D0595215F5BED0050BA9F /* UITableView+SDAutoTableViewCellHeight.m in Sources */, + 485302F71C3F65EE004361A7 /* TAPageControl.m in Sources */, + 99D694EF1C8C567A00DA97E1 /* SDBaseNavigationController.m in Sources */, + C96D0594215F5BED0050BA9F /* UIView+SDAutoLayout.m in Sources */, + 485302E01C3F6490004361A7 /* UIImage+WebP.m in Sources */, + 4808F3981CE4AC1700242779 /* DemoVC14Cell.m in Sources */, + 99D694F01C8C567A00DA97E1 /* SDBaseTableViewController.m in Sources */, + 4853033E1C3F977A004361A7 /* DemoVC6.m in Sources */, + 485302D11C3F6490004361A7 /* SDRefreshHeaderView.m in Sources */, + 99D694DE1C8C553500DA97E1 /* NSString+MLExpression.m in Sources */, + 99D694B81C8C54D100DA97E1 /* SDTimeLineCellCommentView.m in Sources */, + 485302E11C3F6490004361A7 /* UIImageView+HighlightedWebCache.m in Sources */, + C94A1348215F572E00A92F35 /* LEETheme.m in Sources */, + 485303401C3F977A004361A7 /* DemoVC7Cell.m in Sources */, + 99D694B51C8C54D000DA97E1 /* SDTimeLineCellModel.m in Sources */, + 48EEAD701C9C3006001138FE /* DemoVC13.m in Sources */, + 485303351C3F977A004361A7 /* DemoVC0.m in Sources */, + 99D694DC1C8C553500DA97E1 /* MLTextAttachment.m in Sources */, + 48FC0E1B1C4FB3A8004098B8 /* ThreeFirstCell.m in Sources */, + 48FC0E211C4FB3EA004098B8 /* DemoVC10.m in Sources */, + 99D694E01C8C553500DA97E1 /* MLLabel.m in Sources */, + 489489091CE5FA1700C9BB71 /* PhotosContainerView.m in Sources */, + 48EB72FF1C748DF5003A818E /* MLEmojiLabel.m in Sources */, + 99D694B91C8C54D100DA97E1 /* SDTimeLineTableHeaderView.m in Sources */, + 485302D81C3F6490004361A7 /* SDWebImageDecoder.m in Sources */, + 485302DC1C3F6490004361A7 /* SDWebImagePrefetcher.m in Sources */, + 48FC0E1C1C4FB3A8004098B8 /* ThreeFourthCell.m in Sources */, + 485302D21C3F6490004361A7 /* SDRefreshView.m in Sources */, + 48EB72F01C748D6C003A818E /* SDChatModel.m in Sources */, + 4853033D1C3F977A004361A7 /* DemoVC5Model.m in Sources */, + 485302DE1C3F6490004361A7 /* UIImage+GIF.m in Sources */, + 48EB72F21C748D6C003A818E /* SDChatTableViewCell.m in Sources */, + 485302F91C3F65EE004361A7 /* SDCycleScrollView.m in Sources */, + 485302E21C3F6490004361A7 /* UIImageView+WebCache.m in Sources */, + 485303451C3F977A004361A7 /* DemoVC9.m in Sources */, + 99D694DD1C8C553500DA97E1 /* NSAttributedString+MLExpression.m in Sources */, + 485302D61C3F6490004361A7 /* SDImageCache.m in Sources */, + 48FC0E181C4FB3A8004098B8 /* Imgextra.m in Sources */, + 48FC0E191C4FB3A8004098B8 /* ThreeModel.m in Sources */, + 99D694DF1C8C553500DA97E1 /* MLLabelLayoutManager.m in Sources */, + 48EB72EF1C748D6C003A818E /* SDWebViewController.m in Sources */, + 48FC0E171C4FB3A8004098B8 /* Editor.m in Sources */, + 485303551C3F97E6004361A7 /* DemoCell.m in Sources */, + 99D694DA1C8C553500DA97E1 /* NSString+MLLabel.m in Sources */, + 48EEAD6C1C9C25F4001138FE /* DemoVC12.m in Sources */, + 485303391C3F977A004361A7 /* TestCell2.m in Sources */, + 485302CC1C3F6490004361A7 /* SDBrowserImageView.m in Sources */, + 485302CD1C3F6490004361A7 /* SDPhotoBrowser.m in Sources */, + 4808F3941CE4ABFB00242779 /* DemoVC14.m in Sources */, + 99D694D91C8C553500DA97E1 /* NSMutableAttributedString+MLLabel.m in Sources */, + 485303511C3F97AA004361A7 /* DemoTableViewControler.m in Sources */, + 48FC0ECB1C4FB7CA004098B8 /* XYString.m in Sources */, + 485302F41C3F65EE004361A7 /* TAAbstractDotView.m in Sources */, + B1B944B220BCFB44008F658A /* SDAutolayout+extension.swift in Sources */, + 485302F81C3F65EE004361A7 /* SDCollectionViewCell.m in Sources */, + 99D694F81C8C7F7600DA97E1 /* SDTimeLineRefreshFooter.m in Sources */, + 485302DA1C3F6490004361A7 /* SDWebImageDownloaderOperation.m in Sources */, + 485303521C3F97AA004361A7 /* ViewController.m in Sources */, + 99D694E31C8C553500DA97E1 /* README.md in Sources */, + 99D694B61C8C54D100DA97E1 /* SDTimeLineTableViewController.m in Sources */, + 48EB73431C748F26003A818E /* DemoVC11.m in Sources */, + 485302D71C3F6490004361A7 /* SDWebImageCompat.m in Sources */, + 99D694EE1C8C567A00DA97E1 /* SDBaseRefreshView.m in Sources */, + 48FC0E1A1C4FB3A8004098B8 /* ThreeBaseCell.m in Sources */, + 485302D51C3F6490004361A7 /* NSData+ImageContentType.m in Sources */, + 99D694D81C8C553500DA97E1 /* NSAttributedString+MLLabel.m in Sources */, + 485303381C3F977A004361A7 /* DemoVC3.m in Sources */, + 99D694B71C8C54D100DA97E1 /* SDTimeLineCell.m in Sources */, + 99D694BB1C8C54D100DA97E1 /* SDTimeLineRefreshHeader.m in Sources */, + 99D694BA1C8C54D100DA97E1 /* SDWeiXinPhotoContainerView.m in Sources */, + 485302D01C3F6490004361A7 /* SDRefreshFooterView.m in Sources */, + 485302DD1C3F6490004361A7 /* UIButton+WebCache.m in Sources */, + 485303371C3F977A004361A7 /* DemoVC2.m in Sources */, + 48FC0E1E1C4FB3A8004098B8 /* ThreeThirdCell.m in Sources */, + 487D3EB71BCB9EC700A66173 /* main.m in Sources */, + 48FC0E161C4FB3A8004098B8 /* Ads.m in Sources */, + 4853033F1C3F977A004361A7 /* DemoVC7.m in Sources */, + 485302E31C3F6490004361A7 /* UIView+WebCacheOperation.m in Sources */, + 99D694DB1C8C553500DA97E1 /* MLExpressionManager.m in Sources */, + 4853033A1C3F977A004361A7 /* DemoVC4.m in Sources */, + 485302D41C3F6490004361A7 /* MKAnnotationView+WebCache.m in Sources */, + 4853033B1C3F977A004361A7 /* DemoVC5.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 487D3ECA1BCB9EC700A66173 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A1B2C3D11BCB9EC700A66173 /* SDAutoLayoutDemoTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B2C3D4E21BCB9EC700A66173 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B2C3D4EC1BCB9EC700A66173 /* SDAutoLayoutDemoUITests.swift in Sources */, + B2C3D4F71BCB9EC700A66173 /* LayoutAssertionHelper.swift in Sources */, + B2C3D4F81BCB9EC700A66173 /* DemoLayoutCatalog.swift in Sources */, + B2C3D4F91BCB9EC700A66173 /* DemoLayoutContext.swift in Sources */, + B2C3D4FB1BCB9EC700A66173 /* DemoVC1LayoutAssertions.swift in Sources */, + B2C3D4FD1BCB9EC700A66173 /* DemoScreenLayoutAssertions.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C94A1349215F591A00A92F35 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C94A1352215F591A00A92F35 /* ViewController.swift in Sources */, + C94A1350215F591A00A92F35 /* AppDelegate.swift in Sources */, + C96D059D215F5C040050BA9F /* UITableView+SDAutoTableViewCellHeight.m in Sources */, + C96D059C215F5C040050BA9F /* UIView+SDAutoLayout.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 487D3ED01BCB9EC700A66173 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 487D3EB01BCB9EC700A66173 /* SDAutoLayoutDemo */; + targetProxy = 487D3ED11BCB9EC700A66173 /* PBXContainerItemProxy */; + }; + B2C3D4E81BCB9EC700A66173 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 487D3EB01BCB9EC700A66173 /* SDAutoLayoutDemo */; + targetProxy = B2C3D4E91BCB9EC700A66173 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 487D3EBE1BCB9EC700A66173 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 487D3EBF1BCB9EC700A66173 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 487D3EC31BCB9EC700A66173 /* LaunchScreen.xib */ = { + isa = PBXVariantGroup; + children = ( + 487D3EC41BCB9EC700A66173 /* Base */, + ); + name = LaunchScreen.xib; + sourceTree = ""; + }; + C94A1353215F591A00A92F35 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + C94A1354215F591A00A92F35 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + C94A1358215F591C00A92F35 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + C94A1359215F591C00A92F35 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 487D3ECD1BCB9EC700A66173 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A1B2C3D71BCB9EC700A66173 /* Pods-SDAutoLayoutDemoTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + DEVELOPMENT_TEAM = 8D5EHT9KTL; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/SDAutoLayoutDemo", + "$(SRCROOT)/SDAutoLayout", + "$(SRCROOT)/SDAutoLayoutDemo/DemoVC/DemoVC10/model", + "$(SRCROOT)/SDAutoLayoutDemo/DemoVC/DemoVC10/view", + "$(SRCROOT)/SDAutoLayoutDemo/DemoVC/DemoVC10/Lib/XYString", + "$(SRCROOT)/SDAutoLayoutDemo/Vender", + ); + INFOPLIST_FILE = SDAutoLayoutDemoTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.gsd.test.unit; + PRODUCT_NAME = SDAutoLayoutDemoTests; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SDAutoLayoutDemo.app/SDAutoLayoutDemo"; + WRAPPER_EXTENSION = xctest; + }; + name = Debug; + }; + 487D3ECE1BCB9EC700A66173 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A1B2C3D81BCB9EC700A66173 /* Pods-SDAutoLayoutDemoTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + DEVELOPMENT_TEAM = 8D5EHT9KTL; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/SDAutoLayoutDemo", + "$(SRCROOT)/SDAutoLayout", + "$(SRCROOT)/SDAutoLayoutDemo/DemoVC/DemoVC10/model", + "$(SRCROOT)/SDAutoLayoutDemo/DemoVC/DemoVC10/view", + "$(SRCROOT)/SDAutoLayoutDemo/DemoVC/DemoVC10/Lib/XYString", + "$(SRCROOT)/SDAutoLayoutDemo/Vender", + ); + INFOPLIST_FILE = SDAutoLayoutDemoTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.gsd.test.unit; + PRODUCT_NAME = SDAutoLayoutDemoTests; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SDAutoLayoutDemo.app/SDAutoLayoutDemo"; + WRAPPER_EXTENSION = xctest; + }; + name = Release; + }; + B2C3D4E61BCB9EC700A66173 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEVELOPMENT_TEAM = 8D5EHT9KTL; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = SDAutoLayoutDemoUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.gsd.test.ui; + PRODUCT_NAME = SDAutoLayoutDemoUITests; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = SDAutoLayoutDemo; + }; + name = Debug; + }; + B2C3D4E71BCB9EC700A66173 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEVELOPMENT_TEAM = 8D5EHT9KTL; + INFOPLIST_FILE = SDAutoLayoutDemoUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.gsd.test.ui; + PRODUCT_NAME = SDAutoLayoutDemoUITests; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = SDAutoLayoutDemo; + }; + name = Release; + }; + 487D3ED21BCB9EC700A66173 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 487D3ED31BCB9EC700A66173 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 487D3ED51BCB9EC700A66173 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 59F464B58FAB1B71D18C24E1 /* Pods-SDAutoLayoutDemo.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEVELOPMENT_TEAM = 3GNXU2LRUU; + INFOPLIST_FILE = SDAutoLayoutDemo/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.gsd.test; + PRODUCT_NAME = SDAutoLayoutDemo; + PROVISIONING_PROFILE = ""; + SWIFT_OBJC_BRIDGING_HEADER = "SDAutoLayoutDemo/SwiftExtension/SDAutoLayoutDemo-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.2; + }; + name = Debug; + }; + 487D3ED61BCB9EC700A66173 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = EF70C3084D41C4217B66DF2B /* Pods-SDAutoLayoutDemo.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEVELOPMENT_TEAM = 3GNXU2LRUU; + INFOPLIST_FILE = SDAutoLayoutDemo/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.gsd.test; + PRODUCT_NAME = SDAutoLayoutDemo; + PROVISIONING_PROFILE = ""; + SWIFT_OBJC_BRIDGING_HEADER = "SDAutoLayoutDemo/SwiftExtension/SDAutoLayoutDemo-Bridging-Header.h"; + SWIFT_VERSION = 4.2; + }; + name = Release; + }; + C94A135D215F591C00A92F35 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 3GNXU2LRUU; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = SDAutoLayoutDemoTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.lee.SDAutoLayoutSwiftDemo; + PRODUCT_NAME = SDAutoLayoutDemo; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/SDAutoLayoutSwiftDemo/Bridge-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SDAutoLayoutDemo.app/SDAutoLayoutDemo"; + }; + name = Debug; + }; + C94A135E215F591C00A92F35 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 3GNXU2LRUU; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = SDAutoLayoutDemoTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.lee.SDAutoLayoutSwiftDemo; + PRODUCT_NAME = SDAutoLayoutDemo; + SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/SDAutoLayoutSwiftDemo/Bridge-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SDAutoLayoutDemo.app/SDAutoLayoutDemo"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 487D3EAC1BCB9EC700A66173 /* Build configuration list for PBXProject "SDAutoLayoutDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 487D3ED21BCB9EC700A66173 /* Debug */, + 487D3ED31BCB9EC700A66173 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 487D3ECC1BCB9EC700A66173 /* Build configuration list for PBXNativeTarget "SDAutoLayoutDemoTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 487D3ECD1BCB9EC700A66173 /* Debug */, + 487D3ECE1BCB9EC700A66173 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B2C3D4E51BCB9EC700A66173 /* Build configuration list for PBXNativeTarget "SDAutoLayoutDemoUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B2C3D4E61BCB9EC700A66173 /* Debug */, + B2C3D4E71BCB9EC700A66173 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 487D3ED41BCB9EC700A66173 /* Build configuration list for PBXNativeTarget "SDAutoLayoutDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 487D3ED51BCB9EC700A66173 /* Debug */, + 487D3ED61BCB9EC700A66173 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C94A135C215F591C00A92F35 /* Build configuration list for PBXNativeTarget "SDAutoLayoutSwiftDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C94A135D215F591C00A92F35 /* Debug */, + C94A135E215F591C00A92F35 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 487D3EA91BCB9EC700A66173 /* Project object */; +} diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata" b/SDAutoLayoutDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 51% rename from "SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata" rename to SDAutoLayoutDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 83e88dc..495c374 100644 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata" +++ b/SDAutoLayoutDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:/Users/gsd/Desktop/开源/SDAutoLayout/SDAutoLayoutDemo.xcodeproj"> diff --git a/SDAutoLayoutDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SDAutoLayoutDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/SDAutoLayoutDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/project.xcworkspace/xcshareddata/SDAutoLayout \346\265\213\350\257\225 Demo.xccheckout" "b/SDAutoLayoutDemo.xcodeproj/project.xcworkspace/xcshareddata/SDAutoLayout \346\265\213\350\257\225 Demo.xccheckout" similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/project.xcworkspace/xcshareddata/SDAutoLayout \346\265\213\350\257\225 Demo.xccheckout" rename to "SDAutoLayoutDemo.xcodeproj/project.xcworkspace/xcshareddata/SDAutoLayout \346\265\213\350\257\225 Demo.xccheckout" diff --git a/SDAutoLayoutDemo.xcodeproj/project.xcworkspace/xcuserdata/LEE.xcuserdatad/UserInterfaceState.xcuserstate b/SDAutoLayoutDemo.xcodeproj/project.xcworkspace/xcuserdata/LEE.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..a18fbb9 Binary files /dev/null and b/SDAutoLayoutDemo.xcodeproj/project.xcworkspace/xcuserdata/LEE.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/SDAutoLayoutDemo.xcodeproj/project.xcworkspace/xcuserdata/aier.xcuserdatad/UserInterfaceState.xcuserstate b/SDAutoLayoutDemo.xcodeproj/project.xcworkspace/xcuserdata/aier.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..5dffe7a Binary files /dev/null and b/SDAutoLayoutDemo.xcodeproj/project.xcworkspace/xcuserdata/aier.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/SDAutoLayoutDemo.xcodeproj/project.xcworkspace/xcuserdata/gaoshaodong.xcuserdatad/UserInterfaceState.xcuserstate b/SDAutoLayoutDemo.xcodeproj/project.xcworkspace/xcuserdata/gaoshaodong.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..43d2723 Binary files /dev/null and b/SDAutoLayoutDemo.xcodeproj/project.xcworkspace/xcuserdata/gaoshaodong.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/SDAutoLayoutDemo.xcodeproj/project.xcworkspace/xcuserdata/gsd.xcuserdatad/UserInterfaceState.xcuserstate b/SDAutoLayoutDemo.xcodeproj/project.xcworkspace/xcuserdata/gsd.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..5e3bd7f Binary files /dev/null and b/SDAutoLayoutDemo.xcodeproj/project.xcworkspace/xcuserdata/gsd.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/SDAutoLayoutDemo.xcodeproj/xcshareddata/xcschemes/SDAutoLayoutDemo.xcscheme b/SDAutoLayoutDemo.xcodeproj/xcshareddata/xcschemes/SDAutoLayoutDemo.xcscheme new file mode 100644 index 0000000..ff39255 --- /dev/null +++ b/SDAutoLayoutDemo.xcodeproj/xcshareddata/xcschemes/SDAutoLayoutDemo.xcscheme @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/xcuserdata/aier.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist" b/SDAutoLayoutDemo.xcodeproj/xcuserdata/LEE.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/xcuserdata/aier.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist" rename to SDAutoLayoutDemo.xcodeproj/xcuserdata/LEE.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/SDAutoLayout \346\265\213\350\257\225 Demo.xcscheme" b/SDAutoLayoutDemo.xcodeproj/xcuserdata/LEE.xcuserdatad/xcschemes/SDAutoLayoutDemo.xcscheme similarity index 61% rename from "SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/SDAutoLayout \346\265\213\350\257\225 Demo.xcscheme" rename to SDAutoLayoutDemo.xcodeproj/xcuserdata/LEE.xcuserdatad/xcschemes/SDAutoLayoutDemo.xcscheme index 3133a4e..71d7588 100644 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/SDAutoLayout \346\265\213\350\257\225 Demo.xcscheme" +++ b/SDAutoLayoutDemo.xcodeproj/xcuserdata/LEE.xcuserdatad/xcschemes/SDAutoLayoutDemo.xcscheme @@ -1,6 +1,6 @@ - - - - + BuildableName = "SDAutoLayoutDemo.app" + BlueprintName = "SDAutoLayoutDemo" + ReferencedContainer = "container:SDAutoLayoutDemo.xcodeproj"> @@ -47,9 +33,9 @@ + BuildableName = "SDAutoLayoutDemo.xctest" + BlueprintName = "SDAutoLayoutDemoTests" + ReferencedContainer = "container:SDAutoLayoutDemo.xcodeproj"> @@ -57,9 +43,9 @@ + BuildableName = "SDAutoLayoutDemo.app" + BlueprintName = "SDAutoLayoutDemo" + ReferencedContainer = "container:SDAutoLayoutDemo.xcodeproj"> @@ -80,12 +66,17 @@ + BuildableName = "SDAutoLayoutDemo.app" + BlueprintName = "SDAutoLayoutDemo" + ReferencedContainer = "container:SDAutoLayoutDemo.xcodeproj"> + + + BuildableName = "SDAutoLayoutDemo.app" + BlueprintName = "SDAutoLayoutDemo" + ReferencedContainer = "container:SDAutoLayoutDemo.xcodeproj"> diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/xcuserdata/aier.xcuserdatad/xcschemes/xcschememanagement.plist" b/SDAutoLayoutDemo.xcodeproj/xcuserdata/LEE.xcuserdatad/xcschemes/xcschememanagement.plist similarity index 91% rename from "SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/xcuserdata/aier.xcuserdatad/xcschemes/xcschememanagement.plist" rename to SDAutoLayoutDemo.xcodeproj/xcuserdata/LEE.xcuserdatad/xcschemes/xcschememanagement.plist index 85e0a4c..eebe53d 100644 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/xcuserdata/aier.xcuserdatad/xcschemes/xcschememanagement.plist" +++ b/SDAutoLayoutDemo.xcodeproj/xcuserdata/LEE.xcuserdatad/xcschemes/xcschememanagement.plist @@ -4,7 +4,7 @@ SchemeUserState - SDAutoLayout 测试 Demo.xcscheme + SDAutoLayoutDemo.xcscheme orderHint 0 diff --git a/SDAutoLayoutDemo.xcodeproj/xcuserdata/aier.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/SDAutoLayoutDemo.xcodeproj/xcuserdata/aier.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..4b38e25 --- /dev/null +++ b/SDAutoLayoutDemo.xcodeproj/xcuserdata/aier.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,401 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SDAutoLayoutDemo.xcodeproj/xcuserdata/aier.xcuserdatad/xcschemes/SDAutoLayoutDemo.xcscheme b/SDAutoLayoutDemo.xcodeproj/xcuserdata/aier.xcuserdatad/xcschemes/SDAutoLayoutDemo.xcscheme new file mode 100644 index 0000000..ea29986 --- /dev/null +++ b/SDAutoLayoutDemo.xcodeproj/xcuserdata/aier.xcuserdatad/xcschemes/SDAutoLayoutDemo.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/xcschememanagement.plist" b/SDAutoLayoutDemo.xcodeproj/xcuserdata/aier.xcuserdatad/xcschemes/xcschememanagement.plist similarity index 87% rename from "SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/xcschememanagement.plist" rename to SDAutoLayoutDemo.xcodeproj/xcuserdata/aier.xcuserdatad/xcschemes/xcschememanagement.plist index 85e0a4c..d124b41 100644 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/xcschememanagement.plist" +++ b/SDAutoLayoutDemo.xcodeproj/xcuserdata/aier.xcuserdatad/xcschemes/xcschememanagement.plist @@ -4,10 +4,10 @@ SchemeUserState - SDAutoLayout 测试 Demo.xcscheme + SDAutoLayoutDemo.xcscheme orderHint - 0 + 1 SuppressBuildableAutocreation diff --git a/SDAutoLayoutDemo.xcodeproj/xcuserdata/gaoshaodong.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/SDAutoLayoutDemo.xcodeproj/xcuserdata/gaoshaodong.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..ce39bca --- /dev/null +++ b/SDAutoLayoutDemo.xcodeproj/xcuserdata/gaoshaodong.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/xcuserdata/aier.xcuserdatad/xcschemes/SDAutoLayout \346\265\213\350\257\225 Demo.xcscheme" b/SDAutoLayoutDemo.xcodeproj/xcuserdata/gaoshaodong.xcuserdatad/xcschemes/SDAutoLayoutDemo.xcscheme similarity index 56% rename from "SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/xcuserdata/aier.xcuserdatad/xcschemes/SDAutoLayout \346\265\213\350\257\225 Demo.xcscheme" rename to SDAutoLayoutDemo.xcodeproj/xcuserdata/gaoshaodong.xcuserdatad/xcschemes/SDAutoLayoutDemo.xcscheme index 5b45e04..850025e 100644 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo.xcodeproj/xcuserdata/aier.xcuserdatad/xcschemes/SDAutoLayout \346\265\213\350\257\225 Demo.xcscheme" +++ b/SDAutoLayoutDemo.xcodeproj/xcuserdata/gaoshaodong.xcuserdatad/xcschemes/SDAutoLayoutDemo.xcscheme @@ -1,6 +1,6 @@ - - - - + BuildableName = "SDAutoLayoutDemo.app" + BlueprintName = "SDAutoLayoutDemo" + ReferencedContainer = "container:SDAutoLayoutDemo.xcodeproj"> + shouldUseLaunchSchemeArgsEnv = "YES"> + BuildableName = "SDAutoLayoutDemo.xctest" + BlueprintName = "SDAutoLayoutDemoTests" + ReferencedContainer = "container:SDAutoLayoutDemo.xcodeproj"> @@ -57,46 +43,51 @@ + BuildableName = "SDAutoLayoutDemo.app" + BlueprintName = "SDAutoLayoutDemo" + ReferencedContainer = "container:SDAutoLayoutDemo.xcodeproj"> + + - + + BuildableName = "SDAutoLayoutDemo.app" + BlueprintName = "SDAutoLayoutDemo" + ReferencedContainer = "container:SDAutoLayoutDemo.xcodeproj"> - + + BuildableName = "SDAutoLayoutDemo.app" + BlueprintName = "SDAutoLayoutDemo" + ReferencedContainer = "container:SDAutoLayoutDemo.xcodeproj"> diff --git a/SDAutoLayoutDemo.xcodeproj/xcuserdata/gaoshaodong.xcuserdatad/xcschemes/xcschememanagement.plist b/SDAutoLayoutDemo.xcodeproj/xcuserdata/gaoshaodong.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..de74d41 --- /dev/null +++ b/SDAutoLayoutDemo.xcodeproj/xcuserdata/gaoshaodong.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,37 @@ + + + + + SchemeUserState + + SDAutoLayoutDemo.xcscheme + + orderHint + 0 + + SDAutoLayoutSwiftDemo.xcscheme + + orderHint + 4 + + SDAutoLayoutSwiftDemo.xcscheme_^#shared#^_ + + orderHint + 2 + + + SuppressBuildableAutocreation + + 487D3EB01BCB9EC700A66173 + + primary + + + 487D3EC91BCB9EC700A66173 + + primary + + + + + diff --git a/SDAutoLayoutDemo.xcodeproj/xcuserdata/gsd.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/SDAutoLayoutDemo.xcodeproj/xcuserdata/gsd.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..4b3761b --- /dev/null +++ b/SDAutoLayoutDemo.xcodeproj/xcuserdata/gsd.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SDAutoLayoutDemo.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/SDAutoLayoutDemo.xcscheme b/SDAutoLayoutDemo.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/SDAutoLayoutDemo.xcscheme new file mode 100644 index 0000000..ff39255 --- /dev/null +++ b/SDAutoLayoutDemo.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/SDAutoLayoutDemo.xcscheme @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SDAutoLayoutDemo.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/xcschememanagement.plist b/SDAutoLayoutDemo.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..2562ac0 --- /dev/null +++ b/SDAutoLayoutDemo.xcodeproj/xcuserdata/gsd.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,42 @@ + + + + + SchemeUserState + + SDAutoLayoutDemo.xcscheme + + orderHint + 0 + + SDAutoLayoutDemo.xcscheme_^#shared#^_ + + orderHint + 2 + + SDAutoLayoutDemoUITests.xcscheme_^#shared#^_ + + orderHint + 7 + + SDAutoLayoutSwiftDemo.xcscheme_^#shared#^_ + + orderHint + 6 + + + SuppressBuildableAutocreation + + 487D3EB01BCB9EC700A66173 + + primary + + + 487D3EC91BCB9EC700A66173 + + primary + + + + + diff --git a/SDAutoLayoutDemo.xcodeproj/xcuserdata/lixiang.xcuserdatad/xcschemes/xcschememanagement.plist b/SDAutoLayoutDemo.xcodeproj/xcuserdata/lixiang.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..7632750 --- /dev/null +++ b/SDAutoLayoutDemo.xcodeproj/xcuserdata/lixiang.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,19 @@ + + + + + SchemeUserState + + SDAutoLayoutDemo.xcscheme + + orderHint + 1 + + SDAutoLayoutSwiftDemo.xcscheme + + orderHint + 0 + + + + diff --git a/SDAutoLayoutDemo.xcworkspace/contents.xcworkspacedata b/SDAutoLayoutDemo.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..ba989ed --- /dev/null +++ b/SDAutoLayoutDemo.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/SDAutoLayoutDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SDAutoLayoutDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/SDAutoLayoutDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/SDAutoLayoutDemo.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/SDAutoLayoutDemo.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..949b678 --- /dev/null +++ b/SDAutoLayoutDemo.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + BuildSystemType + Original + + diff --git a/SDAutoLayoutDemo.xcworkspace/xcuserdata/gaoshaodong.xcuserdatad/UserInterfaceState.xcuserstate b/SDAutoLayoutDemo.xcworkspace/xcuserdata/gaoshaodong.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..af4b35d Binary files /dev/null and b/SDAutoLayoutDemo.xcworkspace/xcuserdata/gaoshaodong.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/SDAutoLayoutDemo.xcworkspace/xcuserdata/gaoshaodong.xcuserdatad/WorkspaceSettings.xcsettings b/SDAutoLayoutDemo.xcworkspace/xcuserdata/gaoshaodong.xcuserdatad/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f25782d --- /dev/null +++ b/SDAutoLayoutDemo.xcworkspace/xcuserdata/gaoshaodong.xcuserdatad/WorkspaceSettings.xcsettings @@ -0,0 +1,18 @@ + + + + + BuildLocationStyle + UseAppPreferences + CustomBuildLocationType + RelativeToDerivedData + DerivedDataLocationStyle + Default + EnabledFullIndexStoreVisibility + + IssueFilterStyle + ShowActiveSchemeOnly + LiveSourceIssuesEnabled + + + diff --git a/SDAutoLayoutDemo.xcworkspace/xcuserdata/gaoshaodong.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/SDAutoLayoutDemo.xcworkspace/xcuserdata/gaoshaodong.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..470af70 --- /dev/null +++ b/SDAutoLayoutDemo.xcworkspace/xcuserdata/gaoshaodong.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SDAutoLayoutDemo.xcworkspace/xcuserdata/gsd.xcuserdatad/UserInterfaceState.xcuserstate b/SDAutoLayoutDemo.xcworkspace/xcuserdata/gsd.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..1826e1c Binary files /dev/null and b/SDAutoLayoutDemo.xcworkspace/xcuserdata/gsd.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/SDAutoLayoutDemo.xcworkspace/xcuserdata/gsd.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/SDAutoLayoutDemo.xcworkspace/xcuserdata/gsd.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..dffd64c --- /dev/null +++ b/SDAutoLayoutDemo.xcworkspace/xcuserdata/gsd.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + diff --git a/SDAutoLayoutDemo.xcworkspace/xcuserdata/lixiang.xcuserdatad/UserInterfaceState.xcuserstate b/SDAutoLayoutDemo.xcworkspace/xcuserdata/lixiang.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..475764f Binary files /dev/null and b/SDAutoLayoutDemo.xcworkspace/xcuserdata/lixiang.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/Base.lproj/LaunchScreen.xib" b/SDAutoLayoutDemo/Base.lproj/LaunchScreen.xib similarity index 87% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/Base.lproj/LaunchScreen.xib" rename to SDAutoLayoutDemo/Base.lproj/LaunchScreen.xib index a1d5ca1..c442988 100644 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/Base.lproj/LaunchScreen.xib" +++ b/SDAutoLayoutDemo/Base.lproj/LaunchScreen.xib @@ -1,7 +1,8 @@ - + - + + @@ -17,7 +18,7 @@ - @@ -28,7 +32,6 @@ - diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC0.h" b/SDAutoLayoutDemo/DemoVC/DemoVC0/DemoVC0.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC0.h" rename to SDAutoLayoutDemo/DemoVC/DemoVC0/DemoVC0.h diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC0/DemoVC0.m b/SDAutoLayoutDemo/DemoVC/DemoVC0/DemoVC0.m new file mode 100644 index 0000000..739ec60 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC0/DemoVC0.m @@ -0,0 +1,174 @@ +// +// DemoVC0.m +// SDAutoLayout 测试 Demo +// +// Created by gsd on 15/10/12. +// Copyright (c) 2015年 gsd. All rights reserved. +// + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * QQ : 2689718696(gsdios) * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * * + ********************************************************************************* + + */ + +#import "DemoVC0.h" + +#define kTimeInterval 0.8 + +/// UI 测试启动参数:DemoVC0 的循环动画会导致 XCUITest 长时间等待 app idle。 +static BOOL SDIsAutoLayoutUITestRunning(void) +{ + return [[NSProcessInfo processInfo].arguments containsObject:@"-SDAutoLayoutUITest"]; +} + +@implementation DemoVC0 +{ + NSTimer *_timer; + CGFloat _widthRatio; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + _widthRatio = 0.4; + + if (!SDIsAutoLayoutUITestRunning()) { + _timer = [NSTimer scheduledTimerWithTimeInterval:kTimeInterval target:self selector:@selector(animation) userInfo:nil repeats:YES]; + } + + self.edgesForExtendedLayout = UIRectEdgeNone; + + self.view0.sd_layout + .leftSpaceToView(self.view, 20) + .topSpaceToView(self.view,80) + .heightIs(130) + .widthRatioToView(self.view, _widthRatio); + + self.view1.sd_layout + .leftSpaceToView(self.view0, 10) + .topEqualToView(self.view0) + .heightIs(60) + .widthRatioToView(self.view0, 0.5); + + self.view2.sd_layout + .leftSpaceToView(self.view1, 10) + .topEqualToView(self.view1) + .heightRatioToView(self.view1, 1) + .widthIs(50); + + self.view3.sd_layout + .leftEqualToView(self.view1) + .topSpaceToView(self.view1, 10) + .heightRatioToView(self.view1, 1) + .widthRatioToView(self.view1, 1); + + self.view4.sd_layout + .leftEqualToView(self.view2) + .topEqualToView(self.view3) + .heightRatioToView(self.view3, 1) + .widthRatioToView(self.view2, 1); + + + [self.view0 addSubview:self.view5]; + self.view5.sd_layout + .centerYEqualToView(self.view0) + .rightSpaceToView(self.view0, 10) + .widthRatioToView(self.view0, 0.5) + .heightIs(20); + + /** button作为父视图示例 */ + /* + UIButton *button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + + [button1 setTitle:@"一个button" forState:UIControlStateNormal]; + + [button1 setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; + + [button1 setBackgroundColor:[UIColor lightGrayColor]]; + + [self.view addSubview:button1]; + + button1.sd_layout + .centerYEqualToView(self.view) + .centerXEqualToView(self.view) + .widthIs(200) + .heightIs(100); + + UIView *buttonSubView = [[UIView alloc]init]; + + buttonSubView.backgroundColor = [UIColor grayColor]; + + [button1 addSubview:buttonSubView]; + + buttonSubView.sd_layout + .topSpaceToView(button1 , 10) + .leftSpaceToView(button1 , 10) + .rightSpaceToView(button1 , 20) + .heightIs(20); + + UIView *buttonSubView2 = [[UIView alloc]init]; + + buttonSubView2.backgroundColor = [UIColor grayColor]; + + [button1 addSubview:buttonSubView2]; + + buttonSubView2.sd_layout + .topSpaceToView(buttonSubView , 10) + .leftSpaceToView(button1 , 20) + .rightSpaceToView(button1 , 10) + .heightIs(20); + */ +} + + + +- (void)animation +{ + if (_widthRatio >= 0.4) { + _widthRatio = 0.1; + } else { + _widthRatio = 0.4; + } + + // 开启动画 + [UIView animateWithDuration:0.8 animations:^{ + self.view0.sd_layout + .widthRatioToView(self.view, self->_widthRatio); + [self.view0 updateLayout]; // 调用此方法开启view0动画效果 + [self.view5 updateLayout]; // 调用此方法开启view5动画效果 + /* + 调用[self.view0 updateLayout]就可以实现view0和其它兄弟view一起动画,view0和view5是父子关系,如果view0需要动画要再主动调用一次[self.view5 updateLayout] + */ + }]; + + + /* + + SDAutoLayout(新建QQ交流群:497140713) + github:https://github.com/gsdios/SDAutoLayout + ☆☆ SDAutoLayout 视频教程:http://www.letv.com/ptv/vplay/24038772.html ☆☆ + 一行代码搞定自动布局!致力于做最简单易用的Autolayout库。The most easy way for autolayout. + + */ + +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [_timer invalidate]; + _timer = nil; +} + +@end + diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC1.h" b/SDAutoLayoutDemo/DemoVC/DemoVC1/DemoVC1.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC1.h" rename to SDAutoLayoutDemo/DemoVC/DemoVC1/DemoVC1.h diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC1/DemoVC1.m b/SDAutoLayoutDemo/DemoVC/DemoVC1/DemoVC1.m new file mode 100644 index 0000000..f546f00 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC1/DemoVC1.m @@ -0,0 +1,204 @@ +// +// DemoVC1.m +// SDAutoLayout 测试 Demo +// +// Created by gsd on 15/10/12. +// Copyright (c) 2015年 gsd. All rights reserved. +// + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * QQ : 2689718696(gsdios) * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * * + ********************************************************************************* + + */ + +#import "DemoVC1.h" + +@implementation DemoVC1 +{ + UILabel *_autoWidthLabel; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + // demo1.内容自适应view + [self setupAutoHeightView]; + + // demo2.宽度自适应label + [self setupAutoWidthLabel]; + + // demo3.高度自适应label + [self setupAutoHeightLabel]; + + // demo4.设置button根据文字size自适应 + [self setupAutoSizeButton]; + +} + + + +// demo1.内容自适应view +- (void)setupAutoHeightView +{ + /* + 设置view1高度根据子view而自适应(在view1中加入两个子view(testLabel和testView) + ,然后设置view1高度根据子view内容自适应) + */ + + UILabel *subview1 = [UILabel new]; // 初始化子view1 + subview1.text = @"这个紫色的label会根据这些文字内容高度自适应;而这个灰色的父view会根据紫色的label和橙色的view具体情况实现高度自适应。\nGot it! OH YAEH!"; + subview1.backgroundColor = [UIColor purpleColor]; + subview1.accessibilityIdentifier = @"demo1.purpleLabel"; + + UIView *subview2 = [UIView new]; // 初始化子view2 + subview2.backgroundColor = [UIColor orangeColor]; + subview2.isAccessibilityElement = YES; + subview2.accessibilityIdentifier = @"demo1.orangeBar"; + + // 将子view添加进父view + [self.view1 sd_addSubviews:@[subview1, subview2]]; + + + subview1.sd_layout + .leftSpaceToView(self.view1, 10) + .rightSpaceToView(self.view1, 10) + .topSpaceToView(self.view1, 10) + .autoHeightRatio(0); // 设置文本内容自适应,如果这里的参数为大于0的数值则会以此数值作为view的高宽比设置view的高度 + + subview2.sd_layout + .topSpaceToView(subview1, 10) + .widthRatioToView(subview1, 1) + .heightIs(30) + .leftEqualToView(subview1); + + + // view1使用高度根据子view内容自适应,所以不需要设置高度,而是设置“[self.view1 setupAutoHeightWithBottomView:testView bottomMargin:10];”实现高度根据内容自适应 + self.view1.sd_layout + .leftSpaceToView(self.view, 10) + .topSpaceToView(self.view, 80) + .rightSpaceToView(self.view, 10); + + // 设置view1高度根据子其内容自适应 + [self.view1 setupAutoHeightWithBottomView:subview2 bottomMargin:10]; +} + + +// demo2.宽度自适应label +- (void)setupAutoWidthLabel +{ + UILabel *autoWidthlabel = [UILabel new]; + autoWidthlabel.backgroundColor = [[UIColor orangeColor] colorWithAlphaComponent:0.5]; + autoWidthlabel.accessibilityIdentifier = @"demo1.autoWidthLabel"; + _autoWidthLabel = autoWidthlabel; + autoWidthlabel.font = [UIFont systemFontOfSize:12]; + autoWidthlabel.text = @"宽度自适应(距离父view右边距10)"; + + [self.view addSubview:autoWidthlabel]; + + autoWidthlabel.sd_layout + .rightSpaceToView(self.view, 10) + .heightIs(20) + .bottomSpaceToView(self.view, 50); + + [autoWidthlabel setSingleLineAutoResizeWithMaxWidth:180]; +} + + +// demo3.高度自适应label +- (void)setupAutoHeightLabel +{ + UILabel *autoHeightlabel = [UILabel new]; + autoHeightlabel.backgroundColor = [[UIColor redColor] colorWithAlphaComponent:0.5]; + autoHeightlabel.accessibilityIdentifier = @"demo1.autoHeightLabel"; + autoHeightlabel.font = [UIFont systemFontOfSize:12]; + autoHeightlabel.text = @"高度自适应(距离父view左边距10,底部和其右侧label相同,宽度为100)"; + + [self.view addSubview:autoHeightlabel]; + + autoHeightlabel.sd_layout + .bottomEqualToView(_autoWidthLabel) + .leftSpaceToView(self.view, 10) + .widthIs(100) + .autoHeightRatio(0); + +} + +// demo4.设置button根据文字size自适应 +- (void)setupAutoSizeButton +{ + UIButton *btn = [UIButton new]; + btn.backgroundColor = [UIColor redColor]; + btn.accessibilityIdentifier = @"demo1.autoSizeButton"; + [btn setTitle:@"button根据文字自适应" forState:UIControlStateNormal]; + [self.view addSubview:btn]; + + btn.sd_layout + .centerXEqualToView(self.view) + .topSpaceToView(self.view1, 20); + + // 设置button根据文字size自适应 + [btn setupAutoSizeWithHorizontalPadding:10 buttonHeight:25]; +} + + +@end + + + + + +/* + + + self.view0.sd_layout + .leftSpaceToView(self.view, 10) + .widthIs(150) + .topSpaceToView(self.view, 100) + .heightIs(30); + + self.view1.sd_layout + .leftEqualToView(self.view0) + .widthRatioToView(self.view, 0.3) + .heightIs(50) + .topSpaceToView(self.view0, 20); + + self.view2.sd_layout + .leftSpaceToView(@[self.view0, self.view1], 30) + .widthIs(100) + .heightEqualToWidth() + .topEqualToView(self.view0); + + + self.view4.sd_layout + .leftSpaceToView(self.view, 50) + .topSpaceToView(self.view, 100) + .heightRatioToView(self.view, 0.3) + .widthIs(50); + + self.view3.sd_layout + .leftSpaceToView(self.view4, 20) + .topEqualToView(self.view4) + .widthIs(100) + .heightIs(150); + + self.view5.sd_layout + .leftEqualToView(self.view4) + .topSpaceToView(@[self.view3, self.view4], 30) + .widthIs(200) + .heightIs(20); + + + + + */ diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/DemoVC10.h b/SDAutoLayoutDemo/DemoVC/DemoVC10/DemoVC10.h new file mode 100644 index 0000000..983cb7d --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/DemoVC10.h @@ -0,0 +1,17 @@ +// +// DemoVC10.h +// SDAutoLayoutDemo +// +// Created by gsd on 16/1/20. +// Copyright © 2016年 gsd. All rights reserved. +// + +#import + +/* + 本demo由SDAutoLayout库的使用者“李西亚”提供,感谢“李西亚”对本库的关注与支持! + */ + +@interface DemoVC10 : UIViewController + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/DemoVC10.m b/SDAutoLayoutDemo/DemoVC/DemoVC10/DemoVC10.m new file mode 100644 index 0000000..ccf2df7 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/DemoVC10.m @@ -0,0 +1,268 @@ +// +// DemoVC10.m +// SDAutoLayoutDemo +// +// Created by gsd on 16/1/20. +// Copyright © 2016年 gsd. All rights reserved. +// + + +/* + 本demo由SDAutoLayout库的使用者“李西亚”提供,感谢“李西亚”对本库的关注与支持! + */ + +#import "DemoVC10.h" + +#import "UITableView+SDAutoTableViewCellHeight.h" +#import +#import +#import +#import "XYString.h" + +#import "ThreeBaseCell.h" +#import "ThreeFirstCell.h" +#import "ThreeSecondCell.h" +#import "ThreeThirdCell.h" +#import "ThreeFourthCell.h" + +/* + 本demo由SDAutoLayout库的使用者“李西亚”提供,感谢“李西亚”对本库的关注与支持! + */ + +@interface DemoVC10 () + +@property(nonatomic ,strong) UITableView *tv; +@property(nonatomic ,strong) NSMutableArray *listArry; + +@property (nonatomic, assign) NSInteger page; +@property (nonatomic, strong) MJRefreshComponent *myRefreshView; + +@end + +@implementation DemoVC10 + +- (void)viewDidLoad { + + /* + 本demo由SDAutoLayout库的使用者“李西亚”提供,感谢“李西亚”对本库的关注与支持! + */ + + [super viewDidLoad]; + + /* + 本demo日夜间主题切换由SDAutoLayout库的使用者“LEE”提供,感谢“LEE”对本库的关注与支持! + */ + + //LEETheme 分为两种模式 , 默认设置模式 标识符设置模式 , 朋友圈demo展示的是默认设置模式的使用 , 微信聊天demo和Demo10 展示的是标识符模式的使用 + + UIBarButtonItem *rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"日间" style:UIBarButtonItemStyleDone target:self action:@selector(rightBarButtonItemAction:)]; + + self.navigationItem.rightBarButtonItem = rightBarButtonItem; + + rightBarButtonItem.lee_theme + .LeeAddCustomConfig(DAY , ^(UIBarButtonItem *item){ + + item.title = @"夜间"; + + }).LeeAddCustomConfig(NIGHT , ^(UIBarButtonItem *item){ + + item.title = @"日间"; + }); + + self.view.lee_theme.LeeConfigBackgroundColor(@"demovc10_backgroundcolor"); + + self.automaticallyAdjustsScrollViewInsets = YES; + + [self.view addSubview:self.tv]; + + self.tv.sd_layout.spaceToSuperView(UIEdgeInsetsMake(0, 0, 0, 0)); +} + +// 右栏目按钮点击事件 + +- (void)rightBarButtonItemAction:(UIBarButtonItem *)sender{ + + if ([[LEETheme currentThemeTag] isEqualToString:DAY]) { + + [LEETheme startTheme:NIGHT]; + + } else { + + [LEETheme startTheme:DAY]; + + } + +} + +#pragma mark - getter +- (UITableView *)tv{ + + /* + 本demo由SDAutoLayout库的使用者“李西亚”提供,感谢“李西亚”对本库的关注与支持! + */ + + if (!_tv) { + + _tv = [[UITableView alloc] initWithFrame:self.view.bounds]; + _tv.accessibilityIdentifier = @"demoVC10TableView"; + _tv.separatorColor = [UIColor clearColor]; + _tv.delegate = self; + _tv.dataSource = self; + _tv.backgroundColor = [UIColor clearColor]; + + + __weak typeof(self) weakSelf = self; + + //..下拉刷新 + _tv.header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{ + weakSelf.myRefreshView = weakSelf.tv.header; + weakSelf.page = 0; + [weakSelf loadData]; + }]; + + // 马上进入刷新状态 + [_tv.header beginRefreshing]; + + //..上拉刷新 + _tv.footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{ + weakSelf.myRefreshView = weakSelf.tv.footer; + weakSelf.page = weakSelf.page + 10; + [weakSelf loadData]; + }]; + + _tv.footer.hidden = YES; + + + } + return _tv; +} + +-(NSMutableArray *)listArry{ + + if (!_listArry) { + _listArry = [[NSMutableArray alloc] init]; + } + return _listArry; +} + +#pragma mark - 请求数据 +-(void)loadData{ + /* + 本demo由SDAutoLayout库的使用者“李西亚”提供,感谢“李西亚”对本库的关注与支持! + */ + NSString * urlString = [NSString stringWithFormat:@"http://c.m.163.com/nc/article/%@/%ld-20.html",@"headline/T1348647853363",self.page]; + NSLog(@"______%@",urlString); + AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; + manager.requestSerializer = [AFJSONRequestSerializer serializer]; + manager.responseSerializer = [AFHTTPResponseSerializer serializer]; + [manager GET:urlString parameters:nil headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { + NSString *responseString = nil; + if ([responseObject isKindOfClass:[NSData class]]) { + responseString = [[NSString alloc] initWithData:(NSData *)responseObject encoding:NSUTF8StringEncoding]; + } else if ([responseObject isKindOfClass:[NSString class]]) { + responseString = responseObject; + } + NSDictionary *dict = [XYString getObjectFromJsonString:responseString]; + //..keyEnumerator 获取字典里面所有键 objectEnumerator得到里面的对象 keyEnumerator得到里面的键值 + NSString *key = [dict.keyEnumerator nextObject];//.取键值 + NSArray *temArray = dict[key]; + + // 数组>>model数组 + NSMutableArray *arrayM = [NSMutableArray arrayWithArray:[ThreeModel mj_objectArrayWithKeyValuesArray:temArray]]; + + //..下拉刷新 + if (self.myRefreshView == self->_tv.header) { + self.listArry = arrayM; + self->_tv.footer.hidden = self.listArry.count==0?YES:NO; + + }else if(self.myRefreshView == self->_tv.footer){ + [self.listArry addObjectsFromArray:arrayM]; + } + + + [self doneWithView:self.myRefreshView]; + + + } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { + + NSLog(@"请求失败"); + [self->_myRefreshView endRefreshing]; + }]; +} + +#pragma mark - 回调刷新 +-(void)doneWithView:(MJRefreshComponent*)refreshView{ + [_tv reloadData]; + [_myRefreshView endRefreshing]; +} + +#pragma mark - 表的协议方法 + +-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ + + return self.listArry.count; +} + +-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ + /* + 本demo由SDAutoLayout库的使用者“李西亚”提供,感谢“李西亚”对本库的关注与支持! + */ + ThreeBaseCell * cell = nil; + ThreeModel * threeModel = self.listArry[indexPath.row]; + + NSString * identifier = [ThreeBaseCell cellIdentifierForRow:threeModel]; + Class mClass = NSClassFromString(identifier); + + cell = [tableView dequeueReusableCellWithIdentifier:identifier]; + if (!cell) { + cell = [[mClass alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; + } + cell.threeModel = threeModel; + + ////// 此步设置用于实现cell的frame缓存,可以让tableview滑动更加流畅 ////// + + cell.sd_tableView = tableView; + cell.sd_indexPath = indexPath; + + /////////////////////////////////////////////////////////////////////// + + return cell; + +} + +-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ + + // cell自适应设置 + ThreeModel * threeModel = self.listArry[indexPath.row]; + + NSString * identifier = [ThreeBaseCell cellIdentifierForRow:threeModel]; + Class mClass = NSClassFromString(identifier); + + // 返回计算出的cell高度(普通简化版方法,同样只需一步设置即可完成) + return [self.tv cellHeightForIndexPath:indexPath model:threeModel keyPath:@"threeModel" cellClass:mClass contentViewWidth:[self cellContentViewWith]]; + +} + +-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ + +} + + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + + +- (CGFloat)cellContentViewWith +{ + CGFloat width = [UIScreen mainScreen].bounds.size.width; + + // 适配ios7横屏 + if ([UIApplication sharedApplication].statusBarOrientation != UIInterfaceOrientationPortrait && [[UIDevice currentDevice].systemVersion floatValue] < 8) { + width = [UIScreen mainScreen].bounds.size.height; + } + return width; +} + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/Lib/XYString/XYString.h b/SDAutoLayoutDemo/DemoVC/DemoVC10/Lib/XYString/XYString.h new file mode 100644 index 0000000..42c2397 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/Lib/XYString/XYString.h @@ -0,0 +1,60 @@ + +#import +#import + +@interface XYString : NSObject + +#pragma mark - json转换 ++(id )getObjectFromJsonString:(NSString *)jsonString; ++(NSString *)getJsonStringFromObject:(id)object; + +#pragma mark - NSDate互转NSString ++(NSDate *)NSStringToDate:(NSString *)dateString; ++(NSDate *)NSStringToDate:(NSString *)dateString withFormat:(NSString *)formatestr; ++(NSString *)NSDateToString:(NSDate *)dateFromString withFormat:(NSString *)formatestr; + +#pragma mark - 判断字符串是否为空,为空的话返回 “” (一般用于保存字典时) ++(NSString *)IsNotNull:(NSString*)string; ++(BOOL) isBlankString:(id)string; + + +#pragma mark - 如何通过一个整型的变量来控制数值保留的小数点位数。以往我们通类似@"%.2f"来指定保留2位小数位,现在我想通过一个变量来控制保留的位数 ++(NSString *)newFloat:(float)value withNumber:(int)numberOfPlace; + + +#pragma mark - 使用subString去除float后面无效的0 ++(NSString *)changeFloatWithString:(NSString *)stringFloat; + +#pragma mark - 去除float后面无效的0 ++(NSString *)changeFloatWithFloat:(CGFloat)floatValue; + + +#pragma mark - 手机号码验证 ++(BOOL) isValidateMobile:(NSString *)mobile; + +#pragma mark - 阿里云压缩图片 ++(NSURL*)UrlWithStringForImage:(NSString*)string; ++(NSString*)removeYaSuoAttribute:(NSString*)string; + +#pragma mark - 字符串类型判断 ++ (BOOL)isPureInt:(NSString*)string; ++ (BOOL)isPureFloat:(NSString*)string; + +#pragma mark - 计算内容文本的高度方法 ++ (CGFloat)HeightForText:(NSString *)text withSizeOfLabelFont:(CGFloat)font withWidthOfContent:(CGFloat)contentWidth; + +#pragma mark - 计算字符串长度 ++ (CGFloat)WidthForString:(NSString *)text withSizeOfFont:(CGFloat)font; + + +#pragma mark - 计算两个时间相差多少秒 + ++(NSInteger)getSecondsWithBeginDate:(NSString*)currentDateString AndEndDate:(NSString*)tomDateString; + +#pragma mark - 根据出生日期获取年龄 ++ (NSInteger)ageWithDateOfBirth:(NSDate *)date; + +#pragma mark - 根据经纬度计算两个位置之间的距离 ++(double)distanceBetweenOrderBylat1:(double)lat1 lat2:(double)lat2 lng1:(double)lng1 lng2:(double)lng2; + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/Lib/XYString/XYString.m b/SDAutoLayoutDemo/DemoVC/DemoVC10/Lib/XYString/XYString.m new file mode 100644 index 0000000..8a23b6e --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/Lib/XYString/XYString.m @@ -0,0 +1,271 @@ + + +#import "XYString.h" + +@implementation XYString + +#pragma mark - json转换 ++(id )getObjectFromJsonString:(NSString *)jsonString +{ + NSError *error = nil; + if (jsonString) { + id rev=[NSJSONSerialization JSONObjectWithData:[jsonString dataUsingEncoding:NSUnicodeStringEncoding] options:NSJSONReadingMutableLeaves error:&error]; + if (error==nil) { + return rev; + } + else + { + return nil; + } + } + return nil; +} + ++(NSString *)getJsonStringFromObject:(id)object +{ + if ([NSJSONSerialization isValidJSONObject:object]) + + { + + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:object options:0 error:nil]; + + + + return [[NSString alloc]initWithData:jsonData encoding:NSUTF8StringEncoding]; + + } + + return nil; +} + +#pragma mark - NSDate互转NSString ++(NSDate *)NSStringToDate:(NSString *)dateString +{ + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"]; + NSDate *dateFromString = [[NSDate alloc] init]; + dateFromString = [dateFormatter dateFromString:dateString]; + return dateFromString; +} + ++(NSDate *)NSStringToDate:(NSString *)dateString withFormat:(NSString *)formatestr{ + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateFormat:formatestr]; + NSDate *dateFromString = [[NSDate alloc] init]; + dateFromString = [dateFormatter dateFromString:dateString]; + return dateFromString; +} + ++(NSString *)NSDateToString:(NSDate *)dateFromString withFormat:(NSString *)formatestr +{ + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateFormat:formatestr]; + NSString *strDate = [dateFormatter stringFromDate:dateFromString]; + return strDate; +} + +#pragma mark - 判断字符串是否为空,为空的话返回 “” (一般用于保存字典时) ++(NSString *)IsNotNull:(id)string +{ + NSString * str = (NSString*)string; + if ([self isBlankString:str]){ + string = @""; + } + return string; + +} + +//..判断字符串是否为空字符的方法 ++(BOOL) isBlankString:(id)string { + NSString * str = (NSString*)string; + if ([str isEqualToString:@"(null)"]) { + return YES; + } + if (str == nil || str == NULL) { + return YES; + } + if ([str isKindOfClass:[NSNull class]]) { + return YES; + } + if ([[str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length]==0) { + return YES; + } + return NO; +} + + +#pragma mark - 使用subString去除float后面无效的0 ++(NSString *)changeFloatWithString:(NSString *)stringFloat + +{ + const char *floatChars = [stringFloat UTF8String]; + NSUInteger length = [stringFloat length]; + NSUInteger zeroLength = 0; + NSInteger i = length-1; + for(; i>=0; i--) + { + if(floatChars[i] == '0') { + zeroLength++; + } else { + if(floatChars[i] == '.') + i--; + break; + } + } + NSString *returnString; + if(i == -1) { + returnString = @"0"; + } else { + returnString = [stringFloat substringToIndex:i+1]; + } + return returnString; +} + +#pragma mark - 去除float后面无效的0 ++(NSString *)changeFloatWithFloat:(CGFloat)floatValue + +{ + return [self changeFloatWithString:[NSString stringWithFormat:@"%f",floatValue]]; +} + +#pragma mark - 如何通过一个整型的变量来控制数值保留的小数点位数。以往我们通类似@"%.2f"来指定保留2位小数位,现在我想通过一个变量来控制保留的位数 ++(NSString *)newFloat:(float)value withNumber:(int)numberOfPlace +{ + NSString *formatStr = @"%0."; + formatStr = [formatStr stringByAppendingFormat:@"%df", numberOfPlace]; + NSLog(@"____%@",formatStr); + + formatStr = [NSString stringWithFormat:formatStr, value]; + NSLog(@"____%@",formatStr); + + printf("formatStr %s\n", [formatStr UTF8String]); + return formatStr; +} + + +#pragma mark - 手机号码验证 ++(BOOL) isValidateMobile:(NSString *)mobile +{ + /* + //手机号以13, 15,18开头,八个 \d 数字字符 + NSString *phoneRegex = @"^((13[0-9])|(15[^4,\\D])|(18[0,0-9]))\\d{8}$"; + NSPredicate *phoneTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",phoneRegex]; + return [phoneTest evaluateWithObject:mobile]; + */ + + NSPredicate* phoneTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", @"1[34578]([0-9]){9}"]; + + return [phoneTest evaluateWithObject:mobile]; + +} + +#pragma mark - 阿里云压缩图片 ++(NSURL*)UrlWithStringForImage:(NSString*)string{ + NSString * str = [NSString stringWithFormat:@"%@@800w_600h_10Q.jpg",string]; + NSLog(@"加载图片地址=%@",str); + return [NSURL URLWithString:str]; +} + +//..去掉压缩属性“@800w_600h_10Q.jpg” ++(NSString*)removeYaSuoAttribute:(NSString*)string{ + NSString * str = @""; + if ([string rangeOfString:@"@"].location != NSNotFound) { + NSArray * arry = [string componentsSeparatedByString:@"@"]; + str = arry[0]; + } + return str; +} + +#pragma mark - 字符串类型判断 +//..判断是否为整形: ++ (BOOL)isPureInt:(NSString*)string{ + NSScanner* scan = [NSScanner scannerWithString:string]; + int val; + return[scan scanInt:&val] && [scan isAtEnd]; +} + +//判断是否为浮点形: ++ (BOOL)isPureFloat:(NSString*)string{ + NSScanner* scan = [NSScanner scannerWithString:string]; + float val; + return[scan scanFloat:&val] && [scan isAtEnd]; +} + + +#pragma mark - 计算内容文本的高度方法 ++ (CGFloat)HeightForText:(NSString *)text withSizeOfLabelFont:(CGFloat)font withWidthOfContent:(CGFloat)contentWidth +{ + NSDictionary *dict = @{NSFontAttributeName:[UIFont systemFontOfSize:font]}; + CGSize size = CGSizeMake(contentWidth, 2000); + CGRect frame = [text boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:dict context:nil]; + return frame.size.height; +} + +#pragma mark - 计算字符串长度 ++ (CGFloat)WidthForString:(NSString *)text withSizeOfFont:(CGFloat)font +{ + NSDictionary *dict = @{NSFontAttributeName:[UIFont systemFontOfSize:font]}; + CGSize size = [text sizeWithAttributes:dict]; + return size.width; +} + + + +#pragma mark - 计算两个时间相差多少秒 + ++(NSInteger)getSecondsWithBeginDate:(NSString*)currentDateString AndEndDate:(NSString*)tomDateString{ + + NSDate * currentDate = [XYString NSStringToDate:currentDateString withFormat:@"yyyy-MM-dd HH:mm:ss"]; + NSInteger currSec = [currentDate timeIntervalSince1970]; + + NSDate *tomDate = [XYString NSStringToDate:tomDateString withFormat:@"yyyy-MM-dd HH:mm:ss"]; + NSInteger tomSec = [tomDate timeIntervalSince1970]; + + NSInteger newSec = tomSec - currSec; + NSLog(@"相差秒:%ld",(long)newSec); + return newSec; +} + + +#pragma mark - 根据出生日期获取年龄 ++ (NSInteger)ageWithDateOfBirth:(NSDate *)date; +{ + // 出生日期转换 年月日 + NSDateComponents *components1 = [[NSCalendar currentCalendar] components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit fromDate:date]; + NSInteger brithDateYear = [components1 year]; + NSInteger brithDateDay = [components1 day]; + NSInteger brithDateMonth = [components1 month]; + + // 获取系统当前 年月日 + NSDateComponents *components2 = [[NSCalendar currentCalendar] components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit fromDate:[NSDate date]]; + NSInteger currentDateYear = [components2 year]; + NSInteger currentDateDay = [components2 day]; + NSInteger currentDateMonth = [components2 month]; + + // 计算年龄 + NSInteger iAge = currentDateYear - brithDateYear - 1; + if ((currentDateMonth > brithDateMonth) || (currentDateMonth == brithDateMonth && currentDateDay >= brithDateDay)) { + iAge++; + } + + return iAge; +} + + +#pragma mark - 根据经纬度计算两个位置之间的距离 ++(double)distanceBetweenOrderBylat1:(double)lat1 lat2:(double)lat2 lng1:(double)lng1 lng2:(double)lng2{ + double dd = M_PI/180; + double x1=lat1*dd,x2=lat2*dd; + double y1=lng1*dd,y2=lng2*dd; + double R = 6371004; + double distance = (2*R*asin(sqrt(2-2*cos(x1)*cos(x2)*cos(y1-y2) - 2*sin(x1)*sin(x2))/2)); + //返回km + return distance/1000; + + //返回m + //return distance; + +} + + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/model/Ads.h b/SDAutoLayoutDemo/DemoVC/DemoVC10/model/Ads.h new file mode 100644 index 0000000..b8e242f --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/model/Ads.h @@ -0,0 +1,19 @@ +// +// Ads.h +// SDAutoLayoutDemo +// +// Created by lixiya on 16/1/14. +// Copyright © 2016年 lixiya. All rights reserved. +// + +#import + +@interface Ads : NSObject + +@property (nonatomic,copy) NSString *title; +@property (nonatomic,copy) NSString *tag; +@property (nonatomic,copy) NSString *imgsrc; +@property (nonatomic,copy) NSString *subtitle; +@property (nonatomic,copy) NSString *url; + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/model/Ads.m b/SDAutoLayoutDemo/DemoVC/DemoVC10/model/Ads.m new file mode 100644 index 0000000..89d7f8c --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/model/Ads.m @@ -0,0 +1,13 @@ +// +// Ads.m +// SDAutoLayoutDemo +// +// Created by lixiya on 16/1/14. +// Copyright © 2016年 lixiya. All rights reserved. +// + +#import "Ads.h" + +@implementation Ads + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/model/Editor.h b/SDAutoLayoutDemo/DemoVC/DemoVC10/model/Editor.h new file mode 100644 index 0000000..cb16dea --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/model/Editor.h @@ -0,0 +1,13 @@ +// +// Editor.h +// SDAutoLayoutDemo +// +// Created by lixiya on 16/1/14. +// Copyright © 2016年 lixiya. All rights reserved. +// + +#import + +@interface Editor : NSObject + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/model/Editor.m b/SDAutoLayoutDemo/DemoVC/DemoVC10/model/Editor.m new file mode 100644 index 0000000..78643cf --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/model/Editor.m @@ -0,0 +1,13 @@ +// +// Editor.m +// SDAutoLayoutDemo +// +// Created by lixiya on 16/1/14. +// Copyright © 2016年 lixiya. All rights reserved. +// + +#import "Editor.h" + +@implementation Editor + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/model/Imgextra.h b/SDAutoLayoutDemo/DemoVC/DemoVC10/model/Imgextra.h new file mode 100644 index 0000000..d5a540c --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/model/Imgextra.h @@ -0,0 +1,14 @@ +// +// Imgextra.h +// SDAutoLayoutDemo +// +// Created by lixiya on 16/1/14. +// Copyright © 2016年 lixiya. All rights reserved. +// + +#import + +@interface Imgextra : NSObject +@property (nonatomic,copy) NSString *imgsrc; + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/model/Imgextra.m b/SDAutoLayoutDemo/DemoVC/DemoVC10/model/Imgextra.m new file mode 100644 index 0000000..9a885b9 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/model/Imgextra.m @@ -0,0 +1,13 @@ +// +// Imgextra.m +// SDAutoLayoutDemo +// +// Created by lixiya on 16/1/14. +// Copyright © 2016年 lixiya. All rights reserved. +// + +#import "Imgextra.h" + +@implementation Imgextra + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/model/ThreeModel.h b/SDAutoLayoutDemo/DemoVC/DemoVC10/model/ThreeModel.h new file mode 100644 index 0000000..988e78c --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/model/ThreeModel.h @@ -0,0 +1,129 @@ +// +// ThreeModel.h +// SDAutoLayoutDemo +// +// Created by lixiya on 16/1/14. +// Copyright © 2016年 lixiya. All rights reserved. +// + +#import +#import "Ads.h" +#import "Imgextra.h" +#import "Editor.h" + +@interface ThreeModel : NSObject + +@property (nonatomic,copy) NSString *tname; + +/** + * 新闻发布时间 + */ +@property (nonatomic,copy) NSString *ptime; + +/** + * 标题 + */ +@property (nonatomic,copy) NSString *title; + +/** + * 跟帖人数 + */ +@property (nonatomic,copy)NSNumber *replyCount; + +/** + * 新闻ID + */ +@property (nonatomic,copy) NSString *docid; + +/** + * 图片连接 + */ +@property (nonatomic,copy) NSString *imgsrc; + +/** + * 描述 + */ +@property (nonatomic,copy) NSString *digest; + +/** + * 大图样式 + */ +@property (nonatomic,copy)NSNumber *imgType; + + +/** + * 多图数组 + * 里面放的是Imgextra模型 + */ +@property (nonatomic,strong)NSArray *imgextra; + +/** + * 里面放的是Editor模型 + */ +@property (nonatomic,strong)NSArray *editor; + +/** + * 里面放的是Ads模型 + */ +@property (nonatomic,strong)NSArray *ads; + +@property (nonatomic,strong)NSArray *videoID; +@property (nonatomic,strong)NSArray *specialextra; +@property (nonatomic,strong)NSArray *applist; + +@property (nonatomic,copy) NSString *photosetID; +@property (nonatomic,copy)NSNumber *hasHead; +@property (nonatomic,copy)NSNumber *hasImg; +@property (nonatomic,copy) NSString *lmodify; +@property (nonatomic,copy) NSString *template; +@property (nonatomic,copy) NSString *skipType; + + +@property (nonatomic,copy)NSNumber *votecount; +@property (nonatomic,copy)NSNumber *voteCount; +@property (nonatomic,copy) NSString *alias; + + +@property (nonatomic,assign)BOOL hasCover; +@property (nonatomic,copy)NSNumber *hasAD; +@property (nonatomic,copy)NSNumber *priority; +@property (nonatomic,copy) NSString *cid; + + +@property (nonatomic,assign)BOOL hasIcon; +@property (nonatomic,copy) NSString *ename; +@property (nonatomic,copy) NSString *skipID; +@property (nonatomic,copy)NSNumber *order; + + + +@property (nonatomic,copy) NSString *url_3w; +@property (nonatomic,copy) NSString *specialID; +@property (nonatomic,copy) NSString *timeConsuming; +@property (nonatomic,copy) NSString *subtitle; +@property (nonatomic,copy) NSString *adTitle; +@property (nonatomic,copy) NSString *url; +@property (nonatomic,copy) NSString *source; + + +@property (nonatomic,copy) NSString *TAGS; +@property (nonatomic,copy) NSString *TAG; + + +@property (nonatomic,copy) NSString *boardid; +@property (nonatomic,copy) NSString *commentid; +@property (nonatomic,copy)NSNumber *speciallogo; +@property (nonatomic,copy) NSString *specialtip; +@property (nonatomic,copy) NSString *specialadlogo; + +@property (nonatomic,copy) NSString *pixel; + + +@property (nonatomic,copy) NSString *wap_portal; +@property (nonatomic,copy) NSString *live_info; + + + +@property (nonatomic,copy) NSString *videosource; + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/model/ThreeModel.m b/SDAutoLayoutDemo/DemoVC/DemoVC10/model/ThreeModel.m new file mode 100644 index 0000000..a420fa9 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/model/ThreeModel.m @@ -0,0 +1,22 @@ +// +// ThreeModel.m +// SDAutoLayoutDemo +// +// Created by lixiya on 16/1/14. +// Copyright © 2016年 lixiya. All rights reserved. +// + +#import "ThreeModel.h" +#import +@implementation ThreeModel + ++(NSDictionary*)mj_objectClassInArray{ + + return @{ + @"imgextra":@"Imgextra", + @"editor":@"Editor", + @"ads":@"Ads" + }; +} + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeBaseCell.h b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeBaseCell.h new file mode 100644 index 0000000..96cb6a5 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeBaseCell.h @@ -0,0 +1,65 @@ +// +// ThreeBaseCell.h +// SDAutoLayoutDemo +// +// Created by lixiya on 16/1/14. +// Copyright © 2016年 lixiya. All rights reserved. +// + +#import +#import "UIView+SDAutoLayout.h" +#import "UIImageView+WebCache.h" +#import "SDCycleScrollView.h" +#import "ThreeModel.h" +#import "LEETheme.h" + +#define DAY @"day" + +#define NIGHT @"night" + +@interface ThreeBaseCell : UITableViewCell + +/** + * 图片 + */ +@property (strong, nonatomic) UIImageView *imgIcon; + + +/** + * 标题 + */ +@property (strong, nonatomic) UILabel *lblTitle; + + +/** + * 描述 + */ +@property (strong, nonatomic) UILabel *lblSubtitle; + +/** + * 第二张图片(如果有的话) + */ +@property (strong, nonatomic) UIImageView *imgOther1; + + +/** + * 第三张图片(如果有的话) + */ +@property (strong, nonatomic) UIImageView *imgOther2; + +/** + * 底部分界线 + */ +@property (strong, nonatomic) UIView *lineView; + +/** + * 滚动图片区 + */ +@property(nonatomic ,strong) SDCycleScrollView * cycleScrollView; + +// 数据模型 +@property(nonatomic ,strong) ThreeModel * threeModel; + ++(NSString *)cellIdentifierForRow:(ThreeModel *)threeModel; + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeBaseCell.m b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeBaseCell.m new file mode 100644 index 0000000..cbc9bcc --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeBaseCell.m @@ -0,0 +1,82 @@ +// +// ThreeBaseCell.m +// SDAutoLayoutDemo +// +// Created by lixiya on 16/1/14. +// Copyright © 2016年 lixiya. All rights reserved. +// + +#define rgba(r,g,b,a) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:a] + +#import "ThreeBaseCell.h" + +@implementation ThreeBaseCell + +// 获取数据类型对应的cell ++(NSString *)cellIdentifierForRow:(ThreeModel *)threeModel{ + + if (threeModel.hasHead.boolValue) { + return @"ThreeFourthCell"; + } else if (threeModel.imgType.integerValue != 0) { + return @"ThreeThirdCell"; + } else if (threeModel.imgextra) { + return @"ThreeSecondCell"; + }else{ + return @"ThreeFirstCell"; + } + +} + +-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ + + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + if (self) { + + self.selectionStyle = UITableViewCellSelectionStyleNone; + + self.imgIcon = [UIImageView new]; + [self.contentView addSubview:self.imgIcon]; + + self.lblTitle = [UILabel new]; + self.lblTitle.font = [UIFont systemFontOfSize:15]; + [self.contentView addSubview:self.lblTitle]; + + self.lblSubtitle = [UILabel new]; + self.lblSubtitle.textColor = [UIColor grayColor]; + self.lblSubtitle.font = [UIFont systemFontOfSize:13]; + self.lblSubtitle.numberOfLines = 0; + [self.contentView addSubview:self.lblSubtitle]; + + self.lineView = [UIView new]; + [self.contentView addSubview:self.lineView]; + + self.imgOther1 = [UIImageView new]; + [self.contentView addSubview:self.imgOther1]; + + self.imgOther2 = [UIImageView new]; + [self.contentView addSubview:self.imgOther2]; + + //设置主题 + + [self configTheme]; + } + return self; +} + +//设置主题 + +- (void)configTheme{ + + //使用标识符设置模式 + + self.lblTitle.lee_theme.LeeConfigTextColor(@"demovc10_cell_titlecolor"); + + self.lblSubtitle.lee_theme.LeeConfigTextColor(@"demovc10_cell_summarycolor"); + + self.lineView.lee_theme.LeeConfigBackgroundColor(@"demovc10_cell_linecolor"); + + self.lee_theme.LeeConfigBackgroundColor(@"demovc10_cell_backgroundcolor"); + +} + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeFirstCell.h b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeFirstCell.h new file mode 100644 index 0000000..2cb8a6d --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeFirstCell.h @@ -0,0 +1,13 @@ +// +// ThreeFirstCell.h +// SDAutoLayoutDemo +// +// Created by lixiya on 16/1/14. +// Copyright © 2016年 lixiya. All rights reserved. +// + +#import "ThreeBaseCell.h" + +@interface ThreeFirstCell : ThreeBaseCell + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeFirstCell.m b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeFirstCell.m new file mode 100644 index 0000000..1a2cd08 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeFirstCell.m @@ -0,0 +1,68 @@ +// +// ThreeFirstCell.m +// SDAutoLayoutDemo +// +// Created by lixiya on 16/1/14. +// Copyright © 2016年 lixiya. All rights reserved. +// + +#import "ThreeFirstCell.h" + +@implementation ThreeFirstCell + + +-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ + + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + if (self) { + + [self setup]; + } + return self; +} + +-(void)setup{ + + + // 设置约束 + CGFloat margin = 10; + + self.imgIcon.sd_layout + .leftSpaceToView(self.contentView,margin) + .topSpaceToView(self.contentView,margin) + .widthIs(90) + .heightIs(65); + + self.lblTitle.sd_layout + .leftSpaceToView(self.imgIcon ,margin) + .topSpaceToView(self.contentView,margin) + .rightSpaceToView(self.contentView,margin) + .autoHeightRatio(0); + self.lblTitle.numberOfLines = 0; + + self.lblSubtitle.sd_layout + .topSpaceToView(self.lblTitle,margin) + .leftSpaceToView(self.imgIcon,margin) + .rightSpaceToView(self.contentView,margin) + .autoHeightRatio(0); + + self.lineView.sd_layout + .bottomEqualToView(self.contentView) + .rightSpaceToView(self.contentView,0) + .leftSpaceToView(self.contentView,0) + .heightIs(0.5f); + + [self setupAutoHeightWithBottomViewsArray:@[self.lblSubtitle, self.imgIcon] bottomMargin:(margin + 1)]; +} + + +-(void)setThreeModel:(ThreeModel *)threeModel{ + + self.lblTitle.text = threeModel.title; + self.lblSubtitle.text = [NSString stringWithFormat:@"%@",threeModel.digest]; + [self.imgIcon sd_setImageWithURL:[NSURL URLWithString:threeModel.imgsrc] placeholderImage:[UIImage imageNamed:@"303"]]; + +} + + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeFourthCell.h b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeFourthCell.h new file mode 100644 index 0000000..6873344 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeFourthCell.h @@ -0,0 +1,13 @@ +// +// ThreeFourthCell.h +// SDAutoLayoutDemo +// +// Created by lixiya on 16/1/14. +// Copyright © 2016年 lixiya. All rights reserved. +// + +#import "ThreeBaseCell.h" + +@interface ThreeFourthCell : ThreeBaseCell + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeFourthCell.m b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeFourthCell.m new file mode 100644 index 0000000..a9b9682 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeFourthCell.m @@ -0,0 +1,63 @@ +// +// ThreeFourthCell.m +// SDAutoLayoutDemo +// +// Created by lixiya on 16/1/14. +// Copyright © 2016年 lixiya. All rights reserved. +// + +#import "ThreeFourthCell.h" + +@implementation ThreeFourthCell + +-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ + + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + if (self) { + + [self setup]; + } + return self; +} + +-(void)setup{ + // 滚动图片区 + self.cycleScrollView = [[SDCycleScrollView alloc] init]; + [self.contentView addSubview:self.cycleScrollView]; + + + //设置约束 + CGFloat margin = 0; + self.cycleScrollView.sd_layout + .topSpaceToView(self.contentView,margin) + .leftSpaceToView(self.contentView,margin) + .rightSpaceToView(self.contentView,margin) + .heightIs(175); + + [self setupAutoHeightWithBottomView:self.cycleScrollView bottomMargin:0]; + +} + +-(void)setThreeModel:(ThreeModel *)threeModel{ + + // 获取滚动图片区数据 + NSMutableArray * imagesURLStrings = [NSMutableArray array]; + NSMutableArray * titles = [NSMutableArray array]; + [threeModel.ads enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + Ads * ads = (Ads*)obj; + [imagesURLStrings addObject:ads.imgsrc]; + [titles addObject:ads.title]; + + }]; + + self.cycleScrollView.pageControlAliment = SDCycleScrollViewPageContolAlimentRight;// 分页控件位置 + self.cycleScrollView.pageControlStyle = SDCycleScrollViewPageContolStyleClassic;// 分页控件风格 + self.cycleScrollView.delegate = self; + self.cycleScrollView.titlesGroup = titles; + self.cycleScrollView.imageURLStringsGroup = imagesURLStrings; + self.cycleScrollView.currentPageDotColor = [UIColor whiteColor]; // 自定义分页控件小圆标颜色 + self.cycleScrollView.placeholderImage = [UIImage imageNamed:@"303"]; + +} + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeSecondCell.h b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeSecondCell.h new file mode 100644 index 0000000..6eaca4c --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeSecondCell.h @@ -0,0 +1,13 @@ +// +// ThreeSecondCell.h +// SDAutoLayoutDemo +// +// Created by lixiya on 16/1/14. +// Copyright © 2016年 lixiya. All rights reserved. +// + +#import "ThreeBaseCell.h" + +@interface ThreeSecondCell : ThreeBaseCell + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeSecondCell.m b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeSecondCell.m new file mode 100644 index 0000000..910c767 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeSecondCell.m @@ -0,0 +1,78 @@ +// +// ThreeSecondCell.m +// SDAutoLayoutDemo +// +// Created by lixiya on 16/1/14. +// Copyright © 2016年 lixiya. All rights reserved. +// + +#import "ThreeSecondCell.h" + +@implementation ThreeSecondCell + + +-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ + + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + if (self) { + + [self setup]; + } + return self; +} + +-(void)setup{ + + // 设置约束 + CGFloat margin = 10; + UIView * contentView = self.contentView; + + NSArray * equalSubViews = [NSArray arrayWithObjects:self.imgIcon,self.imgOther1,self.imgOther2,nil]; + self.contentView.sd_equalWidthSubviews = equalSubViews; + + self.lblTitle.sd_layout + .topSpaceToView(contentView,margin) + .leftSpaceToView(contentView,margin) + .rightSpaceToView(contentView,margin) + .heightIs(21); + + self.imgIcon.sd_layout + .leftSpaceToView(contentView,margin) + .topSpaceToView(self.lblTitle,margin) + .autoHeightRatio(0.75); + + self.imgOther1.sd_layout + .leftSpaceToView(self.imgIcon,margin) + .topSpaceToView(self.lblTitle,margin) + .autoHeightRatio(0.75); + + self.imgOther2.sd_layout + .leftSpaceToView(self.imgOther1,margin) + .rightSpaceToView(contentView,margin) + .topSpaceToView(self.lblTitle,margin) + .autoHeightRatio(0.75); + + self.lineView.sd_layout + .topSpaceToView(self.imgOther2,margin) + .leftSpaceToView(contentView,margin) + .rightSpaceToView(contentView,margin) + .heightIs(0.5f); + + [self setupAutoHeightWithBottomView:self.lineView bottomMargin:0]; + +} + +-(void)setThreeModel:(ThreeModel *)threeModel{ + + + [self.imgIcon sd_setImageWithURL:[NSURL URLWithString:threeModel.imgsrc] placeholderImage:[UIImage imageNamed:@"303"]]; + self.lblTitle.text = threeModel.title; + + // 多图 + Imgextra * imgextra1 = threeModel.imgextra[0]; + Imgextra * imgextra2 = threeModel.imgextra[1]; + [self.imgOther1 sd_setImageWithURL:[NSURL URLWithString:imgextra1.imgsrc] placeholderImage:[UIImage imageNamed:@"303"]]; + [self.imgOther2 sd_setImageWithURL:[NSURL URLWithString:imgextra2.imgsrc] placeholderImage:[UIImage imageNamed:@"303"]]; +} + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeThirdCell.h b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeThirdCell.h new file mode 100644 index 0000000..55718cf --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeThirdCell.h @@ -0,0 +1,13 @@ +// +// ThreeThirdCell.h +// SDAutoLayoutDemo +// +// Created by lixiya on 16/1/14. +// Copyright © 2016年 lixiya. All rights reserved. +// + +#import "ThreeBaseCell.h" + +@interface ThreeThirdCell : ThreeBaseCell + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeThirdCell.m b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeThirdCell.m new file mode 100644 index 0000000..1c0734f --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC10/view/ThreeThirdCell.m @@ -0,0 +1,65 @@ +// +// ThreeThirdCell.m +// SDAutoLayoutDemo +// +// Created by lixiya on 16/1/14. +// Copyright © 2016年 lixiya. All rights reserved. +// + +#import "ThreeThirdCell.h" + +@implementation ThreeThirdCell + + +-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ + + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + if (self) { + + [self setup]; + } + return self; +} + +-(void)setup{ + + // 设置约束 + CGFloat margin = 10; + + self.lblTitle.sd_layout + .leftSpaceToView(self.contentView,margin) + .rightSpaceToView(self.contentView,margin) + .topSpaceToView(self.contentView,margin) + .heightIs(21); + + self.imgIcon.sd_layout + .topSpaceToView(self.lblTitle,margin) + .rightSpaceToView(self.contentView,margin) + .leftSpaceToView(self.contentView,margin) + .heightIs(130); + + self.lblSubtitle.sd_layout + .topSpaceToView(self.imgIcon,margin) + .rightSpaceToView(self.contentView,margin) + .leftSpaceToView(self.contentView,margin) + .autoHeightRatio(0); + + self.lineView.sd_layout + .topSpaceToView(self.lblSubtitle,margin) + .rightSpaceToView(self.contentView,0) + .leftSpaceToView(self.contentView,0) + .heightIs(0.5f); + + [self setupAutoHeightWithBottomView:self.lineView bottomMargin:0]; + +} + +-(void)setThreeModel:(ThreeModel *)threeModel{ + + self.lblTitle.text = threeModel.title; + self.lblSubtitle.text = [NSString stringWithFormat:@"%@",threeModel.digest]; + [self.imgIcon sd_setImageWithURL:[NSURL URLWithString:threeModel.imgsrc] placeholderImage:[UIImage imageNamed:@"303"]]; + +} + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC11/AnalogDataGenerator/SDAnalogDataGenerator.h b/SDAutoLayoutDemo/DemoVC/DemoVC11/AnalogDataGenerator/SDAnalogDataGenerator.h new file mode 100644 index 0000000..c6321ca --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC11/AnalogDataGenerator/SDAnalogDataGenerator.h @@ -0,0 +1,35 @@ +// +// SDAnalogDataGenerator.h +// GSD_WeiXin(wechat) +// +// Created by aier on 16/2/10. +// Copyright © 2016年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * 持续更新地址: https://github.com/gsdios/SDAutoLayout * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * QQ交流群:519489682(已满)497140713 * + ********************************************************************************* + + */ + + + + +#import + +@interface SDAnalogDataGenerator : NSObject + ++ (NSString *)randomName; ++ (NSString *)randomIconImageName; ++ (NSString *)randomMessage; + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC11/AnalogDataGenerator/SDAnalogDataGenerator.m b/SDAutoLayoutDemo/DemoVC/DemoVC11/AnalogDataGenerator/SDAnalogDataGenerator.m new file mode 100644 index 0000000..9a165a7 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC11/AnalogDataGenerator/SDAnalogDataGenerator.m @@ -0,0 +1,131 @@ +// +// SDAnalogDataGenerator.m +// GSD_WeiXin(wechat) +// +// Created by aier on 16/2/10. +// Copyright © 2016年 GSD. All rights reserved. +// + + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * 持续更新地址: https://github.com/gsdios/SDAutoLayout * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * QQ交流群:519489682(已满)497140713 * + ********************************************************************************* + + */ + + + + +#import "SDAnalogDataGenerator.h" + +static NSArray *namesArray; +static NSArray *iconNamesArray; +static NSArray *messagesArray; + +@implementation SDAnalogDataGenerator + ++ (NSString *)randomName +{ + int randomIndex = arc4random_uniform((int)[self names].count); + return [self names][randomIndex]; +} + ++ (NSString *)randomIconImageName +{ + int randomIndex = arc4random_uniform((int)[self iconNames].count); + return iconNamesArray[randomIndex]; +} + ++ (NSString *)randomMessage +{ + int randomIndex = arc4random_uniform((int)[self messages].count); + return messagesArray[randomIndex]; +} + ++ (NSArray *)names +{ + if (!namesArray) { + namesArray = @[@"二龙湖浩哥", + @"微风", + @"夜在哭泣", + @"GSD_iOS", + @"hello world", + @"大脸猫", + @"你似不似傻", + @"天天向上", + @"不爱掏粪男孩", + @"最爱欧巴", + @"大长腿思密达", + @"别给我晒脸", + @"可爱男孩", + @"筷子姐妹", + @"法海你不懂爱", + @"长城长", + @"老北京麻辣烫", + @"我不搞笑", + @"原来我不帅", + @"亲亲我的宝贝", + @"请叫我吴彦祖", + @"帅锅莱昂纳多", + @"星星之火", + @"雅蠛蝶~雅蠛蝶" + ]; + } + return namesArray; +} + ++ (NSArray *)iconNames +{ + if (!iconNamesArray) { + NSMutableArray *temp = [NSMutableArray new]; + for (int i = 0; i < 24; i++) { + NSString *iconName = [NSString stringWithFormat:@"%d.jpg", i]; + [temp addObject:iconName]; + } + iconNamesArray = [temp copy]; + } + return iconNamesArray; +} + ++ (NSArray *)messages +{ + if (!messagesArray) { + messagesArray = @[@"二龙湖浩哥:什么事?🐂🐂🐂🐂", + @"微风:麻蛋!!!", + @"夜在哭泣:好好地,🐂别瞎胡闹", + @"GSD_iOS:SDAutoLayout 下载地址http://www.cocoachina.com/ios/20151223/14778.html", + @"hello world:🐂🐂🐂我不懂", + @"大脸猫:这。。。。。。酸爽~ http://www.cocoachina.com/ios/20151223/14778.html", + @"你似不似傻:呵呵🐎🐎🐎🐎🐎🐎", + @"天天向上:辛苦了!", + @"不爱掏粪男孩:新年快乐!猴年大吉!摸摸哒 http://www.cocoachina.com/ios/20151223/14778.html", + @"最爱欧巴:[呲牙][呲牙][呲牙]", + @"大长腿思密达:[图片]", + @"别给我晒脸:坑死我了。。。。。", + @"可爱男孩:你谁???🐎🐎🐎🐎", + @"筷子姐妹:和尚。。尼姑。。", + @"法海你不懂爱:春晚太难看啦,妈蛋的🐎🐎🐎🐎🐎🐎🐎🐎", + @"长城长:好好好~~~", + @"老北京麻辣烫:约起 http://www.cocoachina.com/ios/20151223/14778.html", + @"我不搞笑:寒假过得真快", + @"原来我不帅:有木有人儿?", + @"亲亲我的宝贝:你🐎说🐎啥🐎呢", + @"请叫我吴彦祖:好搞笑🐎🐎🐎,下次还来", + @"帅锅莱昂纳多:我不理解 http://www.cocoachina.com/ios/20151223/14778.html", + @"星星之火:脱掉,脱掉,统统脱掉🐎", + @"雅蠛蝶~雅蠛蝶:好脏,好污,好喜欢" + ]; + } + return messagesArray; +} + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC11/ChatController/DemoVC11.h b/SDAutoLayoutDemo/DemoVC/DemoVC11/ChatController/DemoVC11.h new file mode 100644 index 0000000..831d835 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC11/ChatController/DemoVC11.h @@ -0,0 +1,30 @@ +// +// DemoVC11.h +// SDAutoLayoutDemo +// +// Created by gsd on 16/2/17. +// Copyright © 2016年 gsd. All rights reserved. +// + + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * 持续更新地址: https://github.com/gsdios/SDAutoLayout * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * QQ交流群:519489682(已满)497140713 * + ********************************************************************************* + + */ + + +#import + +@interface DemoVC11 : UITableViewController + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC11/ChatController/DemoVC11.m b/SDAutoLayoutDemo/DemoVC/DemoVC11/ChatController/DemoVC11.m new file mode 100644 index 0000000..f8e50ff --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC11/ChatController/DemoVC11.m @@ -0,0 +1,173 @@ +// +// DemoVC11.m +// SDAutoLayoutDemo +// +// Created by gsd on 16/2/17. +// Copyright © 2016年 gsd. All rights reserved. +// + + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * 持续更新地址: https://github.com/gsdios/SDAutoLayout * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * QQ交流群:519489682(已满)497140713 * + ********************************************************************************* + + */ + + + + +#import "DemoVC11.h" + +#import "GlobalDefines.h" + +#import "SDChatTableViewCell.h" +#import "SDAnalogDataGenerator.h" +#import "SDWebViewController.h" + +#import "UITableView+SDAutoTableViewCellHeight.h" + +#import "UIView+SDAutoLayout.h" + +#import "LEETheme.h" + +#define kChatTableViewControllerCellId @"ChatTableViewController" + +@interface DemoVC11 () + +@property (nonatomic, strong) NSMutableArray *dataArray; + +@end + +@implementation DemoVC11 + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.tableView.accessibilityIdentifier = @"demo.11.table"; + + UIBarButtonItem *rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"日间" style:UIBarButtonItemStyleDone target:self action:@selector(rightBarButtonItemAction:)]; + + //通过添加自定义block 实现不同主题下 barbutton的title显示不同标题 , 当然这只是举个例子 , block中你想做任何羞羞的事都可以. + + rightBarButtonItem.lee_theme + .LeeAddCustomConfig(@"day" , ^(UIBarButtonItem *item){ + + item.title = @"夜间"; + + }).LeeAddCustomConfig(@"night" , ^(UIBarButtonItem *item){ + + item.title = @"日间"; + }); + + //设置背景颜色的标识符 , 这个标识符和你的json文件中配置的标识符对应 + + self.view.lee_theme.LeeConfigBackgroundColor(@"demovc11_backgroundcolor"); + + self.navigationItem.rightBarButtonItem = rightBarButtonItem; + + [self setupDataWithCount:30]; + + self.tableView.lee_theme.LeeConfigBackgroundColor(@"demovc11_backgroundcolor"); + + self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + + [self.tableView registerClass:[SDChatTableViewCell class] forCellReuseIdentifier:kChatTableViewControllerCellId]; +} + + +- (void)setupDataWithCount:(NSInteger)count +{ + if (!self.dataArray) { + self.dataArray = [NSMutableArray new]; + } + for (int i = 0; i < count; i++) { + SDChatModel *model = [SDChatModel new]; + model.messageType = arc4random_uniform(2); + if (model.messageType) { + model.iconName = [SDAnalogDataGenerator randomIconImageName]; + if (arc4random_uniform(10) > 5) { + int index = arc4random_uniform(5); + model.imageName = [NSString stringWithFormat:@"test%d.jpg", index]; + } + } else { + if (arc4random_uniform(10) < 5) { + int index = arc4random_uniform(5); + model.imageName = [NSString stringWithFormat:@"test%d.jpg", index]; + } + model.iconName = @"2.jpg"; + } + + + model.text = [SDAnalogDataGenerator randomMessage]; + [self.dataArray addObject:model]; + } +} + +// 右栏目按钮点击事件 + +- (void)rightBarButtonItemAction:(UIBarButtonItem *)sender{ + + if ([[LEETheme currentThemeTag] isEqualToString:DAY]) { + + [LEETheme startTheme:NIGHT]; + + } else { + + [LEETheme startTheme:DAY]; + + } + +} + + +#pragma mark - tableview delegate and datasource + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return self.dataArray.count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + SDChatTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kChatTableViewControllerCellId]; + cell.model = self.dataArray[indexPath.row]; + + __weak typeof(self) weakSelf = self; + [cell setDidSelectLinkTextOperationBlock:^(NSString *link, MLEmojiLabelLinkType type) { + if (type == MLEmojiLabelLinkTypeURL) { + [weakSelf.navigationController pushViewController:[SDWebViewController webViewControllerWithUrlString:link] animated:YES]; + } + }]; + + ////// 此步设置用于实现cell的frame缓存,可以让tableview滑动更加流畅 ////// + + cell.sd_tableView = tableView; + cell.sd_indexPath = indexPath; + + /////////////////////////////////////////////////////////////////////// + + return cell; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + CGFloat h = [self.tableView cellHeightForIndexPath:indexPath model:self.dataArray[indexPath.row] keyPath:@"model" cellClass:[SDChatTableViewCell class] contentViewWidth:[UIScreen mainScreen].bounds.size.width]; + return h; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + NSLog(@">>>>> %@", [self.dataArray[indexPath.row] text]); +} + + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC11/ChatController/Models/SDChatModel.h b/SDAutoLayoutDemo/DemoVC/DemoVC11/ChatController/Models/SDChatModel.h new file mode 100644 index 0000000..15a830a --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC11/ChatController/Models/SDChatModel.h @@ -0,0 +1,43 @@ +// +// SDChatModel.h +// GSD_WeiXin(wechat) +// +// Created by aier on 16/2/13. +// Copyright © 2016年 GSD. All rights reserved. +// + + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * 持续更新地址: https://github.com/gsdios/SDAutoLayout * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * QQ交流群:519489682(已满)497140713 * + ********************************************************************************* + + */ + + + + +#import + +typedef enum : NSUInteger { + SDMessageTypeSendToOthers, + SDMessageTypeSendToMe +} SDMessageType; + +@interface SDChatModel : NSObject + +@property (nonatomic, assign) SDMessageType messageType; + +@property (nonatomic, copy) NSString *text; +@property (nonatomic, copy) NSString *iconName; +@property (nonatomic, copy) NSString *imageName; + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC11/ChatController/Models/SDChatModel.m b/SDAutoLayoutDemo/DemoVC/DemoVC11/ChatController/Models/SDChatModel.m new file mode 100644 index 0000000..36e5b27 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC11/ChatController/Models/SDChatModel.m @@ -0,0 +1,32 @@ +// +// SDChatModel.m +// GSD_WeiXin(wechat) +// +// Created by aier on 16/2/13. +// Copyright © 2016年 GSD. All rights reserved. +// + + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * 持续更新地址: https://github.com/gsdios/SDAutoLayout * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * QQ交流群:519489682(已满)497140713 * + ********************************************************************************* + + */ + + + + +#import "SDChatModel.h" + +@implementation SDChatModel + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC11/ChatController/Views/SDChatTableViewCell.h b/SDAutoLayoutDemo/DemoVC/DemoVC11/ChatController/Views/SDChatTableViewCell.h new file mode 100644 index 0000000..ea88d07 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC11/ChatController/Views/SDChatTableViewCell.h @@ -0,0 +1,39 @@ +// +// SDChatTableViewCell.h +// GSD_WeiXin(wechat) +// +// Created by aier on 16/2/13. +// Copyright © 2016年 GSD. All rights reserved. +// + + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * 持续更新地址: https://github.com/gsdios/SDAutoLayout * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * QQ交流群:519489682(已满)497140713 * + ********************************************************************************* + + */ + + + + +#import + +#import "SDChatModel.h" +#import "MLEmojiLabel.h" + +@interface SDChatTableViewCell : UITableViewCell + +@property (nonatomic, strong) SDChatModel *model; + +@property (nonatomic, copy) void (^didSelectLinkTextOperationBlock)(NSString *link, MLEmojiLabelLinkType type); + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC11/ChatController/Views/SDChatTableViewCell.m b/SDAutoLayoutDemo/DemoVC/DemoVC11/ChatController/Views/SDChatTableViewCell.m new file mode 100644 index 0000000..9d3d945 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC11/ChatController/Views/SDChatTableViewCell.m @@ -0,0 +1,263 @@ +// +// SDChatTableViewCell.m +// GSD_WeiXin(wechat) +// +// Created by aier on 16/2/13. +// Copyright © 2016年 GSD. All rights reserved. +// + + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * 持续更新地址: https://github.com/gsdios/SDAutoLayout * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * QQ交流群:519489682(已满)497140713 * + ********************************************************************************* + + */ + + + + +#import "SDChatTableViewCell.h" + +#import "UIView+SDAutoLayout.h" + +#import "LEETheme.h" + +#define kLabelMargin 20.f +#define kLabelTopMargin 8.f +#define kLabelBottomMargin 20.f + +#define kChatCellItemMargin 10.f + +#define kChatCellIconImageViewWH 35.f + +#define kMaxContainerWidth 220.f +#define kMaxLabelWidth (kMaxContainerWidth - kLabelMargin * 2) + +#define kMaxChatImageViewWidth 200.f +#define kMaxChatImageViewHeight 300.f + +@interface SDChatTableViewCell () + +@property (nonatomic, strong) UIView *container; +@property (nonatomic, strong) UIImageView *containerBackgroundImageView; +@property (nonatomic, strong) MLEmojiLabel *label; +@property (nonatomic, strong) UIImageView *iconImageView; +@property (nonatomic, strong) UIImageView *messageImageView; +@property (nonatomic, strong) UIImageView *maskImageView; + +@end + +@implementation SDChatTableViewCell + +- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier +{ + if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { + + [self setupView]; + + //设置主题 + + [self configTheme]; + + } + return self; +} + +- (void)setupView +{ + self.selectionStyle = UITableViewCellSelectionStyleNone; + + _iconImageView = [UIImageView new]; + [self.contentView addSubview:_iconImageView]; + + _container = [UIView new]; + [self.contentView addSubview:_container]; + + _label = [[MLEmojiLabel alloc] initWithFrame:CGRectZero]; + _label.delegate = self; + _label.font = [UIFont systemFontOfSize:16.0f]; + _label.numberOfLines = 0; + _label.textInsets = UIEdgeInsetsMake(0, 0, 0, 0); + _label.isAttributedContent = YES; + [_container addSubview:_label]; + + _messageImageView = [UIImageView new]; + [_container addSubview:_messageImageView]; + + _containerBackgroundImageView = [UIImageView new]; + [_container insertSubview:_containerBackgroundImageView atIndex:0]; + + _maskImageView = [UIImageView new]; + + + [self setupAutoHeightWithBottomView:_container bottomMargin:0]; + + // 设置containerBackgroundImageView填充父view + _containerBackgroundImageView.sd_layout.spaceToSuperView(UIEdgeInsetsMake(0, 0, 0, 0)); +} + +- (void)configTheme{ + + self.lee_theme.LeeConfigBackgroundColor(@"demovc11_backgroundcolor"); + +// self.label.lee_theme.LeeConfigTextColor(@"demovc11_textcolor"); + + //其实UILabel的话只要按照上面的设置textcolor就可以的 , 这个label是第三方的 设置后不会马上变 , 需要重新赋值一下才会改变 - ,- 针对你这个情况 所以这样处理一下先 + self.label.lee_theme + .LeeAddCustomConfig(@"day", ^(MLEmojiLabel *item) { + + item.textColor = [UIColor blackColor]; + + item.text = item.text; // 重新赋值text + }) + .LeeAddCustomConfig(@"night", ^(MLEmojiLabel *item) { + + item.textColor = [UIColor lightGrayColor]; + + item.text = item.text; + }); + + //正常应该设置image的标识符 完成切换不同图片的效果 , 但是这个demo没有夜间图片 暂时用这个方式演示一下咯 , 要不然会看着不太和谐 + + self.containerBackgroundImageView.lee_theme + .LeeAddCustomConfig(@"day" , ^(UIImageView * item){ + + item.alpha = 1.0f; + + }).LeeAddCustomConfig(@"night" , ^(UIImageView * item){ + + item.alpha = 0.2f; + }); + +} + +- (void)setModel:(SDChatModel *)model{ + + _model = model; + + _label.text = model.text; + self.iconImageView.image = [UIImage imageNamed:model.iconName]; + + // 根据model设置cell左浮动或者右浮动样式 + [self setMessageOriginWithModel:model]; + + if (model.imageName) { // 有图片的先看下设置图片自动布局 + + // cell重用时候清除只有文字的情况下设置的container宽度自适应约束 + [self.container clearAutoWidthSettings]; + self.messageImageView.hidden = NO; + + self.messageImageView.image = [UIImage imageNamed:model.imageName]; + + // 根据图片的宽高尺寸设置图片约束 + CGFloat standardWidthHeightRatio = kMaxChatImageViewWidth / kMaxChatImageViewHeight; + CGFloat widthHeightRatio = 0; + UIImage *image = [UIImage imageNamed:model.imageName]; + CGFloat h = image.size.height; + CGFloat w = image.size.width; + + if (w > kMaxChatImageViewWidth || w > kMaxChatImageViewHeight) { + + widthHeightRatio = w / h; + + if (widthHeightRatio > standardWidthHeightRatio) { + w = kMaxChatImageViewWidth; + h = w * (image.size.height / image.size.width); + } else { + h = kMaxChatImageViewHeight; + w = h * widthHeightRatio; + } + } + + self.messageImageView.size_sd = CGSizeMake(w, h); + _container.sd_layout.widthIs(w).heightIs(h); + + // 设置container以messageImageView为bottomView高度自适应 + [_container setupAutoHeightWithBottomView:self.messageImageView bottomMargin:kChatCellItemMargin]; + + // container按照maskImageView裁剪 + self.container.layer.mask = self.maskImageView.layer; + + __weak typeof(self) weakself = self; + [_containerBackgroundImageView setDidFinishAutoLayoutBlock:^(CGRect frame) { + // 在_containerBackgroundImageView的frame确定之后设置maskImageView的size等于containerBackgroundImageView的size + weakself.maskImageView.size_sd = frame.size; + }]; + + } else if (model.text) { // 没有图片有文字情况下设置文字自动布局 + + // 清除展示图片时候用到的mask + [_container.layer.mask removeFromSuperlayer]; + + self.messageImageView.hidden = YES; + + // 清除展示图片时候_containerBackgroundImageView用到的didFinishAutoLayoutBlock + _containerBackgroundImageView.didFinishAutoLayoutBlock = nil; + + _label.sd_resetLayout + .leftSpaceToView(_container, kLabelMargin) + .topSpaceToView(_container, kLabelTopMargin) + .autoHeightRatio(0); // 设置label纵向自适应 + + // 设置label横向自适应 + [_label setSingleLineAutoResizeWithMaxWidth:kMaxContainerWidth]; + + // container以label为rightView宽度自适应 + [_container setupAutoWidthWithRightView:_label rightMargin:kLabelMargin]; + + // container以label为bottomView高度自适应 + [_container setupAutoHeightWithBottomView:_label bottomMargin:kLabelBottomMargin]; + } +} + + +- (void)setMessageOriginWithModel:(SDChatModel *)model +{ + if (model.messageType == SDMessageTypeSendToOthers) { + // 发出去的消息设置居右样式 + self.iconImageView.sd_resetLayout + .rightSpaceToView(self.contentView, kChatCellItemMargin) + .topSpaceToView(self.contentView, kChatCellItemMargin) + .widthIs(kChatCellIconImageViewWH) + .heightIs(kChatCellIconImageViewWH); + + _container.sd_resetLayout.topEqualToView(self.iconImageView).rightSpaceToView(self.iconImageView, kChatCellItemMargin); + + _containerBackgroundImageView.image = [[UIImage imageNamed:@"SenderTextNodeBkg"] stretchableImageWithLeftCapWidth:50 topCapHeight:30]; + } else if (model.messageType == SDMessageTypeSendToMe) { + + // 收到的消息设置居左样式 + self.iconImageView.sd_resetLayout + .leftSpaceToView(self.contentView, kChatCellItemMargin) + .topSpaceToView(self.contentView, kChatCellItemMargin) + .widthIs(kChatCellIconImageViewWH) + .heightIs(kChatCellIconImageViewWH); + + _container.sd_resetLayout.topEqualToView(self.iconImageView).leftSpaceToView(self.iconImageView, kChatCellItemMargin); + + _containerBackgroundImageView.image = [[UIImage imageNamed:@"ReceiverTextNodeBkg"] stretchableImageWithLeftCapWidth:50 topCapHeight:30]; + } + + _maskImageView.image = _containerBackgroundImageView.image; +} + + +#pragma mark - MLEmojiLabelDelegate + +- (void)mlEmojiLabel:(MLEmojiLabel *)emojiLabel didSelectLink:(NSString *)link withType:(MLEmojiLabelLinkType)type +{ + if (self.didSelectLinkTextOperationBlock) { + self.didSelectLinkTextOperationBlock(link, type); + } +} + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC11/WebViewController/SDWebViewController.h b/SDAutoLayoutDemo/DemoVC/DemoVC11/WebViewController/SDWebViewController.h new file mode 100644 index 0000000..10411eb --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC11/WebViewController/SDWebViewController.h @@ -0,0 +1,17 @@ +// +// SDWebViewController.h +// GSD_WeiXin(wechat) +// +// Created by gsd on 16/2/16. +// Copyright © 2016年 GSD. All rights reserved. +// + +#import + +@interface SDWebViewController : UIViewController + ++ (instancetype)webViewControllerWithUrlString:(NSString *)urlString; + +@property (nonatomic, copy) NSString *urlString; + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC11/WebViewController/SDWebViewController.m b/SDAutoLayoutDemo/DemoVC/DemoVC11/WebViewController/SDWebViewController.m new file mode 100644 index 0000000..8aa0236 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC11/WebViewController/SDWebViewController.m @@ -0,0 +1,56 @@ +// +// SDWebViewController.m +// GSD_WeiXin(wechat) +// +// Created by gsd on 16/2/16. +// Copyright © 2016年 GSD. All rights reserved. +// + +#import "SDWebViewController.h" + +@interface SDWebViewController () + +@property (nonatomic, strong) UIWebView *webView; + +@end + +@implementation SDWebViewController + ++ (instancetype)webViewControllerWithUrlString:(NSString *)urlString +{ + SDWebViewController *vc = [SDWebViewController new]; + vc.urlString = urlString; + return vc; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.webView = [UIWebView new]; + self.webView.frame = self.view.bounds; + self.webView.delegate = self; + [self.view addSubview:self.webView]; + + [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.urlString]]]; +} + + +#pragma mark - UIWebViewDelegate + +- (void)webViewDidStartLoad:(UIWebView *)webView +{ + +} + +- (void)webViewDidFinishLoad:(UIWebView *)webView +{ + +} + +- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error +{ + +} + + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC12/DemoVC12.h b/SDAutoLayoutDemo/DemoVC/DemoVC12/DemoVC12.h new file mode 100644 index 0000000..8230781 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC12/DemoVC12.h @@ -0,0 +1,29 @@ +// +// DemoVC12.h +// SDAutoLayoutDemo +// +// Created by gsd on 16/3/18. +// Copyright © 2016年 gsd. All rights reserved. +// + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * 持续更新地址: https://github.com/gsdios/SDAutoLayout * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * QQ交流群:519489682(一群)497140713(二群) * + ********************************************************************************* + + */ + + +#import + +@interface DemoVC12 : UIViewController + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC12/DemoVC12.m b/SDAutoLayoutDemo/DemoVC/DemoVC12/DemoVC12.m new file mode 100644 index 0000000..37c75b4 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC12/DemoVC12.m @@ -0,0 +1,247 @@ +// +// DemoVC12.m +// SDAutoLayoutDemo +// +// Created by gsd on 16/3/18. +// Copyright © 2016年 gsd. All rights reserved. +// + + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * 持续更新地址: https://github.com/gsdios/SDAutoLayout * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * QQ交流群:519489682(一群)497140713(二群) * + ********************************************************************************* + + */ + + + +#import "DemoVC12.h" + +#import "UIView+SDAutoLayout.h" + +@implementation DemoVC12 +{ + UIScrollView *_scrollView; + + UIView *_flowItemContentView; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.view.backgroundColor = [UIColor whiteColor]; + + [self setupScrollView]; +} + + +// 添加scrollview +- (void)setupScrollView +{ + UIScrollView *scroll = [UIScrollView new]; + scroll.accessibilityIdentifier = @"demo.scroll.main"; + [self.view addSubview:scroll]; + _scrollView = scroll; + + // 设置scrollview与父view的边距 + scroll.sd_layout.spaceToSuperView(UIEdgeInsetsZero); + + [self setupFlowItemContentView]; + + // 设置scrollview的contentsize自适应 + [scroll setupAutoContentSizeWithBottomView:_flowItemContentView bottomMargin:10]; +} + + +// 添加flowItemContentView +- (void)setupFlowItemContentView +{ + _flowItemContentView = [UIView new]; + _flowItemContentView.isAccessibilityElement = YES; + _flowItemContentView.accessibilityIdentifier = @"demo12.flowContent"; + _flowItemContentView.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.4]; + [_scrollView addSubview:_flowItemContentView]; + + _flowItemContentView.sd_layout + .leftEqualToView(_scrollView) + .rightEqualToView(_scrollView) + .topEqualToView(_scrollView); + + [self setupFlowItemViews]; +} + + + +- (void)setupFlowItemViews +{ + NSMutableArray *temp = [NSMutableArray new]; + for (int i = 0; i < 35; i++) { + UIView *view = [UIView new]; + view.backgroundColor = [self randomColor]; + [_flowItemContentView addSubview:view]; + view.sd_layout.autoHeightRatio(0.8); + [temp addObject:view]; + } + + // 关键步骤:设置类似collectionView的展示效果 + [_flowItemContentView setupAutoWidthFlowItems:[temp copy] withPerRowItemsCount:3 verticalMargin:10 horizontalMargin:10 verticalEdgeInset:0 horizontalEdgeInset:0]; +} + + +- (UIColor *)randomColor +{ + CGFloat r = arc4random_uniform(256) / 255.0; + CGFloat g = arc4random_uniform(256) / 255.0; + CGFloat b = arc4random_uniform(256) / 255.0; + + return [UIColor colorWithRed:r green:g blue:b alpha:1]; +} + + + + + + + + + + + + + + + + + + + + + + + + +/* + +// 设置scrollview的第一个子深粉色view(包含左边一个label、右边一个button的深粉色view,这个view根据label文字高度自适应) +- (void)setupScrollViewSubView1 +{ + // 深粉色view + UIView *container = [UIView new]; + container.backgroundColor = [[UIColor redColor] colorWithAlphaComponent:0.3]; + [_scrollView addSubview:container]; + _subview1 = container; + + + // 深粉色view第一个子view:左边的lable + UILabel *leftLabel = [UILabel new]; + leftLabel.text = @"我是SDAutoLayout,你是谁?么么哒!"; + _subview1_label = leftLabel; + + // 深粉色view第二个子view:白色文字button + UIButton *rightButton = [UIButton new]; + [rightButton setTitle:@"添加文字" forState:UIControlStateNormal]; + [rightButton addTarget:self action:@selector(subView1ButtonClicked) forControlEvents:UIControlEventTouchUpInside]; + + + [container sd_addSubviews:@[leftLabel, rightButton]]; + + leftLabel.sd_layout + .leftSpaceToView(container, 10) + .rightSpaceToView(container, 100) + .topSpaceToView(container, 10) + .autoHeightRatio(0); // 此行设置label文字自适应 + + rightButton.sd_layout + .bottomEqualToView(container) + .rightSpaceToView(container, 10) + .widthIs(80) + .heightIs(20); + + + container.sd_layout + .leftSpaceToView(_scrollView, 10) + .rightSpaceToView(_scrollView, 10) + .topSpaceToView(_scrollView, 10); + [container setupAutoHeightWithBottomView:leftLabel bottomMargin:10]; // 设置深粉色view根据label的具体内容而自适应 +} + + +- (void)setupScrollViewSubView2 +{ + UILabel *label = [UILabel new]; + label.text = [NSString stringWithFormat:@" 共有%lu个字符 ", [_subview1_label.text length]]; + label.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.5]; + _subview2 = label; + [_scrollView addSubview:label]; + + label.sd_layout + .topSpaceToView(_subview1, 10) + .centerXEqualToView(_subview1) + .heightIs(30); + [label setSingleLineAutoResizeWithMaxWidth:400]; +} + +- (void)setupScrollViewSubView3 +{ + UIView *red = [UIView new]; + red.backgroundColor = [UIColor redColor]; + _redView = red; + + UIView *green = [UIView new]; + green.backgroundColor = [UIColor greenColor]; + + UIView *blue = [UIView new]; + blue.backgroundColor = [UIColor blueColor]; + + [_scrollView sd_addSubviews:@[red, green, blue]]; + + // 设置scrollview底部三个等宽子view(红绿蓝三个view) + _scrollView.sd_equalWidthSubviews = @[red, green, blue]; + + red.sd_layout + .leftSpaceToView(_scrollView, 20) // 设置redview的左边距 + .topSpaceToView(_subview2, 20) + .heightEqualToWidth(); + + green.sd_layout + .leftSpaceToView(red, 20) // 设置greenview的左边距 + .topEqualToView(red) + .heightEqualToWidth(); + + blue.sd_layout + .leftSpaceToView(green, 20) // 设置blueview的左边距 + .topEqualToView(red) + .rightSpaceToView(_scrollView, 20) // 设置blueview的右边距 + .heightEqualToWidth(); +} + + +- (void)subView1ButtonClicked +{ + NSString *addStr = [NSString stringWithFormat:@"新增文字,发生时间:%@", [[NSDate date] descriptionWithLocale:nil]]; + _subview1_label.text = [NSString stringWithFormat:@"%@ ---> %@", _subview1_label.text, addStr]; + [_subview1_label updateLayout]; + + _subview2.text = [NSString stringWithFormat:@" 共有%lu个字符 ", [_subview1_label.text length]]; + + if (_scrollView.contentSize.height > _scrollView.height) { + CGPoint point = _scrollView.contentOffset; + point.y = _scrollView.contentSize.height - _scrollView.height; + [UIView animateWithDuration:0.2 animations:^{ + _scrollView.contentOffset = point; + }]; + } +} + +*/ + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC13/DemoVC13.h b/SDAutoLayoutDemo/DemoVC/DemoVC13/DemoVC13.h new file mode 100644 index 0000000..ab9d193 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC13/DemoVC13.h @@ -0,0 +1,29 @@ +// +// DemoVC13.h +// SDAutoLayoutDemo +// +// Created by gsd on 16/3/18. +// Copyright © 2016年 gsd. All rights reserved. +// + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * 持续更新地址: https://github.com/gsdios/SDAutoLayout * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * QQ交流群:519489682(一群)497140713(二群) * + ********************************************************************************* + + */ + + +#import + +@interface DemoVC13 : UIViewController + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC13/DemoVC13.m b/SDAutoLayoutDemo/DemoVC/DemoVC13/DemoVC13.m new file mode 100644 index 0000000..0e6ed05 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC13/DemoVC13.m @@ -0,0 +1,438 @@ +// +// DemoVC13.m +// SDAutoLayoutDemo +// +// Created by gsd on 16/3/18. +// Copyright © 2016年 gsd. All rights reserved. +// + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * 持续更新地址: https://github.com/gsdios/SDAutoLayout * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * QQ交流群:519489682(一群)497140713(二群) * + ********************************************************************************* + + */ + +#import "DemoVC13.h" + +#import "UIView+SDAutoLayout.h" + +NSString *const kCommonText = @"Hello, everybody! 我是SDAutoLayout,我是不是萌萌哒呢? ^_^ ^(***)^ ^_^ "; +static const CGFloat maxImageViewWidth = 200.f; + +#define kGridItemViewColor [[UIColor greenColor] colorWithAlphaComponent:0.5] + +@interface DemoVC13 () + +@property (nonatomic, strong) UIScrollView *scroollView; + +@property (nonatomic, strong) UIView *wrapperView; + +@property (nonatomic, strong) UILabel *contentLabel; + +@property (nonatomic, strong) UIView *lastBottomLine; + +@property (nonatomic, strong) UIImageView *imageView; + +@property (nonatomic, strong) UIView *gridItemContainerView; + +@property (nonatomic, strong) UIButton *addGridItemButton; + +@end + +@implementation DemoVC13 + +- (void)viewDidLoad { + [super viewDidLoad]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarOrientationChange:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; + + self.edgesForExtendedLayout = UIRectEdgeNone; + self.view.backgroundColor = [UIColor whiteColor]; + + self.wrapperView = [UIView new]; + self.wrapperView.isAccessibilityElement = YES; + self.wrapperView.accessibilityIdentifier = @"demo13.scrollContent"; + [self.scroollView addSubview:self.wrapperView]; + [self.scroollView setupAutoContentSizeWithBottomView:self.wrapperView bottomMargin:0]; + + + [self setupContentCell]; + [self setupImageCell]; + [self setupGridViewCell]; + + self.wrapperView.sd_layout. + leftEqualToView(self.scroollView) + .rightEqualToView(self.scroollView) + .topEqualToView(self.scroollView); + [self.wrapperView setupAutoHeightWithBottomView:self.lastBottomLine bottomMargin:10]; + + +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (UIScrollView *)scroollView +{ + if (!_scroollView) { + _scroollView = [UIScrollView new]; + _scroollView.accessibilityIdentifier = @"demo.scroll.main"; + _scroollView.delegate = self; + [self.view addSubview:_scroollView]; + + _scroollView.sd_layout.spaceToSuperView(UIEdgeInsetsZero); + } + return _scroollView; +} + +- (UIView *)addSeparatorLineBellowView:(UIView *)view margin:(CGFloat)margin +{ + UIView *line = [UIView new]; + line.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.6]; + [self.wrapperView addSubview:line]; + + line.sd_layout + .leftSpaceToView(self.wrapperView, 5) + .rightSpaceToView(self.wrapperView, 5) + .heightIs(1) + .topSpaceToView(view, margin); + + return line; +} + +- (UILabel *)titleLabelWithText:(NSString *)text +{ + UILabel *titleLabel = [UILabel new]; + titleLabel.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.5]; + titleLabel.text = text; + titleLabel.textAlignment = NSTextAlignmentCenter; + return titleLabel; +} + +- (UIButton *)buttonWithTitle:(NSString *)title bgColor:(UIColor *)bgColor sel:(SEL)sel +{ + UIButton *button = [UIButton new]; + [button setTitle:title forState:UIControlStateNormal]; + button.backgroundColor = bgColor; + [button addTarget:self action:sel forControlEvents:UIControlEventTouchUpInside]; + return button; +} + +// 设置展示文字的模拟cell +- (void)setupContentCell +{ + UILabel *titleLabel = [self titleLabelWithText:@"文字内容"]; + + UILabel *contentLabel = [UILabel new]; + contentLabel.backgroundColor = titleLabel.backgroundColor; + contentLabel.text = kCommonText; + self.contentLabel = contentLabel; + + UIView *buttonContainer = [UIView new]; + + { + UIButton *addButton = [self buttonWithTitle:@"添加文字" bgColor:[[UIColor blueColor] colorWithAlphaComponent:0.3] sel:@selector(addText)]; + + UIButton *delButton = [self buttonWithTitle:@"删减文字" bgColor:[[UIColor redColor] colorWithAlphaComponent:0.7] sel:@selector(delText)]; + + [buttonContainer sd_addSubviews:@[addButton, delButton]]; + + + buttonContainer.sd_equalWidthSubviews = @[addButton, delButton]; + + addButton.sd_layout + .leftSpaceToView(buttonContainer, 10) + .topEqualToView(buttonContainer) + .bottomEqualToView(buttonContainer); + + delButton.sd_layout + .leftSpaceToView(addButton, 30) + .rightSpaceToView(buttonContainer, 10) + .topEqualToView(buttonContainer) + .bottomEqualToView(buttonContainer); + } + + [self.wrapperView sd_addSubviews:@[titleLabel, contentLabel, buttonContainer]]; + + titleLabel.sd_layout + .leftSpaceToView(self.wrapperView, 10) + .topSpaceToView(self.wrapperView, 20) + .widthIs(80) + .heightIs(20); + + contentLabel.sd_layout + .leftSpaceToView(titleLabel, 10) + .topEqualToView(titleLabel) + .rightSpaceToView(self.wrapperView, 10) + .autoHeightRatio(0); + + buttonContainer.sd_layout + .leftEqualToView(contentLabel) + .rightEqualToView(contentLabel) + .topSpaceToView(contentLabel, 10) + .heightIs(25); + + self.lastBottomLine = [self addSeparatorLineBellowView:buttonContainer margin:10]; + +} + +// 设置展示图片的模拟cell +- (void)setupImageCell +{ + UILabel *titleLabel = [self titleLabelWithText:@"图片展示"]; + + UIImageView *imageView = [UIImageView new]; + imageView.image = [UIImage imageNamed:@"pic0.jpg"]; + self.imageView = imageView; + + UIButton *magnifyButton = [self buttonWithTitle:@"放大" bgColor:[[UIColor blueColor] colorWithAlphaComponent:0.3] sel:@selector(magnifyImage)]; + + UIButton *shrinkButton = [self buttonWithTitle:@"缩小" bgColor:[[UIColor redColor] colorWithAlphaComponent:0.7] sel:@selector(shrinkImage)]; + + [self.wrapperView sd_addSubviews:@[titleLabel, imageView, magnifyButton, shrinkButton]]; + + titleLabel.sd_layout + .leftSpaceToView(self.wrapperView, 10) + .topSpaceToView(self.lastBottomLine, 10) + .widthIs(80) + .heightIs(20); + + imageView.sd_layout + .leftSpaceToView(titleLabel, 10) + .topEqualToView(titleLabel) + .widthIs(50) + .autoHeightRatio(2) + .maxWidthIs(maxImageViewWidth) + .maxHeightIs(800); + + magnifyButton.sd_layout + .centerXEqualToView(imageView) + .topSpaceToView(imageView, 10) + .widthIs(50) + .heightIs(25); + + shrinkButton.sd_layout + .leftEqualToView(magnifyButton) + .rightEqualToView(magnifyButton) + .topSpaceToView(magnifyButton, 10) + .heightIs(25); + + self.lastBottomLine = [self addSeparatorLineBellowView:shrinkButton margin:10]; +} + +// 设置展示列表的模拟cell +- (void)setupGridViewCell +{ + + UILabel *titleLabel = [self titleLabelWithText:@"列表展示"]; + + UIView *contanerView = [UIView new]; + contanerView.backgroundColor = [UIColor redColor]; + self.gridItemContainerView = contanerView; + + [self.wrapperView sd_addSubviews:@[titleLabel, contanerView]]; + + + { + UIButton *addButton = [UIButton new]; + self.addGridItemButton = addButton; + [addButton setTitle:@"添加" forState:UIControlStateNormal]; + addButton.backgroundColor = kGridItemViewColor; + [addButton addTarget:self action:@selector(addGridItemView) forControlEvents:UIControlEventTouchUpInside]; + [contanerView addSubview:addButton]; + + addButton.sd_layout + .leftSpaceToView(contanerView, 10) + .topSpaceToView(contanerView, 10) + .rightSpaceToView(contanerView, 10) + .heightIs(50); + } + + + + titleLabel.sd_layout + .leftSpaceToView(self.wrapperView, 10) + .topSpaceToView(self.lastBottomLine, 10) + .widthIs(80) + .heightIs(20); + + contanerView.sd_layout + .leftSpaceToView(titleLabel, 10) + .topEqualToView(titleLabel) + .rightSpaceToView(self.wrapperView, 10); + [contanerView setupAutoHeightWithBottomView:contanerView.subviews.lastObject bottomMargin:10]; + + self.lastBottomLine = [self addSeparatorLineBellowView:contanerView margin:10]; +} + + +- (void)addText +{ + self.contentLabel.text = [NSString stringWithFormat:@"%@ %@", self.contentLabel.text, kCommonText]; + [self updateViews]; +} + +- (void)delText +{ + long to = self.contentLabel.text.length - 20; + to = to < 1 ? 1 : to; + self.contentLabel.text = [self.contentLabel.text substringToIndex:to]; + [self updateViews]; +} + +// 放大图片 +- (void)magnifyImage +{ + CGFloat w = self.imageView.width_sd * 1.3; + if (w > maxImageViewWidth) { + [self showAlertWithText:@"已经达到最大宽度"]; + return; + } + [UIView animateWithDuration:0.2 animations:^{ + self.imageView.sd_layout.widthIs(self.imageView.width_sd * 1.3); + [self.wrapperView layoutSubviews]; + [self.scroollView layoutSubviews]; + }]; +} + +// 缩小图片 +- (void)shrinkImage +{ + [UIView animateWithDuration:0.2 animations:^{ + self.imageView.sd_layout.widthIs(self.imageView.width_sd * 0.7); + [self.wrapperView layoutSubviews]; + [self.scroollView layoutSubviews]; + }]; +} + +- (void)updateViews +{ + [self.wrapperView layoutSubviews]; + [self.scroollView layoutSubviews]; +} + +- (void)addGridItemView +{ + UIView *view = [UIView new]; + view.backgroundColor = kGridItemViewColor; + [self.gridItemContainerView insertSubview:view belowSubview:self.addGridItemButton]; + + __block UIView *lastView = self.gridItemContainerView; + + [self.gridItemContainerView.subviews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) { + if (idx < 3 && (idx == (self.gridItemContainerView.subviews.count > 2 ? 2 : 1))) { // 如果idx等于当第一行最后一个view的index时开始设置一行等宽子view + NSMutableArray *equalWidthSubviews = [NSMutableArray new]; + for (int i = 0; i <= idx; i++) { + UIView *subview = self.gridItemContainerView.subviews[i]; + [equalWidthSubviews addObject:subview]; + if (i == 0) { // 设置一排等宽子view的第一个view的约束 + subview.sd_resetLayout + .leftSpaceToView(lastView, 10) + .topSpaceToView(lastView, 10) + .heightIs(50); + } else if (i == idx) { // 设置一排等宽子view的中间view的约束 + subview.sd_resetLayout + .leftSpaceToView(lastView, 10) + .topEqualToView(lastView) + .rightSpaceToView(self.gridItemContainerView, 10) + .heightRatioToView(lastView, 1); + self.gridItemContainerView.sd_equalWidthSubviews = [equalWidthSubviews copy]; + } else { // 设置一排等宽子view的最后一个view的约束 + subview.sd_resetLayout + .leftSpaceToView(lastView, 10) + .topEqualToView(lastView) + .heightIs(50); + } + lastView = subview; + } + } else if (idx > 2){ // 设置第一排之后的子view的的约束 + long lastViewIndex = idx - 3; + lastView = self.gridItemContainerView.subviews[lastViewIndex]; + + view.sd_resetNewLayout + .leftEqualToView(lastView) + .rightEqualToView(lastView) + .heightRatioToView(lastView, 1) + .topSpaceToView(lastView, 10); + } + }]; + + [self.gridItemContainerView setupAutoHeightWithBottomView:self.gridItemContainerView.subviews.lastObject bottomMargin:10]; + + [self.wrapperView layoutSubviews]; +} + + +- (void)showAlertWithText:(NSString *)text +{ + UIView *alert = [UIView new]; + alert.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5]; + + UILabel *label = [UILabel new]; + label.text = text; + label.textColor = [UIColor whiteColor]; + + [alert addSubview:label]; + + label.sd_layout + .leftSpaceToView(alert, 20) + .rightSpaceToView(alert, 20) + .topSpaceToView(alert, 20) + .autoHeightRatio(0); + + + + [[UIApplication sharedApplication].keyWindow addSubview:alert]; + + alert.sd_layout + .centerXEqualToView(alert.superview) + .centerYEqualToView(alert.superview) + .widthIs(200); + [alert setupAutoHeightWithBottomView:label bottomMargin:20]; + + alert.sd_cornerRadius = @(5); + + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [alert removeFromSuperview]; + }); +} + + + +#pragma mark - 监听屏幕旋转 + +- (void)statusBarOrientationChange:(NSNotification *)notification +{ + // 由于scrollview在滚动时会不断调用layoutSubvies方法,这就会不断触发自动布局计算,而很多时候这种计算是不必要的,所以可以通过控制“sd_closeAutoLayout”属性来设置要不要触发自动布局计算 + self.scroollView.sd_closeAutoLayout = NO; +} + + +#pragma mark - UIScrollViewDelegate + +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView +{ + // 由于scrollview在滚动时会不断调用layoutSubvies方法,这就会不断触发自动布局计算,而很多时候这种计算是不必要的,所以可以通过控制“sd_closeAutoLayout”属性来设置要不要触发自动布局计算 + self.wrapperView.sd_closeAutoLayout = YES; +} + +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView +{ + // 由于scrollview在滚动时会不断调用layoutSubvies方法,这就会不断触发自动布局计算,而很多时候这种计算是不必要的,所以可以通过控制“sd_closeAutoLayout”属性来设置要不要触发自动布局计算 + self.wrapperView.sd_closeAutoLayout = NO; +} + + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC14/DemoVC14.h b/SDAutoLayoutDemo/DemoVC/DemoVC14/DemoVC14.h new file mode 100644 index 0000000..51a386a --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC14/DemoVC14.h @@ -0,0 +1,13 @@ +// +// DemoVC14.h +// SDAutoLayoutDemo +// +// Created by gsd on 16/5/12. +// Copyright © 2016年 gsd. All rights reserved. +// + +#import + +@interface DemoVC14 : UITableViewController + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC14/DemoVC14.m b/SDAutoLayoutDemo/DemoVC/DemoVC14/DemoVC14.m new file mode 100644 index 0000000..63ee176 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC14/DemoVC14.m @@ -0,0 +1,139 @@ +// +// DemoVC14.m +// SDAutoLayoutDemo +// +// Created by gsd on 16/5/12. +// Copyright © 2016年 gsd. All rights reserved. +// + +#import "DemoVC14.h" + +#import "DemoVC14Cell.h" +#import "DemoVC7Model.h" +#import "UITableView+SDAutoTableViewCellHeight.h" + +@interface DemoVC14 () + +@property (nonatomic, strong) NSMutableArray *modelsArray; + +@end + +@implementation DemoVC14 + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.tableView.accessibilityIdentifier = @"demo.14.table"; + + [self creatModelsWithCount:20]; + + [self.tableView registerNib:[UINib nibWithNibName:@"DemoVC14Cell" bundle:nil] forCellReuseIdentifier:@"DemoVC14Cell"]; +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)creatModelsWithCount:(NSInteger)count +{ + if (!_modelsArray) { + _modelsArray = [NSMutableArray new]; + } + + NSArray *iconImageNamesArray = @[@"icon0.jpg", + @"icon1.jpg", + @"icon2.jpg", + @"icon3.jpg", + @"icon4.jpg", + @"icon0.jpg", + @"icon1.jpg", + @"icon2.jpg", + @"icon3.jpg" + ]; + + + NSArray *textArray = @[@"当你的 app 没有提供 3x 的LaunchImage 时。然后等比例拉伸屏幕宽度返回 320;然后等比例拉伸到大屏。这种情况下对界面不会产生任小。但是建议不要长期处于这种模式下,否则在大屏上会显得字大,内容少,容易遭到用户投诉。但是建议不要长期处于这种模式下,否则在大屏上会显得字大,内容少,容易遭到用户投诉。", + @"然后等比例拉伸到大屏。屏幕宽度返回 320否则在大屏上会显得字大长期处于这种模式下,否则在大屏上会显得字大,内容少这种情况下对界面不会", + @"长期处于这种模式下,否则在大屏上会显得字大,内容少这种情况下对界面不会但是建议不要长期处于这种模式下,否则在大屏上会显得字大,内容少,容易遭到用户投诉。", + @"但是建议不要长期处于这种模式下,否则在大屏上会显得字大,内容少,容易遭到用户投诉。", + @"屏幕宽度返回 320;然后等比例拉伸到大屏。这种情况下对界面不会产生任小。但是建议不要长期处于这种模式下,否则在大屏上会显得字大,内容少,容易遭到用户投诉。但是建议不要长期处于这种模式下,否则在大屏上会显得字大,内容少,容易遭到用户投诉。但是建议不要长期处于这种模式下,否则在大屏上会显得字大,内容少,容易遭到用户投诉。" + ]; + + for (int i = 0; i < count; i++) { + + int nameRandomIndex = arc4random_uniform(5); + + DemoVC7Model *model = [DemoVC7Model new]; + model.title = textArray[nameRandomIndex]; + model.iconImagePath = iconImageNamesArray[arc4random_uniform(9)]; + + // 模拟“有或者无图片” + int random = arc4random_uniform(1); + if (random <= 8) { + NSMutableArray *temp = [NSMutableArray new]; + + int randomImagesCount = arc4random_uniform(9); + for (int i = 0; i < randomImagesCount; i++) { + int randomIndex = arc4random_uniform(9); + NSString *text = iconImageNamesArray[randomIndex]; + [temp addObject:text]; + } + + model.imagePathsArray = [temp copy]; + } + + [self.modelsArray addObject:model]; + } +} + + +#pragma mark - Table view data source + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return self.modelsArray.count; +} + + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + DemoVC14Cell *cell = [tableView dequeueReusableCellWithIdentifier:@"DemoVC14Cell"]; + + cell.model = self.modelsArray[indexPath.row]; + + return cell; +} + +#pragma mark - Table view delegate + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + DemoVC7Model *demoVC14Modelodel = self.modelsArray[indexPath.row]; + + return [self.tableView cellHeightForIndexPath:indexPath model:demoVC14Modelodel keyPath:@"model" cellClass:[DemoVC14Cell class] contentViewWidth:[self cellContentViewWith]]; + + /* + * 第二种返回cell高度的方法,此方法不需要设置keyPath,适合一个cell与多个model的情况 + + AutoCellHeightDataSettingBlock dataSetting = ^(UITableViewCell *cell) { + DemoVC14Cell *myCell = (DemoVC14Cell *)cell; + myCell.model = demoVC14Modelodel; + }; + + return [self.tableView cellHeightForIndexPath:indexPath + cellClass:[DemoVC14Cell class] + cellContentViewWidth:[UIScreen mainScreen].bounds.size.width cellDataSetting:dataSetting]; + */ +} + +- (CGFloat)cellContentViewWith +{ + CGFloat width = [UIScreen mainScreen].bounds.size.width; + + // 适配ios7横屏 + if ([UIApplication sharedApplication].statusBarOrientation != UIInterfaceOrientationPortrait && [[UIDevice currentDevice].systemVersion floatValue] < 8) { + width = [UIScreen mainScreen].bounds.size.height; + } + return width; +} + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC14/DemoVC14Cell.h b/SDAutoLayoutDemo/DemoVC/DemoVC14/DemoVC14Cell.h new file mode 100644 index 0000000..e4b715c --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC14/DemoVC14Cell.h @@ -0,0 +1,19 @@ +// +// DemoVC14Cell.h +// SDAutoLayoutDemo +// +// Created by gsd on 16/5/12. +// Copyright © 2016年 gsd. All rights reserved. +// + +#import +#import "DemoVC7Model.h" + +@interface DemoVC14Cell : UITableViewCell + +@property (weak, nonatomic) IBOutlet UIImageView *iconView; +@property (weak, nonatomic) IBOutlet UILabel *contentLabel; + +@property (nonatomic, strong) DemoVC7Model *model; + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC14/DemoVC14Cell.m b/SDAutoLayoutDemo/DemoVC/DemoVC14/DemoVC14Cell.m new file mode 100644 index 0000000..067a465 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC14/DemoVC14Cell.m @@ -0,0 +1,89 @@ +// +// DemoVC14Cell.m +// SDAutoLayoutDemo +// +// Created by gsd on 16/5/12. +// Copyright © 2016年 gsd. All rights reserved. +// + +#import "DemoVC14Cell.h" +#import "UIView+SDAutoLayout.h" + +#import "PhotosContainerView.h" + +@implementation DemoVC14Cell +{ + PhotosContainerView *_photosContainer; +} + +- (void)awakeFromNib { + + [super awakeFromNib]; + + self.contentView.backgroundColor = [UIColor colorWithRed:0.98 green:0.98 blue:0.98 alpha:1]; + + PhotosContainerView *photosContainer = [[PhotosContainerView alloc] initWithMaxItemsCount:9]; + _photosContainer = photosContainer; + [self.contentView addSubview:photosContainer]; + + + self.contentLabel.font = [UIFont systemFontOfSize:15]; + self.contentLabel.textColor = [UIColor grayColor]; + + self.iconView.sd_layout + .leftSpaceToView(self.contentView, 10) + .topSpaceToView(self.contentView, 10) + .widthIs(50) + .heightEqualToWidth(); + + self.contentLabel.sd_layout + .leftSpaceToView(self.iconView, 10) + .topEqualToView(self.iconView) + .rightSpaceToView(self.contentView, 10) + .autoHeightRatio(0); + + /* + // 如果你需要限制最多显示多少行文字可以这样做 + [self.contentLabel setMaxNumberOfLinesToShow:6]; + */ + + _photosContainer.sd_layout + .leftEqualToView(self.contentLabel) + .rightEqualToView(self.contentLabel) + .topSpaceToView(self.contentLabel, 10); // 高度自适应了,不需要再设置约束 + + + + +} + + +- (void)setModel:(DemoVC7Model *)model +{ + _model = model; + + _contentLabel.text = model.title; + _iconView.image = [UIImage imageNamed:model.iconImagePath]; + + + UIView *bottomView = _contentLabel; + + _photosContainer.photoNamesArray = model.imagePathsArray; + if (model.imagePathsArray.count > 0) { + _photosContainer.hidden = NO; + bottomView = _photosContainer; + } else { + _photosContainer.hidden = YES; + } + + + [self setupAutoHeightWithBottomView:bottomView bottomMargin:10]; // 如果你不能确定具体哪个view会在contentview的最底部,你可以把所有可能的view都包装在一个数组里面传过去,对应的方法为 [self setupAutoHeightWithBottomViewsArray:<#(NSArray *)#> bottomMargin:<#(CGFloat)#>] +} + +- (void)setSelected:(BOOL)selected animated:(BOOL)animated { + [super setSelected:selected animated:animated]; + + // Configure the view for the selected state +} + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC14/DemoVC14Cell.xib b/SDAutoLayoutDemo/DemoVC/DemoVC14/DemoVC14Cell.xib new file mode 100644 index 0000000..10a0d99 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC14/DemoVC14Cell.xib @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC14/PhotosContainerView.h b/SDAutoLayoutDemo/DemoVC/DemoVC14/PhotosContainerView.h new file mode 100644 index 0000000..e02d943 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC14/PhotosContainerView.h @@ -0,0 +1,19 @@ +// +// PhotosContainerView.h +// SDAutoLayoutDemo +// +// Created by gsd on 16/5/13. +// Copyright © 2016年 gsd. All rights reserved. +// + +#import + +@interface PhotosContainerView : UIView + +- (instancetype)initWithMaxItemsCount:(NSInteger)count; + +@property (nonatomic, strong) NSArray *photoNamesArray; + +@property (nonatomic, assign) NSInteger maxItemsCount; + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC14/PhotosContainerView.m b/SDAutoLayoutDemo/DemoVC/DemoVC14/PhotosContainerView.m new file mode 100644 index 0000000..d82a9c1 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC14/PhotosContainerView.m @@ -0,0 +1,62 @@ +// +// PhotosContainerView.m +// SDAutoLayoutDemo +// +// Created by gsd on 16/5/13. +// Copyright © 2016年 gsd. All rights reserved. +// + +#import "PhotosContainerView.h" +#import "UIView+SDAutoLayout.h" + +@implementation PhotosContainerView +{ + NSMutableArray *_imageViewsArray; +} + +- (instancetype)initWithMaxItemsCount:(NSInteger)count +{ + if (self = [super init]) { + self.maxItemsCount = count; + } + return self; +} + +- (void)setPhotoNamesArray:(NSArray *)photoNamesArray +{ + _photoNamesArray = photoNamesArray; + + if (!_imageViewsArray) { + _imageViewsArray = [NSMutableArray new]; + } + + int needsToAddItemsCount = (int)(_photoNamesArray.count - _imageViewsArray.count); + + if (needsToAddItemsCount > 0) { + for (int i = 0; i < needsToAddItemsCount; i++) { + UIImageView *imageView = [UIImageView new]; + [self addSubview:imageView]; + [_imageViewsArray addObject:imageView]; + } + } + + NSMutableArray *temp = [NSMutableArray new]; + + [_imageViewsArray enumerateObjectsUsingBlock:^(UIImageView *imageView, NSUInteger idx, BOOL *stop) { + if (idx < _photoNamesArray.count) { + imageView.hidden = NO; + imageView.sd_layout.autoHeightRatio(1); + imageView.image = [UIImage imageNamed:_photoNamesArray[idx]]; + [temp addObject:imageView]; + } else { + [imageView sd_clearAutoLayoutSettings]; + imageView.hidden = YES; + } + }]; + + [self setupAutoWidthFlowItems:[temp copy] withPerRowItemsCount:3 verticalMargin:10 horizontalMargin:10 verticalEdgeInset:0 horizontalEdgeInset:0]; +} + + + +@end diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC2.h" b/SDAutoLayoutDemo/DemoVC/DemoVC2/DemoVC2.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC2.h" rename to SDAutoLayoutDemo/DemoVC/DemoVC2/DemoVC2.h diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC2/DemoVC2.m b/SDAutoLayoutDemo/DemoVC/DemoVC2/DemoVC2.m new file mode 100644 index 0000000..299b4fe --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC2/DemoVC2.m @@ -0,0 +1,173 @@ +// +// DemoVC2.m +// SDAutoLayout 测试 Demo +// +// Created by gsd on 15/10/12. +// Copyright (c) 2015年 gsd. All rights reserved. +// + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * QQ : 2689718696(gsdios) * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * * + ********************************************************************************* + + */ + +#import "DemoVC2.h" + +@implementation DemoVC2 +{ + UIButton *_centerButton; + UIView *_autoWidthViewsContainer; + UIView *_autoMarginViewsContainer; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + // 设置自定义图片和label位置的button + [self setupCustomButton]; + + // 设置一排固定间距自动宽度子view + [self setupAutoWidthViewsWithCount:4 margin:10]; + + // 设置一排固定宽度自动间距子view + [self setupAutoMarginViewsWithCount:4 itemWidth:80]; +} + +// 设置自定义图片和label位置的button +- (void)setupCustomButton +{ + _centerButton = [UIButton new]; + _centerButton.accessibilityIdentifier = @"demo2.customButton"; + _centerButton.backgroundColor = [UIColor orangeColor]; + [_centerButton setTitle:@"自定义Button" forState:UIControlStateNormal]; + [_centerButton setImage:[UIImage imageNamed:@"7.jpg"] forState:UIControlStateNormal]; + _centerButton.titleLabel.backgroundColor = [UIColor lightGrayColor]; + _centerButton.titleLabel.textAlignment = NSTextAlignmentCenter; + _centerButton.imageView.contentMode = UIViewContentModeScaleAspectFit; + [self.view addSubview:_centerButton]; + + + _centerButton.sd_layout + .centerXEqualToView(self.view) + .topSpaceToView(self.view, 10) + .widthRatioToView(self.view, 0.5) + .heightIs(120); + + + + // 设置button的图片的约束 + _centerButton.imageView.sd_layout + .widthRatioToView(_centerButton, 0.8) + .topSpaceToView(_centerButton, 10) + .centerXEqualToView(_centerButton) + .heightRatioToView(_centerButton, 0.6); + + // 设置button的label的约束 + _centerButton.titleLabel.sd_layout + .topSpaceToView(_centerButton.imageView, 10) + .leftEqualToView(_centerButton.imageView) + .rightEqualToView(_centerButton.imageView) + .bottomSpaceToView(_centerButton, 10); +} + +// 设置一排固定间距自动宽度子view +- (void)setupAutoWidthViewsWithCount:(NSInteger)count margin:(CGFloat)margin +{ + _autoWidthViewsContainer = [UIView new]; + _autoWidthViewsContainer.isAccessibilityElement = YES; + _autoWidthViewsContainer.accessibilityIdentifier = @"demo2.autoWidthContainer"; + _autoWidthViewsContainer.backgroundColor = [UIColor greenColor]; + [self.view addSubview:_autoWidthViewsContainer]; + + + NSMutableArray *temp = [NSMutableArray new]; + for (int i = 0; i < count; i++) { + UIView *view = [UIView new]; + view.backgroundColor = [UIColor orangeColor]; + [_autoWidthViewsContainer addSubview:view]; + view.sd_layout.autoHeightRatio(0.4); // 设置高度约束 + [temp addObject:view]; + } + + _autoWidthViewsContainer.sd_layout + .leftSpaceToView(self.view, 10) + .rightSpaceToView(self.view, 10) + .topSpaceToView(_centerButton, 10); + + // 此步设置之后_autoWidthViewsContainer的高度可以根据子view自适应 + [_autoWidthViewsContainer setupAutoWidthFlowItems:[temp copy] withPerRowItemsCount:4 verticalMargin:margin horizontalMargin:margin verticalEdgeInset:5 horizontalEdgeInset:10]; + +} + +// 设置一排固定宽度自动间距子view +- (void)setupAutoMarginViewsWithCount:(NSInteger)count itemWidth:(CGFloat)itemWidth +{ + _autoMarginViewsContainer = [UIView new]; + _autoMarginViewsContainer.isAccessibilityElement = YES; + _autoMarginViewsContainer.accessibilityIdentifier = @"demo2.autoMarginContainer"; + _autoMarginViewsContainer.backgroundColor = [UIColor blueColor]; + [self.view addSubview:_autoMarginViewsContainer]; + + NSMutableArray *temp = [NSMutableArray new]; + for (int i = 0; i < count; i++) { + UIView *view = [UIView new]; + view.backgroundColor = [UIColor orangeColor]; + [_autoMarginViewsContainer addSubview:view]; + view.sd_layout.autoHeightRatio(0.5); // 设置高度约束 + [temp addObject:view]; + } + + // 此步设置之后_autoMarginViewsContainer的高度可以根据子view自适应 + [_autoMarginViewsContainer setupAutoMarginFlowItems:[temp copy] withPerRowItemsCount:3 itemWidth:itemWidth verticalMargin:10 verticalEdgeInset:4 horizontalEdgeInset:10]; + + _autoMarginViewsContainer.sd_layout + .leftSpaceToView(self.view, 10) + .rightSpaceToView(self.view, 10) + .topSpaceToView(_autoWidthViewsContainer, 10); +} + + + + + + + + +/* + + // ------------------设置3个水平等宽子view---------------- + + self.view.sd_equalWidthSubviews = @[self.view0, self.view1, self.view2]; + + self.view0.sd_layout + .leftSpaceToView(self.view, 0) // 左边距父view为0 + .topSpaceToView(self.view, 100) // 上边距离父view为100 + .heightEqualToWidth(); // 高度等于自身宽度 + + self.view1.sd_layout + .leftSpaceToView(self.view0, 0) // 左边距离view0为0 + .topEqualToView(self.view0) // top和view0相同 + .heightEqualToWidth(); // 高度等于自身宽度 + + self.view2.sd_layout + .leftSpaceToView(self.view1, 0) // 左边距离view1为0 + .topEqualToView(self.view1) // top和view1相同 + .rightSpaceToView(self.view, 0) // 右边距离父view为0 + .heightEqualToWidth(); // 高度等于自身宽度 + + // ------------------------------------------------------ + + */ + +@end diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC3.h" b/SDAutoLayoutDemo/DemoVC/DemoVC3/DemoVC3.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC3.h" rename to SDAutoLayoutDemo/DemoVC/DemoVC3/DemoVC3.h diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC3.m" b/SDAutoLayoutDemo/DemoVC/DemoVC3/DemoVC3.m similarity index 87% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC3.m" rename to SDAutoLayoutDemo/DemoVC/DemoVC3/DemoVC3.m index 4d0d43a..21323ce 100644 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC3.m" +++ b/SDAutoLayoutDemo/DemoVC/DemoVC3/DemoVC3.m @@ -23,7 +23,7 @@ #import "DemoVC3.h" #import "TestCell2.h" -#import "SDRefeshView/SDRefresh.h" +#import "SDRefresh.h" #import "UITableView+SDAutoTableViewCellHeight.h" @@ -37,6 +37,8 @@ - (void)viewDidLoad { [super viewDidLoad]; + self.tableView.accessibilityIdentifier = @"demo.3.table"; + _rowCount = (long)10; NSArray *textArray = @[@"当你的 app 没有提供 3x 的 LaunchImage 时,系统默认进入兼容模式,大屏幕一切按照 320 宽度渲染,屏幕宽度返回 320;然后等比例拉伸到大屏。这种情况下对界面不会产生任何影响,等于把小屏完全拉伸。", @@ -64,11 +66,6 @@ - (void)viewDidLoad - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - // >>>>>>>>>>>>>>>>>>>>> * cell自适应步骤1 * >>>>>>>>>>>>>>>>>>>>>>>> - - [self.tableView startAutoCellHeightWithCellClass:[TestCell2 class] contentViewWidth:[UIScreen mainScreen].bounds.size.width]; - - return _rowCount; } @@ -91,7 +88,21 @@ - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPa // >>>>>>>>>>>>>>>>>>>>> * cell自适应步骤2 * >>>>>>>>>>>>>>>>>>>>>>>> /* model 为模型实例, keyPath 为 model 的属性名,通过 kvc 统一赋值接口 */ - return [self.tableView cellHeightForIndexPath:indexPath model:str keyPath:@"text"]; + return [self.tableView cellHeightForIndexPath:indexPath model:str keyPath:@"text" cellClass:[TestCell2 class] contentViewWidth:[self cellContentViewWith]]; +} + + + + +- (CGFloat)cellContentViewWith +{ + CGFloat width = [UIScreen mainScreen].bounds.size.width; + + // 适配ios7横屏 + if ([UIApplication sharedApplication].statusBarOrientation != UIInterfaceOrientationPortrait && [[UIDevice currentDevice].systemVersion floatValue] < 8) { + width = [UIScreen mainScreen].bounds.size.height; + } + return width; } @end diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/TestCell2.h" b/SDAutoLayoutDemo/DemoVC/DemoVC3/TestCell2.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/TestCell2.h" rename to SDAutoLayoutDemo/DemoVC/DemoVC3/TestCell2.h diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/TestCell2.m" b/SDAutoLayoutDemo/DemoVC/DemoVC3/TestCell2.m similarity index 93% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/TestCell2.m" rename to SDAutoLayoutDemo/DemoVC/DemoVC3/TestCell2.m index 5db71aa..7035f92 100644 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/TestCell2.m" +++ b/SDAutoLayoutDemo/DemoVC/DemoVC3/TestCell2.m @@ -66,13 +66,9 @@ - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSStr UIView *view5 = [UIView new]; view5.backgroundColor = [UIColor yellowColor]; _view5 = view5; + - [self.contentView addSubview:view0]; - [self.contentView addSubview:view1]; - [self.contentView addSubview:view2]; - [self.contentView addSubview:view3]; - [self.contentView addSubview:view4]; - [self.contentView addSubview:view5]; + [self.contentView sd_addSubviews:@[view0, view1, view2, view3, view4, view5]]; diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC4.h" b/SDAutoLayoutDemo/DemoVC/DemoVC4/DemoVC4.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC4.h" rename to SDAutoLayoutDemo/DemoVC/DemoVC4/DemoVC4.h diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC4/DemoVC4.m b/SDAutoLayoutDemo/DemoVC/DemoVC4/DemoVC4.m new file mode 100644 index 0000000..4484273 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC4/DemoVC4.m @@ -0,0 +1,113 @@ +// +// DemoVC4.m +// SDAutoLayout 测试 Demo +// +// Created by gsd on 15/10/16. +// Copyright (c) 2015年 gsd. All rights reserved. +// + + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * QQ : 2689718696(gsdios) * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * * + ********************************************************************************* + + */ + +#import "DemoVC4.h" + +@implementation DemoVC4 +{ + UILabel *_label; +} + + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + +// --------- attributedString测试:行间距为8 --------------------------- + + + + + UILabel *label = [UILabel new]; + label.accessibilityIdentifier = @"demo4.attributedLabel"; + [self.view addSubview:label]; + _label = label; + [self setupAttributedStringForTestLabel]; + + label.sd_layout + .leftSpaceToView(self.view, 10) + .rightSpaceToView(self.view, 10) + .topSpaceToView(self.view, 0) + .autoHeightRatio(0); + + // 标注lable的text为attributedString + label.isAttributedContent = YES; + + UIButton *button = [UIButton new]; + button.accessibilityIdentifier = @"demo4.refreshButton"; + [button setTitle:@"点我随机刷新文字" forState:UIControlStateNormal]; + button.backgroundColor = [[UIColor redColor] colorWithAlphaComponent:0.8]; + [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + [button addTarget:self action:@selector(onClickButton) forControlEvents:UIControlEventTouchUpInside]; + [self.view addSubview:button]; + + button.sd_layout + .leftEqualToView(self.view) + .rightEqualToView(self.view) + .topSpaceToView(label, 20) + .heightIs(30); + +// --------- attributedString测试:行间距为8 --------------------------- + +} + +- (void)onClickButton +{ + [self setupAttributedStringForTestLabel]; + [_label updateLayout]; +} + +- (void)setupAttributedStringForTestLabel +{ + NSString *text = @"富文本高度自适应demo\nattributedString测试:行间距为8。彩虹网络卡福利费绿调查开房;卡法看得出来分开了的出口来反馈率打开了房;快烦死了;了; 调查开房;;v单纯考虑分离开都快来反馈来看发v离开的积分房积分jdhflgfkkvvm.cm。attributedString测试:行间距为8。彩虹网络卡福利费绿调查开房;卡法看得出来分开了的出口来反馈率打开了房;"; + NSMutableParagraphStyle * paragraphStyle = [[NSMutableParagraphStyle alloc] init]; + [paragraphStyle setLineSpacing:8]; + UIColor *color = [UIColor blackColor]; + NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:text attributes:@{NSForegroundColorAttributeName : color, NSParagraphStyleAttributeName: paragraphStyle}]; + + NSInteger length = text.length; + int loc = 0; + while (length > 8) { + int random = arc4random_uniform(10); + if (loc + random < text.length) { + [string addAttribute:NSFontAttributeName + value:[UIFont systemFontOfSize:(10 + arc4random_uniform(20))] + range:NSMakeRange(loc, random)]; + + [string addAttribute:NSForegroundColorAttributeName + value:[UIColor colorWithRed:(arc4random_uniform(255) / 255.0) + green:(arc4random_uniform(255) / 255.0) + blue:(arc4random_uniform(255) / 255.0) + alpha:(arc4random_uniform(255) / 255.0) + 0.2] + range:NSMakeRange(loc, random)]; + } + loc += random; + length -= random; + } + + _label.attributedText = string; +} + +@end diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC5.h" b/SDAutoLayoutDemo/DemoVC/DemoVC5/DemoVC5.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC5.h" rename to SDAutoLayoutDemo/DemoVC/DemoVC5/DemoVC5.h diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC5.m" b/SDAutoLayoutDemo/DemoVC/DemoVC5/DemoVC5.m similarity index 65% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC5.m" rename to SDAutoLayoutDemo/DemoVC/DemoVC5/DemoVC5.m index e3eca35..82f0e17 100644 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC5.m" +++ b/SDAutoLayoutDemo/DemoVC/DemoVC5/DemoVC5.m @@ -26,10 +26,14 @@ #import "DemoVC5CellTableViewCell.h" -#import "SDRefeshView/SDRefresh.h" +#import "SDRefresh.h" #import "UITableView+SDAutoTableViewCellHeight.h" +#import "SDCycleScrollView.h" + +#import "UIView+SDAutoLayout.h" + @interface DemoVC5 () @property (nonatomic, strong) NSMutableArray *modelsArray; @@ -45,7 +49,11 @@ - (void)viewDidLoad { [super viewDidLoad]; - self.tableView.estimatedRowHeight = 100; +// self.tableView.estimatedRowHeight = 100; + + self.tableView.accessibilityIdentifier = @"demo.5.table"; + + [self setupHeaderView]; [self creatModelsWithCount:10]; @@ -64,6 +72,58 @@ - (void)viewDidLoad }; } +- (void)setupHeaderView +{ + UIView *header = [UIView new]; + + // 由于tableviewHeaderView的特殊性,在使他高度自适应之前你最好先给它设置一个宽度 + header.width = [UIScreen mainScreen].bounds.size.width; + + NSArray *picImageNamesArray = @[ @"pic1.jpg", + @"pic2.jpg", + @"pic3.jpg", + @"pic4.jpg", + ]; + + SDCycleScrollView *scrollView = [SDCycleScrollView new]; + scrollView.localizationImageNamesGroup = picImageNamesArray; + [header addSubview:scrollView]; + + UILabel *tagLabel = [UILabel new]; + tagLabel.accessibilityIdentifier = @"demo5.header.dateLabel"; + tagLabel.font = [UIFont systemFontOfSize:13]; + tagLabel.textColor = [UIColor lightGrayColor]; + tagLabel.text = @"更新时间:2016.01.08"; + [header addSubview:tagLabel]; + + UIView *bottomLine = [UIView new]; + bottomLine.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.3]; + [header addSubview:bottomLine]; + + scrollView.sd_layout + .leftSpaceToView(header, 0) + .topSpaceToView(header, 0) + .rightSpaceToView(header, 0) + .heightIs(150); + + tagLabel.sd_layout + .leftSpaceToView(header, 10) + .topSpaceToView(scrollView, 0) + .heightIs(25) + .rightSpaceToView(header, 0); + + bottomLine.sd_layout + .topSpaceToView(tagLabel, 0) + .leftSpaceToView(header, 0) + .rightSpaceToView(header, 0) + .heightIs(1); + + [header setupAutoHeightWithBottomView:bottomLine bottomMargin:0]; + [header layoutSubviews]; + + self.tableView.tableHeaderView = header; +} + - (void)creatModelsWithCount:(NSInteger)count { if (!_modelsArray) { @@ -121,11 +181,6 @@ - (void)creatModelsWithCount:(NSInteger)count - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - // >>>>>>>>>>>>>>>>>>>>> * cell自适应步骤1 * >>>>>>>>>>>>>>>>>>>>>>>> - - [self.tableView startAutoCellHeightWithCellClass:[DemoVC5CellTableViewCell class] contentViewWidth:[UIScreen mainScreen].bounds.size.width]; - - return self.modelsArray.count; } @@ -137,14 +192,43 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N cell = [[DemoVC5CellTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID]; } cell.model = self.modelsArray[indexPath.row]; + + ////// 此步设置用于实现cell的frame缓存,可以让tableview滑动更加流畅 ////// + + [cell useCellFrameCacheWithIndexPath:indexPath tableView:tableView]; + + /////////////////////////////////////////////////////////////////////// + return cell; } +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + [self.modelsArray removeObjectAtIndex:indexPath.row]; + [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; +} + - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { // >>>>>>>>>>>>>>>>>>>>> * cell自适应步骤2 * >>>>>>>>>>>>>>>>>>>>>>>> /* model 为模型实例, keyPath 为 model 的属性名,通过 kvc 统一赋值接口 */ - return [self.tableView cellHeightForIndexPath:indexPath model:self.modelsArray[indexPath.row] keyPath:@"model"]; + + return [self.tableView cellHeightForIndexPath:indexPath model:self.modelsArray[indexPath.row] keyPath:@"model" cellClass:[DemoVC5CellTableViewCell class] contentViewWidth:[self cellContentViewWith]]; +} + + + + + +- (CGFloat)cellContentViewWith +{ + CGFloat width = [UIScreen mainScreen].bounds.size.width; + + // 适配ios7横屏 + if ([UIApplication sharedApplication].statusBarOrientation != UIInterfaceOrientationPortrait && [[UIDevice currentDevice].systemVersion floatValue] < 8) { + width = [UIScreen mainScreen].bounds.size.height; + } + return width; } diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC5CellTableViewCell.h" b/SDAutoLayoutDemo/DemoVC/DemoVC5/DemoVC5CellTableViewCell.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC5CellTableViewCell.h" rename to SDAutoLayoutDemo/DemoVC/DemoVC5/DemoVC5CellTableViewCell.h diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC5CellTableViewCell.m" b/SDAutoLayoutDemo/DemoVC/DemoVC5/DemoVC5CellTableViewCell.m similarity index 92% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC5CellTableViewCell.m" rename to SDAutoLayoutDemo/DemoVC/DemoVC5/DemoVC5CellTableViewCell.m index d8f0392..afa0116 100644 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC5CellTableViewCell.m" +++ b/SDAutoLayoutDemo/DemoVC/DemoVC5/DemoVC5CellTableViewCell.m @@ -58,6 +58,7 @@ - (void)setup UILabel *view2 = [UILabel new]; view2.textColor = [UIColor grayColor]; view2.font = [UIFont systemFontOfSize:16]; + view2.numberOfLines = 0; _view2 = view2; UIImageView *view3 = [UIImageView new]; @@ -71,11 +72,7 @@ - (void)setup view4.font = [UIFont systemFontOfSize:13]; _view4 = view4; - [self.contentView addSubview:view0]; - [self.contentView addSubview:view1]; - [self.contentView addSubview:view2]; - [self.contentView addSubview:view3]; - [self.contentView addSubview:view4]; + [self.contentView sd_addSubviews:@[view0, view1, view2, view3, view4]]; _view0.sd_layout @@ -105,11 +102,11 @@ - (void)setup .centerYEqualToView(_view1) .heightIs(14); - [_view4 setSigleLineAutoResizeWithMaxWidth:50]; + [_view4 setSingleLineAutoResizeWithMaxWidth:50]; _view0.sd_cornerRadiusFromWidthRatio = @(0.5); - [_view1 setSigleLineAutoResizeWithMaxWidth:200]; + [_view1 setSingleLineAutoResizeWithMaxWidth:200]; } @@ -118,7 +115,9 @@ - (void)setModel:(DemoVC5Model *)model _model = model; _view0.image = [UIImage imageNamed:model.iconName]; + _view1.text = model.name; + _view2.text = model.content; CGFloat bottomMargin = 0; diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC5Model.h" b/SDAutoLayoutDemo/DemoVC/DemoVC5/DemoVC5Model.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC5Model.h" rename to SDAutoLayoutDemo/DemoVC/DemoVC5/DemoVC5Model.h diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC5Model.m" b/SDAutoLayoutDemo/DemoVC/DemoVC5/DemoVC5Model.m similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC5Model.m" rename to SDAutoLayoutDemo/DemoVC/DemoVC5/DemoVC5Model.m diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC6.h" b/SDAutoLayoutDemo/DemoVC/DemoVC6/DemoVC6.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC6.h" rename to SDAutoLayoutDemo/DemoVC/DemoVC6/DemoVC6.h diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC6.m" b/SDAutoLayoutDemo/DemoVC/DemoVC6/DemoVC6.m similarity index 75% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC6.m" rename to SDAutoLayoutDemo/DemoVC/DemoVC6/DemoVC6.m index bfa027f..49bc10d 100644 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC6.m" +++ b/SDAutoLayoutDemo/DemoVC/DemoVC6/DemoVC6.m @@ -20,17 +20,10 @@ - (void)viewDidLoad { [super viewDidLoad]; UIScrollView *scrollView = [UIScrollView new]; + scrollView.accessibilityIdentifier = @"demo.scroll.main"; [self.view addSubview:scrollView]; - [scrollView addSubview:self.view0]; - [scrollView addSubview:self.view1]; - [scrollView addSubview:self.view2]; - [scrollView addSubview:self.view3]; - [scrollView addSubview:self.view4]; - [scrollView addSubview:self.view5]; - [scrollView addSubview:self.view6]; - [scrollView addSubview:self.view7]; - [scrollView addSubview:self.view8]; + [scrollView sd_addSubviews:@[self.view0, self.view1, self.view2, self.view3, self.view4, self.view5, self.view6, self.view7, self.view8]]; scrollView.sd_layout.spaceToSuperView(UIEdgeInsetsZero); @@ -55,7 +48,7 @@ - (void)viewDidLoad { self.view3.sd_layout .widthIs(250) - .heightIs(250) + .heightEqualToWidth() .centerXEqualToView(scrollView) .topSpaceToView(self.view2, 20); @@ -63,7 +56,7 @@ - (void)viewDidLoad { [scrollView setupAutoContentSizeWithBottomView:self.view3 bottomMargin:20]; // 设置圆角 - self.view0.sd_cornerRadiusFromHeightRatio = @(0.5); + self.view0.sd_cornerRadiusFromHeightRatio = @(0.5); // 设置view0的圆角半径为自身高度的0.5倍 self.view1.sd_cornerRadiusFromWidthRatio = @(0.5); self.view2.sd_cornerRadiusFromWidthRatio = @(0.5); } diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7.h" b/SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7.h" rename to SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7.h diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7.m b/SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7.m new file mode 100644 index 0000000..af212f5 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7.m @@ -0,0 +1,179 @@ +// +// DemoVC7.m +// SDAutoLayout 测试 Demo +// +// Created by gsd on 15/12/17. +// Copyright © 2015年 gsd. All rights reserved. +// + +#import "DemoVC7.h" + +#import "DemoVC7Model.h" + +#import "DemoVC7Cell.h" +#import "DemoVC7Cell2.h" + +#import "UITableView+SDAutoTableViewCellHeight.h" + +#import "UIView+SDAutoLayout.h" + +#import "SDRefresh.h" + + + +@interface DemoVC7 () + +@property (nonatomic, strong) NSMutableArray *modelsArray; + +@end + +@implementation DemoVC7 +{ + SDRefreshFooterView *_refreshFooter; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + +// self.tableView.estimatedRowHeight = 100; + + self.tableView.accessibilityIdentifier = @"demo.7.table"; + + [self creatModelsWithCount:18]; + + [self.tableView registerClass:[DemoVC7Cell class] forCellReuseIdentifier:NSStringFromClass([DemoVC7Cell class])]; + [self.tableView registerClass:[DemoVC7Cell2 class] forCellReuseIdentifier:NSStringFromClass([DemoVC7Cell2 class])]; + + __weak typeof(self) weakSelf = self; + + // 上拉加载 + _refreshFooter = [SDRefreshFooterView refreshView]; + [_refreshFooter addToScrollView:self.tableView]; + __weak typeof(_refreshFooter) weakRefreshFooter = _refreshFooter; + _refreshFooter.beginRefreshingOperation = ^() { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [weakSelf creatModelsWithCount:10]; + [weakSelf.tableView reloadData]; + [weakRefreshFooter endRefreshing]; + }); + }; + +} + +- (void)creatModelsWithCount:(NSInteger)count +{ + if (!_modelsArray) { + _modelsArray = [NSMutableArray new]; + } + + NSArray *iconImageNamesArray = @[@"icon0.jpg", + @"icon1.jpg", + @"icon2.jpg", + @"icon3.jpg", + @"icon4.jpg", + ]; + + + NSArray *textArray = @[@"当你的 app 没有提供 3x 的LaunchImage 时。然后等比例拉伸", + @"然后等比例拉伸到大屏。屏幕宽度返回 320否则在大屏上会显得字大", + @"长期处于这种模式下,否则在大屏上会显得字大,内容少这种情况下对界面不会", + @"但是建议不要长期处于这种模式下,否则在大屏上会显得字大,内容少,容易遭到用户投诉。", + @"屏幕宽度返回 320;然后等比例拉伸到大屏。这种情况下对界面不会产生任小。但是建议不要长期处于这种模式下,否则在大屏上会显得字大,内容少,容易遭到用户投诉。但是建议不要长期处于这种模式下,否则在大屏上会显得字大,内容少,容易遭到用户投诉。" + ]; + + for (int i = 0; i < count; i++) { + int iconRandomIndex = arc4random_uniform(5); + int nameRandomIndex = arc4random_uniform(5); + + DemoVC7Model *model = [DemoVC7Model new]; + model.title = textArray[nameRandomIndex]; + + + // 模拟“有或者无图片” + int random = arc4random_uniform(100); + if (random <= 30) { + NSMutableArray *temp = [NSMutableArray new]; + + for (int i = 0; i < 3; i++) { + int randomIndex = arc4random_uniform(5); + NSString *text = iconImageNamesArray[randomIndex]; + [temp addObject:text]; + } + + model.imagePathsArray = [temp copy]; + } else { + model.imagePathsArray = @[iconImageNamesArray[iconRandomIndex]]; + } + + [self.modelsArray addObject:model]; + } +} + +#pragma mark - Table view data source + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + /* + 普通版也可实现一步设置搞定高度自适应,不再推荐使用此套方法,具体参看“UITableView+SDAutoTableViewCellHeight”头文件 + [self.tableView startAutoCellHeightWithCellClasses:@[[DemoVC7Cell class], [DemoVC7Cell2 class]] contentViewWidth:[UIScreen mainScreen].bounds.size.width]; + */ + return self.modelsArray.count; +} + + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + + Class currentClass = [DemoVC7Cell class]; + DemoVC7Cell *cell = nil; + + DemoVC7Model *model = self.modelsArray[indexPath.row]; + + if (model.imagePathsArray.count > 1) { + currentClass = [DemoVC7Cell2 class]; + } + cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass(currentClass)]; + + cell.model = model; + + ////// 此步设置用于实现cell的frame缓存,可以让tableview滑动更加流畅 ////// + +// [cell useCellFrameCacheWithIndexPath:indexPath tableView:tableView]; + + /////////////////////////////////////////////////////////////////////// + + return cell; +} + + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + Class currentClass = [DemoVC7Cell class]; + + DemoVC7Model *model = self.modelsArray[indexPath.row]; + + if (model.imagePathsArray.count > 1) { + currentClass = [DemoVC7Cell2 class]; + } + /* + 普通版也可实现一步设置搞定高度自适应,不再推荐使用此套方法,具体参看“UITableView+SDAutoTableViewCellHeight”头文件 + return [self.tableView cellHeightForIndexPath:indexPath model:model keyPath:@"model" cellClass:currentClass]; + */ + + // 推荐使用此普通简化版方法(一步设置搞定高度自适应,性能好,易用性好) + return [self.tableView cellHeightForIndexPath:indexPath model:model keyPath:@"model" cellClass:currentClass contentViewWidth:[self cellContentViewWith]]; +} + + + + +- (CGFloat)cellContentViewWith +{ + CGFloat width = [UIScreen mainScreen].bounds.size.width; + + // 适配ios7横屏 + if ([UIApplication sharedApplication].statusBarOrientation != UIInterfaceOrientationPortrait && [[UIDevice currentDevice].systemVersion floatValue] < 8) { + width = [UIScreen mainScreen].bounds.size.height; + } + return width; +} + +@end diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7Cell.h" b/SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7Cell.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7Cell.h" rename to SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7Cell.h diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7Cell.m" b/SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7Cell.m similarity index 86% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7Cell.m" rename to SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7Cell.m index d9fb192..69e00a3 100644 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7Cell.m" +++ b/SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7Cell.m @@ -55,7 +55,8 @@ - (void)setup .rightSpaceToView(contentView, margin) .heightIs(60); - [self setupAutoHeightWithBottomView:_imageView bottomMargin:margin]; + // 当你不确定哪个view在自动布局之后会排布在cell最下方的时候可以调用次方法将所有可能在最下方的view都传过去 + [self setupAutoHeightWithBottomViewsArray:@[_titleLabel, _imageView] bottomMargin:margin]; } - (void)setModel:(DemoVC7Model *)model diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7Cell2.h" b/SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7Cell2.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7Cell2.h" rename to SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7Cell2.h diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7Cell2.m" b/SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7Cell2.m similarity index 94% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7Cell2.m" rename to SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7Cell2.m index a494031..266f97d 100644 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7Cell2.m" +++ b/SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7Cell2.m @@ -69,18 +69,18 @@ - (void)setup imageView0.sd_layout .topSpaceToView(_titleLabel, margin) .leftSpaceToView(contentView, margin) - .heightIs(60); + .autoHeightRatio(0.8); imageView1.sd_layout .topSpaceToView(_titleLabel, margin) .leftSpaceToView(imageView0, margin) - .heightIs(60); + .autoHeightRatio(0.8); imageView2.sd_layout .topSpaceToView(_titleLabel, margin) .leftSpaceToView(imageView1, margin) .rightSpaceToView(contentView, margin) - .heightIs(60); + .autoHeightRatio(0.8); [self setupAutoHeightWithBottomView:imageView0 bottomMargin:margin]; } @@ -92,7 +92,7 @@ - (void)setModel:(DemoVC7Model *)model _titleLabel.text = model.title; [model.imagePathsArray enumerateObjectsUsingBlock:^(NSString * _Nonnull str, NSUInteger idx, BOOL * _Nonnull stop) { - UIImageView *imageView = _imageViewsArray[idx]; + UIImageView *imageView = self->_imageViewsArray[idx]; imageView.image = [UIImage imageNamed:str]; }]; } diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7Model.h" b/SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7Model.h similarity index 85% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7Model.h" rename to SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7Model.h index 5d4464a..7d4522c 100644 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7Model.h" +++ b/SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7Model.h @@ -10,6 +10,8 @@ @interface DemoVC7Model : NSObject + +@property (nonatomic, copy) NSString *iconImagePath; @property (nonatomic, copy) NSString *title; @property (nonatomic, strong) NSArray *imagePathsArray; diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7Model.m" b/SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7Model.m similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7Model.m" rename to SDAutoLayoutDemo/DemoVC/DemoVC7/DemoVC7Model.m diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC8/DemoVC8.h b/SDAutoLayoutDemo/DemoVC/DemoVC8/DemoVC8.h new file mode 100644 index 0000000..ee7687c --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC8/DemoVC8.h @@ -0,0 +1,13 @@ +// +// DemoVC8.h +// SDAutoLayout 测试 Demo +// +// Created by gsd on 15/12/22. +// Copyright © 2015年 gsd. All rights reserved. +// + +#import + +@interface DemoVC8 : UITableViewController + +@end diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7.m" b/SDAutoLayoutDemo/DemoVC/DemoVC8/DemoVC8.m similarity index 74% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7.m" rename to SDAutoLayoutDemo/DemoVC/DemoVC8/DemoVC8.m index 23ee8cf..92a5511 100644 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoVC7.m" +++ b/SDAutoLayoutDemo/DemoVC/DemoVC8/DemoVC8.m @@ -1,12 +1,12 @@ // -// DemoVC7.m +// DemoVC8.m // SDAutoLayout 测试 Demo // -// Created by gsd on 15/12/17. +// Created by gsd on 15/12/22. // Copyright © 2015年 gsd. All rights reserved. // -#import "DemoVC7.h" +#import "DemoVC8.h" #import "DemoVC7Model.h" @@ -15,17 +15,17 @@ #import "UITableView+SDAutoTableViewCellHeight.h" -#import "SDRefresh.h" - +#import "UIView+SDAutoLayout.h" +#import "SDRefresh.h" -@interface DemoVC7 () +@interface DemoVC8 () @property (nonatomic, strong) NSMutableArray *modelsArray; @end -@implementation DemoVC7 +@implementation DemoVC8 { SDRefreshFooterView *_refreshFooter; } @@ -33,7 +33,9 @@ @implementation DemoVC7 - (void)viewDidLoad { [super viewDidLoad]; - self.tableView.estimatedRowHeight = 100; +// self.tableView.estimatedRowHeight = 100; + + self.tableView.accessibilityIdentifier = @"demo.8.table"; [self creatModelsWithCount:18]; @@ -53,7 +55,7 @@ - (void)viewDidLoad { [weakRefreshFooter endRefreshing]; }); }; - + } - (void)creatModelsWithCount:(NSInteger)count @@ -89,7 +91,7 @@ - (void)creatModelsWithCount:(NSInteger)count int random = arc4random_uniform(100); if (random <= 30) { NSMutableArray *temp = [NSMutableArray new]; - + for (int i = 0; i < 3; i++) { int randomIndex = arc4random_uniform(5); NSString *text = iconImageNamesArray[randomIndex]; @@ -108,8 +110,6 @@ - (void)creatModelsWithCount:(NSInteger)count #pragma mark - Table view data source - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - - [self.tableView startAutoCellHeightWithCellClasses:@[[DemoVC7Cell class], [DemoVC7Cell2 class]] contentViewWidth:[UIScreen mainScreen].bounds.size.width]; return self.modelsArray.count; } @@ -128,21 +128,38 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N cell.model = model; + ////// 此步设置用于实现cell的frame缓存,可以让tableview滑动更加流畅 ////// + + [cell useCellFrameCacheWithIndexPath:indexPath tableView:tableView]; + + /////////////////////////////////////////////////////////////////////// + return cell; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { - Class currentClass = [DemoVC7Cell class]; + // >>>>>>>>>>>>>>>>>>>>> * cell自适应设置 * >>>>>>>>>>>>>>>>>>>>>>>> + // 只需一行代码即可实现单cell以及多cell的tableview高度自适应 - DemoVC7Model *model = self.modelsArray[indexPath.row]; + // 此升级版方法适用于cell的model有多个的情况下,性能比普通版稍微差一些,不建议在数据量大的tableview中使用,推荐使用“cellHeightForIndexPath:model:keyPath:cellClass:contentViewWidth:”方法同样是一步设置即可完成 + return [self cellHeightForIndexPath:indexPath cellContentViewWidth:[self cellContentViewWith]]; +} + + + + +- (CGFloat)cellContentViewWith +{ + CGFloat width = [UIScreen mainScreen].bounds.size.width; - if (model.imagePathsArray.count > 1) { - currentClass = [DemoVC7Cell2 class]; + // 适配ios7横屏 + if ([UIApplication sharedApplication].statusBarOrientation != UIInterfaceOrientationPortrait && [[UIDevice currentDevice].systemVersion floatValue] < 8) { + width = [UIScreen mainScreen].bounds.size.height; } - - return [self.tableView cellHeightForIndexPath:indexPath model:model keyPath:@"model" cellClass:currentClass]; + return width; } + @end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/DemoVC9.h b/SDAutoLayoutDemo/DemoVC/DemoVC9/DemoVC9.h new file mode 100644 index 0000000..bcf7dd8 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/DemoVC9.h @@ -0,0 +1,28 @@ +// +// DemoVC9.h +// SDAutoLayout 测试 Demo +// +// Created by gsd on 15/12/23. +// Copyright © 2015年 gsd. All rights reserved. +// + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * 持续更新地址: https://github.com/gsdios/SDAutoLayout * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * * + ********************************************************************************* + + */ + +#import "SDBaseTableViewController.h" + +@interface DemoVC9 : SDBaseTableViewController + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/DemoVC9.m b/SDAutoLayoutDemo/DemoVC/DemoVC9/DemoVC9.m new file mode 100644 index 0000000..32e9989 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/DemoVC9.m @@ -0,0 +1,505 @@ +// +// DemoVC9.m +// SDAutoLayout 测试 Demo +// +// Created by gsd on 15/12/23. +// Copyright © 2015年 gsd. All rights reserved. +// + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * 持续更新地址: https://github.com/gsdios/SDAutoLayout * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * * + ********************************************************************************* + + */ + +#import "DemoVC9.h" + +#import "SDTimeLineTableHeaderView.h" +#import "SDTimeLineRefreshHeader.h" +#import "SDTimeLineRefreshFooter.h" +#import "SDTimeLineCell.h" + +#import "SDTimeLineCellModel.h" + +#import "UITableView+SDAutoTableViewCellHeight.h" +#import "UIView+SDAutoLayout.h" + +#import "LEETheme.h" + +#define kTimeLineTableViewCellId @"SDTimeLineCell" + +static CGFloat textFieldH = 40; + + +@interface DemoVC9 () + +@end + +@implementation DemoVC9 + +{ + SDTimeLineRefreshFooter *_refreshFooter; + SDTimeLineRefreshHeader *_refreshHeader; + CGFloat _lastScrollViewOffsetY; + UITextField *_textField; + CGFloat _totalKeybordHeight; + NSIndexPath *_currentEditingIndexthPath; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.tableView.accessibilityIdentifier = @"demo.9.table"; + + //LEETheme 分为两种模式 , 默认设置模式 标识符设置模式 , 朋友圈demo展示的是默认设置模式的使用 , 微信聊天demo和Demo10 展示的是标识符模式的使用 + + UIBarButtonItem *rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"日间" style:UIBarButtonItemStyleDone target:self action:@selector(rightBarButtonItemAction:)]; + + rightBarButtonItem.lee_theme + .LeeAddCustomConfig(DAY , ^(UIBarButtonItem *item){ + + item.title = @"夜间"; + + }).LeeAddCustomConfig(NIGHT , ^(UIBarButtonItem *item){ + + item.title = @"日间"; + }); + + //为self.view 添加背景颜色设置 + + self.view.lee_theme + .LeeAddBackgroundColor(DAY , [UIColor whiteColor]) + .LeeAddBackgroundColor(NIGHT , [UIColor blackColor]); + + self.navigationItem.rightBarButtonItem = rightBarButtonItem; + + self.automaticallyAdjustsScrollViewInsets = NO; + + self.edgesForExtendedLayout = UIRectEdgeTop; + + [self.dataArray addObjectsFromArray:[self creatModelsWithCount:10]]; + + __weak typeof(self) weakSelf = self; + + + // 上拉加载 + _refreshFooter = [SDTimeLineRefreshFooter refreshFooterWithRefreshingText:@"正在加载数据..."]; + __weak typeof(_refreshFooter) weakRefreshFooter = _refreshFooter; + [_refreshFooter addToScrollView:self.tableView refreshOpration:^{ + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [weakSelf.dataArray addObjectsFromArray:[weakSelf creatModelsWithCount:10]]; + + /** + [weakSelf.tableView reloadDataWithExistedHeightCache] + 作用等同于 + [weakSelf.tableView reloadData] + 只是“reloadDataWithExistedHeightCache”刷新tableView但不清空之前已经计算好的高度缓存,用于直接将新数据拼接在旧数据之后的tableView刷新 + */ + [weakSelf.tableView reloadDataWithExistedHeightCache]; + + [weakRefreshFooter endRefreshing]; + }); + }]; + + SDTimeLineTableHeaderView *headerView = [SDTimeLineTableHeaderView new]; + headerView.frame = CGRectMake(0, 0, 0, 260); + self.tableView.tableHeaderView = headerView; + + //添加分隔线颜色设置 + + self.tableView.lee_theme + .LeeAddSeparatorColor(DAY , [[UIColor lightGrayColor] colorWithAlphaComponent:0.5f]) + .LeeAddSeparatorColor(NIGHT , [[UIColor grayColor] colorWithAlphaComponent:0.5f]); + + [self.tableView registerClass:[SDTimeLineCell class] forCellReuseIdentifier:kTimeLineTableViewCellId]; + + [self setupTextField]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardNotification:) name:UIKeyboardWillChangeFrameNotification object:nil]; + + // 解决在iOS11上朋友圈demo文字收折或者展开时出现cell跳动问题 + self.tableView.estimatedRowHeight = 0; + self.tableView.estimatedSectionFooterHeight = 0; + self.tableView.estimatedSectionHeaderHeight = 0; +} + +- (void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear:animated]; + + if (!_refreshHeader.superview) { + + _refreshHeader = [SDTimeLineRefreshHeader refreshHeaderWithCenter:CGPointMake(40, 45)]; + _refreshHeader.scrollView = self.tableView; + __weak typeof(_refreshHeader) weakHeader = _refreshHeader; + __weak typeof(self) weakSelf = self; + [_refreshHeader setRefreshingBlock:^{ + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.dataArray = [[weakSelf creatModelsWithCount:10] mutableCopy]; + [weakHeader endRefreshing]; + dispatch_async(dispatch_get_main_queue(), ^{ + [weakSelf.tableView reloadData]; + }); + }); + }]; + [self.tableView.superview addSubview:_refreshHeader]; + } else { + [self.tableView.superview bringSubviewToFront:_refreshHeader]; + } +} + +- (void)viewWillDisappear:(BOOL)animated +{ + [_textField resignFirstResponder]; +} + +- (void)dealloc +{ + [_refreshHeader removeFromSuperview]; + [_refreshFooter removeFromSuperview]; + + [_textField removeFromSuperview]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)setupTextField +{ + _textField = [UITextField new]; + _textField.returnKeyType = UIReturnKeyDone; + _textField.delegate = self; + _textField.layer.borderColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.8].CGColor; + _textField.layer.borderWidth = 1; + + //为textfield添加背景颜色 字体颜色的设置 还有block设置 , 在block中改变它的键盘样式 (当然背景颜色和字体颜色也可以直接在block中写) + + _textField.lee_theme + .LeeAddBackgroundColor(DAY , [UIColor whiteColor]) + .LeeAddBackgroundColor(NIGHT , [UIColor blackColor]) + .LeeAddTextColor(DAY , [UIColor blackColor]) + .LeeAddTextColor(NIGHT , [UIColor grayColor]) + .LeeAddCustomConfig(DAY , ^(UITextField *item){ + + item.keyboardAppearance = UIKeyboardAppearanceDefault; + if ([item isFirstResponder]) { + [item resignFirstResponder]; + [item becomeFirstResponder]; + } + }).LeeAddCustomConfig(NIGHT , ^(UITextField *item){ + + item.keyboardAppearance = UIKeyboardAppearanceDark; + if ([item isFirstResponder]) { + [item resignFirstResponder]; + [item becomeFirstResponder]; + } + }); + + _textField.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height, self.view.width_sd, textFieldH); + [[UIApplication sharedApplication].keyWindow addSubview:_textField]; + + [_textField becomeFirstResponder]; + [_textField resignFirstResponder]; +} + +// 右栏目按钮点击事件 + +- (void)rightBarButtonItemAction:(UIBarButtonItem *)sender{ + + if ([[LEETheme currentThemeTag] isEqualToString:DAY]) { + + [LEETheme startTheme:NIGHT]; + + } else { + [LEETheme startTheme:DAY]; + } +} + +- (NSArray *)creatModelsWithCount:(NSInteger)count +{ + NSArray *iconImageNamesArray = @[@"icon0.jpg", + @"icon1.jpg", + @"icon2.jpg", + @"icon3.jpg", + @"icon4.jpg", + ]; + + NSArray *namesArray = @[@"GSD_iOS", + @"风口上的猪", + @"当今世界网名都不好起了", + @"我叫郭德纲", + @"Hello Kitty"]; + + NSArray *textArray = @[@"当你的 app 没有提供 3x 的 LaunchImage 时,系统默认进入兼容模式,https://github.com/gsdios/SDAutoLayout大屏幕一切按照 320 宽度渲染,屏幕宽度返回 320;然后等比例拉伸到大屏。这种情况下对界面不会产生任何影响,等于把小屏完全拉伸。", + @"然后等比例拉伸到大屏。这种情况下对界面不会产生任何影响,https://github.com/gsdios/SDAutoLayout等于把小屏完全拉伸。", + @"当你的 app 没有提供 3x 的 LaunchImage 时屏幕宽度返回 320;然后等比例拉伸到大屏。这种情况下对界面不会产生任何影响,等于把小屏完全拉伸。但是建议不要长期处于这种模式下。屏幕宽度返回 320;https://github.com/gsdios/SDAutoLayout然后等比例拉伸到大屏。这种情况下对界面不会产生任何影响,等于把小屏完全拉伸。但是建议不要长期处于这种模式下。屏幕宽度返回 320;然后等比例拉伸到大屏。这种情况下对界面不会产生任何影响,等于把小屏完全拉伸。但是建议不要长期处于这种模式下。", + @"但是建议不要长期处于这种模式下,否则在大屏上会显得字大,内容少,容易遭到用户投诉。", + @"屏幕宽度返回 320;https://github.com/gsdios/SDAutoLayout然后等比例拉伸到大屏。这种情况下对界面不会产生任何影响,等于把小屏完全拉伸。但是建议不要长期处于这种模式下。" + ]; + + NSArray *commentsArray = @[@"社会主义好!👌👌👌👌", + @"正宗好凉茶,正宗好声音。。。", + @"你好,我好,大家好才是真的好", + @"有意思", + @"你瞅啥?", + @"瞅你咋地???!!!", + @"hello,看我", + @"曾经在幽幽暗暗反反复复中追问,才知道平平淡淡从从容容才是真", + @"人艰不拆", + @"咯咯哒", + @"呵呵~~~~~~~~", + @"我勒个去,啥世道啊", + @"真有意思啊你💢💢💢"]; + + NSArray *picImageNamesArray = @[ @"pic0.jpg", + @"pic1.jpg", + @"pic2.jpg", + @"pic3.jpg", + @"pic4.jpg", + @"pic5.jpg", + @"pic6.jpg", + @"pic7.jpg", + @"pic8.jpg" + ]; + NSMutableArray *resArr = [NSMutableArray new]; + + for (int i = 0; i < count; i++) { + int iconRandomIndex = arc4random_uniform(5); + int nameRandomIndex = arc4random_uniform(5); + int contentRandomIndex = arc4random_uniform(5); + + SDTimeLineCellModel *model = [SDTimeLineCellModel new]; + model.iconName = iconImageNamesArray[iconRandomIndex]; + model.name = namesArray[nameRandomIndex]; + model.msgContent = textArray[contentRandomIndex]; + + + // 模拟“随机图片” + int random = arc4random_uniform(6); + + NSMutableArray *temp = [NSMutableArray new]; + for (int i = 0; i < random; i++) { + int randomIndex = arc4random_uniform(9); + [temp addObject:picImageNamesArray[randomIndex]]; + } + if (temp.count) { + model.picNamesArray = [temp copy]; + } + + // 模拟随机评论数据 + int commentRandom = arc4random_uniform(3); + NSMutableArray *tempComments = [NSMutableArray new]; + for (int i = 0; i < commentRandom; i++) { + SDTimeLineCellCommentItemModel *commentItemModel = [SDTimeLineCellCommentItemModel new]; + int index = arc4random_uniform((int)namesArray.count); + commentItemModel.firstUserName = namesArray[index]; + commentItemModel.firstUserId = @"666"; + if (arc4random_uniform(10) < 5) { + commentItemModel.secondUserName = namesArray[arc4random_uniform((int)namesArray.count)]; + commentItemModel.secondUserId = @"888"; + } + commentItemModel.commentString = commentsArray[arc4random_uniform((int)commentsArray.count)]; + [tempComments addObject:commentItemModel]; + } + model.commentItemsArray = [tempComments copy]; + + // 模拟随机点赞数据 + int likeRandom = arc4random_uniform(3); + NSMutableArray *tempLikes = [NSMutableArray new]; + for (int i = 0; i < likeRandom; i++) { + SDTimeLineCellLikeItemModel *model = [SDTimeLineCellLikeItemModel new]; + int index = arc4random_uniform((int)namesArray.count); + model.userName = namesArray[index]; + model.userId = namesArray[index]; + [tempLikes addObject:model]; + } + + model.likeItemsArray = [tempLikes copy]; + + + + [resArr addObject:model]; + } + return [resArr copy]; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return self.dataArray.count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + SDTimeLineCell *cell = [tableView dequeueReusableCellWithIdentifier:kTimeLineTableViewCellId]; + cell.indexPath = indexPath; + __weak typeof(self) weakSelf = self; + if (!cell.moreButtonClickedBlock) { + [cell setMoreButtonClickedBlock:^(NSIndexPath *indexPath) { + SDTimeLineCellModel *model = weakSelf.dataArray[indexPath.row]; + model.isOpening = !model.isOpening; + [weakSelf.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; + }]; + cell.delegate = self; + } + + ////// 此步设置用于实现cell的frame缓存,可以让tableview滑动更加流畅 ////// + + [cell useCellFrameCacheWithIndexPath:indexPath tableView:tableView]; + + /////////////////////////////////////////////////////////////////////// + + cell.model = self.dataArray[indexPath.row]; + return cell; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + // >>>>>>>>>>>>>>>>>>>>> * cell自适应 * >>>>>>>>>>>>>>>>>>>>>>>> + id model = self.dataArray[indexPath.row]; + return [self.tableView cellHeightForIndexPath:indexPath model:model keyPath:@"model" cellClass:[SDTimeLineCell class] contentViewWidth:[self cellContentViewWith]]; +} + +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView +{ + [_textField resignFirstResponder]; +} + + + +- (CGFloat)cellContentViewWith +{ + CGFloat width = [UIScreen mainScreen].bounds.size.width; + + // 适配ios7横屏 + if ([UIApplication sharedApplication].statusBarOrientation != UIInterfaceOrientationPortrait && [[UIDevice currentDevice].systemVersion floatValue] < 8) { + width = [UIScreen mainScreen].bounds.size.height; + } + return width; +} + + +#pragma mark - SDTimeLineCellDelegate + +- (void)didClickcCommentButtonInCell:(UITableViewCell *)cell +{ + [_textField becomeFirstResponder]; + _currentEditingIndexthPath = [self.tableView indexPathForCell:cell]; + + [self adjustTableViewToFitKeyboard]; + +} + +- (void)didClickLikeButtonInCell:(UITableViewCell *)cell +{ + NSIndexPath *index = [self.tableView indexPathForCell:cell]; + SDTimeLineCellModel *model = self.dataArray[index.row]; + NSMutableArray *temp = [NSMutableArray arrayWithArray:model.likeItemsArray]; + + if (!model.isLiked) { + SDTimeLineCellLikeItemModel *likeModel = [SDTimeLineCellLikeItemModel new]; + likeModel.userName = @"GSD_iOS"; + likeModel.userId = @"gsdios"; + [temp addObject:likeModel]; + model.liked = YES; + } else { + SDTimeLineCellLikeItemModel *tempLikeModel = nil; + for (SDTimeLineCellLikeItemModel *likeModel in model.likeItemsArray) { + if ([likeModel.userId isEqualToString:@"gsdios"]) { + tempLikeModel = likeModel; + break; + } + } + [temp removeObject:tempLikeModel]; + model.liked = NO; + } + model.likeItemsArray = [temp copy]; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self.tableView reloadRowsAtIndexPaths:@[index] withRowAnimation:UITableViewRowAnimationNone]; + }); +} + + +- (void)adjustTableViewToFitKeyboard +{ + UIWindow *window = [UIApplication sharedApplication].keyWindow; + UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:_currentEditingIndexthPath]; + CGRect rect = [cell.superview convertRect:cell.frame toView:window]; + CGFloat delta = CGRectGetMaxY(rect) - (window.bounds.size.height - _totalKeybordHeight); + + CGPoint offset = self.tableView.contentOffset; + offset.y += delta; + if (offset.y < 0) { + offset.y = 0; + } + + [self.tableView setContentOffset:offset animated:YES]; +} + +#pragma mark - UITextFieldDelegate + +- (BOOL)textFieldShouldReturn:(UITextField *)textField +{ + if (textField.text.length) { + [_textField resignFirstResponder]; + + SDTimeLineCellModel *model = self.dataArray[_currentEditingIndexthPath.row]; + NSMutableArray *temp = [NSMutableArray new]; + [temp addObjectsFromArray:model.commentItemsArray]; + + SDTimeLineCellCommentItemModel *commentItemModel = [SDTimeLineCellCommentItemModel new]; + commentItemModel.firstUserName = @"GSD_iOS"; + commentItemModel.commentString = textField.text; + commentItemModel.firstUserId = @"GSD_iOS"; + [temp addObject:commentItemModel]; + + model.commentItemsArray = [temp copy]; + + [self.tableView reloadRowsAtIndexPaths:@[_currentEditingIndexthPath] withRowAnimation:UITableViewRowAnimationNone]; + + _textField.text = @""; + + return YES; + } + return NO; +} + + + +- (void)keyboardNotification:(NSNotification *)notification +{ + NSDictionary *dict = notification.userInfo; + CGRect rect = [dict[@"UIKeyboardFrameEndUserInfoKey"] CGRectValue]; + + + + CGRect textFieldRect = CGRectMake(0, rect.origin.y - textFieldH, rect.size.width, textFieldH); + if (rect.origin.y == [UIScreen mainScreen].bounds.size.height) { + textFieldRect = rect; + } + + [UIView animateWithDuration:0.25 animations:^{ + self->_textField.frame = textFieldRect; + }]; + + CGFloat h = rect.size.height + textFieldH; + if (_totalKeybordHeight != h) { + _totalKeybordHeight = h; + [self adjustTableViewToFitKeyboard]; + } +} + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/BaseClasses/BaseRefreshView/SDBaseRefreshView.h b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/BaseClasses/BaseRefreshView/SDBaseRefreshView.h new file mode 100644 index 0000000..ee17fda --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/BaseClasses/BaseRefreshView/SDBaseRefreshView.h @@ -0,0 +1,48 @@ +// +// SDBaseRefreshView.h +// GSD_WeiXin(wechat) +// +// Created by gsd on 16/3/5. +// Copyright © 2016年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import + +UIKIT_EXTERN NSString *const kSDBaseRefreshViewObserveKeyPath; + +typedef enum { + SDWXRefreshViewStateNormal, + SDWXRefreshViewStateWillRefresh, + SDWXRefreshViewStateRefreshing, +} SDWXRefreshViewState; + +@interface SDBaseRefreshView : UIView + +@property (nonatomic, strong) UIScrollView *scrollView; + +- (void)endRefreshing; + +@property (nonatomic, assign) UIEdgeInsets scrollViewOriginalInsets; +@property (nonatomic, assign) SDWXRefreshViewState refreshState; + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/BaseClasses/BaseRefreshView/SDBaseRefreshView.m b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/BaseClasses/BaseRefreshView/SDBaseRefreshView.m new file mode 100644 index 0000000..03ae68f --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/BaseClasses/BaseRefreshView/SDBaseRefreshView.m @@ -0,0 +1,60 @@ +// +// SDBaseRefreshView.m +// GSD_WeiXin(wechat) +// +// Created by gsd on 16/3/5. +// Copyright © 2016年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import "SDBaseRefreshView.h" + +NSString *const kSDBaseRefreshViewObserveKeyPath = @"contentOffset"; + +@implementation SDBaseRefreshView + +- (void)setScrollView:(UIScrollView *)scrollView +{ + _scrollView = scrollView; + + [scrollView addObserver:self forKeyPath:kSDBaseRefreshViewObserveKeyPath options:NSKeyValueObservingOptionNew context:nil]; +} + +- (void)willMoveToSuperview:(UIView *)newSuperview +{ + if (!newSuperview) { + [self.scrollView removeObserver:self forKeyPath:kSDBaseRefreshViewObserveKeyPath]; + } +} + +- (void)endRefreshing +{ + self.refreshState = SDWXRefreshViewStateNormal; +} + + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + // 子类实现 +} + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/BaseClasses/SDBaseNavigationController.h b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/BaseClasses/SDBaseNavigationController.h new file mode 100644 index 0000000..07a5ebd --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/BaseClasses/SDBaseNavigationController.h @@ -0,0 +1,35 @@ +// +// SDBaseNavigationController.h +// GSD_WeiXin(wechat) +// +// Created by aier on 16/2/10. +// Copyright © 2016年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import + +#import "GlobalDefines.h" + +@interface SDBaseNavigationController : UINavigationController + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/BaseClasses/SDBaseNavigationController.m b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/BaseClasses/SDBaseNavigationController.m new file mode 100644 index 0000000..4ca8f36 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/BaseClasses/SDBaseNavigationController.m @@ -0,0 +1,49 @@ +// +// SDBaseNavigationController.m +// GSD_WeiXin(wechat) +// +// Created by aier on 16/2/10. +// Copyright © 2016年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import "SDBaseNavigationController.h" + +@interface SDBaseNavigationController () + +@end + +@implementation SDBaseNavigationController + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view. +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + + + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/BaseClasses/SDBaseTableViewController.h b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/BaseClasses/SDBaseTableViewController.h new file mode 100644 index 0000000..7670778 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/BaseClasses/SDBaseTableViewController.h @@ -0,0 +1,38 @@ +// +// SDBaseTableViewController.h +// GSD_WeiXin(wechat) +// +// Created by aier on 16/2/10. +// Copyright © 2016年 GSD. All rights reserved. +// + + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import + +#import "GlobalDefines.h" + +@interface SDBaseTableViewController : UITableViewController + +@property (nonatomic, strong) NSMutableArray *dataArray; + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/BaseClasses/SDBaseTableViewController.m b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/BaseClasses/SDBaseTableViewController.m new file mode 100644 index 0000000..8cba40b --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/BaseClasses/SDBaseTableViewController.m @@ -0,0 +1,50 @@ +// +// SDBaseTableViewController.m +// GSD_WeiXin(wechat) +// +// Created by aier on 16/2/10. +// Copyright © 2016年 GSD. All rights reserved. +// + + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import "SDBaseTableViewController.h" + +@interface SDBaseTableViewController () + +@end + +@implementation SDBaseTableViewController + +- (void)viewDidLoad { + [super viewDidLoad]; +} + +- (NSMutableArray *)dataArray +{ + if (!_dataArray) { + _dataArray = [NSMutableArray new]; + } + return _dataArray; +} + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/GloabalDefines/GlobalDefines.h b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/GloabalDefines/GlobalDefines.h new file mode 100644 index 0000000..01c40e6 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/GloabalDefines/GlobalDefines.h @@ -0,0 +1,44 @@ +// +// GlobalDefines.h +// GSD_WeiXin(wechat) +// +// Created by aier on 16/2/12. +// Copyright © 2016年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#ifndef GlobalDefines_h +#define GlobalDefines_h + +#define SDColor(r, g, b, a) [UIColor colorWithRed:(r / 255.0) green:(g / 255.0) blue:(b / 255.0) alpha:a] + +#define Global_tintColor [UIColor colorWithRed:0 green:(190 / 255.0) blue:(12 / 255.0) alpha:1] + +#define Global_mainBackgroundColor SDColor(248, 248, 248, 1) + +#define TimeLineCellHighlightedColor [UIColor colorWithRed:92/255.0 green:140/255.0 blue:193/255.0 alpha:1.0] + +#define DAY @"day" + +#define NIGHT @"night" + +#endif diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Model/SDTimeLineCellModel.h b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Model/SDTimeLineCellModel.h new file mode 100644 index 0000000..46b3797 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Model/SDTimeLineCellModel.h @@ -0,0 +1,75 @@ +// +// SDTimeLineCellModel.h +// GSD_WeiXin(wechat) +// +// Created by gsd on 16/2/25. +// Copyright © 2016年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import + +@class SDTimeLineCellLikeItemModel, SDTimeLineCellCommentItemModel; + +@interface SDTimeLineCellModel : NSObject + +@property (nonatomic, copy) NSString *iconName; +@property (nonatomic, copy) NSString *name; +@property (nonatomic, copy) NSString *msgContent; +@property (nonatomic, strong) NSArray *picNamesArray; + +@property (nonatomic, assign, getter = isLiked) BOOL liked; + +@property (nonatomic, strong) NSArray *likeItemsArray; +@property (nonatomic, strong) NSArray *commentItemsArray; + +@property (nonatomic, assign) BOOL isOpening; + +@property (nonatomic, assign, readonly) BOOL shouldShowMoreButton; + + +@end + + +@interface SDTimeLineCellLikeItemModel : NSObject + +@property (nonatomic, copy) NSString *userName; +@property (nonatomic, copy) NSString *userId; + +@property (nonatomic, copy) NSAttributedString *attributedContent; + +@end + + +@interface SDTimeLineCellCommentItemModel : NSObject + +@property (nonatomic, copy) NSString *commentString; + +@property (nonatomic, copy) NSString *firstUserName; +@property (nonatomic, copy) NSString *firstUserId; + +@property (nonatomic, copy) NSString *secondUserName; +@property (nonatomic, copy) NSString *secondUserId; + +@property (nonatomic, copy) NSAttributedString *attributedContent; + +@end \ No newline at end of file diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Model/SDTimeLineCellModel.m b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Model/SDTimeLineCellModel.m new file mode 100644 index 0000000..b79ab59 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Model/SDTimeLineCellModel.m @@ -0,0 +1,85 @@ +// +// SDTimeLineCellModel.m +// GSD_WeiXin(wechat) +// +// Created by gsd on 16/2/25. +// Copyright © 2016年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import "SDTimeLineCellModel.h" + +#import + +extern const CGFloat contentLabelFontSize; +extern CGFloat maxContentLabelHeight; + +@implementation SDTimeLineCellModel +{ + CGFloat _lastContentWidth; +} + +@synthesize msgContent = _msgContent; + +- (void)setMsgContent:(NSString *)msgContent +{ + _msgContent = msgContent; +} + +- (NSString *)msgContent +{ + CGFloat contentW = [UIScreen mainScreen].bounds.size.width - 70; + if (contentW != _lastContentWidth) { + _lastContentWidth = contentW; + CGRect textRect = [_msgContent boundingRectWithSize:CGSizeMake(contentW, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:contentLabelFontSize]} context:nil]; + if (textRect.size.height > maxContentLabelHeight) { + _shouldShowMoreButton = YES; + } else { + _shouldShowMoreButton = NO; + } + } + + return _msgContent; +} + +- (void)setIsOpening:(BOOL)isOpening +{ + if (!_shouldShowMoreButton) { + _isOpening = NO; + } else { + _isOpening = isOpening; + } +} + + +@end + + +@implementation SDTimeLineCellLikeItemModel + + +@end + +@implementation SDTimeLineCellCommentItemModel + + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/TimeLineController/SDTimeLineTableViewController.h b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/TimeLineController/SDTimeLineTableViewController.h new file mode 100644 index 0000000..cf05c74 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/TimeLineController/SDTimeLineTableViewController.h @@ -0,0 +1,33 @@ +// +// SDTimeLineTableViewController.h +// GSD_WeiXin(wechat) +// +// Created by gsd on 16/2/25. +// Copyright © 2016年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import "SDBaseTableViewController.h" + +@interface SDTimeLineTableViewController : SDBaseTableViewController + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/TimeLineController/SDTimeLineTableViewController.m b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/TimeLineController/SDTimeLineTableViewController.m new file mode 100644 index 0000000..d380e38 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/TimeLineController/SDTimeLineTableViewController.m @@ -0,0 +1,244 @@ +// +// SDTimeLineTableViewController.m +// GSD_WeiXin(wechat) +// +// Created by gsd on 16/2/25. +// Copyright © 2016年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import "SDTimeLineTableViewController.h" + +#import "SDRefresh.h" +#import "SDTimeLineTableHeaderView.h" +#import "SDTimeLineRefreshHeader.h" +#import "SDTimeLineCell.h" + +#import "SDTimeLineCellModel.h" + +#import "UITableView+SDAutoTableViewCellHeight.h" + +#define kTimeLineTableViewCellId @"SDTimeLineCell" + +@implementation SDTimeLineTableViewController + +{ + SDRefreshFooterView *_refreshFooter; + SDTimeLineRefreshHeader *_refreshHeader; + CGFloat _lastScrollViewOffsetY; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.automaticallyAdjustsScrollViewInsets = NO; + + [self.dataArray addObjectsFromArray:[self creatModelsWithCount:10]]; + + __weak typeof(self) weakSelf = self; + + + // 上拉加载 + _refreshFooter = [SDRefreshFooterView refreshView]; + [_refreshFooter addToScrollView:self.tableView]; + __weak typeof(_refreshFooter) weakRefreshFooter = _refreshFooter; + _refreshFooter.beginRefreshingOperation = ^() { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [weakSelf.dataArray addObjectsFromArray:[weakSelf creatModelsWithCount:10]]; + [weakSelf.tableView reloadData]; + [weakRefreshFooter endRefreshing]; + }); + }; + + SDTimeLineTableHeaderView *headerView = [SDTimeLineTableHeaderView new]; + headerView.frame = CGRectMake(0, 0, 0, 260); + self.tableView.tableHeaderView = headerView; + + [self.tableView registerClass:[SDTimeLineCell class] forCellReuseIdentifier:kTimeLineTableViewCellId]; +} + +- (void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear:animated]; + + if (!_refreshHeader.superview) { + + _refreshHeader = [SDTimeLineRefreshHeader refreshHeaderWithCenter:CGPointMake(40, 45)]; + _refreshHeader.scrollView = self.tableView; + __weak typeof(_refreshHeader) weakHeader = _refreshHeader; + __weak typeof(self) weakSelf = self; + [_refreshHeader setRefreshingBlock:^{ + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + weakSelf.dataArray = [[weakSelf creatModelsWithCount:10] mutableCopy]; + [weakHeader endRefreshing]; + dispatch_async(dispatch_get_main_queue(), ^{ + [weakSelf.tableView reloadData]; + }); + }); + }]; + [self.tableView.superview addSubview:_refreshHeader]; + } +} + +- (void)dealloc +{ + [_refreshHeader removeFromSuperview]; +} + +- (NSArray *)creatModelsWithCount:(NSInteger)count +{ + NSArray *iconImageNamesArray = @[@"icon0.jpg", + @"icon1.jpg", + @"icon2.jpg", + @"icon3.jpg", + @"icon4.jpg", + ]; + + NSArray *namesArray = @[@"GSD_iOS", + @"风口上的猪", + @"当今世界网名都不好起了", + @"我叫郭德纲", + @"Hello Kitty"]; + + NSArray *textArray = @[@"当你的 app 没有提供 3x 的 LaunchImage 时,系统默认进入兼容模式,https://github.com/gsdios/SDAutoLayout大屏幕一切按照 320 宽度渲染,屏幕宽度返回 320;然后等比例拉伸到大屏。这种情况下对界面不会产生任何影响,等于把小屏完全拉伸。", + @"然后等比例拉伸到大屏。这种情况下对界面不会产生任何影响,https://github.com/gsdios/SDAutoLayout等于把小屏完全拉伸。", + @"当你的 app 没有提供 3x 的 LaunchImage 时屏幕宽度返回 320;然后等比例拉伸到大屏。这种情况下对界面不会产生任何影响,等于把小屏完全拉伸。但是建议不要长期处于这种模式下。屏幕宽度返回 320;https://github.com/gsdios/SDAutoLayout然后等比例拉伸到大屏。这种情况下对界面不会产生任何影响,等于把小屏完全拉伸。但是建议不要长期处于这种模式下。屏幕宽度返回 320;然后等比例拉伸到大屏。这种情况下对界面不会产生任何影响,等于把小屏完全拉伸。但是建议不要长期处于这种模式下。", + @"但是建议不要长期处于这种模式下,否则在大屏上会显得字大,内容少,容易遭到用户投诉。", + @"屏幕宽度返回 320;https://github.com/gsdios/SDAutoLayout然后等比例拉伸到大屏。这种情况下对界面不会产生任何影响,等于把小屏完全拉伸。但是建议不要长期处于这种模式下。" + ]; + + NSArray *commentsArray = @[@"社会主义好!👌👌👌👌", + @"正宗好凉茶,正宗好声音。。。", + @"你好,我好,大家好才是真的好", + @"有意思", + @"你瞅啥?", + @"瞅你咋地???!!!", + @"hello,看我", + @"曾经在幽幽暗暗反反复复中追问,才知道平平淡淡从从容容才是真,再回首恍然如梦,再回首我心依旧,只有那不变的长路伴着我", + @"人艰不拆", + @"咯咯哒", + @"呵呵~~~~~~~~", + @"我勒个去,啥世道啊", + @"真有意思啊你💢💢💢"]; + + NSArray *picImageNamesArray = @[ @"pic0.jpg", + @"pic1.jpg", + @"pic2.jpg", + @"pic3.jpg", + @"pic4.jpg", + @"pic5.jpg", + @"pic6.jpg", + @"pic7.jpg", + @"pic8.jpg" + ]; + NSMutableArray *resArr = [NSMutableArray new]; + + for (int i = 0; i < count; i++) { + int iconRandomIndex = arc4random_uniform(5); + int nameRandomIndex = arc4random_uniform(5); + int contentRandomIndex = arc4random_uniform(5); + + SDTimeLineCellModel *model = [SDTimeLineCellModel new]; + model.iconName = iconImageNamesArray[iconRandomIndex]; + model.name = namesArray[nameRandomIndex]; + model.msgContent = textArray[contentRandomIndex]; + + + // 模拟“随机图片” + int random = arc4random_uniform(10); + + NSMutableArray *temp = [NSMutableArray new]; + for (int i = 0; i < random; i++) { + int randomIndex = arc4random_uniform(9); + [temp addObject:picImageNamesArray[randomIndex]]; + } + if (temp.count) { + model.picNamesArray = [temp copy]; + } + + int commentRandom = arc4random_uniform(6); + NSMutableArray *tempComments = [NSMutableArray new]; + for (int i = 0; i < commentRandom; i++) { + SDTimeLineCellCommentItemModel *commentItemModel = [SDTimeLineCellCommentItemModel new]; + int index = arc4random_uniform((int)namesArray.count); + commentItemModel.firstUserName = namesArray[index]; + commentItemModel.firstUserId = @"666"; + if (arc4random_uniform(10) < 5) { + commentItemModel.secondUserName = namesArray[arc4random_uniform((int)namesArray.count)]; + commentItemModel.secondUserId = @"888"; + } + commentItemModel.commentString = commentsArray[arc4random_uniform((int)commentsArray.count)]; + [tempComments addObject:commentItemModel]; + } + model.commentItemsArray = [tempComments copy]; + + [resArr addObject:model]; + } + return [resArr copy]; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return self.dataArray.count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + SDTimeLineCell *cell = [tableView dequeueReusableCellWithIdentifier:kTimeLineTableViewCellId]; + cell.indexPath = indexPath; + __weak typeof(self) weakSelf = self; + if (!cell.moreButtonClickedBlock) { + [cell setMoreButtonClickedBlock:^(NSIndexPath *indexPath) { + SDTimeLineCellModel *model = weakSelf.dataArray[indexPath.row]; + model.isOpening = !model.isOpening; + [weakSelf.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; + }]; + } + + cell.model = self.dataArray[indexPath.row]; + return cell; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + // >>>>>>>>>>>>>>>>>>>>> * cell自适应 * >>>>>>>>>>>>>>>>>>>>>>>> + id model = self.dataArray[indexPath.row]; + return [self.tableView cellHeightForIndexPath:indexPath model:model keyPath:@"model" cellClass:[SDTimeLineCell class] contentViewWidth:[self cellContentViewWith]]; +} + + + + +- (CGFloat)cellContentViewWith +{ + CGFloat width = [UIScreen mainScreen].bounds.size.width; + + // 适配ios7横屏 + if ([UIApplication sharedApplication].statusBarOrientation != UIInterfaceOrientationPortrait && [[UIDevice currentDevice].systemVersion floatValue] < 8) { + width = [UIScreen mainScreen].bounds.size.height; + } + return width; +} + + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/Cell/SDTimeLineCell.h b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/Cell/SDTimeLineCell.h new file mode 100644 index 0000000..3b1e272 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/Cell/SDTimeLineCell.h @@ -0,0 +1,50 @@ +// +// SDTimeLineCell.h +// GSD_WeiXin(wechat) +// +// Created by gsd on 16/2/25. +// Copyright © 2016年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import + +@protocol SDTimeLineCellDelegate + +- (void)didClickLikeButtonInCell:(UITableViewCell *)cell; +- (void)didClickcCommentButtonInCell:(UITableViewCell *)cell; + +@end + +@class SDTimeLineCellModel; + +@interface SDTimeLineCell : UITableViewCell + +@property (nonatomic, weak) id delegate; + +@property (nonatomic, strong) SDTimeLineCellModel *model; + +@property (nonatomic, strong) NSIndexPath *indexPath; + +@property (nonatomic, copy) void (^moreButtonClickedBlock)(NSIndexPath *indexPath); + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/Cell/SDTimeLineCell.m b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/Cell/SDTimeLineCell.m new file mode 100644 index 0000000..a29de47 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/Cell/SDTimeLineCell.m @@ -0,0 +1,294 @@ +// +// SDTimeLineCell.m +// GSD_WeiXin(wechat) +// +// Created by gsd on 16/2/25. +// Copyright © 2016年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import "SDTimeLineCell.h" + +#import "SDTimeLineCellModel.h" +#import "UIView+SDAutoLayout.h" +#import "SDTimeLineCellCommentView.h" +#import "SDWeiXinPhotoContainerView.h" +#import "SDTimeLineCellOperationMenu.h" +#import "LEETheme.h" + +const CGFloat contentLabelFontSize = 15; +CGFloat maxContentLabelHeight = 0; // 根据具体font而定 + +NSString *const kSDTimeLineCellOperationButtonClickedNotification = @"SDTimeLineCellOperationButtonClickedNotification"; + +@implementation SDTimeLineCell + +{ + UIImageView *_iconView; + UILabel *_nameLable; + UILabel *_contentLabel; + SDWeiXinPhotoContainerView *_picContainerView; + UILabel *_timeLabel; + UIButton *_moreButton; + UIButton *_operationButton; + SDTimeLineCellCommentView *_commentView; + SDTimeLineCellOperationMenu *_operationMenu; +} + + +- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier +{ + if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { + + [self setup]; + + //设置主题 + [self configTheme]; + + self.selectionStyle = UITableViewCellSelectionStyleNone; + } + return self; +} + +- (void)setup +{ + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveOperationButtonClickedNotification:) name:kSDTimeLineCellOperationButtonClickedNotification object:nil]; + + _iconView = [UIImageView new]; + + _nameLable = [UILabel new]; + _nameLable.font = [UIFont systemFontOfSize:14]; + _nameLable.textColor = [UIColor colorWithRed:(54 / 255.0) green:(71 / 255.0) blue:(121 / 255.0) alpha:0.9]; + + _contentLabel = [UILabel new]; + _contentLabel.font = [UIFont systemFontOfSize:contentLabelFontSize]; + _contentLabel.numberOfLines = 0; + if (maxContentLabelHeight == 0) { + maxContentLabelHeight = _contentLabel.font.lineHeight * 3; + } + + _moreButton = [UIButton new]; + [_moreButton setTitle:@"全文" forState:UIControlStateNormal]; + [_moreButton setTitleColor:TimeLineCellHighlightedColor forState:UIControlStateNormal]; + [_moreButton addTarget:self action:@selector(moreButtonClicked) forControlEvents:UIControlEventTouchUpInside]; + _moreButton.titleLabel.font = [UIFont systemFontOfSize:14]; + + _operationButton = [UIButton new]; + [_operationButton setImage:[UIImage imageNamed:@"AlbumOperateMore"] forState:UIControlStateNormal]; + [_operationButton addTarget:self action:@selector(operationButtonClicked) forControlEvents:UIControlEventTouchUpInside]; + + _picContainerView = [SDWeiXinPhotoContainerView new]; + + _commentView = [SDTimeLineCellCommentView new]; + + _timeLabel = [UILabel new]; + _timeLabel.font = [UIFont systemFontOfSize:13]; + + + _operationMenu = [SDTimeLineCellOperationMenu new]; + __weak typeof(self) weakSelf = self; + [_operationMenu setLikeButtonClickedOperation:^{ + if ([weakSelf.delegate respondsToSelector:@selector(didClickLikeButtonInCell:)]) { + [weakSelf.delegate didClickLikeButtonInCell:weakSelf]; + } + }]; + [_operationMenu setCommentButtonClickedOperation:^{ + if ([weakSelf.delegate respondsToSelector:@selector(didClickcCommentButtonInCell:)]) { + [weakSelf.delegate didClickcCommentButtonInCell:weakSelf]; + } + }]; + + + NSArray *views = @[_iconView, _nameLable, _contentLabel, _moreButton, _picContainerView, _timeLabel, _operationButton, _operationMenu, _commentView]; + + [self.contentView sd_addSubviews:views]; + + UIView *contentView = self.contentView; + CGFloat margin = 10; + + _iconView.sd_layout + .leftSpaceToView(contentView, margin) + .topSpaceToView(contentView, margin + 5) + .widthIs(40) + .heightIs(40); + + _nameLable.sd_layout + .leftSpaceToView(_iconView, margin) + .topEqualToView(_iconView) + .heightIs(18); + [_nameLable setSingleLineAutoResizeWithMaxWidth:200]; + + _contentLabel.sd_layout + .leftEqualToView(_nameLable) + .topSpaceToView(_nameLable, margin) + .rightSpaceToView(contentView, margin) + .autoHeightRatio(0); + + // morebutton的高度在setmodel里面设置 + _moreButton.sd_layout + .leftEqualToView(_contentLabel) + .topSpaceToView(_contentLabel, 0) + .widthIs(30); + + + _picContainerView.sd_layout + .leftEqualToView(_contentLabel); // 已经在内部实现宽度和高度自适应所以不需要再设置宽度高度,top值是具体有无图片在setModel方法中设置 + + _timeLabel.sd_layout + .leftEqualToView(_contentLabel) + .topSpaceToView(_picContainerView, margin) + .heightIs(15); + [_timeLabel setSingleLineAutoResizeWithMaxWidth:200]; + + _operationButton.sd_layout + .rightSpaceToView(contentView, margin) + .centerYEqualToView(_timeLabel) + .heightIs(25) + .widthIs(25); + + _commentView.sd_layout + .leftEqualToView(_contentLabel) + .rightSpaceToView(self.contentView, margin) + .topSpaceToView(_timeLabel, margin); // 已经在内部实现高度自适应所以不需要再设置高度 + + _operationMenu.sd_layout + .rightSpaceToView(_operationButton, 0) + .heightIs(36) + .centerYEqualToView(_operationButton) + .widthIs(0); +} + +- (void)configTheme{ + + self.lee_theme + .LeeAddBackgroundColor(DAY , [UIColor whiteColor]) + .LeeAddBackgroundColor(NIGHT , [UIColor blackColor]); + + _contentLabel.lee_theme + .LeeAddTextColor(DAY , [UIColor blackColor]) + .LeeAddTextColor(NIGHT , [UIColor grayColor]); + + _timeLabel.lee_theme + .LeeAddTextColor(DAY , [UIColor lightGrayColor]) + .LeeAddTextColor(NIGHT , [UIColor grayColor]); +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)setModel:(SDTimeLineCellModel *)model +{ + _model = model; + + [_commentView setupWithLikeItemsArray:model.likeItemsArray commentItemsArray:model.commentItemsArray]; + + _iconView.image = [UIImage imageNamed:model.iconName]; + _nameLable.text = model.name; + _contentLabel.text = model.msgContent; + _picContainerView.picPathStringsArray = model.picNamesArray; + + if (model.shouldShowMoreButton) { // 如果文字高度超过60 + _moreButton.sd_layout.heightIs(20); + _moreButton.hidden = NO; + if (model.isOpening) { // 如果需要展开 + _contentLabel.sd_layout.maxHeightIs(MAXFLOAT); + [_moreButton setTitle:@"收起" forState:UIControlStateNormal]; + } else { + _contentLabel.sd_layout.maxHeightIs(maxContentLabelHeight); + [_moreButton setTitle:@"全文" forState:UIControlStateNormal]; + } + } else { + _moreButton.sd_layout.heightIs(0); + _moreButton.hidden = YES; + } + + CGFloat picContainerTopMargin = 0; + if (model.picNamesArray.count) { + picContainerTopMargin = 10; + } + _picContainerView.sd_layout.topSpaceToView(_moreButton, picContainerTopMargin); + + UIView *bottomView; + + if (!model.commentItemsArray.count && !model.likeItemsArray.count) { + bottomView = _timeLabel; + } else { + bottomView = _commentView; + } + + [self setupAutoHeightWithBottomView:bottomView bottomMargin:15]; + + _timeLabel.text = @"1分钟前"; +} + +- (void)setFrame:(CGRect)frame +{ + [super setFrame:frame]; + if (_operationMenu.isShowing) { + _operationMenu.show = NO; + } +} + +#pragma mark - private actions + +- (void)moreButtonClicked +{ + if (self.moreButtonClickedBlock) { + self.moreButtonClickedBlock(self.indexPath); + } +} + +- (void)operationButtonClicked +{ + [self postOperationButtonClickedNotification]; + _operationMenu.show = !_operationMenu.isShowing; +} + +- (void)receiveOperationButtonClickedNotification:(NSNotification *)notification +{ + UIButton *btn = [notification object]; + + if (btn != _operationButton && _operationMenu.isShowing) { + _operationMenu.show = NO; + } +} + + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesBegan:touches withEvent:event]; + [self postOperationButtonClickedNotification]; + if (_operationMenu.isShowing) { + _operationMenu.show = NO; + } +} + +- (void)postOperationButtonClickedNotification +{ + [[NSNotificationCenter defaultCenter] postNotificationName:kSDTimeLineCellOperationButtonClickedNotification object:_operationButton]; +} + +@end + diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/CommentView/SDTimeLineCellCommentView.h b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/CommentView/SDTimeLineCellCommentView.h new file mode 100644 index 0000000..6b79e6b --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/CommentView/SDTimeLineCellCommentView.h @@ -0,0 +1,37 @@ +// +// SDTimeLineCellCommentView.h +// GSD_WeiXin(wechat) +// +// Created by gsd on 16/2/25. +// Copyright © 2016年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import + +#import "GlobalDefines.h" + +@interface SDTimeLineCellCommentView : UIView + +- (void)setupWithLikeItemsArray:(NSArray *)likeItemsArray commentItemsArray:(NSArray *)commentItemsArray; + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/CommentView/SDTimeLineCellCommentView.m b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/CommentView/SDTimeLineCellCommentView.m new file mode 100644 index 0000000..479e5cb --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/CommentView/SDTimeLineCellCommentView.m @@ -0,0 +1,279 @@ +// +// SDTimeLineCellCommentView.m +// GSD_WeiXin(wechat) +// +// Created by gsd on 16/2/25. +// Copyright © 2016年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import "SDTimeLineCellCommentView.h" +#import "UIView+SDAutoLayout.h" +#import "SDTimeLineCellModel.h" +#import "MLLinkLabel.h" + +#import "LEETheme.h" + +@interface SDTimeLineCellCommentView () + +@property (nonatomic, strong) NSArray *likeItemsArray; +@property (nonatomic, strong) NSArray *commentItemsArray; + +@property (nonatomic, strong) UIImageView *bgImageView; + +@property (nonatomic, strong) MLLinkLabel *likeLabel; +@property (nonatomic, strong) UIView *likeLableBottomLine; + +@property (nonatomic, strong) NSMutableArray *commentLabelsArray; + + +@end + +@implementation SDTimeLineCellCommentView + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + + [self setupViews]; + + //设置主题 + [self configTheme]; + + } + return self; +} + +- (void)setupViews +{ + _bgImageView = [UIImageView new]; + UIImage *bgImage = [[[UIImage imageNamed:@"LikeCmtBg"] stretchableImageWithLeftCapWidth:40 topCapHeight:30] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + _bgImageView.image = bgImage; + _bgImageView.backgroundColor = [UIColor clearColor]; + [self addSubview:_bgImageView]; + + _likeLabel = [MLLinkLabel new]; + _likeLabel.font = [UIFont systemFontOfSize:14]; + _likeLabel.linkTextAttributes = @{NSForegroundColorAttributeName : TimeLineCellHighlightedColor}; + _likeLabel.isAttributedContent = YES; + [self addSubview:_likeLabel]; + + _likeLableBottomLine = [UIView new]; + [self addSubview:_likeLableBottomLine]; + + _bgImageView.sd_layout.spaceToSuperView(UIEdgeInsetsMake(0, 0, 0, 0)); +} + +- (void)configTheme{ + + self.lee_theme + .LeeAddBackgroundColor(DAY , [UIColor whiteColor]) + .LeeAddBackgroundColor(NIGHT , [UIColor blackColor]); + + _bgImageView.lee_theme + .LeeAddTintColor(DAY , SDColor(230, 230, 230, 1.0f)) + .LeeAddTintColor(NIGHT , SDColor(30, 30, 30, 1.0f)); + + _likeLabel.lee_theme + .LeeAddTextColor(DAY , [UIColor blackColor]) + .LeeAddTextColor(NIGHT , [UIColor grayColor]); + + _likeLableBottomLine.lee_theme + .LeeAddBackgroundColor(DAY , SDColor(210, 210, 210, 1.0f)) + .LeeAddBackgroundColor(NIGHT , SDColor(60, 60, 60, 1.0f)); + +} + +- (void)setCommentItemsArray:(NSArray *)commentItemsArray +{ + _commentItemsArray = commentItemsArray; + + long originalLabelsCount = self.commentLabelsArray.count; + long needsToAddCount = commentItemsArray.count > originalLabelsCount ? (commentItemsArray.count - originalLabelsCount) : 0; + for (int i = 0; i < needsToAddCount; i++) { + MLLinkLabel *label = [MLLinkLabel new]; + UIColor *highLightColor = TimeLineCellHighlightedColor; + label.linkTextAttributes = @{NSForegroundColorAttributeName : highLightColor}; + label.lee_theme + .LeeAddTextColor(DAY , [UIColor blackColor]) + .LeeAddTextColor(NIGHT , [UIColor grayColor]); + label.font = [UIFont systemFontOfSize:14]; + label.delegate = self; + [self addSubview:label]; + [self.commentLabelsArray addObject:label]; + } + + for (int i = 0; i < commentItemsArray.count; i++) { + SDTimeLineCellCommentItemModel *model = commentItemsArray[i]; + MLLinkLabel *label = self.commentLabelsArray[i]; + if (!model.attributedContent) { + model.attributedContent = [self generateAttributedStringWithCommentItemModel:model]; + } + label.attributedText = model.attributedContent; + } +} + +- (void)setLikeItemsArray:(NSArray *)likeItemsArray +{ + _likeItemsArray = likeItemsArray; + + NSTextAttachment *attach = [NSTextAttachment new]; + attach.image = [UIImage imageNamed:@"Like"]; + attach.bounds = CGRectMake(0, -3, 16, 16); + NSAttributedString *likeIcon = [NSAttributedString attributedStringWithAttachment:attach]; + + NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithAttributedString:likeIcon]; + + for (int i = 0; i < likeItemsArray.count; i++) { + SDTimeLineCellLikeItemModel *model = likeItemsArray[i]; + if (i > 0) { + [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:@","]]; + } + if (!model.attributedContent) { + model.attributedContent = [self generateAttributedStringWithLikeItemModel:model]; + } + [attributedText appendAttributedString:model.attributedContent]; + } + + _likeLabel.attributedText = [attributedText copy]; + _likeLabel.hidden = likeItemsArray.count == 0; +} + +- (NSMutableArray *)commentLabelsArray +{ + if (!_commentLabelsArray) { + _commentLabelsArray = [NSMutableArray new]; + } + return _commentLabelsArray; +} + +- (void)setupWithLikeItemsArray:(NSArray *)likeItemsArray commentItemsArray:(NSArray *)commentItemsArray +{ + self.likeItemsArray = likeItemsArray; + self.commentItemsArray = commentItemsArray; + + if (self.commentLabelsArray.count) { + [self.commentLabelsArray enumerateObjectsUsingBlock:^(UILabel *label, NSUInteger idx, BOOL *stop) { + [label sd_clearAutoLayoutSettings]; + label.hidden = YES; //重用时先隐藏所以评论label,然后根据评论个数显示label + }]; + } + + if (!commentItemsArray.count && !likeItemsArray.count) { + self.fixedWidth = @(0); // 如果没有评论或者点赞,设置commentview的固定宽度为0(设置了fixedWith的控件将不再在自动布局过程中调整宽度) + self.fixedHeight = @(0); // 如果没有评论或者点赞,设置commentview的固定高度为0(设置了fixedHeight的控件将不再在自动布局过程中调整高度) + return; + } else { + self.fixedHeight = nil; // 取消固定宽度约束 + self.fixedWidth = nil; // 取消固定高度约束 + } + + CGFloat margin = 5; + + UIView *lastTopView = nil; + + if (likeItemsArray.count) { + _likeLabel.sd_resetLayout + .leftSpaceToView(self, margin) + .rightSpaceToView(self, margin) + .topSpaceToView(lastTopView, 10) + .autoHeightRatio(0); + + [_likeLabel sizeToFit]; + lastTopView = _likeLabel; + } else { + _likeLabel.attributedText = nil; + _likeLabel.sd_resetLayout + .heightIs(0); + } + + + if (self.commentItemsArray.count && self.likeItemsArray.count) { + _likeLableBottomLine.sd_resetLayout + .leftSpaceToView(self, 0) + .rightSpaceToView(self, 0) + .heightIs(1) + .topSpaceToView(lastTopView, 3); + + lastTopView = _likeLableBottomLine; + } else { + _likeLableBottomLine.sd_resetLayout.heightIs(0); + } + + for (int i = 0; i < self.commentItemsArray.count; i++) { + UILabel *label = (UILabel *)self.commentLabelsArray[i]; + label.hidden = NO; + CGFloat topMargin = (i == 0 && likeItemsArray.count == 0) ? 10 : 5; + label.sd_layout + .leftSpaceToView(self, 8) + .rightSpaceToView(self, 5) + .topSpaceToView(lastTopView, topMargin) + .autoHeightRatio(0); + + label.isAttributedContent = YES; + lastTopView = label; + } + + [self setupAutoHeightWithBottomView:lastTopView bottomMargin:6]; +} + +- (void)setFrame:(CGRect)frame +{ + [super setFrame:frame]; +} + +#pragma mark - private actions + +- (NSMutableAttributedString *)generateAttributedStringWithCommentItemModel:(SDTimeLineCellCommentItemModel *)model +{ + NSString *text = model.firstUserName; + if (model.secondUserName.length) { + text = [text stringByAppendingString:[NSString stringWithFormat:@"回复%@", model.secondUserName]]; + } + text = [text stringByAppendingString:[NSString stringWithFormat:@":%@", model.commentString]]; + NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:text]; + [attString setAttributes:@{NSLinkAttributeName : model.firstUserId} range:[text rangeOfString:model.firstUserName]]; + if (model.secondUserName) { + [attString setAttributes:@{NSLinkAttributeName : model.secondUserId} range:[text rangeOfString:model.secondUserName]]; + } + return attString; +} + +- (NSMutableAttributedString *)generateAttributedStringWithLikeItemModel:(SDTimeLineCellLikeItemModel *)model +{ + NSString *text = model.userName; + NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:text]; + UIColor *highLightColor = [UIColor blueColor]; + [attString setAttributes:@{NSForegroundColorAttributeName : highLightColor, NSLinkAttributeName : model.userId} range:[text rangeOfString:model.userName]]; + + return attString; +} + + +#pragma mark - MLLinkLabelDelegate + +- (void)didClickLink:(MLLink *)link linkText:(NSString *)linkText linkLabel:(MLLinkLabel *)linkLabel +{ + NSLog(@"%@", link.linkValue); +} + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/HeaderView/SDTimeLineTableHeaderView.h b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/HeaderView/SDTimeLineTableHeaderView.h new file mode 100644 index 0000000..a69cd78 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/HeaderView/SDTimeLineTableHeaderView.h @@ -0,0 +1,33 @@ +// +// SDTimeLineTableHeaderView.h +// GSD_WeiXin(wechat) +// +// Created by gsd on 16/2/25. +// Copyright © 2016年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import + +@interface SDTimeLineTableHeaderView : UIView + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/HeaderView/SDTimeLineTableHeaderView.m b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/HeaderView/SDTimeLineTableHeaderView.m new file mode 100644 index 0000000..e8c3339 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/HeaderView/SDTimeLineTableHeaderView.m @@ -0,0 +1,113 @@ +// +// SDTimeLineTableHeaderView.m +// GSD_WeiXin(wechat) +// +// Created by gsd on 16/2/25. +// Copyright © 2016年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import "SDTimeLineTableHeaderView.h" + +#import "UIView+SDAutoLayout.h" + +#import "GlobalDefines.h" + +#import "LEETheme.h" + +@implementation SDTimeLineTableHeaderView + +{ + UIImageView *_backgroundImageView; + UIImageView *_iconView; + UILabel *_nameLabel; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + + [self setup]; + + //设置主题 + + [self configTheme]; + + } + return self; +} + +- (void)setup +{ + + _backgroundImageView = [UIImageView new]; + _backgroundImageView.image = [UIImage imageNamed:@"pbg.jpg"]; + [self addSubview:_backgroundImageView]; + + _iconView = [UIImageView new]; + _iconView.image = [UIImage imageNamed:@"picon.jpg"]; + + _iconView.layer.lee_theme + .LeeAddBorderColor(DAY , [UIColor whiteColor]) + .LeeAddBorderColor(NIGHT , [UIColor blackColor]); + + _iconView.layer.borderWidth = 3; + [self addSubview:_iconView]; + + _nameLabel = [UILabel new]; + _nameLabel.text = @"GSD_iOS"; + _nameLabel.textColor = [UIColor whiteColor]; + _nameLabel.textAlignment = NSTextAlignmentRight; + _nameLabel.font = [UIFont boldSystemFontOfSize:15]; + [self addSubview:_nameLabel]; + + + _backgroundImageView.sd_layout.spaceToSuperView(UIEdgeInsetsMake(-60, 0, 40, 0)); + + _iconView.sd_layout + .widthIs(70) + .heightIs(70) + .rightSpaceToView(self, 15) + .bottomSpaceToView(self, 20); + + + _nameLabel.tag = 1000; + [_nameLabel setSingleLineAutoResizeWithMaxWidth:200]; + _nameLabel.sd_layout + .rightSpaceToView(_iconView, 20) + .bottomSpaceToView(_iconView, -35) + .heightIs(20); +} + +- (void)configTheme{ + + self.lee_theme + .LeeAddBackgroundColor(DAY , [UIColor whiteColor]) + .LeeAddBackgroundColor(NIGHT , [UIColor blackColor]); +} + +- (void)dealloc{ + + +} + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/OperationMenu/SDTimeLineCellOperationMenu.h b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/OperationMenu/SDTimeLineCellOperationMenu.h new file mode 100644 index 0000000..7d18a13 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/OperationMenu/SDTimeLineCellOperationMenu.h @@ -0,0 +1,19 @@ +// +// SDTimeLineCellOperationMenu.h +// GSD_WeiXin(wechat) +// +// Created by aier on 16/4/2. +// Copyright © 2016年 GSD. All rights reserved. +// + +#import + +@interface SDTimeLineCellOperationMenu : UIView + +@property (nonatomic, assign, getter = isShowing) BOOL show; + +@property (nonatomic, copy) void (^likeButtonClickedOperation)(void); +@property (nonatomic, copy) void (^commentButtonClickedOperation)(void); + + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/OperationMenu/SDTimeLineCellOperationMenu.m b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/OperationMenu/SDTimeLineCellOperationMenu.m new file mode 100644 index 0000000..65dd09e --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/OperationMenu/SDTimeLineCellOperationMenu.m @@ -0,0 +1,109 @@ +// +// SDTimeLineCellOperationMenu.m +// GSD_WeiXin(wechat) +// +// Created by aier on 16/4/2. +// Copyright © 2016年 GSD. All rights reserved. +// + +#import "SDTimeLineCellOperationMenu.h" +#import "UIView+SDAutoLayout.h" +#import "GlobalDefines.h" + +@implementation SDTimeLineCellOperationMenu +{ + UIButton *_likeButton; + UIButton *_commentButton; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + [self setup]; + } + return self; +} + +- (void)setup +{ + self.clipsToBounds = YES; + self.layer.cornerRadius = 5; + self.backgroundColor = SDColor(69, 74, 76, 1); + + _likeButton = [self creatButtonWithTitle:@"赞" image:[UIImage imageNamed:@"AlbumLike"] selImage:[UIImage imageNamed:@""] target:self selector:@selector(likeButtonClicked)]; + _commentButton = [self creatButtonWithTitle:@"评论" image:[UIImage imageNamed:@"AlbumComment"] selImage:[UIImage imageNamed:@""] target:self selector:@selector(commentButtonClicked)]; + + UIView *centerLine = [UIView new]; + centerLine.backgroundColor = [UIColor grayColor]; + + + [self sd_addSubviews:@[_likeButton, _commentButton, centerLine]]; + + CGFloat margin = 5; + + _likeButton.sd_layout + .leftSpaceToView(self, margin) + .topEqualToView(self) + .bottomEqualToView(self) + .widthIs(80); + + centerLine.sd_layout + .leftSpaceToView(_likeButton, margin) + .topSpaceToView(self, margin) + .bottomSpaceToView(self, margin) + .widthIs(1); + + _commentButton.sd_layout + .leftSpaceToView(centerLine, margin) + .topEqualToView(_likeButton) + .bottomEqualToView(_likeButton) + .widthRatioToView(_likeButton, 1); + +} + +- (UIButton *)creatButtonWithTitle:(NSString *)title image:(UIImage *)image selImage:(UIImage *)selImage target:(id)target selector:(SEL)sel +{ + UIButton *btn = [UIButton new]; + [btn setTitle:title forState:UIControlStateNormal]; + [btn setImage:image forState:UIControlStateNormal]; + [btn setImage:selImage forState:UIControlStateSelected]; + [btn addTarget:target action:sel forControlEvents:UIControlEventTouchUpInside]; + btn.titleLabel.font = [UIFont systemFontOfSize:14]; + btn.titleEdgeInsets = UIEdgeInsetsMake(0, 3, 0, 0); + return btn; +} + +- (void)likeButtonClicked +{ + if (self.likeButtonClickedOperation) { + self.likeButtonClickedOperation(); + } + self.show = NO; +} + +- (void)commentButtonClicked +{ + if (self.commentButtonClickedOperation) { + self.commentButtonClickedOperation(); + } + self.show = NO; +} + +- (void)setShow:(BOOL)show +{ + _show = show; + + [UIView animateWithDuration:0.2 animations:^{ + if (!show) { + [self clearAutoWidthSettings]; + self.sd_layout + .widthIs(0); + } else { + self.fixedWidth = nil; + [self setupAutoWidthWithRightView:self->_commentButton rightMargin:5]; + } + [self updateLayoutWithCellContentView:self.superview]; + }]; +} + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/PhotoContainer/SDWeiXinPhotoContainerView.h b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/PhotoContainer/SDWeiXinPhotoContainerView.h new file mode 100644 index 0000000..035383c --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/PhotoContainer/SDWeiXinPhotoContainerView.h @@ -0,0 +1,36 @@ +// +// SDWeiXinPhotoContainerView.h +// SDAutoLayout 测试 Demo +// +// Created by gsd on 15/12/23. +// Copyright © 2015年 gsd. All rights reserved. +// + + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import + +@interface SDWeiXinPhotoContainerView : UIView + +@property (nonatomic, strong) NSArray *picPathStringsArray; + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/PhotoContainer/SDWeiXinPhotoContainerView.m b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/PhotoContainer/SDWeiXinPhotoContainerView.m new file mode 100644 index 0000000..ac8654c --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/PhotoContainer/SDWeiXinPhotoContainerView.m @@ -0,0 +1,167 @@ +// +// SDWeiXinPhotoContainerView.m +// SDAutoLayout 测试 Demo +// +// Created by gsd on 15/12/23. +// Copyright © 2015年 gsd. All rights reserved. +// + + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import "SDWeiXinPhotoContainerView.h" + +#import "UIView+SDAutoLayout.h" + +#import "SDPhotoBrowser.h" + +@interface SDWeiXinPhotoContainerView () + +@property (nonatomic, strong) NSArray *imageViewsArray; + +@end + +@implementation SDWeiXinPhotoContainerView + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + [self setup]; + } + return self; +} + +- (void)setup +{ + NSMutableArray *temp = [NSMutableArray new]; + + for (int i = 0; i < 9; i++) { + UIImageView *imageView = [UIImageView new]; + [self addSubview:imageView]; + imageView.userInteractionEnabled = YES; + imageView.tag = i; + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapImageView:)]; + [imageView addGestureRecognizer:tap]; + [temp addObject:imageView]; + } + + self.imageViewsArray = [temp copy]; +} + + +- (void)setPicPathStringsArray:(NSArray *)picPathStringsArray +{ + _picPathStringsArray = picPathStringsArray; + + for (long i = _picPathStringsArray.count; i < self.imageViewsArray.count; i++) { + UIImageView *imageView = [self.imageViewsArray objectAtIndex:i]; + imageView.hidden = YES; + } + + if (_picPathStringsArray.count == 0) { + self.height_sd = 0; + self.fixedHeight = @(0); + return; + } + + CGFloat itemW = [self itemWidthForPicPathArray:_picPathStringsArray]; + CGFloat itemH = 0; + if (_picPathStringsArray.count == 1) { + UIImage *image = [UIImage imageNamed:_picPathStringsArray.firstObject]; + if (image.size.width) { + itemH = image.size.height / image.size.width * itemW; + } + } else { + itemH = itemW; + } + long perRowItemCount = [self perRowItemCountForPicPathArray:_picPathStringsArray]; + CGFloat margin = 5; + + [_picPathStringsArray enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + long columnIndex = idx % perRowItemCount; + long rowIndex = idx / perRowItemCount; + UIImageView *imageView = [self->_imageViewsArray objectAtIndex:idx]; + imageView.hidden = NO; + imageView.image = [UIImage imageNamed:obj]; + imageView.frame = CGRectMake(columnIndex * (itemW + margin), rowIndex * (itemH + margin), itemW, itemH); + }]; + + CGFloat w = perRowItemCount * itemW + (perRowItemCount - 1) * margin; + int columnCount = ceilf(_picPathStringsArray.count * 1.0 / perRowItemCount); + CGFloat h = columnCount * itemH + (columnCount - 1) * margin; + self.width_sd = w; + self.height_sd = h; + + self.fixedHeight = @(h); + self.fixedWidth = @(w); +} + +#pragma mark - private actions + +- (void)tapImageView:(UITapGestureRecognizer *)tap +{ + UIView *imageView = tap.view; + SDPhotoBrowser *browser = [[SDPhotoBrowser alloc] init]; + browser.currentImageIndex = imageView.tag; + browser.sourceImagesContainerView = self; + browser.imageCount = self.picPathStringsArray.count; + browser.delegate = self; + [browser show]; +} + +- (CGFloat)itemWidthForPicPathArray:(NSArray *)array +{ + if (array.count == 1) { + return 120; + } else { + CGFloat w = [UIScreen mainScreen].bounds.size.width > 320 ? 80 : 70; + return w; + } +} + +- (NSInteger)perRowItemCountForPicPathArray:(NSArray *)array +{ + if (array.count < 3) { + return array.count; + } else if (array.count <= 4) { + return 2; + } else { + return 3; + } +} + + +#pragma mark - SDPhotoBrowserDelegate + +- (NSURL *)photoBrowser:(SDPhotoBrowser *)browser highQualityImageURLForIndex:(NSInteger)index +{ + NSString *imageName = self.picPathStringsArray[index]; + NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil]; + return url; +} + +- (UIImage *)photoBrowser:(SDPhotoBrowser *)browser placeholderImageForIndex:(NSInteger)index +{ + UIImageView *imageView = self.subviews[index]; + return imageView.image; +} + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/TimeLineRefreshFooter/SDTimeLineRefreshFooter.h b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/TimeLineRefreshFooter/SDTimeLineRefreshFooter.h new file mode 100644 index 0000000..97ae4cd --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/TimeLineRefreshFooter/SDTimeLineRefreshFooter.h @@ -0,0 +1,22 @@ +// +// SDTimeLineRefreshFooter.h +// GSD_WeiXin(wechat) +// +// Created by aier on 16/3/6. +// Copyright © 2016年 GSD. All rights reserved. +// + +#import "SDBaseRefreshView.h" + +@interface SDTimeLineRefreshFooter : SDBaseRefreshView + ++ (instancetype)refreshFooterWithRefreshingText:(NSString *)text; + +- (void)addToScrollView:(UIScrollView *)scrollView refreshOpration:(void(^)(void))refrsh; + +@property (nonatomic, strong) UILabel *indicatorLabel; +@property (nonatomic, strong) UIActivityIndicatorView *indicator; + +@property (nonatomic, copy) void (^refreshBlock)(void); + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/TimeLineRefreshFooter/SDTimeLineRefreshFooter.m b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/TimeLineRefreshFooter/SDTimeLineRefreshFooter.m new file mode 100644 index 0000000..ef04228 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/TimeLineRefreshFooter/SDTimeLineRefreshFooter.m @@ -0,0 +1,111 @@ +// +// SDTimeLineRefreshFooter.m +// GSD_WeiXin(wechat) +// +// Created by aier on 16/3/6. +// Copyright © 2016年 GSD. All rights reserved. +// + +#import "SDTimeLineRefreshFooter.h" +#import "UIView+SDAutoLayout.h" + +#define kSDTimeLineRefreshFooterHeight 50 + +@implementation SDTimeLineRefreshFooter + ++ (instancetype)refreshFooterWithRefreshingText:(NSString *)text +{ + SDTimeLineRefreshFooter *footer = [SDTimeLineRefreshFooter new]; + footer.indicatorLabel.text = text; + return footer; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + [self setupView]; + } + return self; +} + +- (void)addToScrollView:(UIScrollView *)scrollView refreshOpration:(void (^)(void))refrsh +{ + self.scrollView = scrollView; + self.refreshBlock = refrsh; +} + +- (void)setupView +{ + UIView *containerView = [UIView new]; + [self addSubview:containerView]; + + self.indicatorLabel = [UILabel new]; + self.indicatorLabel.textColor = [UIColor lightGrayColor]; + self.indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + [self.indicator startAnimating]; + [containerView sd_addSubviews:@[self.indicatorLabel, self.indicator]]; + + containerView.sd_layout + .heightIs(20) + .centerYEqualToView(self) + .centerXEqualToView(self); + [containerView setupAutoWidthWithRightView:self.indicatorLabel rightMargin:0]; // 宽度自适应 + + self.indicator.sd_layout + .leftEqualToView(containerView) + .topEqualToView(containerView); // ActivityIndicatorView 宽高固定不用约束 + + self.indicatorLabel.sd_layout + .leftSpaceToView(self.indicator, 5) + .topEqualToView(containerView) + .bottomEqualToView(containerView); + [self.indicatorLabel setSingleLineAutoResizeWithMaxWidth:250]; // label宽度自适应 +} + +- (void)setScrollView:(UIScrollView *)scrollView +{ + [super setScrollView:scrollView]; + + [scrollView addSubview:self]; + self.hidden = YES; +} + +- (void)endRefreshing +{ + [super endRefreshing]; + + [UIView animateWithDuration:0.2 animations:^{ + self.scrollView.contentInset = self.scrollViewOriginalInsets; + }]; +} + +- (void)setRefreshState:(SDWXRefreshViewState)refreshState +{ + [super setRefreshState:refreshState]; + + if (refreshState == SDWXRefreshViewStateRefreshing) { + self.scrollViewOriginalInsets = self.scrollView.contentInset; + UIEdgeInsets insets = self.scrollView.contentInset; + insets.bottom += kSDTimeLineRefreshFooterHeight; + self.scrollView.contentInset = insets; + if (self.refreshBlock) { + self.refreshBlock(); + } + } +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if (keyPath != kSDBaseRefreshViewObserveKeyPath) return; + + if (self.scrollView.contentOffset.y > self.scrollView.contentSize.height - self.scrollView.height_sd && self.refreshState != SDWXRefreshViewStateRefreshing) { + self.frame = CGRectMake(0, self.scrollView.contentSize.height, self.scrollView.width_sd, kSDTimeLineRefreshFooterHeight); + self.hidden = NO; + self.refreshState = SDWXRefreshViewStateRefreshing; + } else if (self.refreshState == SDWXRefreshViewStateNormal) { + self.hidden = YES; + } +} + + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/TimeLineRefreshHeader/SDTimeLineRefreshHeader.h b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/TimeLineRefreshHeader/SDTimeLineRefreshHeader.h new file mode 100644 index 0000000..22ebe73 --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/TimeLineRefreshHeader/SDTimeLineRefreshHeader.h @@ -0,0 +1,38 @@ +// +// SDTimeLineRefreshHeader.h +// GSD_WeiXin(wechat) +// +// Created by gsd on 16/3/5. +// Copyright © 2016年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import +#import "SDBaseRefreshView.h" + +@interface SDTimeLineRefreshHeader : SDBaseRefreshView + ++ (instancetype)refreshHeaderWithCenter:(CGPoint)center; + +@property (nonatomic, copy) void(^refreshingBlock)(void); + +@end diff --git a/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/TimeLineRefreshHeader/SDTimeLineRefreshHeader.m b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/TimeLineRefreshHeader/SDTimeLineRefreshHeader.m new file mode 100644 index 0000000..54793ab --- /dev/null +++ b/SDAutoLayoutDemo/DemoVC/DemoVC9/TimeLine/Views/TimeLineRefreshHeader/SDTimeLineRefreshHeader.m @@ -0,0 +1,121 @@ +// +// SDTimeLineRefreshHeader.m +// GSD_WeiXin(wechat) +// +// Created by gsd on 16/3/5. +// Copyright © 2016年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * GSD_WeiXin + * + * QQ交流群: 459274049 + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios/GSD_WeiXin + * 新浪微博:GSD_iOS + * + * 此“高仿微信”用到了很高效方便的自动布局库SDAutoLayout(一行代码搞定自动布局) + * SDAutoLayout地址:https://github.com/gsdios/SDAutoLayout + * SDAutoLayout视频教程:http://www.letv.com/ptv/vplay/24038772.html + * SDAutoLayout用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * + ********************************************************************************* + + */ + +#import "SDTimeLineRefreshHeader.h" + +static const CGFloat criticalY = -60.f; + +#define kSDTimeLineRefreshHeaderRotateAnimationKey @"RotateAnimationKey" + +@implementation SDTimeLineRefreshHeader +{ + CABasicAnimation *_rotateAnimation; +} + ++ (instancetype)refreshHeaderWithCenter:(CGPoint)center +{ + SDTimeLineRefreshHeader *header = [SDTimeLineRefreshHeader new]; + header.center = center; + return header; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + [self setupView]; + } + return self; +} + +- (void)setupView +{ + self.backgroundColor = [UIColor clearColor]; + + UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"AlbumReflashIcon"]]; + self.bounds = imageView.bounds; + [self addSubview:imageView]; + + _rotateAnimation = [[CABasicAnimation alloc] init]; + _rotateAnimation.keyPath = @"transform.rotation.z"; + _rotateAnimation.fromValue = @0; + _rotateAnimation.toValue = @(M_PI * 2); + _rotateAnimation.duration = 1.0; + _rotateAnimation.repeatCount = MAXFLOAT; +} + +- (void)setRefreshState:(SDWXRefreshViewState)refreshState +{ + [super setRefreshState:refreshState]; + + if (refreshState == SDWXRefreshViewStateRefreshing) { + if (self.refreshingBlock) { + self.refreshingBlock(); + } + [self.layer addAnimation:_rotateAnimation forKey:kSDTimeLineRefreshHeaderRotateAnimationKey]; + } else if (refreshState == SDWXRefreshViewStateNormal) { + [self.layer removeAnimationForKey:kSDTimeLineRefreshHeaderRotateAnimationKey]; + [UIView animateWithDuration:0.3 animations:^{ + self.transform = CGAffineTransformIdentity; + }]; + } +} + + +- (void)updateRefreshHeaderWithOffsetY:(CGFloat)y +{ + + CGFloat rotateValue = y / 50.0 * M_PI; + + if (y < criticalY) { + y = criticalY; + + if (self.scrollView.isDragging && self.refreshState != SDWXRefreshViewStateWillRefresh) { + self.refreshState = SDWXRefreshViewStateWillRefresh; + } else if (!self.scrollView.isDragging && self.refreshState == SDWXRefreshViewStateWillRefresh) { + self.refreshState = SDWXRefreshViewStateRefreshing; + } + } + + if (self.refreshState == SDWXRefreshViewStateRefreshing) return; + + CGAffineTransform transform = CGAffineTransformIdentity; + transform = CGAffineTransformTranslate(transform, 0, -y); + transform = CGAffineTransformRotate(transform, rotateValue); + + self.transform = transform; + +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if (keyPath != kSDBaseRefreshViewObserveKeyPath) return; + + [self updateRefreshHeaderWithOffsetY:self.scrollView.contentOffset.y]; +} + +@end diff --git a/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/Contents.json b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..12247f0 --- /dev/null +++ b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "icon-20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "icon-20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "icon-29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "icon-29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "icon-40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "icon-40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "icon-60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "icon-60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "icon-20.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "icon-20@2x-1.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "icon-29.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "icon-29@2x-1.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "icon-40.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "icon-40@2x-1.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "icon-76.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "icon-76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "icon-83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "icon.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-20.png b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-20.png new file mode 100644 index 0000000..1f8fef9 Binary files /dev/null and b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-20.png differ diff --git a/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-20@2x-1.png b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-20@2x-1.png new file mode 100644 index 0000000..ccbea68 Binary files /dev/null and b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-20@2x-1.png differ diff --git a/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-20@2x.png b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-20@2x.png new file mode 100644 index 0000000..ccbea68 Binary files /dev/null and b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-20@2x.png differ diff --git a/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-20@3x.png b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-20@3x.png new file mode 100644 index 0000000..54c8102 Binary files /dev/null and b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-20@3x.png differ diff --git a/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-29.png b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-29.png new file mode 100644 index 0000000..8359a2d Binary files /dev/null and b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-29.png differ diff --git a/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-29@2x-1.png b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-29@2x-1.png new file mode 100644 index 0000000..4db14ed Binary files /dev/null and b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-29@2x-1.png differ diff --git a/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-29@2x.png b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-29@2x.png new file mode 100644 index 0000000..4db14ed Binary files /dev/null and b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-29@2x.png differ diff --git a/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-29@3x.png b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-29@3x.png new file mode 100644 index 0000000..b87e74a Binary files /dev/null and b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-29@3x.png differ diff --git a/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-40.png b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-40.png new file mode 100644 index 0000000..ccbea68 Binary files /dev/null and b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-40.png differ diff --git a/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-40@2x-1.png b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-40@2x-1.png new file mode 100644 index 0000000..acc4732 Binary files /dev/null and b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-40@2x-1.png differ diff --git a/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-40@2x.png b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-40@2x.png new file mode 100644 index 0000000..acc4732 Binary files /dev/null and b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-40@2x.png differ diff --git a/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-40@3x.png b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-40@3x.png new file mode 100644 index 0000000..f158d6d Binary files /dev/null and b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-40@3x.png differ diff --git a/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-60@2x.png b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-60@2x.png new file mode 100644 index 0000000..f158d6d Binary files /dev/null and b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-60@2x.png differ diff --git a/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-60@3x.png b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-60@3x.png new file mode 100644 index 0000000..d5c8a37 Binary files /dev/null and b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-60@3x.png differ diff --git a/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-76.png b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-76.png new file mode 100644 index 0000000..39871c5 Binary files /dev/null and b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-76.png differ diff --git a/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-76@2x.png b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-76@2x.png new file mode 100644 index 0000000..ac87791 Binary files /dev/null and b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-76@2x.png differ diff --git a/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-83.5@2x.png new file mode 100644 index 0000000..55de275 Binary files /dev/null and b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon-83.5@2x.png differ diff --git a/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon.png b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon.png new file mode 100644 index 0000000..393611f Binary files /dev/null and b/SDAutoLayoutDemo/Images.xcassets/AppIcon.appiconset/icon.png differ diff --git a/SDAutoLayoutDemo/Images.xcassets/Contents.json b/SDAutoLayoutDemo/Images.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/SDAutoLayoutDemo/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images.xcassets/LaunchImage.launchimage/Contents.json b/SDAutoLayoutDemo/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000..a96f2a2 --- /dev/null +++ b/SDAutoLayoutDemo/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "extent" : "full-screen", + "idiom" : "iphone", + "subtype" : "retina4", + "filename" : "Launch.png", + "minimum-system-version" : "7.0", + "orientation" : "portrait", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images.xcassets/LaunchImage.launchimage/Launch.png b/SDAutoLayoutDemo/Images.xcassets/LaunchImage.launchimage/Launch.png new file mode 100644 index 0000000..c84361d Binary files /dev/null and b/SDAutoLayoutDemo/Images.xcassets/LaunchImage.launchimage/Launch.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Action_JD_mainMenu.imageset/Action_JD_mainMenu@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/Action_JD_mainMenu.imageset/Action_JD_mainMenu@2x.png new file mode 100755 index 0000000..5bdf2b2 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/Action_JD_mainMenu.imageset/Action_JD_mainMenu@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Action_JD_mainMenu.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/Action_JD_mainMenu.imageset/Contents.json new file mode 100755 index 0000000..c49725e --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/Action_JD_mainMenu.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "Action_JD_mainMenu@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/CreditCard_ShoppingBag.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/CreditCard_ShoppingBag.imageset/Contents.json new file mode 100755 index 0000000..f499cb2 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/CreditCard_ShoppingBag.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "CreditCard_ShoppingBag@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/CreditCard_ShoppingBag.imageset/CreditCard_ShoppingBag@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/CreditCard_ShoppingBag.imageset/CreditCard_ShoppingBag@2x.png new file mode 100755 index 0000000..658c460 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/CreditCard_ShoppingBag.imageset/CreditCard_ShoppingBag@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/FootStep.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/FootStep.imageset/Contents.json new file mode 100755 index 0000000..5bb5bff --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/FootStep.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "FootStep@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/FootStep.imageset/FootStep@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/FootStep.imageset/FootStep@2x.png new file mode 100755 index 0000000..4de358d Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/FootStep.imageset/FootStep@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/MoreGame.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/MoreGame.imageset/Contents.json new file mode 100755 index 0000000..ad95ed4 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/MoreGame.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "MoreGame.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "MoreGame@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x", + "filename" : "MoreGame@3x.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/MoreGame.imageset/MoreGame.png b/SDAutoLayoutDemo/Images/Discover.xcassets/MoreGame.imageset/MoreGame.png new file mode 100755 index 0000000..aed95e5 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/MoreGame.imageset/MoreGame.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/MoreGame.imageset/MoreGame@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/MoreGame.imageset/MoreGame@2x.png new file mode 100755 index 0000000..e8d7a43 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/MoreGame.imageset/MoreGame@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/MoreGame.imageset/MoreGame@3x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/MoreGame.imageset/MoreGame@3x.png new file mode 100755 index 0000000..73a5d38 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/MoreGame.imageset/MoreGame@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ShakeHideImg_women.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/ShakeHideImg_women.imageset/Contents.json new file mode 100755 index 0000000..fde6c0b --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/ShakeHideImg_women.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "ShakeHideImg_women@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ShakeHideImg_women.imageset/ShakeHideImg_women@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/ShakeHideImg_women.imageset/ShakeHideImg_women@2x.png new file mode 100755 index 0000000..7388547 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/ShakeHideImg_women.imageset/ShakeHideImg_women@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Line_Down.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Line_Down.imageset/Contents.json new file mode 100755 index 0000000..808a9c8 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Line_Down.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "Shake_Line_Down@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Line_Down.imageset/Shake_Line_Down@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Line_Down.imageset/Shake_Line_Down@2x.png new file mode 100755 index 0000000..07b3cce Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Line_Down.imageset/Shake_Line_Down@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Line_Up.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Line_Up.imageset/Contents.json new file mode 100755 index 0000000..fdb84eb --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Line_Up.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "Shake_Line_Up@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Line_Up.imageset/Shake_Line_Up@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Line_Up.imageset/Shake_Line_Up@2x.png new file mode 100755 index 0000000..55faff9 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Line_Up.imageset/Shake_Line_Up@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Logo_Down.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Logo_Down.imageset/Contents.json new file mode 100755 index 0000000..348bd1b --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Logo_Down.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "Shake_Logo_Down@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Logo_Down.imageset/Shake_Logo_Down@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Logo_Down.imageset/Shake_Logo_Down@2x.png new file mode 100755 index 0000000..63d08f7 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Logo_Down.imageset/Shake_Logo_Down@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Logo_Up.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Logo_Up.imageset/Contents.json new file mode 100755 index 0000000..f79eb55 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Logo_Up.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "Shake_Logo_Up@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Logo_Up.imageset/Shake_Logo_Up@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Logo_Up.imageset/Shake_Logo_Up@2x.png new file mode 100755 index 0000000..644b272 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_Logo_Up.imageset/Shake_Logo_Up@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_music.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_music.imageset/Contents.json new file mode 100755 index 0000000..fefbd45 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_music.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "Shake_icon_music@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_music.imageset/Shake_icon_music@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_music.imageset/Shake_icon_music@2x.png new file mode 100755 index 0000000..ccf5895 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_music.imageset/Shake_icon_music@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_musicHL.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_musicHL.imageset/Contents.json new file mode 100755 index 0000000..4c6b7c1 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_musicHL.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "Shake_icon_musicHL@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_musicHL.imageset/Shake_icon_musicHL@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_musicHL.imageset/Shake_icon_musicHL@2x.png new file mode 100755 index 0000000..a128cb5 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_musicHL.imageset/Shake_icon_musicHL@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_people.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_people.imageset/Contents.json new file mode 100755 index 0000000..14675df --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_people.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "Shake_icon_people@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_people.imageset/Shake_icon_people@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_people.imageset/Shake_icon_people@2x.png new file mode 100755 index 0000000..65468d1 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_people.imageset/Shake_icon_people@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_peopleHL.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_peopleHL.imageset/Contents.json new file mode 100755 index 0000000..9ba17cd --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_peopleHL.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "Shake_icon_peopleHL@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_peopleHL.imageset/Shake_icon_peopleHL@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_peopleHL.imageset/Shake_icon_peopleHL@2x.png new file mode 100755 index 0000000..86621a2 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_peopleHL.imageset/Shake_icon_peopleHL@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_tv.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_tv.imageset/Contents.json new file mode 100755 index 0000000..c7e5b22 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_tv.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "Shake_icon_tv@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_tv.imageset/Shake_icon_tv@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_tv.imageset/Shake_icon_tv@2x.png new file mode 100755 index 0000000..bbca787 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_tv.imageset/Shake_icon_tv@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_tvHL.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_tvHL.imageset/Contents.json new file mode 100755 index 0000000..018d4d4 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_tvHL.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "Shake_icon_tvHL@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_tvHL.imageset/Shake_icon_tvHL@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_tvHL.imageset/Shake_icon_tvHL@2x.png new file mode 100755 index 0000000..1a19b4c Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/Shake_icon_tvHL.imageset/Shake_icon_tvHL@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumAddBtn.imageset/AlbumAddBtn@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumAddBtn.imageset/AlbumAddBtn@2x.png new file mode 100644 index 0000000..b458d89 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumAddBtn.imageset/AlbumAddBtn@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumAddBtn.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumAddBtn.imageset/Contents.json new file mode 100644 index 0000000..bcfdb24 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumAddBtn.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "AlbumAddBtn@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumComment.imageset/AlbumComment@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumComment.imageset/AlbumComment@2x.png new file mode 100644 index 0000000..92e2585 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumComment.imageset/AlbumComment@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumComment.imageset/AlbumComment@3x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumComment.imageset/AlbumComment@3x.png new file mode 100644 index 0000000..4337aa5 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumComment.imageset/AlbumComment@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumComment.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumComment.imageset/Contents.json new file mode 100644 index 0000000..afa9b59 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumComment.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "AlbumComment@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "AlbumComment@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumLike.imageset/AlbumLike@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumLike.imageset/AlbumLike@2x.png new file mode 100644 index 0000000..9246647 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumLike.imageset/AlbumLike@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumLike.imageset/AlbumLike@3x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumLike.imageset/AlbumLike@3x.png new file mode 100644 index 0000000..0f1e12b Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumLike.imageset/AlbumLike@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumLike.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumLike.imageset/Contents.json new file mode 100644 index 0000000..b56ec74 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumLike.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "AlbumLike@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "AlbumLike@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMore.imageset/AlbumOperateMore@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMore.imageset/AlbumOperateMore@2x.png new file mode 100644 index 0000000..d42e9ab Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMore.imageset/AlbumOperateMore@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMore.imageset/AlbumOperateMore@3x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMore.imageset/AlbumOperateMore@3x.png new file mode 100644 index 0000000..bbc3551 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMore.imageset/AlbumOperateMore@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMore.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMore.imageset/Contents.json new file mode 100644 index 0000000..c94bdf9 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMore.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "AlbumOperateMore@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "AlbumOperateMore@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMoreHL.imageset/AlbumOperateMoreHL@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMoreHL.imageset/AlbumOperateMoreHL@2x.png new file mode 100644 index 0000000..40df750 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMoreHL.imageset/AlbumOperateMoreHL@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMoreHL.imageset/AlbumOperateMoreHL@3x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMoreHL.imageset/AlbumOperateMoreHL@3x.png new file mode 100644 index 0000000..c9a4739 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMoreHL.imageset/AlbumOperateMoreHL@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMoreHL.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMoreHL.imageset/Contents.json new file mode 100644 index 0000000..c4135eb --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMoreHL.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "AlbumOperateMoreHL@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "AlbumOperateMoreHL@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMoreViewBkg.imageset/AlbumOperateMoreViewBkg@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMoreViewBkg.imageset/AlbumOperateMoreViewBkg@2x.png new file mode 100644 index 0000000..c14a097 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMoreViewBkg.imageset/AlbumOperateMoreViewBkg@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMoreViewBkg.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMoreViewBkg.imageset/Contents.json new file mode 100644 index 0000000..5d9c07e --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumOperateMoreViewBkg.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "AlbumOperateMoreViewBkg@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumReflashIcon.imageset/AlbumReflashIcon@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumReflashIcon.imageset/AlbumReflashIcon@2x.png new file mode 100644 index 0000000..eeb16fa Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumReflashIcon.imageset/AlbumReflashIcon@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumReflashIcon.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumReflashIcon.imageset/Contents.json new file mode 100644 index 0000000..9a050db --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/AlbumReflashIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "AlbumReflashIcon@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Camera.imageset/Camera@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Camera.imageset/Camera@2x.png new file mode 100644 index 0000000..85de12d Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Camera.imageset/Camera@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Camera.imageset/Camera@3x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Camera.imageset/Camera@3x.png new file mode 100644 index 0000000..58026ec Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Camera.imageset/Camera@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Camera.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Camera.imageset/Contents.json new file mode 100644 index 0000000..e9bc18f --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Camera.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Camera@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Camera@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Like.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Like.imageset/Contents.json new file mode 100644 index 0000000..5d2aada --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Like.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Like@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Like@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Like.imageset/Like@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Like.imageset/Like@2x.png new file mode 100644 index 0000000..24c3dba Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Like.imageset/Like@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Like.imageset/Like@3x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Like.imageset/Like@3x.png new file mode 100644 index 0000000..eab5b81 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/Like.imageset/Like@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/LikeCmtBg.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/LikeCmtBg.imageset/Contents.json new file mode 100644 index 0000000..ec18268 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/LikeCmtBg.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LikeCmtBg@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LikeCmtBg@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/LikeCmtBg.imageset/LikeCmtBg@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/LikeCmtBg.imageset/LikeCmtBg@2x.png new file mode 100644 index 0000000..42c949e Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/LikeCmtBg.imageset/LikeCmtBg@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/LikeCmtBg.imageset/LikeCmtBg@3x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/LikeCmtBg.imageset/LikeCmtBg@3x.png new file mode 100644 index 0000000..77d09ca Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/TimeLine/LikeCmtBg.imageset/LikeCmtBg@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBkg.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBkg.imageset/Contents.json new file mode 100755 index 0000000..5a2c736 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBkg.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "bottleBkg@2x.jpg" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBkg.imageset/bottleBkg@2x.jpg b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBkg.imageset/bottleBkg@2x.jpg new file mode 100755 index 0000000..d8da4e2 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBkg.imageset/bottleBkg@2x.jpg differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBkgSpotLight.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBkgSpotLight.imageset/Contents.json new file mode 100755 index 0000000..bdcbe60 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBkgSpotLight.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "bottleBkgSpotLight@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBkgSpotLight.imageset/bottleBkgSpotLight@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBkgSpotLight.imageset/bottleBkgSpotLight@2x.png new file mode 100755 index 0000000..4959ea3 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBkgSpotLight.imageset/bottleBkgSpotLight@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBoard.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBoard.imageset/Contents.json new file mode 100755 index 0000000..926bf79 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBoard.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "bottleBoard.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "bottleBoard@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBoard.imageset/bottleBoard.png b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBoard.imageset/bottleBoard.png new file mode 100755 index 0000000..154d6ff Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBoard.imageset/bottleBoard.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBoard.imageset/bottleBoard@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBoard.imageset/bottleBoard@2x.png new file mode 100755 index 0000000..3dfb06b Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleBoard.imageset/bottleBoard@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonFish.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonFish.imageset/Contents.json new file mode 100755 index 0000000..471fe3f --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonFish.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "bottleButtonFish.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "bottleButtonFish@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonFish.imageset/bottleButtonFish.png b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonFish.imageset/bottleButtonFish.png new file mode 100755 index 0000000..1e8632c Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonFish.imageset/bottleButtonFish.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonFish.imageset/bottleButtonFish@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonFish.imageset/bottleButtonFish@2x.png new file mode 100755 index 0000000..962d620 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonFish.imageset/bottleButtonFish@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonMine.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonMine.imageset/Contents.json new file mode 100755 index 0000000..5bc8870 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonMine.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "bottleButtonMine.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "bottleButtonMine@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonMine.imageset/bottleButtonMine.png b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonMine.imageset/bottleButtonMine.png new file mode 100755 index 0000000..df95366 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonMine.imageset/bottleButtonMine.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonMine.imageset/bottleButtonMine@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonMine.imageset/bottleButtonMine@2x.png new file mode 100755 index 0000000..56e8f70 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonMine.imageset/bottleButtonMine@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonThrow.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonThrow.imageset/Contents.json new file mode 100755 index 0000000..6dd97a1 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonThrow.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "bottleButtonThrow.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "bottleButtonThrow@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonThrow.imageset/bottleButtonThrow.png b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonThrow.imageset/bottleButtonThrow.png new file mode 100755 index 0000000..74f6dd5 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonThrow.imageset/bottleButtonThrow.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonThrow.imageset/bottleButtonThrow@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonThrow.imageset/bottleButtonThrow@2x.png new file mode 100755 index 0000000..45d4ac1 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleButtonThrow.imageset/bottleButtonThrow@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleNightBkg.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleNightBkg.imageset/Contents.json new file mode 100755 index 0000000..60b2b99 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleNightBkg.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "bottleNightBkg@2x.jpg" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleNightBkg.imageset/bottleNightBkg@2x.jpg b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleNightBkg.imageset/bottleNightBkg@2x.jpg new file mode 100755 index 0000000..cc2611b Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleNightBkg.imageset/bottleNightBkg@2x.jpg differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleRecord.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleRecord.imageset/Contents.json new file mode 100755 index 0000000..5e913f6 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleRecord.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "bottleRecord@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/bottleRecord.imageset/bottleRecord@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleRecord.imageset/bottleRecord@2x.png new file mode 100755 index 0000000..47a7cf7 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/bottleRecord.imageset/bottleRecord@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconBottle.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconBottle.imageset/Contents.json new file mode 100755 index 0000000..c9e472f --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconBottle.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "ff_IconBottle.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "ff_IconBottle@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x", + "filename" : "ff_IconBottle@3x.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconBottle.imageset/ff_IconBottle.png b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconBottle.imageset/ff_IconBottle.png new file mode 100755 index 0000000..7e97c06 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconBottle.imageset/ff_IconBottle.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconBottle.imageset/ff_IconBottle@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconBottle.imageset/ff_IconBottle@2x.png new file mode 100755 index 0000000..957ab77 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconBottle.imageset/ff_IconBottle@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconBottle.imageset/ff_IconBottle@3x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconBottle.imageset/ff_IconBottle@3x.png new file mode 100755 index 0000000..75fd0e0 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconBottle.imageset/ff_IconBottle@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconLocationService.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconLocationService.imageset/Contents.json new file mode 100755 index 0000000..dabcd3d --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconLocationService.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "ff_IconLocationService.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "ff_IconLocationService@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x", + "filename" : "ff_IconLocationService@3x.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconLocationService.imageset/ff_IconLocationService.png b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconLocationService.imageset/ff_IconLocationService.png new file mode 100755 index 0000000..147b133 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconLocationService.imageset/ff_IconLocationService.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconLocationService.imageset/ff_IconLocationService@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconLocationService.imageset/ff_IconLocationService@2x.png new file mode 100755 index 0000000..1594024 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconLocationService.imageset/ff_IconLocationService@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconLocationService.imageset/ff_IconLocationService@3x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconLocationService.imageset/ff_IconLocationService@3x.png new file mode 100755 index 0000000..2c2359b Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconLocationService.imageset/ff_IconLocationService@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconQRCode.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconQRCode.imageset/Contents.json new file mode 100755 index 0000000..6a38b9c --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconQRCode.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "ff_IconQRCode.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "ff_IconQRCode@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x", + "filename" : "ff_IconQRCode@3x.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconQRCode.imageset/ff_IconQRCode.png b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconQRCode.imageset/ff_IconQRCode.png new file mode 100755 index 0000000..27c11c2 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconQRCode.imageset/ff_IconQRCode.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconQRCode.imageset/ff_IconQRCode@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconQRCode.imageset/ff_IconQRCode@2x.png new file mode 100755 index 0000000..4d51665 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconQRCode.imageset/ff_IconQRCode@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconQRCode.imageset/ff_IconQRCode@3x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconQRCode.imageset/ff_IconQRCode@3x.png new file mode 100755 index 0000000..aeb1da6 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconQRCode.imageset/ff_IconQRCode@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShake.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShake.imageset/Contents.json new file mode 100755 index 0000000..e4d16c7 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShake.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "ff_IconShake.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "ff_IconShake@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x", + "filename" : "ff_IconShake@3x.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShake.imageset/ff_IconShake.png b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShake.imageset/ff_IconShake.png new file mode 100755 index 0000000..a463e82 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShake.imageset/ff_IconShake.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShake.imageset/ff_IconShake@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShake.imageset/ff_IconShake@2x.png new file mode 100755 index 0000000..8a53a88 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShake.imageset/ff_IconShake@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShake.imageset/ff_IconShake@3x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShake.imageset/ff_IconShake@3x.png new file mode 100755 index 0000000..1cbc980 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShake.imageset/ff_IconShake@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShowAlbum.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShowAlbum.imageset/Contents.json new file mode 100755 index 0000000..8aa6f41 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShowAlbum.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "ff_IconShowAlbum.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "ff_IconShowAlbum@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x", + "filename" : "ff_IconShowAlbum@3x.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShowAlbum.imageset/ff_IconShowAlbum.png b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShowAlbum.imageset/ff_IconShowAlbum.png new file mode 100755 index 0000000..f720506 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShowAlbum.imageset/ff_IconShowAlbum.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShowAlbum.imageset/ff_IconShowAlbum@2x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShowAlbum.imageset/ff_IconShowAlbum@2x.png new file mode 100755 index 0000000..cbcaae4 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShowAlbum.imageset/ff_IconShowAlbum@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShowAlbum.imageset/ff_IconShowAlbum@3x.png b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShowAlbum.imageset/ff_IconShowAlbum@3x.png new file mode 100755 index 0000000..302a0bc Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/ff_IconShowAlbum.imageset/ff_IconShowAlbum@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/game_tag_icon.imageset/1.png b/SDAutoLayoutDemo/Images/Discover.xcassets/game_tag_icon.imageset/1.png new file mode 100755 index 0000000..cbcfe3f Binary files /dev/null and b/SDAutoLayoutDemo/Images/Discover.xcassets/game_tag_icon.imageset/1.png differ diff --git a/SDAutoLayoutDemo/Images/Discover.xcassets/game_tag_icon.imageset/Contents.json b/SDAutoLayoutDemo/Images/Discover.xcassets/game_tag_icon.imageset/Contents.json new file mode 100755 index 0000000..d036d40 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Discover.xcassets/game_tag_icon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "1.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverAppNodeBkg.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverAppNodeBkg.imageset/Contents.json new file mode 100644 index 0000000..a50af5c --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverAppNodeBkg.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ReceiverAppNodeBkg@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverAppNodeBkg.imageset/ReceiverAppNodeBkg@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverAppNodeBkg.imageset/ReceiverAppNodeBkg@2x.png new file mode 100644 index 0000000..de3c4c5 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverAppNodeBkg.imageset/ReceiverAppNodeBkg@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverAppNodeBkg_HL.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverAppNodeBkg_HL.imageset/Contents.json new file mode 100644 index 0000000..d62e8b4 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverAppNodeBkg_HL.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ReceiverAppNodeBkg_HL@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ReceiverAppNodeBkg_HL@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverAppNodeBkg_HL.imageset/ReceiverAppNodeBkg_HL@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverAppNodeBkg_HL.imageset/ReceiverAppNodeBkg_HL@2x.png new file mode 100644 index 0000000..2fc61be Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverAppNodeBkg_HL.imageset/ReceiverAppNodeBkg_HL@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverAppNodeBkg_HL.imageset/ReceiverAppNodeBkg_HL@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverAppNodeBkg_HL.imageset/ReceiverAppNodeBkg_HL@3x.png new file mode 100644 index 0000000..36d0e6c Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverAppNodeBkg_HL.imageset/ReceiverAppNodeBkg_HL@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverTextNodeBkg.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverTextNodeBkg.imageset/Contents.json new file mode 100644 index 0000000..ff19de7 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverTextNodeBkg.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ReceiverTextNodeBkg@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ReceiverTextNodeBkg@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverTextNodeBkg.imageset/ReceiverTextNodeBkg@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverTextNodeBkg.imageset/ReceiverTextNodeBkg@2x.png new file mode 100644 index 0000000..56ccc55 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverTextNodeBkg.imageset/ReceiverTextNodeBkg@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverTextNodeBkg.imageset/ReceiverTextNodeBkg@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverTextNodeBkg.imageset/ReceiverTextNodeBkg@3x.png new file mode 100644 index 0000000..c447922 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverTextNodeBkg.imageset/ReceiverTextNodeBkg@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverTextNodeBkgHL.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverTextNodeBkgHL.imageset/Contents.json new file mode 100644 index 0000000..707db8c --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverTextNodeBkgHL.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ReceiverTextNodeBkgHL@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ReceiverTextNodeBkgHL@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverTextNodeBkgHL.imageset/ReceiverTextNodeBkgHL@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverTextNodeBkgHL.imageset/ReceiverTextNodeBkgHL@2x.png new file mode 100644 index 0000000..3ef3231 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverTextNodeBkgHL.imageset/ReceiverTextNodeBkgHL@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverTextNodeBkgHL.imageset/ReceiverTextNodeBkgHL@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverTextNodeBkgHL.imageset/ReceiverTextNodeBkgHL@3x.png new file mode 100644 index 0000000..67f0951 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ReceiverTextNodeBkgHL.imageset/ReceiverTextNodeBkgHL@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanBook.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanBook.imageset/Contents.json new file mode 100644 index 0000000..1d96809 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanBook.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanBook@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ScanBook@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanBook.imageset/ScanBook@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanBook.imageset/ScanBook@2x.png new file mode 100644 index 0000000..923c9b0 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanBook.imageset/ScanBook@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanBook.imageset/ScanBook@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanBook.imageset/ScanBook@3x.png new file mode 100644 index 0000000..9581598 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanBook.imageset/ScanBook@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanBook_HL.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanBook_HL.imageset/Contents.json new file mode 100644 index 0000000..730b1c3 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanBook_HL.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanBook_HL@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ScanBook_HL@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanBook_HL.imageset/ScanBook_HL@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanBook_HL.imageset/ScanBook_HL@2x.png new file mode 100644 index 0000000..cda2dc4 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanBook_HL.imageset/ScanBook_HL@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanBook_HL.imageset/ScanBook_HL@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanBook_HL.imageset/ScanBook_HL@3x.png new file mode 100644 index 0000000..affc367 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanBook_HL.imageset/ScanBook_HL@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanBtombarBG.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanBtombarBG.imageset/Contents.json new file mode 100644 index 0000000..7a95d8b --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanBtombarBG.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanBtombarBG@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanBtombarBG.imageset/ScanBtombarBG@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanBtombarBG.imageset/ScanBtombarBG@2x.png new file mode 100644 index 0000000..9f9d809 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanBtombarBG.imageset/ScanBtombarBG@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetaiPictureRectangle.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetaiPictureRectangle.imageset/Contents.json new file mode 100644 index 0000000..d252816 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetaiPictureRectangle.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanDetaiPictureRectangle@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetaiPictureRectangle.imageset/ScanDetaiPictureRectangle@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetaiPictureRectangle.imageset/ScanDetaiPictureRectangle@2x.png new file mode 100644 index 0000000..70efde2 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetaiPictureRectangle.imageset/ScanDetaiPictureRectangle@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetaiPictureSquare.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetaiPictureSquare.imageset/Contents.json new file mode 100644 index 0000000..17d04bd --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetaiPictureSquare.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanDetaiPictureSquare@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetaiPictureSquare.imageset/ScanDetaiPictureSquare@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetaiPictureSquare.imageset/ScanDetaiPictureSquare@2x.png new file mode 100644 index 0000000..9000ce3 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetaiPictureSquare.imageset/ScanDetaiPictureSquare@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailCellBg.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailCellBg.imageset/Contents.json new file mode 100644 index 0000000..f9bcc41 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailCellBg.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanDetailCellBg@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailCellBg.imageset/ScanDetailCellBg@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailCellBg.imageset/ScanDetailCellBg@2x.png new file mode 100644 index 0000000..5282979 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailCellBg.imageset/ScanDetailCellBg@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailCellBgWithArrow.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailCellBgWithArrow.imageset/Contents.json new file mode 100644 index 0000000..6c599c3 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailCellBgWithArrow.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanDetailCellBgWithArrow@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailCellBgWithArrow.imageset/ScanDetailCellBgWithArrow@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailCellBgWithArrow.imageset/ScanDetailCellBgWithArrow@2x.png new file mode 100644 index 0000000..de0b5c5 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailCellBgWithArrow.imageset/ScanDetailCellBgWithArrow@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailCellBgWithArrowHL.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailCellBgWithArrowHL.imageset/Contents.json new file mode 100644 index 0000000..26955a0 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailCellBgWithArrowHL.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanDetailCellBgWithArrowHL@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailCellBgWithArrowHL.imageset/ScanDetailCellBgWithArrowHL@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailCellBgWithArrowHL.imageset/ScanDetailCellBgWithArrowHL@2x.png new file mode 100644 index 0000000..8844590 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailCellBgWithArrowHL.imageset/ScanDetailCellBgWithArrowHL@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMediaPlayIcon.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMediaPlayIcon.imageset/Contents.json new file mode 100644 index 0000000..38922c5 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMediaPlayIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanDetailMediaPlayIcon@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMediaPlayIcon.imageset/ScanDetailMediaPlayIcon@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMediaPlayIcon.imageset/ScanDetailMediaPlayIcon@2x.png new file mode 100644 index 0000000..03b2e54 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMediaPlayIcon.imageset/ScanDetailMediaPlayIcon@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMediaPlayIconHL.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMediaPlayIconHL.imageset/Contents.json new file mode 100644 index 0000000..481d63b --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMediaPlayIconHL.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanDetailMediaPlayIconHL@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMediaPlayIconHL.imageset/ScanDetailMediaPlayIconHL@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMediaPlayIconHL.imageset/ScanDetailMediaPlayIconHL@2x.png new file mode 100644 index 0000000..994d14f Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMediaPlayIconHL.imageset/ScanDetailMediaPlayIconHL@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMovieIcon.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMovieIcon.imageset/Contents.json new file mode 100644 index 0000000..a951954 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMovieIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanDetailMovieIcon@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMovieIcon.imageset/ScanDetailMovieIcon@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMovieIcon.imageset/ScanDetailMovieIcon@2x.png new file mode 100644 index 0000000..8d04e75 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMovieIcon.imageset/ScanDetailMovieIcon@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMovieIcon_Hl.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMovieIcon_Hl.imageset/Contents.json new file mode 100644 index 0000000..607d5d3 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMovieIcon_Hl.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanDetailMovieIcon_Hl@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMovieIcon_Hl.imageset/ScanDetailMovieIcon_Hl@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMovieIcon_Hl.imageset/ScanDetailMovieIcon_Hl@2x.png new file mode 100644 index 0000000..9e3d03f Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMovieIcon_Hl.imageset/ScanDetailMovieIcon_Hl@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicIcon.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicIcon.imageset/Contents.json new file mode 100644 index 0000000..35c05e4 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanDetailMusicIcon@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicIcon.imageset/ScanDetailMusicIcon@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicIcon.imageset/ScanDetailMusicIcon@2x.png new file mode 100644 index 0000000..c4d543b Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicIcon.imageset/ScanDetailMusicIcon@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicIcon_Hl.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicIcon_Hl.imageset/Contents.json new file mode 100644 index 0000000..da63e29 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicIcon_Hl.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanDetailMusicIcon_Hl@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicIcon_Hl.imageset/ScanDetailMusicIcon_Hl@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicIcon_Hl.imageset/ScanDetailMusicIcon_Hl@2x.png new file mode 100644 index 0000000..516e94c Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicIcon_Hl.imageset/ScanDetailMusicIcon_Hl@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicStopIcon.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicStopIcon.imageset/Contents.json new file mode 100644 index 0000000..8158d9b --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicStopIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanDetailMusicStopIcon@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicStopIcon.imageset/ScanDetailMusicStopIcon@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicStopIcon.imageset/ScanDetailMusicStopIcon@2x.png new file mode 100644 index 0000000..742a60b Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicStopIcon.imageset/ScanDetailMusicStopIcon@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicStopIcon_Hl.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicStopIcon_Hl.imageset/Contents.json new file mode 100644 index 0000000..84079e9 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicStopIcon_Hl.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanDetailMusicStopIcon_Hl@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicStopIcon_Hl.imageset/ScanDetailMusicStopIcon_Hl@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicStopIcon_Hl.imageset/ScanDetailMusicStopIcon_Hl@2x.png new file mode 100644 index 0000000..51cec70 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanDetailMusicStopIcon_Hl.imageset/ScanDetailMusicStopIcon_Hl@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR1.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR1.imageset/Contents.json new file mode 100644 index 0000000..553f10a --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR1.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanQR1@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ScanQR1@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR1.imageset/ScanQR1@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR1.imageset/ScanQR1@2x.png new file mode 100644 index 0000000..2b71ba4 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR1.imageset/ScanQR1@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR1.imageset/ScanQR1@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR1.imageset/ScanQR1@3x.png new file mode 100644 index 0000000..149a20c Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR1.imageset/ScanQR1@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR2.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR2.imageset/Contents.json new file mode 100644 index 0000000..fbb5010 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR2.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanQR2@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ScanQR2@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR2.imageset/ScanQR2@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR2.imageset/ScanQR2@2x.png new file mode 100644 index 0000000..c4a5c53 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR2.imageset/ScanQR2@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR2.imageset/ScanQR2@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR2.imageset/ScanQR2@3x.png new file mode 100644 index 0000000..3c89e9a Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR2.imageset/ScanQR2@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR3.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR3.imageset/Contents.json new file mode 100644 index 0000000..996e6a6 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR3.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanQR3@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ScanQR3@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR3.imageset/ScanQR3@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR3.imageset/ScanQR3@2x.png new file mode 100644 index 0000000..c9639d4 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR3.imageset/ScanQR3@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR3.imageset/ScanQR3@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR3.imageset/ScanQR3@3x.png new file mode 100644 index 0000000..f4bffa6 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR3.imageset/ScanQR3@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR4.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR4.imageset/Contents.json new file mode 100644 index 0000000..81a53f6 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR4.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanQR4@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ScanQR4@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR4.imageset/ScanQR4@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR4.imageset/ScanQR4@2x.png new file mode 100644 index 0000000..6ba0a96 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR4.imageset/ScanQR4@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR4.imageset/ScanQR4@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR4.imageset/ScanQR4@3x.png new file mode 100644 index 0000000..a7ed675 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQR4.imageset/ScanQR4@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanQRCode.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQRCode.imageset/Contents.json new file mode 100644 index 0000000..6c378a2 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQRCode.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanQRCode@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ScanQRCode@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanQRCode.imageset/ScanQRCode@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQRCode.imageset/ScanQRCode@2x.png new file mode 100644 index 0000000..2804b61 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQRCode.imageset/ScanQRCode@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanQRCode.imageset/ScanQRCode@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQRCode.imageset/ScanQRCode@3x.png new file mode 100644 index 0000000..7eb9558 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQRCode.imageset/ScanQRCode@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanQRCode_HL.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQRCode_HL.imageset/Contents.json new file mode 100644 index 0000000..31dc870 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQRCode_HL.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanQRCode_HL@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ScanQRCode_HL@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanQRCode_HL.imageset/ScanQRCode_HL@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQRCode_HL.imageset/ScanQRCode_HL@2x.png new file mode 100644 index 0000000..4db159e Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQRCode_HL.imageset/ScanQRCode_HL@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanQRCode_HL.imageset/ScanQRCode_HL@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQRCode_HL.imageset/ScanQRCode_HL@3x.png new file mode 100644 index 0000000..a5a3922 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanQRCode_HL.imageset/ScanQRCode_HL@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanStreet.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanStreet.imageset/Contents.json new file mode 100644 index 0000000..cd95480 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanStreet.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanStreet@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ScanStreet@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanStreet.imageset/ScanStreet@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanStreet.imageset/ScanStreet@2x.png new file mode 100644 index 0000000..d89969b Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanStreet.imageset/ScanStreet@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanStreet.imageset/ScanStreet@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanStreet.imageset/ScanStreet@3x.png new file mode 100644 index 0000000..c12e804 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanStreet.imageset/ScanStreet@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanStreet_HL.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanStreet_HL.imageset/Contents.json new file mode 100644 index 0000000..2f063c4 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanStreet_HL.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanStreet_HL@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ScanStreet_HL@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanStreet_HL.imageset/ScanStreet_HL@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanStreet_HL.imageset/ScanStreet_HL@2x.png new file mode 100644 index 0000000..a6c1dd1 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanStreet_HL.imageset/ScanStreet_HL@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanStreet_HL.imageset/ScanStreet_HL@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanStreet_HL.imageset/ScanStreet_HL@3x.png new file mode 100644 index 0000000..f18bc69 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanStreet_HL.imageset/ScanStreet_HL@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanWord.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanWord.imageset/Contents.json new file mode 100644 index 0000000..211b56e --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanWord.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanWord@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ScanWord@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanWord.imageset/ScanWord@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanWord.imageset/ScanWord@2x.png new file mode 100644 index 0000000..a02d84f Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanWord.imageset/ScanWord@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanWord.imageset/ScanWord@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanWord.imageset/ScanWord@3x.png new file mode 100644 index 0000000..b9949f6 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanWord.imageset/ScanWord@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanWord_HL.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanWord_HL.imageset/Contents.json new file mode 100644 index 0000000..fb01ba8 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanWord_HL.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanWord_HL@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ScanWord_HL@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanWord_HL.imageset/ScanWord_HL@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanWord_HL.imageset/ScanWord_HL@2x.png new file mode 100644 index 0000000..cfff612 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanWord_HL.imageset/ScanWord_HL@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanWord_HL.imageset/ScanWord_HL@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanWord_HL.imageset/ScanWord_HL@3x.png new file mode 100644 index 0000000..d41332b Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanWord_HL.imageset/ScanWord_HL@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/Scantv.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/Scantv.imageset/Contents.json new file mode 100644 index 0000000..4cb44d4 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/Scantv.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Scantv@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/Scantv.imageset/Scantv@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/Scantv.imageset/Scantv@2x.png new file mode 100644 index 0000000..ffdf87b Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/Scantv.imageset/Scantv@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/Scantv_HL.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/Scantv_HL.imageset/Contents.json new file mode 100644 index 0000000..7eee029 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/Scantv_HL.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Scantv_HL@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/Scantv_HL.imageset/Scantv_HL@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/Scantv_HL.imageset/Scantv_HL@2x.png new file mode 100644 index 0000000..82c830f Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/Scantv_HL.imageset/Scantv_HL@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanwithoutCommodityIcon.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/ScanwithoutCommodityIcon.imageset/Contents.json new file mode 100644 index 0000000..46247ea --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/ScanwithoutCommodityIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ScanwithoutCommodityIcon@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/ScanwithoutCommodityIcon.imageset/ScanwithoutCommodityIcon@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/ScanwithoutCommodityIcon.imageset/ScanwithoutCommodityIcon@2x.png new file mode 100644 index 0000000..9ea62e2 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/ScanwithoutCommodityIcon.imageset/ScanwithoutCommodityIcon@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppCardNodeBkg.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppCardNodeBkg.imageset/Contents.json new file mode 100644 index 0000000..5dd5dad --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppCardNodeBkg.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "SenderAppCardNodeBkg@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppCardNodeBkg.imageset/SenderAppCardNodeBkg@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppCardNodeBkg.imageset/SenderAppCardNodeBkg@2x.png new file mode 100644 index 0000000..d2ad020 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppCardNodeBkg.imageset/SenderAppCardNodeBkg@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppCardNodeBkg_HL.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppCardNodeBkg_HL.imageset/Contents.json new file mode 100644 index 0000000..e18d9da --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppCardNodeBkg_HL.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "SenderAppCardNodeBkg_HL@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppCardNodeBkg_HL.imageset/SenderAppCardNodeBkg_HL@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppCardNodeBkg_HL.imageset/SenderAppCardNodeBkg_HL@2x.png new file mode 100644 index 0000000..f8f8809 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppCardNodeBkg_HL.imageset/SenderAppCardNodeBkg_HL@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppNodeBkg.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppNodeBkg.imageset/Contents.json new file mode 100644 index 0000000..a88e7a2 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppNodeBkg.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "SenderAppNodeBkg@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "SenderAppNodeBkg@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppNodeBkg.imageset/SenderAppNodeBkg@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppNodeBkg.imageset/SenderAppNodeBkg@2x.png new file mode 100644 index 0000000..c0cf6dc Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppNodeBkg.imageset/SenderAppNodeBkg@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppNodeBkg.imageset/SenderAppNodeBkg@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppNodeBkg.imageset/SenderAppNodeBkg@3x.png new file mode 100644 index 0000000..82c17d7 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppNodeBkg.imageset/SenderAppNodeBkg@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppNodeBkg_HL.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppNodeBkg_HL.imageset/Contents.json new file mode 100644 index 0000000..f91089f --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppNodeBkg_HL.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "SenderAppNodeBkg_HL@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "SenderAppNodeBkg_HL@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppNodeBkg_HL.imageset/SenderAppNodeBkg_HL@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppNodeBkg_HL.imageset/SenderAppNodeBkg_HL@2x.png new file mode 100644 index 0000000..c7a889a Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppNodeBkg_HL.imageset/SenderAppNodeBkg_HL@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppNodeBkg_HL.imageset/SenderAppNodeBkg_HL@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppNodeBkg_HL.imageset/SenderAppNodeBkg_HL@3x.png new file mode 100644 index 0000000..4822a70 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderAppNodeBkg_HL.imageset/SenderAppNodeBkg_HL@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderImageNodeBorder.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/SenderImageNodeBorder.imageset/Contents.json new file mode 100644 index 0000000..77494e6 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/SenderImageNodeBorder.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "SenderImageNodeBorder@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "SenderImageNodeBorder@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderImageNodeBorder.imageset/SenderImageNodeBorder@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderImageNodeBorder.imageset/SenderImageNodeBorder@2x.png new file mode 100644 index 0000000..dd51d88 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderImageNodeBorder.imageset/SenderImageNodeBorder@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderImageNodeBorder.imageset/SenderImageNodeBorder@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderImageNodeBorder.imageset/SenderImageNodeBorder@3x.png new file mode 100644 index 0000000..0bf6fce Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderImageNodeBorder.imageset/SenderImageNodeBorder@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderImageNodeMask.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/SenderImageNodeMask.imageset/Contents.json new file mode 100644 index 0000000..f2a3774 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/SenderImageNodeMask.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "SenderImageNodeMask@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderImageNodeMask.imageset/SenderImageNodeMask@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderImageNodeMask.imageset/SenderImageNodeMask@2x.png new file mode 100644 index 0000000..689e954 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderImageNodeMask.imageset/SenderImageNodeMask@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkg.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkg.imageset/Contents.json new file mode 100644 index 0000000..6c6e36e --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkg.imageset/Contents.json @@ -0,0 +1,31 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "1x" + }, + { + "idiom" : "iphone", + "filename" : "SenderTextNodeBkg@2x.png", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "filename" : "SenderTextNodeBkg@3x.png", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "filename" : "SenderTextNodeBkg@2x~ipad.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkg.imageset/SenderTextNodeBkg@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkg.imageset/SenderTextNodeBkg@2x.png new file mode 100644 index 0000000..8184d8a Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkg.imageset/SenderTextNodeBkg@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkg.imageset/SenderTextNodeBkg@2x~ipad.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkg.imageset/SenderTextNodeBkg@2x~ipad.png new file mode 100644 index 0000000..b19087c Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkg.imageset/SenderTextNodeBkg@2x~ipad.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkg.imageset/SenderTextNodeBkg@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkg.imageset/SenderTextNodeBkg@3x.png new file mode 100644 index 0000000..bef150e Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkg.imageset/SenderTextNodeBkg@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkgHL.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkgHL.imageset/Contents.json new file mode 100644 index 0000000..8d257af --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkgHL.imageset/Contents.json @@ -0,0 +1,31 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "1x" + }, + { + "idiom" : "iphone", + "filename" : "SenderTextNodeBkgHL@2x.png", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "filename" : "SenderTextNodeBkgHL@3x.png", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "filename" : "SenderTextNodeBkgHL@2x~ipad.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkgHL.imageset/SenderTextNodeBkgHL@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkgHL.imageset/SenderTextNodeBkgHL@2x.png new file mode 100644 index 0000000..7cda6d4 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkgHL.imageset/SenderTextNodeBkgHL@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkgHL.imageset/SenderTextNodeBkgHL@2x~ipad.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkgHL.imageset/SenderTextNodeBkgHL@2x~ipad.png new file mode 100644 index 0000000..7cb81cd Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkgHL.imageset/SenderTextNodeBkgHL@2x~ipad.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkgHL.imageset/SenderTextNodeBkgHL@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkgHL.imageset/SenderTextNodeBkgHL@3x.png new file mode 100644 index 0000000..344d773 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderTextNodeBkgHL.imageset/SenderTextNodeBkgHL@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBack.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBack.imageset/Contents.json new file mode 100644 index 0000000..6f77b65 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBack.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "SenderVoiceNodeBack@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "SenderVoiceNodeBack@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBack.imageset/SenderVoiceNodeBack@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBack.imageset/SenderVoiceNodeBack@2x.png new file mode 100644 index 0000000..f913eed Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBack.imageset/SenderVoiceNodeBack@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBack.imageset/SenderVoiceNodeBack@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBack.imageset/SenderVoiceNodeBack@3x.png new file mode 100644 index 0000000..344d773 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBack.imageset/SenderVoiceNodeBack@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBkgforward.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBkgforward.imageset/Contents.json new file mode 100644 index 0000000..f77ca7d --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBkgforward.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "SenderVoiceNodeBkgforward@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBkgforward.imageset/SenderVoiceNodeBkgforward@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBkgforward.imageset/SenderVoiceNodeBkgforward@2x.png new file mode 100644 index 0000000..34cffef Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBkgforward.imageset/SenderVoiceNodeBkgforward@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBkgforwardHL.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBkgforwardHL.imageset/Contents.json new file mode 100644 index 0000000..00ca507 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBkgforwardHL.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "SenderVoiceNodeBkgforwardHL@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBkgforwardHL.imageset/SenderVoiceNodeBkgforwardHL@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBkgforwardHL.imageset/SenderVoiceNodeBkgforwardHL@2x.png new file mode 100644 index 0000000..4dce68c Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeBkgforwardHL.imageset/SenderVoiceNodeBkgforwardHL@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeDownloading.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeDownloading.imageset/Contents.json new file mode 100644 index 0000000..26d3fde --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeDownloading.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "SenderVoiceNodeDownloading@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "SenderVoiceNodeDownloading@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeDownloading.imageset/SenderVoiceNodeDownloading@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeDownloading.imageset/SenderVoiceNodeDownloading@2x.png new file mode 100644 index 0000000..6c886a1 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeDownloading.imageset/SenderVoiceNodeDownloading@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeDownloading.imageset/SenderVoiceNodeDownloading@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeDownloading.imageset/SenderVoiceNodeDownloading@3x.png new file mode 100644 index 0000000..93e1b5d Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodeDownloading.imageset/SenderVoiceNodeDownloading@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying.imageset/Contents.json new file mode 100644 index 0000000..919ba42 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "SenderVoiceNodePlaying@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "SenderVoiceNodePlaying@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying.imageset/SenderVoiceNodePlaying@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying.imageset/SenderVoiceNodePlaying@2x.png new file mode 100644 index 0000000..8d11bca Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying.imageset/SenderVoiceNodePlaying@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying.imageset/SenderVoiceNodePlaying@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying.imageset/SenderVoiceNodePlaying@3x.png new file mode 100644 index 0000000..156f3f8 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying.imageset/SenderVoiceNodePlaying@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying001.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying001.imageset/Contents.json new file mode 100644 index 0000000..2b87f16 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying001.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "SenderVoiceNodePlaying001@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "SenderVoiceNodePlaying001@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying001.imageset/SenderVoiceNodePlaying001@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying001.imageset/SenderVoiceNodePlaying001@2x.png new file mode 100644 index 0000000..bae4128 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying001.imageset/SenderVoiceNodePlaying001@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying001.imageset/SenderVoiceNodePlaying001@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying001.imageset/SenderVoiceNodePlaying001@3x.png new file mode 100644 index 0000000..c485862 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying001.imageset/SenderVoiceNodePlaying001@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying002.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying002.imageset/Contents.json new file mode 100644 index 0000000..b45ec5a --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying002.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "SenderVoiceNodePlaying002@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "SenderVoiceNodePlaying002@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying002.imageset/SenderVoiceNodePlaying002@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying002.imageset/SenderVoiceNodePlaying002@2x.png new file mode 100644 index 0000000..350d388 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying002.imageset/SenderVoiceNodePlaying002@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying002.imageset/SenderVoiceNodePlaying002@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying002.imageset/SenderVoiceNodePlaying002@3x.png new file mode 100644 index 0000000..4eaad52 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying002.imageset/SenderVoiceNodePlaying002@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying003.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying003.imageset/Contents.json new file mode 100644 index 0000000..1f22dfb --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying003.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "SenderVoiceNodePlaying003@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "SenderVoiceNodePlaying003@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying003.imageset/SenderVoiceNodePlaying003@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying003.imageset/SenderVoiceNodePlaying003@2x.png new file mode 100644 index 0000000..abe498f Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying003.imageset/SenderVoiceNodePlaying003@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying003.imageset/SenderVoiceNodePlaying003@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying003.imageset/SenderVoiceNodePlaying003@3x.png new file mode 100644 index 0000000..485c1ed Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/SenderVoiceNodePlaying003.imageset/SenderVoiceNodePlaying003@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/barbuttonicon_add.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/barbuttonicon_add.imageset/Contents.json new file mode 100755 index 0000000..15c6b7a --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/barbuttonicon_add.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "barbuttonicon_add.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "barbuttonicon_add@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x", + "filename" : "barbuttonicon_add@3x.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/barbuttonicon_add.imageset/barbuttonicon_add.png b/SDAutoLayoutDemo/Images/Home.xcassets/barbuttonicon_add.imageset/barbuttonicon_add.png new file mode 100755 index 0000000..9a0425a Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/barbuttonicon_add.imageset/barbuttonicon_add.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/barbuttonicon_add.imageset/barbuttonicon_add@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/barbuttonicon_add.imageset/barbuttonicon_add@2x.png new file mode 100755 index 0000000..1d25cd1 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/barbuttonicon_add.imageset/barbuttonicon_add@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/barbuttonicon_add.imageset/barbuttonicon_add@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/barbuttonicon_add.imageset/barbuttonicon_add@3x.png new file mode 100755 index 0000000..d59cbe8 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/barbuttonicon_add.imageset/barbuttonicon_add@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_friend.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_friend.imageset/Contents.json new file mode 100644 index 0000000..177aa45 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_friend.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "contacts_add_friend@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "contacts_add_friend@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_friend.imageset/contacts_add_friend@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_friend.imageset/contacts_add_friend@2x.png new file mode 100644 index 0000000..8fe1af1 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_friend.imageset/contacts_add_friend@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_friend.imageset/contacts_add_friend@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_friend.imageset/contacts_add_friend@3x.png new file mode 100644 index 0000000..7d0a23e Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_friend.imageset/contacts_add_friend@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_newmessage.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_newmessage.imageset/Contents.json new file mode 100644 index 0000000..6c9ae69 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_newmessage.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "contacts_add_newmessage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "contacts_add_newmessage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_newmessage.imageset/contacts_add_newmessage@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_newmessage.imageset/contacts_add_newmessage@2x.png new file mode 100644 index 0000000..6e0da74 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_newmessage.imageset/contacts_add_newmessage@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_newmessage.imageset/contacts_add_newmessage@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_newmessage.imageset/contacts_add_newmessage@3x.png new file mode 100644 index 0000000..b140f01 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_newmessage.imageset/contacts_add_newmessage@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_scan.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_scan.imageset/Contents.json new file mode 100644 index 0000000..471d4ae --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_scan.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "contacts_add_scan@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "contacts_add_scan@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_scan.imageset/contacts_add_scan@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_scan.imageset/contacts_add_scan@2x.png new file mode 100644 index 0000000..15703a0 Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_scan.imageset/contacts_add_scan@2x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_scan.imageset/contacts_add_scan@3x.png b/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_scan.imageset/contacts_add_scan@3x.png new file mode 100644 index 0000000..0f42fda Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/contacts_add_scan.imageset/contacts_add_scan@3x.png differ diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/icon_sight_capture_mask.imageset/Contents.json b/SDAutoLayoutDemo/Images/Home.xcassets/icon_sight_capture_mask.imageset/Contents.json new file mode 100644 index 0000000..87de822 --- /dev/null +++ b/SDAutoLayoutDemo/Images/Home.xcassets/icon_sight_capture_mask.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "icon_sight_capture_mask@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutDemo/Images/Home.xcassets/icon_sight_capture_mask.imageset/icon_sight_capture_mask@2x.png b/SDAutoLayoutDemo/Images/Home.xcassets/icon_sight_capture_mask.imageset/icon_sight_capture_mask@2x.png new file mode 100644 index 0000000..d825b3b Binary files /dev/null and b/SDAutoLayoutDemo/Images/Home.xcassets/icon_sight_capture_mask.imageset/icon_sight_capture_mask@2x.png differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/0.jpg b/SDAutoLayoutDemo/Images/cell_icons/0.jpg new file mode 100644 index 0000000..fba5b42 Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/0.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/1.jpg b/SDAutoLayoutDemo/Images/cell_icons/1.jpg new file mode 100644 index 0000000..b8af759 Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/1.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/10.jpg b/SDAutoLayoutDemo/Images/cell_icons/10.jpg new file mode 100644 index 0000000..9a10956 Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/10.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/11.jpg b/SDAutoLayoutDemo/Images/cell_icons/11.jpg new file mode 100644 index 0000000..e33c66b Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/11.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/12.jpg b/SDAutoLayoutDemo/Images/cell_icons/12.jpg new file mode 100644 index 0000000..8024e15 Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/12.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/13.jpg b/SDAutoLayoutDemo/Images/cell_icons/13.jpg new file mode 100644 index 0000000..ff1956b Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/13.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/14.jpg b/SDAutoLayoutDemo/Images/cell_icons/14.jpg new file mode 100644 index 0000000..a06e1a2 Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/14.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/15.jpg b/SDAutoLayoutDemo/Images/cell_icons/15.jpg new file mode 100644 index 0000000..a2d5e0c Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/15.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/16.jpg b/SDAutoLayoutDemo/Images/cell_icons/16.jpg new file mode 100644 index 0000000..e3f89a5 Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/16.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/17.jpg b/SDAutoLayoutDemo/Images/cell_icons/17.jpg new file mode 100644 index 0000000..3d7eec2 Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/17.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/18.jpg b/SDAutoLayoutDemo/Images/cell_icons/18.jpg new file mode 100644 index 0000000..e1eeb39 Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/18.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/19.jpg b/SDAutoLayoutDemo/Images/cell_icons/19.jpg new file mode 100644 index 0000000..93eb88e Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/19.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/2.jpg b/SDAutoLayoutDemo/Images/cell_icons/2.jpg new file mode 100644 index 0000000..e834fb0 Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/2.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/20.jpg b/SDAutoLayoutDemo/Images/cell_icons/20.jpg new file mode 100644 index 0000000..bdd1f26 Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/20.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/21.jpg b/SDAutoLayoutDemo/Images/cell_icons/21.jpg new file mode 100644 index 0000000..5307c2f Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/21.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/22.jpg b/SDAutoLayoutDemo/Images/cell_icons/22.jpg new file mode 100644 index 0000000..a9ae4d2 Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/22.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/23.jpg b/SDAutoLayoutDemo/Images/cell_icons/23.jpg new file mode 100644 index 0000000..aaa1521 Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/23.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/3.jpg b/SDAutoLayoutDemo/Images/cell_icons/3.jpg new file mode 100644 index 0000000..509b600 Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/3.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/4.jpg b/SDAutoLayoutDemo/Images/cell_icons/4.jpg new file mode 100644 index 0000000..21bfc5d Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/4.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/5.jpg b/SDAutoLayoutDemo/Images/cell_icons/5.jpg new file mode 100644 index 0000000..4041a48 Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/5.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/6.jpg b/SDAutoLayoutDemo/Images/cell_icons/6.jpg new file mode 100644 index 0000000..d84bd52 Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/6.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/7.jpg b/SDAutoLayoutDemo/Images/cell_icons/7.jpg new file mode 100644 index 0000000..53bbb37 Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/7.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/8.jpg b/SDAutoLayoutDemo/Images/cell_icons/8.jpg new file mode 100644 index 0000000..7941637 Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/8.jpg differ diff --git a/SDAutoLayoutDemo/Images/cell_icons/9.jpg b/SDAutoLayoutDemo/Images/cell_icons/9.jpg new file mode 100644 index 0000000..7c1ea70 Binary files /dev/null and b/SDAutoLayoutDemo/Images/cell_icons/9.jpg differ diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/Images/icon0.jpg" b/SDAutoLayoutDemo/Images/icon0.jpg similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/Images/icon0.jpg" rename to SDAutoLayoutDemo/Images/icon0.jpg diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/Images/icon1.jpg" b/SDAutoLayoutDemo/Images/icon1.jpg similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/Images/icon1.jpg" rename to SDAutoLayoutDemo/Images/icon1.jpg diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/Images/icon2.jpg" b/SDAutoLayoutDemo/Images/icon2.jpg similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/Images/icon2.jpg" rename to SDAutoLayoutDemo/Images/icon2.jpg diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/Images/icon3.jpg" b/SDAutoLayoutDemo/Images/icon3.jpg similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/Images/icon3.jpg" rename to SDAutoLayoutDemo/Images/icon3.jpg diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/Images/icon4.jpg" b/SDAutoLayoutDemo/Images/icon4.jpg similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/Images/icon4.jpg" rename to SDAutoLayoutDemo/Images/icon4.jpg diff --git a/SDAutoLayoutDemo/Images/pbg.jpg b/SDAutoLayoutDemo/Images/pbg.jpg new file mode 100644 index 0000000..86e70e8 Binary files /dev/null and b/SDAutoLayoutDemo/Images/pbg.jpg differ diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/Images/pic0.jpg" b/SDAutoLayoutDemo/Images/pic0.jpg similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/Images/pic0.jpg" rename to SDAutoLayoutDemo/Images/pic0.jpg diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/Images/pic1.jpg" b/SDAutoLayoutDemo/Images/pic1.jpg similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/Images/pic1.jpg" rename to SDAutoLayoutDemo/Images/pic1.jpg diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/Images/pic2.jpg" b/SDAutoLayoutDemo/Images/pic2.jpg similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/Images/pic2.jpg" rename to SDAutoLayoutDemo/Images/pic2.jpg diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/Images/pic3.jpg" b/SDAutoLayoutDemo/Images/pic3.jpg similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/Images/pic3.jpg" rename to SDAutoLayoutDemo/Images/pic3.jpg diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/Images/pic4.jpg" b/SDAutoLayoutDemo/Images/pic4.jpg similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/Images/pic4.jpg" rename to SDAutoLayoutDemo/Images/pic4.jpg diff --git a/SDAutoLayoutDemo/Images/pic5.jpg b/SDAutoLayoutDemo/Images/pic5.jpg new file mode 100644 index 0000000..4e26597 Binary files /dev/null and b/SDAutoLayoutDemo/Images/pic5.jpg differ diff --git a/SDAutoLayoutDemo/Images/pic6.jpg b/SDAutoLayoutDemo/Images/pic6.jpg new file mode 100644 index 0000000..7569148 Binary files /dev/null and b/SDAutoLayoutDemo/Images/pic6.jpg differ diff --git a/SDAutoLayoutDemo/Images/pic7.jpg b/SDAutoLayoutDemo/Images/pic7.jpg new file mode 100644 index 0000000..89d87e6 Binary files /dev/null and b/SDAutoLayoutDemo/Images/pic7.jpg differ diff --git a/SDAutoLayoutDemo/Images/pic8.jpg b/SDAutoLayoutDemo/Images/pic8.jpg new file mode 100644 index 0000000..55e4617 Binary files /dev/null and b/SDAutoLayoutDemo/Images/pic8.jpg differ diff --git a/SDAutoLayoutDemo/Images/picon.jpg b/SDAutoLayoutDemo/Images/picon.jpg new file mode 100644 index 0000000..43cb3c8 Binary files /dev/null and b/SDAutoLayoutDemo/Images/picon.jpg differ diff --git a/SDAutoLayoutDemo/Images/test/test0.jpg b/SDAutoLayoutDemo/Images/test/test0.jpg new file mode 100644 index 0000000..42ed8ea Binary files /dev/null and b/SDAutoLayoutDemo/Images/test/test0.jpg differ diff --git a/SDAutoLayoutDemo/Images/test/test1.jpg b/SDAutoLayoutDemo/Images/test/test1.jpg new file mode 100644 index 0000000..0d0fa3e Binary files /dev/null and b/SDAutoLayoutDemo/Images/test/test1.jpg differ diff --git a/SDAutoLayoutDemo/Images/test/test2.jpg b/SDAutoLayoutDemo/Images/test/test2.jpg new file mode 100644 index 0000000..ef6919a Binary files /dev/null and b/SDAutoLayoutDemo/Images/test/test2.jpg differ diff --git a/SDAutoLayoutDemo/Images/test/test3.jpg b/SDAutoLayoutDemo/Images/test/test3.jpg new file mode 100644 index 0000000..737e7bc Binary files /dev/null and b/SDAutoLayoutDemo/Images/test/test3.jpg differ diff --git a/SDAutoLayoutDemo/Images/test/test4.jpg b/SDAutoLayoutDemo/Images/test/test4.jpg new file mode 100644 index 0000000..c36ebb6 Binary files /dev/null and b/SDAutoLayoutDemo/Images/test/test4.jpg differ diff --git a/SDAutoLayoutDemo/Info.plist b/SDAutoLayoutDemo/Info.plist new file mode 100644 index 0000000..028efc3 --- /dev/null +++ b/SDAutoLayoutDemo/Info.plist @@ -0,0 +1,52 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/AppDelegate.h" b/SDAutoLayoutDemo/Others/AppDelegate.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/AppDelegate.h" rename to SDAutoLayoutDemo/Others/AppDelegate.h diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/AppDelegate.m" b/SDAutoLayoutDemo/Others/AppDelegate.m similarity index 51% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/AppDelegate.m" rename to SDAutoLayoutDemo/Others/AppDelegate.m index bb6cbb3..74dd901 100644 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/AppDelegate.m" +++ b/SDAutoLayoutDemo/Others/AppDelegate.m @@ -23,6 +23,8 @@ #import "AppDelegate.h" +#import "LEETheme.h" + @interface AppDelegate () @end @@ -32,9 +34,74 @@ @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. + + [self configureNavigationBarAppearance]; + [self configTheme]; + return YES; } +/// iOS 15+ 默认导航栏在滚动边缘会变成深色;统一为浅色栏 + 深色标题,并与内容区对齐。 +- (void)configureNavigationBarAppearance +{ + UIColor *barColor = [UIColor colorWithRed:0.973 green:0.973 blue:0.973 alpha:1]; + UIColor *titleColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1]; + UIColor *tintColor = [UIColor colorWithRed:0 green:0.478 blue:1 alpha:1]; + + if (@available(iOS 13.0, *)) { + UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] init]; + [appearance configureWithOpaqueBackground]; + appearance.backgroundColor = barColor; + appearance.titleTextAttributes = @{NSForegroundColorAttributeName: titleColor}; + appearance.largeTitleTextAttributes = @{NSForegroundColorAttributeName: titleColor}; + appearance.shadowColor = [UIColor colorWithWhite:0 alpha:0.12]; + + UINavigationBar *navBar = [UINavigationBar appearance]; + navBar.standardAppearance = appearance; + navBar.scrollEdgeAppearance = appearance; + navBar.compactAppearance = appearance; + if (@available(iOS 15.0, *)) { + navBar.compactScrollEdgeAppearance = appearance; + } + navBar.tintColor = tintColor; + navBar.translucent = NO; + navBar.barTintColor = barColor; + navBar.titleTextAttributes = appearance.titleTextAttributes; + if (@available(iOS 13.0, *)) { + navBar.overrideUserInterfaceStyle = UIUserInterfaceStyleLight; + } + } else { + UINavigationBar *navBar = [UINavigationBar appearance]; + navBar.barTintColor = barColor; + navBar.titleTextAttributes = @{NSForegroundColorAttributeName: titleColor}; + navBar.tintColor = tintColor; + navBar.translucent = NO; + navBar.barStyle = UIBarStyleDefault; + } +} + +// 设置LEETheme + +- (void)configTheme{ + + //获取对应主题的json设置数据 + + NSString *dayjson = [NSString stringWithContentsOfFile:[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"theme_day_json.json"] encoding:NSUTF8StringEncoding error:nil]; + + //添加json设置数据 , 设置主题标签 , 设置资源路径 + + [LEETheme addThemeConfigWithJson:dayjson Tag:@"day" ResourcesPath:nil]; + + NSString *nightjson = [NSString stringWithContentsOfFile:[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"theme_night_json.json"] encoding:NSUTF8StringEncoding error:nil]; + + [LEETheme addThemeConfigWithJson:nightjson Tag:@"night" ResourcesPath:nil]; + + //设置默认主题 + + [LEETheme defaultTheme:@"day"]; +} + + - (void)applicationWillResignActive:(UIApplication *)application { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. diff --git a/SDAutoLayoutDemo/Others/DemoCell.h b/SDAutoLayoutDemo/Others/DemoCell.h new file mode 100644 index 0000000..019c521 --- /dev/null +++ b/SDAutoLayoutDemo/Others/DemoCell.h @@ -0,0 +1,16 @@ +// +// DemoCell.h +// SDAutoLayout 测试 Demo +// +// Created by gsd on 16/1/8. +// Copyright © 2016年 gsd. All rights reserved. +// + +#import + +@interface DemoCell : UITableViewCell + +@property (nonatomic, weak) UILabel *titleLabel; +@property (nonatomic, weak) UILabel *contentLabel; + +@end diff --git a/SDAutoLayoutDemo/Others/DemoCell.m b/SDAutoLayoutDemo/Others/DemoCell.m new file mode 100644 index 0000000..b223e99 --- /dev/null +++ b/SDAutoLayoutDemo/Others/DemoCell.m @@ -0,0 +1,54 @@ +// +// DemoCell.m +// SDAutoLayout 测试 Demo +// +// Created by gsd on 16/1/8. +// Copyright © 2016年 gsd. All rights reserved. +// + +#import "DemoCell.h" + +#import "UIView+SDAutoLayout.h" + +@implementation DemoCell + +- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier +{ + if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { + [self setup]; + } + return self; +} + +- (void)setup +{ + UILabel *titleLabel = [UILabel new]; + titleLabel.textColor = [UIColor grayColor]; + titleLabel.font = [UIFont systemFontOfSize:15]; + [self.contentView addSubview:titleLabel]; + self.titleLabel = titleLabel; + + UILabel *contentLabel = [UILabel new]; + contentLabel.textColor = [UIColor lightGrayColor]; + contentLabel.font = [UIFont systemFontOfSize:14]; + [self.contentView addSubview:contentLabel]; + self.contentLabel = contentLabel; + + CGFloat margin = 10; + + self.titleLabel.sd_layout + .leftSpaceToView(self.contentView, margin) + .topSpaceToView(self.contentView, margin) + .rightSpaceToView(self.contentView, margin) + .heightIs(20); + + self.contentLabel.sd_layout + .leftEqualToView(self.titleLabel) + .rightEqualToView(self.titleLabel) + .topSpaceToView(self.titleLabel, margin) + .autoHeightRatio(0); + + [self setupAutoHeightWithBottomView:self.contentLabel bottomMargin:margin]; +} + +@end diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/DemoTableViewControler.h" b/SDAutoLayoutDemo/Others/DemoTableViewControler.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/DemoTableViewControler.h" rename to SDAutoLayoutDemo/Others/DemoTableViewControler.h diff --git a/SDAutoLayoutDemo/Others/DemoTableViewControler.m b/SDAutoLayoutDemo/Others/DemoTableViewControler.m new file mode 100644 index 0000000..fbbf775 --- /dev/null +++ b/SDAutoLayoutDemo/Others/DemoTableViewControler.m @@ -0,0 +1,104 @@ +// +// DemoTableViewControler.m +// SDAutoLayout 测试 Demo +// +// Created by gsd on 15/10/12. +// Copyright (c) 2015年 gsd. All rights reserved. +// + +/* + + ********************************************************************************* + * * + * 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 * + * 帮您解决问题。 * + * QQ : 2689718696(gsdios) * + * Email : gsdios@126.com * + * GitHub: https://github.com/gsdios * + * 新浪微博:GSD_iOS * + * * + ********************************************************************************* + + */ + +#import "DemoTableViewControler.h" + +#import "UITableView+SDAutoTableViewCellHeight.h" + +#import "DemoCell.h" + +NSString * const demo0Description = @"自动布局动画,修改一个view的布局约束,其他view也会自动重新排布"; +NSString * const demo1Description = @"1.设置view1高度根据子view而自适应(在view1中加入两个子view(testLabel和testView),然后设置view1高度根据子view内容自适应)\n2.高度自适应lable\n3.宽度自适应label"; +NSString * const demo2Description = @"1.自定义button内部label和imageView的位置\n2.设置间距固定自动调整宽度的一组子view\n3.设置宽度固定自动调整间距的一组子view"; +NSString * const demo3Description = @"简单tableview展示"; +NSString * const demo4Description = @"1.行间距为8的attributedString的label"; +NSString * const demo5Description = @"1.利用普通view的内容自适应功能添加tableheaderview\n2.利用自动布局功能实现cell内部图文排布,图片可根据原始尺寸按比例缩放后展示\n3.利用“普通版tableview的cell高度自适应”完成tableview的排布"; +NSString * const demo6Description = @"展示scrollview的内容自适应和普通view的动态圆角处理"; +NSString * const demo7Description = @"利用“普通版tableview的《多cell》高度自适应”2步设置完成tableview的排布"; +NSString * const demo8Description = @"利用“升级版tableview的《多cell》高度自适应”1步完成tableview的排布。\n注意:升级版方法适用于cell的model有多个的情况下,性能比普通版稍微差一些,不建议在数据量大的tableview中使用(cell数量尽量少于100个),如果有大量的cell或者cell界面复杂渲染耗费性能较大则推荐使用普通方法简化版“cellHeightForIndexPath:model:keyPath:cellClass:contentViewWidth:”方法同样是一步设置即可完成"; +NSString * const demo9Description = @"利用SDAutoLayout仿制微信朋友圈。高仿微信计划:\n1.高仿朋友圈 \n2.完善细节 \n3.高仿完整微信app \nPS:代码会持续在我的github更新"; +NSString * const demo10Description = @"一个SDAutoLayout使用者“李西亚”同学贡献的仿网易新闻界面"; +NSString * const demo11Description = @"仿微信的聊天界面:\n1.纯文本消息(带可点击链接,表情)\n2.图片消息"; +NSString * const demo12Description = @"scroll任意布局内容自适应"; +NSString * const demo13Description = @"scroll任意布局内容自适应自动布局"; +NSString * const demo14Description = @"xib的cell高度自适应"; + +@implementation DemoTableViewControler +{ + NSArray *_contenArray; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.view.backgroundColor = [UIColor whiteColor]; + self.edgesForExtendedLayout = UIRectEdgeNone; + self.title = @"Demo"; + self.tableView.accessibilityIdentifier = @"demoListTable"; + +// [self.navigationController pushViewController:[NSClassFromString(@"DemoVC13") new] animated:YES]; + + _contenArray = @[demo0Description, demo1Description, demo2Description, demo3Description, demo4Description, demo5Description, demo6Description, demo7Description, demo8Description, demo9Description, demo10Description, demo11Description, demo12Description, demo13Description, demo14Description]; +} + +#pragma mark - tableview datasourece and delegate + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return _contenArray.count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + static NSString *ID = @"test"; + DemoCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; + if (!cell) { + cell = [[DemoCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID]; + } + cell.titleLabel.text = [NSString stringWithFormat:@"Demo -- %ld", (long)indexPath.row]; + cell.titleLabel.accessibilityIdentifier = [NSString stringWithFormat:@"demoCell.%ld", (long)indexPath.row]; + cell.accessibilityIdentifier = cell.titleLabel.accessibilityIdentifier; + cell.contentLabel.text = _contenArray[indexPath.row]; + return cell; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + NSString *demoClassString = [NSString stringWithFormat:@"DemoVC%ld", (long)indexPath.row]; + UIViewController *vc = [NSClassFromString(demoClassString) new]; + vc.title = demoClassString; + // DemoVC9 朋友圈需延伸到导航栏下;其余 Demo 内容区不与导航栏重叠 + if (indexPath.row != 9) { + vc.edgesForExtendedLayout = UIRectEdgeNone; + } + [self.navigationController pushViewController:vc animated:YES]; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + // 此升级版方法适用于cell的model有多个的情况下,性能比普通版稍微差一些,不建议在数据量大的tableview中使用,推荐使用“cellHeightForIndexPath:model:keyPath:cellClass:contentViewWidth:”方法同样是一步设置即可完成 + return [self cellHeightForIndexPath:indexPath cellContentViewWidth:[UIScreen mainScreen].bounds.size.width]; +} + +@end diff --git a/SDAutoLayoutDemo/Others/Theme/theme_day_json.json b/SDAutoLayoutDemo/Others/Theme/theme_day_json.json new file mode 100644 index 0000000..c524e54 --- /dev/null +++ b/SDAutoLayoutDemo/Others/Theme/theme_day_json.json @@ -0,0 +1,16 @@ +{ + "color": { + "demovc11_backgroundcolor": "#fafafa", + "demovc11_textcolor": "#333333", + + "demovc10_cell_backgroundcolor": "#fffafafa", + "demovc10_cell_titlecolor": "#ff444444", + "demovc10_cell_separatorcolor": "#ffD9D9D9", + "demovc10_cell_summarycolor": "#ff686868", + "demovc10_cell_linecolor": "#ffD9D9D9", + "demovc10_backgroundcolor": "#fffafafa" + }, + "image": { + "messageimage": "" + } +} diff --git a/SDAutoLayoutDemo/Others/Theme/theme_night_json.json b/SDAutoLayoutDemo/Others/Theme/theme_night_json.json new file mode 100644 index 0000000..c7ae198 --- /dev/null +++ b/SDAutoLayoutDemo/Others/Theme/theme_night_json.json @@ -0,0 +1,16 @@ +{ + "color": { + "demovc11_backgroundcolor": "#000000", + "demovc11_textcolor": "#555555", + + "demovc10_cell_backgroundcolor": "#ff222222", + "demovc10_cell_titlecolor": "#ff8B8B8B", + "demovc10_cell_separatorcolor": "#ff2f2f2f", + "demovc10_cell_summarycolor": "#ff686868", + "demovc10_cell_linecolor": "#ff2f2f2f", + "demovc10_backgroundcolor": "#ff1c1c1c" + }, + "image": { + "messageimage": "" + } +} diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/ViewController.h" b/SDAutoLayoutDemo/Others/ViewController.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/ViewController.h" rename to SDAutoLayoutDemo/Others/ViewController.h diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/ViewController.m" b/SDAutoLayoutDemo/Others/ViewController.m similarity index 66% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/ViewController.m" rename to SDAutoLayoutDemo/Others/ViewController.m index b3a3a59..2b0d9e2 100644 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/ViewController.m" +++ b/SDAutoLayoutDemo/Others/ViewController.m @@ -23,6 +23,8 @@ #import "ViewController.h" +#import "UIView+SDAutoLayout.h" + @interface ViewController () @@ -35,6 +37,8 @@ - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; + // 与 topSpaceToView(self.view, …) 一致:内容从导航栏下方开始,避免与导航栏重叠 + self.edgesForExtendedLayout = UIRectEdgeNone; [self setupDemoViews]; } @@ -44,50 +48,62 @@ - (void)setupDemoViews { UIView *view0 = [UIView new]; view0.backgroundColor = [UIColor redColor]; + view0.isAccessibilityElement = YES; + view0.accessibilityIdentifier = @"demo.layout.view0"; self.view0 = view0; UIView *view1 = [UIView new]; view1.backgroundColor = [UIColor grayColor]; + view1.isAccessibilityElement = YES; + view1.accessibilityIdentifier = @"demo.layout.view1"; self.view1 = view1; UIView *view2 = [UIView new]; view2.backgroundColor = [UIColor brownColor]; + view2.isAccessibilityElement = YES; + view2.accessibilityIdentifier = @"demo.layout.view2"; self.view2 = view2; UIView *view3 = [UIView new]; view3.backgroundColor = [UIColor orangeColor]; + view3.isAccessibilityElement = YES; + view3.accessibilityIdentifier = @"demo.layout.view3"; self.view3 = view3; UIView *view4 = [UIView new]; view4.backgroundColor = [UIColor purpleColor]; + view4.isAccessibilityElement = YES; + view4.accessibilityIdentifier = @"demo.layout.view4"; self.view4 = view4; UIView *view5 = [UIView new]; view5.backgroundColor = [UIColor yellowColor]; + view5.isAccessibilityElement = YES; + view5.accessibilityIdentifier = @"demo.layout.view5"; self.view5 = view5; UIView *view6 = [UIView new]; view6.backgroundColor = [UIColor cyanColor]; + view6.isAccessibilityElement = YES; + view6.accessibilityIdentifier = @"demo.layout.view6"; self.view6 = view6; UIView *view7 = [UIView new]; view7.backgroundColor = [UIColor magentaColor]; + view7.isAccessibilityElement = YES; + view7.accessibilityIdentifier = @"demo.layout.view7"; self.view7 = view7; UIView *view8 = [UIView new]; view8.backgroundColor = [UIColor blackColor]; + view8.isAccessibilityElement = YES; + view8.accessibilityIdentifier = @"demo.layout.view8"; self.view8 = view8; - [self.view addSubview:view0]; - [self.view addSubview:view1]; - [self.view addSubview:view2]; - [self.view addSubview:view3]; - [self.view addSubview:view4]; - [self.view addSubview:view5]; - [self.view addSubview:view6]; - [self.view addSubview:view7]; - [self.view addSubview:view8]; + [self.view sd_addSubviews:@[view0, view1, view2, view3, view4, view5, view6, view7, view8]]; } + + @end diff --git a/SDAutoLayoutDemo/SwiftExtension/SDAutoLayoutDemo-Bridging-Header.h b/SDAutoLayoutDemo/SwiftExtension/SDAutoLayoutDemo-Bridging-Header.h new file mode 100644 index 0000000..e62c458 --- /dev/null +++ b/SDAutoLayoutDemo/SwiftExtension/SDAutoLayoutDemo-Bridging-Header.h @@ -0,0 +1,5 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#import "SDAutoLayout.h" diff --git a/SDAutoLayoutDemo/SwiftExtension/SDAutolayout+extension.swift b/SDAutoLayoutDemo/SwiftExtension/SDAutolayout+extension.swift new file mode 100644 index 0000000..4fb5eaf --- /dev/null +++ b/SDAutoLayoutDemo/SwiftExtension/SDAutolayout+extension.swift @@ -0,0 +1,166 @@ +// +// SDAutolayout+extension.swift +// SDAutoLayoutDemo +// +// Created by fancy on 2018/5/29. +// Copyright © 2018年 gsd. All rights reserved. +// + +// 若使用cocoapods管理第三方,则打开 import SDAutoLayout 注释 +// import SDAutoLayout + +extension UIView { + @discardableResult + public func layout() -> SDAutoLayoutModel { + return sd_layout() + } +} + +extension SDAutoLayoutModel { + /* 设置距离其它view的间距 */ + @discardableResult + public func topTo(_ view: UIView, _ space: CGFloat) -> SDAutoLayoutModel { + return topSpaceToView(view, space) + } + + @discardableResult + public func bottomTo(_ view: UIView, _ space: CGFloat) -> SDAutoLayoutModel { + return bottomSpaceToView(view, space) + } + + @discardableResult + public func leftTo(_ view: UIView, _ space: CGFloat) -> SDAutoLayoutModel { + return leftSpaceToView(view, space) + } + + @discardableResult + public func rightTo(_ view: UIView, _ space: CGFloat) -> SDAutoLayoutModel { + return rightSpaceToView(view, space) + } + + /* 设置x、y、width、height、centerX、centerY 值 */ + @discardableResult + public func x(is value: CGFloat) -> SDAutoLayoutModel { + return xIs(value) + } + + @discardableResult + public func y(is value: CGFloat) -> SDAutoLayoutModel { + return yIs(value) + } + + @discardableResult + public func width(is value: CGFloat) -> SDAutoLayoutModel { + return widthIs(value) + } + + @discardableResult + public func height(is value: CGFloat) -> SDAutoLayoutModel { + return heightIs(value) + } + + @discardableResult + public func centerX(is value: CGFloat) -> SDAutoLayoutModel { + return centerXIs(value) + } + + @discardableResult + public func centerY(is value: CGFloat) -> SDAutoLayoutModel { + return centerYIs(value) + } + + /* 设置最大宽度和高度、最小宽度和高度 */ + @discardableResult + public func maxWidth(_ value: CGFloat) -> SDAutoLayoutModel { + return maxWidthIs(value) + } + + @discardableResult + public func maxHeight(_ value: CGFloat) -> SDAutoLayoutModel { + return maxHeightIs(value) + } + + @discardableResult + public func minWidth(_ value: CGFloat) -> SDAutoLayoutModel { + return minWidthIs(value) + } + + @discardableResult + public func minHeight(_ value: CGFloat) -> SDAutoLayoutModel { + return minHeightIs(value) + } + + /* 设置和某个参照view的边距相同 */ + @discardableResult + public func leftEqualTo(_ view: UIView) -> SDAutoLayoutModel { + return leftEqualToView(view) + } + + @discardableResult + public func rightEqualTo(_ view: UIView) -> SDAutoLayoutModel { + return rightEqualToView(view) + } + + @discardableResult + public func topEqualTo(_ view: UIView) -> SDAutoLayoutModel { + return topEqualToView(view) + } + + @discardableResult + public func bottomEqualTo(_ view: UIView) -> SDAutoLayoutModel { + return bottomEqualToView(view) + } + + @discardableResult + public func centerXEqualTo(_ view: UIView) -> SDAutoLayoutModel { + return centerXEqualToView(view) + } + + @discardableResult + public func centerYEqualTo(_ view: UIView) -> SDAutoLayoutModel { + return centerYEqualToView(view) + } + + /* 设置宽度或者高度等于参照view的多少倍 */ + @discardableResult + public func widthRatioTo(_ view: UIView, _ value: CGFloat) -> SDAutoLayoutModel { + return widthRatioToView(view, value) + } + + @discardableResult + public func heightRatioTo(_ view: UIView, _ value: CGFloat) -> SDAutoLayoutModel { + return heightRatioToView(view, value) + } + + @discardableResult + public func autoHeight(_ ratio: CGFloat) -> SDAutoLayoutModel { + return autoHeightRatio(ratio) + } + + @discardableResult + public func autoWidth(_ ratio: CGFloat) -> SDAutoLayoutModel { + return autoWidthRatio(ratio) + } + + @discardableResult + public func isWidthEqualToHeight() -> SDAutoLayoutModel { + return widthEqualToHeight() + } + + @discardableResult + public func isHeightEqualToWidth() -> SDAutoLayoutModel { + return heightEqualToWidth() + } + + /* 填充父view(快捷方法) */ + public func spaceToSuperViewIs(_ value: UIEdgeInsets) { + spaceToSuperView(value) + } + + @discardableResult + public func offsetIs(_ value: CGFloat) -> SDAutoLayoutModel { + return offset(value) + } + + +} diff --git a/SDAutoLayoutDemo/Vender/LEETheme/LEETheme.h b/SDAutoLayoutDemo/Vender/LEETheme/LEETheme.h new file mode 100644 index 0000000..4050721 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/LEETheme/LEETheme.h @@ -0,0 +1,437 @@ + +/*! + * @header LEETheme.h + * + * ┌─┐ ┌───────┐ ┌───────┐ 帅™ + * │ │ │ ┌─────┘ │ ┌─────┘ + * │ │ │ └─────┐ │ └─────┐ + * │ │ │ ┌─────┘ │ ┌─────┘ + * │ └─────┐│ └─────┐ │ └─────┐ + * └───────┘└───────┘ └───────┘ + * + * @brief LEE主题管理 + * + * @author LEE + * @copyright Copyright © 2016 - 2017年 lee. All rights reserved. + * @version V1.1.7 + */ + +#import +#import + +#import "LEEThemeHelper.h" + +/* + + ********************************************************************************* + * + * 在使用LEETheme的过程中如果出现bug请及时以以下任意一种方式联系我,我会及时修复bug + * + * QQ : 可以添加SDAutoLayout群 497140713 在这里找到我(LEE 332459523) + * Email : 18611401994@163.com + * GitHub: https://github.com/lixiang1994/LEETheme + * 简书: http://www.jianshu.com/users/a6da0db100c8 + * 博客: http://www.lee1994.com + * + ********************************************************************************* + + */ + +@interface LEETheme : NSObject + +/** + * 启动主题 + * + * @param tag 主题标签 + */ ++ (void)startTheme:(NSString *)tag; + +/** + * 默认主题 (必设置 , 应用程序最少需要一个默认主题) + * + * + * @param tag 主题标签 + */ ++ (void)defaultTheme:(NSString *)tag; + +/** + * 当前主题标签 + * + * @return 主题标签 tag + */ ++ (NSString *)currentThemeTag; + +/** + * 全部主题标签 + * + * @return 主题标签集合 + */ ++ (NSArray *)allThemeTag; + +@end + +@interface LEETheme (JsonModeExtend) + +/** + * 添加主题设置Json + * + * @param json json字符串 + * @param tag 主题标签 + * @param path 资源路径 (在Documents目录下的路径 如果资源不在Documents目录下应传入nil 例: ResourcesPath:@@"themeResources/day/") + */ ++ (void)addThemeConfigWithJson:(NSString *)json Tag:(NSString *)tag ResourcesPath:(NSString *)path; + +/** + * 移除主题设置 + * + * @param tag 主题标签 + */ ++ (void)removeThemeConfigWithTag:(NSString *)tag; + +/** + * 获取指定主题标签的资源路径 + * + * @param tag 主题标签 + * + * @return 资源路径 (如为不存在则返回mainBundle路径) + */ ++ (NSString *)getResourcesPathWithTag:(NSString *)tag; + +/** + * 获取值 + * + * @param tag 主题标签 + * @param identifier 标识符 + * + * @return 值对象 (UIColor或UIImage或NSString 如为不存在则返回nil) + */ ++ (id)getValueWithTag:(NSString *)tag Identifier:(NSString *)identifier; + +@end + +@interface LEEThemeConfigModel : NSObject + +/** ----默认设置方式---- */ + +/** Block */ + +/** 主题改变Block -> 格式: .LeeThemeChangingBlock(^(NSString *tag , id item){ code... }) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToChangingBlock LeeThemeChangingBlock; + +/** 添加自定义设置 -> 格式: .LeeAddCustomConfig(@@"tag" , ^(id item){ code... }) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Block LeeAddCustomConfig; + +/** 添加多标签自定义设置 -> 格式: .LeeAddCustomConfigs(@@[tag1 , tag2] , ^(id item){ code... }) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToTs_Block LeeAddCustomConfigs; + +/** Color快捷设置方法 */ + +/** 添加渲染颜色设置 -> 格式: .LeeAddTintColor(@@"tag" , UIColor) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Color LeeAddTintColor; + +/** 添加文本颜色设置 -> 格式: .LeeAddTextColor(@@"tag" , UIColor) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Color LeeAddTextColor; + +/** 添加填充颜色设置 -> 格式: .LeeAddFillColor(@@"tag" , UIColor) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Color LeeAddFillColor; + +/** 添加笔画颜色设置 -> 格式: .LeeAddStrokeColor(@@"tag" , UIColor) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Color LeeAddStrokeColor; + +/** 添加边框颜色设置 -> 格式: .LeeAddBorderColor(@@"tag" , UIColor) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Color LeeAddBorderColor; + +/** 添加阴影颜色设置 -> 格式: .LeeAddShadowColor(@@"tag" , UIColor) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Color LeeAddShadowColor; + +/** 添加开关开启颜色设置 -> 格式: .LeeAddOnTintColor(@@"tag" , UIColor) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Color LeeAddOnTintColor; + +/** 添加开关按钮颜色设置 -> 格式: .LeeAddThumbTintColor(@@"tag" , UIColor) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Color LeeAddThumbTintColor; + +/** 添加分隔线颜色设置 -> 格式: .LeeAddSeparatorColor(@@"tag" , UIColor) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Color LeeAddSeparatorColor; + +/** 添加bar渲染颜色设置 -> 格式: .LeeAddBarTintColor(@@"tag" , UIColor) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Color LeeAddBarTintColor; + +/** 添加背景颜色设置 -> 格式: .LeeAddBackgroundColor(@@"tag" , UIColor) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Color LeeAddBackgroundColor; + +/** 添加占位符颜色设置 -> 格式: .LeeAddPlaceholderColor(@@"tag" , UIColor) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Color LeeAddPlaceholderColor; + +/** 添加进度轨道渲染颜色设置 -> 格式: .LeeAddTrackTintColor(@@"tag" , UIColor) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Color LeeAddTrackTintColor; + +/** 添加进度渲染颜色设置 -> 格式: .LeeAddProgressTintColor(@@"tag" , UIColor) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Color LeeAddProgressTintColor; + +/** 添加高亮文本颜色设置 -> 格式: .LeeAddHighlightedTextColor(@@"tag" , UIColor) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Color LeeAddHighlightedTextColor; + +/** 添加页数指示渲染颜色设置 -> 格式: .LeeAddPageIndicatorTintColor(@@"tag" , UIColor) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Color LeeAddPageIndicatorTintColor; + +/** 添加当前页数指示渲染颜色设置 -> 格式: .LeeAddCurrentPageIndicatorTintColor(@@"tag" , UIColor) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Color LeeAddCurrentPageIndicatorTintColor; + +/** 添加按钮标题颜色设置 -> 格式: .LeeAddButtonTitleColor(@@"tag" , UIColor , UIControlStateNormal) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_ColorAndState LeeAddButtonTitleColor; + +/** 添加按钮标题阴影颜色设置 -> 格式: .LeeAddButtonTitleShadowColor(@@"tag" , UIColor , UIControlStateNormal) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_ColorAndState LeeAddButtonTitleShadowColor; + +/** Image快捷设置方法 */ + +/** 添加图片设置 -> 格式: .LeeAddImage(@@"tag" , UIImage 或 @@"imageName" 或 @@"imagePath") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Image LeeAddImage; + +/** 添加进度轨道图片设置 -> 格式: .LeeAddTrackImage(@@"tag" , UIImage 或 @@"imageName" 或 @@"imagePath") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Image LeeAddTrackImage; + +/** 添加进度图片设置 -> 格式: .LeeAddProgressImage(@@"tag" , UIImage 或 @@"imageName" 或 @@"imagePath") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Image LeeAddProgressImage; + +/** 添加阴影图片设置 -> 格式: .LeeAddShadowImage(@@"tag" , UIImage 或 @@"imageName" 或 @@"imagePath") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Image LeeAddShadowImage; + +/** 添加选中图片设置 -> 格式: .LeeAddSelectedImage(@@"tag" , UIImage 或 @@"imageName" 或 @@"imagePath") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Image LeeAddSelectedImage; + +/** 添加背景图片设置 -> 格式: .LeeAddBackgroundImage(@@"tag" , UIImage 或 @@"imageName" 或 @@"imagePath") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Image LeeAddBackgroundImage; + +/** 添加返回指示图片设置 -> 格式: .LeeAddBackIndicatorImage(@@"tag" , UIImage 或 @@"imageName" 或 @@"imagePath") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Image LeeAddBackIndicatorImage; + +/** 添加返回指示图片设置 -> 格式: .LeeAddBackIndicatorTransitionMaskImage(@@"tag" , UIImage 或 @@"imageName" 或 @@"imagePath") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Image LeeAddBackIndicatorTransitionMaskImage; + +/** 添加选择指示器图片设置 -> 格式: .LeeAddSelectionIndicatorImage(@@"tag" , UIImage 或 @@"imageName" 或 @@"imagePath") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Image LeeAddSelectionIndicatorImage; + +/** 添加分栏背景图片设置 -> 格式: .LeeAddScopeBarBackgroundImage(@@"tag" , UIImage 或 @@"imageName" 或 @@"imagePath") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Image LeeAddScopeBarBackgroundImage; + +/** 添加按钮图片设置 -> 格式: .LeeAddButtonImage(@@"tag" , UIImage , UIControlStateNormal) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_ImageAndState LeeAddButtonImage; + +/** 添加按钮背景图片设置 -> 格式: .LeeAddButtonBackgroundImage(@@"tag" , UIImage , UIControlStateNormal) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_ImageAndState LeeAddButtonBackgroundImage; + + +/** 添加颜色设置 -> 格式: .LeeAddSelectorAndColor(@@"tag" , @@selector(XXX:) , UIColor 或 @"F3F3F3") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_SelectorAndColor LeeAddSelectorAndColor; + +/** 添加图片设置 -> 格式: .LeeAddSelectorAndImage(@@"tag" , @@selector(XXX:) , UIImage 或 @"imageName" 或 @"imagePath") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_SelectorAndImage LeeAddSelectorAndImage; + + +/** 基础设置方法 */ + +/** 添加路径设置 -> 格式: .LeeAddKeyPathAndValue(@@"tag" , @@"keyPath" , id) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_KeyPathAndValue LeeAddKeyPathAndValue; + +/** 添加方法设置 -> 格式: .LeeAddSelectorAndValues(@@"tag" , @@selector(XXX:XXX:) , id , id) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_SelectorAndValues LeeAddSelectorAndValues; + +/** 添加方法设置 -> 格式: .LeeAddSelectorAndValueArray(@@"tag" , @@selector(XXX:XXX:) , @@[id , id]) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_SelectorAndValueArray LeeAddSelectorAndValueArray; + +/** 移除路径设置 -> 格式: .LeeRemoveKeyPath(@@"tag" , @@"keyPath") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_KeyPath LeeRemoveKeyPath; + +/** 移除方法设置 -> 格式: .LeeRemoveSelector(@@"tag" , @@selector(XXX:XXX:)) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToT_Selector LeeRemoveSelector; + + +/** 移除全部设置 -> 格式: .LeeClearAllConfig() */ +@property (nonatomic , copy , readonly ) LEEConfigTheme LeeClearAllConfig; + +/** 移除标签全部的设置 -> 格式: .LeeClearAllConfig_Tag(@@"tag") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToTag LeeClearAllConfig_Tag; + +/** 移除路径全部的设置 -> 格式: .LeeClearAllConfig_KeyPath(@@"keyPath") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToKeyPath LeeClearAllConfig_KeyPath; + +/** 移除方法全部的设置 -> 格式: .LeeClearAllConfig_Selector(@selector(XXXX:)) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToSelector LeeClearAllConfig_Selector; + +@end + +@interface LEEThemeConfigModel (IdentifierModeExtend) + +/** Block */ + +/** 自定义设置 -> 格式: .LeeCustomConfig(@@"identifier" , ^(id item , id value){ code... }) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifierAndBlock LeeCustomConfig; + +/** Color快捷设置方法 */ + +/** 设置渲染颜色标识符 -> 格式: .LeeConfigTintColor(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigTintColor; + +/** 设置文本颜色标识符 -> 格式: .LeeConfigTextColor(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigTextColor; + +/** 设置填充颜色标识符 -> 格式: .LeeConfigFillColor(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigFillColor; + +/** 设置笔画颜色标识符 -> 格式: .LeeConfigStrokeColor(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigStrokeColor; + +/** 设置边框颜色标识符 -> 格式: .LeeConfigBorderColor(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigBorderColor; + +/** 设置文本颜色标识符 -> 格式: .LeeConfigShadowColor(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigShadowColor; + +/** 设置开关开启颜色标识符 -> 格式: .LeeConfigOnTintColor(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigOnTintColor; + +/** 设置开关按钮颜色标识符 -> 格式: .LeeConfigThumbTintColor(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigThumbTintColor; + +/** 设置分隔线颜色标识符 -> 格式: .LeeConfigSeparatorColor(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigSeparatorColor; + +/** 设置bar渲染颜色标识符 -> 格式: .LeeConfigBarTintColor(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigBarTintColor; + +/** 设置背景颜色标识符 -> 格式: .LeeConfigBackgroundColor(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigBackgroundColor; + +/** 设置占位符颜色标识符 -> 格式: .LeeConfigPlaceholderColor(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigPlaceholderColor; + +/** 设置进度轨道渲染颜色标识符 -> 格式: .LeeConfigTrackTintColor(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigTrackTintColor; + +/** 设置进度渲染颜色标识符 -> 格式: .LeeConfigProgressTintColor(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigProgressTintColor; + +/** 设置高亮文本颜色标识符 -> 格式: .LeeConfigHighlightedTextColor(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigHighlightedTextColor; + +/** 设置页数指示渲染颜色标识符 -> 格式: .LeeConfigPageIndicatorTintColor(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigPageIndicatorTintColor; + +/** 设置当前页数指示渲染颜色标识符 -> 格式: .LeeConfigCurrentPageIndicatorTintColor(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigCurrentPageIndicatorTintColor; + +/** 设置按钮标题颜色标识符 -> 格式: .LeeConfigButtonTitleColor(@@"identifier" , UIControlStateNormal) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifierAndState LeeConfigButtonTitleColor; + +/** 设置按钮标题阴影颜色标识符 -> 格式: .LeeConfigButtonTitleColor(@@"identifier" , UIControlStateNormal) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifierAndState LeeConfigButtonTitleShadowColor; + +/** Image快捷设置方法 */ + +/** 设置图片标识符 -> 格式: .LeeConfigImage(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigImage; + +/** 设置进度轨道图片标识符 -> 格式: .LeeConfigTrackImage(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigTrackImage; + +/** 设置进度图片标识符 -> 格式: .LeeConfigProgressImage(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigProgressImage; + +/** 设置阴影图片标识符 -> 格式: .LeeConfigShadowImage(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigShadowImage; + +/** 设置选中图片标识符 -> 格式: .LeeConfigSelectedImage(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigSelectedImage; + +/** 设置背景图片标识符 -> 格式: .LeeConfigBackgroundImage(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigBackgroundImage; + +/** 设置返回指示图片标识符 -> 格式: .LeeConfigBackIndicatorImage(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigBackIndicatorImage; + +/** 设置返回指示图片标识符 -> 格式: .LeeConfigBackIndicatorTransitionMaskImage(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigBackIndicatorTransitionMaskImage; + +/** 设置选择指示器图片标识符 -> 格式: .LeeConfigSelectionIndicatorImage(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigSelectionIndicatorImage; + +/** 设置分栏背景图片标识符 -> 格式: .LeeConfigScopeBarBackgroundImage(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeConfigScopeBarBackgroundImage; + +/** 设置按钮图片标识符 -> 格式: .LeeConfigButtonImage(@@"identifier" , UIControlStateNormal) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifierAndState LeeConfigButtonImage; + +/** 设置按钮背景图片标识符 -> 格式: .LeeConfigButtonBackgroundImage(@@"identifier" , UIControlStateNormal) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifierAndState LeeConfigButtonBackgroundImage; + +/** 基础设置方法 */ + +/** 设置路径标识符 -> 格式: .LeeConfigKeyPathAndIdentifier(@@"keyPath" , @@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToKeyPathAndIdentifier LeeConfigKeyPathAndIdentifier; + +/** 设置方法标识符 -> 格式: .LeeConfigSelectorAndIdentifier(@@selector(XXX:) , @@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToSelectorAndIdentifier LeeConfigSelectorAndIdentifier; + +/** 设置方法标识符 -> 格式: .LeeConfigSelectorAndValueArray(@@selector(XXX:XXX:) , @@[id , id]) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToSelectorAndValues LeeConfigSelectorAndValueArray; + +/** 移除路径标识符设置 -> 格式: .LeeRemoveKeyPathIdentifier(@@"keyPath") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToKeyPath LeeRemoveKeyPathIdentifier; + +/** 移除方法标识符设置 -> 格式: .LeeRemoveSelectorIdentifier(@@selector(XXX:)) */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToSelector LeeRemoveSelectorIdentifier; + +/** 移除标识符设置 -> 格式: .LeeRemoveIdentifier(@@"identifier") */ +@property (nonatomic , copy , readonly ) LEEConfigThemeToIdentifier LeeRemoveIdentifier; + + +/** 移除全部设置(标识符模式) -> 格式: .LeeClearAllConfigOnIdentifierMode() */ +@property (nonatomic , copy , readonly ) LEEConfigTheme LeeClearAllConfigOnIdentifierMode; + +@end + +@interface LEEThemeIdentifier : NSString + ++ (LEEThemeIdentifier *)ident:(NSString *)ident; + +@end + +@interface NSObject (LEEThemeConfigObject) + +@property (nonatomic , strong ) LEEThemeConfigModel *lee_theme; + +@end + +@interface UIColor (LEEThemeColor) + ++ (UIColor *)leeTheme_ColorWithHexString:(NSString *)hexString; + +@end + + +/* + * + * ┌─┐ ┌─┐ + * ┌──┘ ┴───────┘ ┴──┐ + * │ │ + * │ ─── │ + * │ ─┬┘ └┬─ │ + * │ │ + * │ ─┴─ │ + * │ │ + * └───┐ ┌───┘ + * │ │ + * │ │ + * │ │ + * │ └──────────────┐ + * │ │ + * │ ├─┐ + * │ ┌─┘ + * │ │ + * └─┐ ┐ ┌───────┬──┐ ┌──┘ + * │ ─┤ ─┤ │ ─┤ ─┤ + * └──┴──┘ └──┴──┘ + * 神兽 保佑 + * 代码无BUG! + */ diff --git a/SDAutoLayoutDemo/Vender/LEETheme/LEETheme.m b/SDAutoLayoutDemo/Vender/LEETheme/LEETheme.m new file mode 100644 index 0000000..877a5b4 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/LEETheme/LEETheme.m @@ -0,0 +1,2276 @@ +/*! + * @header LEETheme.m + * + * ┌─┐ ┌───────┐ ┌───────┐ 帅™ + * │ │ │ ┌─────┘ │ ┌─────┘ + * │ │ │ └─────┐ │ └─────┐ + * │ │ │ ┌─────┘ │ ┌─────┘ + * │ └─────┐│ └─────┐ │ └─────┐ + * └───────┘└───────┘ └───────┘ + * + * @brief LEE主题管理 + * + * @author LEE + * @copyright Copyright © 2016 - 2017年 lee. All rights reserved. + * @version V1.1.7 + */ + +#import "LEETheme.h" + +#import +#import + +static NSString * const LEEThemeChangingNotificaiton = @"LEEThemeChangingNotificaiton"; +static NSString * const LEEThemeAddTagNotificaiton = @"LEEThemeAddTagNotificaiton"; +static NSString * const LEEThemeRemoveTagNotificaiton = @"LEEThemeRemoveTagNotificaiton"; +static NSString * const LEEThemeAllTags = @"LEEThemeAllTags"; +static NSString * const LEEThemeCurrentTag = @"LEEThemeCurrentTag"; +static NSString * const LEEThemeConfigInfo = @"LEEThemeConfigInfo"; + +@interface LEETheme () + +@property (nonatomic , copy ) NSString *defaultTag; + +@property (nonatomic , copy ) NSString *currentTag; + +@property (nonatomic , strong ) NSMutableArray *allTags; + +@property (nonatomic , strong ) NSMutableDictionary *configInfo; + +@end + +@implementation LEETheme + +#if !__has_feature(objc_arc) +#error "ARC才可以 ( *・ω・)✄╰ひ╯ " +#endif + ++ (LEETheme *)shareTheme{ + + static LEETheme *themeManager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + + themeManager = [[LEETheme alloc]init]; + }); + + return themeManager; +} + +#pragma mark Public + ++ (void)startTheme:(NSString *)tag{ + + NSAssert([[LEETheme shareTheme].allTags containsObject:tag], @"所启用的主题不存在 - 请检查是否添加了该%@主题的设置" , tag); + + if (!tag) return; + + [LEETheme shareTheme].currentTag = tag; + + [[NSNotificationCenter defaultCenter] postNotificationName:LEEThemeChangingNotificaiton object:nil userInfo:nil]; +} + ++ (void)defaultTheme:(NSString *)tag{ + + if (!tag) return; + + [LEETheme shareTheme].defaultTag = tag; + + if (![LEETheme shareTheme].currentTag && ![[NSUserDefaults standardUserDefaults] objectForKey:LEEThemeCurrentTag]) [LEETheme shareTheme].currentTag = tag; +} + ++ (NSString *)currentThemeTag{ + + return [LEETheme shareTheme].currentTag ? [LEETheme shareTheme].currentTag : [[NSUserDefaults standardUserDefaults] objectForKey:LEEThemeCurrentTag]; +} + ++ (NSArray *)allThemeTag{ + + return [[LEETheme shareTheme].allTags copy]; +} + +#pragma mark Private + +- (void)setCurrentTag:(NSString *)currentTag{ + + _currentTag = currentTag; + + [[NSUserDefaults standardUserDefaults] setObject:currentTag forKey:LEEThemeCurrentTag]; + + [[NSUserDefaults standardUserDefaults] synchronize]; +} + +- (void)saveConfigInfo{ + + [[NSUserDefaults standardUserDefaults] setObject:self.configInfo forKey:LEEThemeConfigInfo]; + + [[NSUserDefaults standardUserDefaults] synchronize]; +} + ++ (void)addTagToAllTags:(NSString *)tag{ + + if (![[LEETheme shareTheme].allTags containsObject:tag]) { + + [[LEETheme shareTheme].allTags addObject:tag]; + + [[NSUserDefaults standardUserDefaults] setObject:[LEETheme shareTheme].allTags forKey:LEEThemeAllTags]; + + [[NSUserDefaults standardUserDefaults] synchronize]; + } + +} + ++ (void)removeTagToAllTags:(NSString *)tag{ + + if ([[LEETheme shareTheme].allTags containsObject:tag]) { + + [[LEETheme shareTheme].allTags removeObject:tag]; + + [[NSUserDefaults standardUserDefaults] setObject:[LEETheme shareTheme].allTags forKey:LEEThemeAllTags]; + + [[NSUserDefaults standardUserDefaults] synchronize]; + } + +} + +#pragma mark - LazyLoading + +- (NSMutableArray *)allTags{ + + if (!_allTags) _allTags = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:LEEThemeAllTags]]; + + return _allTags; +} + +- (NSMutableDictionary *)configInfo{ + + if (!_configInfo) _configInfo = [NSMutableDictionary dictionaryWithDictionary:[[NSUserDefaults standardUserDefaults] objectForKey:LEEThemeConfigInfo]]; + + return _configInfo; +} + +@end + +@implementation LEETheme (JsonModeExtend) + ++ (void)addThemeConfigWithJson:(NSString *)json Tag:(NSString *)tag ResourcesPath:(NSString *)path{ + + if (json) { + + NSError *jsonError = nil; + + NSDictionary *jsonConfigInfo = [NSJSONSerialization JSONObjectWithData:[json dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&jsonError]; + + NSAssert(!jsonError, @"添加的主题json配置数据解析错误 - 错误描述"); + NSAssert(jsonConfigInfo, @"添加的主题json配置数据解析为空 - 请检查"); + NSAssert(tag, @"添加的主题json标签不能为空"); + + if (!jsonError && jsonConfigInfo) { + + [[LEETheme shareTheme].configInfo setValue:[NSMutableDictionary dictionaryWithObjectsAndKeys:jsonConfigInfo , @"info", path , @"path" , nil] forKey:tag]; + + [[LEETheme shareTheme] saveConfigInfo]; + + [LEETheme addTagToAllTags:tag]; + + [[NSNotificationCenter defaultCenter] postNotificationName:LEEThemeAddTagNotificaiton object:nil userInfo:@{@"tag" : tag}]; + } + + } + +} + ++ (void)removeThemeConfigWithTag:(NSString *)tag{ + + if ([[LEETheme shareTheme].allTags containsObject:tag] && ![[LEETheme shareTheme].defaultTag isEqualToString:tag]) { + + [[NSNotificationCenter defaultCenter] postNotificationName:LEEThemeRemoveTagNotificaiton object:nil userInfo:@{@"tag" : tag}]; + + [LEETheme removeTagToAllTags:tag]; + + [[LEETheme shareTheme].configInfo removeObjectForKey:tag]; + + [[LEETheme shareTheme] saveConfigInfo]; + + if ([[LEETheme currentThemeTag] isEqualToString:tag]) [LEETheme startTheme:[LEETheme shareTheme].defaultTag]; + } + +} + ++ (NSString *)getResourcesPathWithTag:(NSString *)tag{ + + NSString *path = [LEETheme shareTheme].configInfo[tag][@"path"]; + + return path ? path : [[NSBundle mainBundle] bundlePath]; +} + ++ (id)getValueWithTag:(NSString *)tag Identifier:(NSString *)identifier{ + + id value = nil; + + NSDictionary *configInfo = [LEETheme shareTheme].configInfo[tag]; + + NSDictionary *info = configInfo[@"info"]; + + NSDictionary *colorInfo = info[@"color"]; + + NSString *colorHexString = colorInfo[identifier]; + + if (colorHexString) { + + UIColor *color = [UIColor leeTheme_ColorWithHexString:colorHexString]; + + if (color && !value) value = color; + } + + NSDictionary *imageInfo = info[@"image"]; + + NSString *imageName = imageInfo[identifier]; + + if (imageName) { + + NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; + + NSString *path = configInfo[@"path"]; + + if (path) path = [documentsPath stringByAppendingPathComponent:path]; + + UIImage *image = path ? [UIImage imageWithContentsOfFile:[path stringByAppendingPathComponent:imageName]] : [UIImage imageNamed:imageName]; + + if (!image) image = [UIImage imageWithContentsOfFile:[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:imageName]]; + + if (!image) image = [UIImage imageNamed:imageName]; + + if (image && !value) value = image; + } + + NSDictionary *otherInfo = info[@"other"]; + + if (!value) value = otherInfo[identifier]; + + return value; +} + +@end + +#pragma mark - ----------------主题设置模型---------------- + +@interface LEEThemeConfigModel () + +@property (nonatomic , copy ) void(^modelUpdateCurrentThemeConfig)(); +@property (nonatomic , copy ) void(^modelConfigThemeChangingBlock)(); + +@property (nonatomic , copy ) LEEThemeChangingBlock modelChangingBlock; + +@property (nonatomic , copy ) NSString *modelCurrentThemeTag; + +@property (nonatomic , strong ) NSMutableDictionary *modelThemeBlockConfigInfo; // @{tag : @{block : value}} +@property (nonatomic , strong ) NSMutableDictionary *modelThemeKeyPathConfigInfo; // @{keypath : @{tag : value}} +@property (nonatomic , strong ) NSMutableDictionary *modelThemeSelectorConfigInfo; // @{selector : @{tag : @[@[parameter, parameter,...] , @[...]]}} + +@end + +@implementation LEEThemeConfigModel + +- (void)dealloc{ + + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + objc_removeAssociatedObjects(self); + + _modelCurrentThemeTag = nil; + _modelThemeBlockConfigInfo = nil; + _modelThemeKeyPathConfigInfo = nil; + _modelThemeSelectorConfigInfo = nil; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(leeTheme_RemoveThemeTagNotify:) name:LEEThemeRemoveTagNotificaiton object:nil]; + } + return self; +} + +- (void)leeTheme_RemoveThemeTagNotify:(NSNotification *)notify{ + + NSString *tag = notify.userInfo[@"tag"]; + + self.LeeClearAllConfig_Tag(tag); +} + +- (void)updateCurrentThemeConfigHandleWithTag:(NSString *)tag{ + + if ([[LEETheme currentThemeTag] isEqualToString:tag]) { + + if ([NSThread isMainThread]) { + + if (self.modelUpdateCurrentThemeConfig) self.modelUpdateCurrentThemeConfig(); + + } else { + + dispatch_async(dispatch_get_main_queue(), ^{ + + if (self.modelUpdateCurrentThemeConfig) self.modelUpdateCurrentThemeConfig(); + }); + } + + } + +} + +- (LEEConfigThemeToChangingBlock)LeeThemeChangingBlock{ + + __weak typeof(self) weakSelf = self; + + return ^(LEEThemeChangingBlock changingBlock){ + + if (changingBlock) { + + weakSelf.modelChangingBlock = changingBlock; + + if (weakSelf.modelConfigThemeChangingBlock) weakSelf.modelConfigThemeChangingBlock(); + } + + return weakSelf; + }; + +} + +- (LEEConfigThemeToT_Block)LeeAddCustomConfig{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , LEEThemeConfigBlock configBlock){ + + if (configBlock) { + + [LEETheme addTagToAllTags:tag]; + + NSMutableDictionary *info = weakSelf.modelThemeBlockConfigInfo[tag]; + + if (!info) info = [NSMutableDictionary dictionary]; + + [info setObject:[NSNull null] forKey:configBlock]; + + [weakSelf.modelThemeBlockConfigInfo setObject:info forKey:tag]; + + [weakSelf updateCurrentThemeConfigHandleWithTag:tag]; + } + + return weakSelf; + }; + +} + +- (LEEConfigThemeToTs_Block)LeeAddCustomConfigs{ + + __weak typeof(self) weakSelf = self; + + return ^(NSArray *tags , LEEThemeConfigBlock configBlock){ + + if (configBlock) { + + [tags enumerateObjectsUsingBlock:^(NSString *tag, NSUInteger idx, BOOL * _Nonnull stop) { + + [LEETheme addTagToAllTags:tag]; + + NSMutableDictionary *info = weakSelf.modelThemeBlockConfigInfo[tag]; + + if (!info) info = [NSMutableDictionary dictionary]; + + [info setObject:[NSNull null] forKey:configBlock]; + + [weakSelf.modelThemeBlockConfigInfo setObject:info forKey:tag]; + + [weakSelf updateCurrentThemeConfigHandleWithTag:tag]; + }]; + + } + + return weakSelf; + }; + +} + +- (LEEConfigThemeToT_Color)LeeAddTintColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id color){ + + return weakSelf.LeeAddSelectorAndColor(tag , @selector(setTintColor:) , color); + }; + +} + +- (LEEConfigThemeToT_Color)LeeAddTextColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id color){ + + return weakSelf.LeeAddSelectorAndColor(tag , @selector(setTextColor:) , color); + }; + +} + +- (LEEConfigThemeToT_Color)LeeAddFillColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id color){ + + return weakSelf.LeeAddSelectorAndColor(tag , @selector(setFillColor:) , color); + }; + +} + +- (LEEConfigThemeToT_Color)LeeAddStrokeColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id color){ + + return weakSelf.LeeAddSelectorAndColor(tag , @selector(setStrokeColor:) , color); + }; + +} + +- (LEEConfigThemeToT_Color)LeeAddBorderColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id color){ + + return weakSelf.LeeAddSelectorAndColor(tag , @selector(setBorderColor:) , color); + }; + +} + +- (LEEConfigThemeToT_Color)LeeAddShadowColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id color){ + + return weakSelf.LeeAddSelectorAndColor(tag , @selector(setShadowColor:) , color); + }; + +} + +- (LEEConfigThemeToT_Color)LeeAddOnTintColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id color){ + + return weakSelf.LeeAddSelectorAndColor(tag , @selector(setOnTintColor:) , color); + }; + +} + +- (LEEConfigThemeToT_Color)LeeAddThumbTintColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id color){ + + return weakSelf.LeeAddSelectorAndColor(tag , @selector(setThumbTintColor:) , color); + }; + +} + +- (LEEConfigThemeToT_Color)LeeAddSeparatorColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id color){ + + return weakSelf.LeeAddSelectorAndColor(tag , @selector(setSeparatorColor:) , color); + }; + +} + +- (LEEConfigThemeToT_Color)LeeAddBarTintColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id color){ + + return weakSelf.LeeAddSelectorAndColor(tag , @selector(setBarTintColor:) , color); + }; + +} + +- (LEEConfigThemeToT_Color)LeeAddBackgroundColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id color){ + + return weakSelf.LeeAddSelectorAndColor(tag , @selector(setBackgroundColor:) , color); + }; + +} + +- (LEEConfigThemeToT_Color)LeeAddPlaceholderColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id color){ + + return weakSelf.LeeAddKeyPathAndValue(tag , @"_placeholderLabel.textColor" , color); + }; + +} + +- (LEEConfigThemeToT_Color)LeeAddTrackTintColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id color){ + + return weakSelf.LeeAddSelectorAndColor(tag , @selector(setTrackTintColor:) , color); + }; + +} + +- (LEEConfigThemeToT_Color)LeeAddProgressTintColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id color){ + + return weakSelf.LeeAddSelectorAndColor(tag , @selector(setProgressTintColor:) , color); + }; + +} + +- (LEEConfigThemeToT_Color)LeeAddHighlightedTextColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id color){ + + return weakSelf.LeeAddSelectorAndColor(tag , @selector(setHighlightedTextColor:) , color); + }; + +} + +- (LEEConfigThemeToT_Color)LeeAddCurrentPageIndicatorTintColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id color){ + + return weakSelf.LeeAddSelectorAndColor(tag , @selector(setCurrentPageIndicatorTintColor:) , color); + }; + +} + +- (LEEConfigThemeToT_Color)LeeAddPageIndicatorTintColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id color){ + + return weakSelf.LeeAddSelectorAndColor(tag , @selector(setPageIndicatorTintColor:) , color); + }; + +} + +- (LEEConfigThemeToT_ColorAndState)LeeAddButtonTitleColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , UIColor *color , UIControlState state){ + + return weakSelf.LeeAddSelectorAndValues(tag , @selector(setTitleColor:forState:) , color , @(state) , nil); + }; + +} + +- (LEEConfigThemeToT_ColorAndState)LeeAddButtonTitleShadowColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , UIColor *color , UIControlState state){ + + return weakSelf.LeeAddSelectorAndValues(tag , @selector(setTitleShadowColor:forState:) , color , @(state), nil); + }; + +} + +- (LEEConfigThemeToT_Image)LeeAddImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id image){ + + return weakSelf.LeeAddSelectorAndImage(tag , @selector(setImage:) , image); + }; + +} + +- (LEEConfigThemeToT_Image)LeeAddTrackImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id image){ + + return weakSelf.LeeAddSelectorAndImage(tag , @selector(setTrackImage:) , image); + }; + +} + +- (LEEConfigThemeToT_Image)LeeAddProgressImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id image){ + + return weakSelf.LeeAddSelectorAndImage(tag , @selector(setProgressImage:) , image); + }; + +} + +- (LEEConfigThemeToT_Image)LeeAddShadowImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id image){ + + return weakSelf.LeeAddSelectorAndImage(tag , @selector(setShadowImage:) , image); + }; + +} + +- (LEEConfigThemeToT_Image)LeeAddSelectedImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id image){ + + return weakSelf.LeeAddSelectorAndImage(tag , @selector(setSelectedImage:) , image); + }; + +} + +- (LEEConfigThemeToT_Image)LeeAddBackgroundImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id image){ + + return weakSelf.LeeAddSelectorAndImage(tag , @selector(setBackgroundImage:) , image); + }; + +} + +- (LEEConfigThemeToT_Image)LeeAddBackIndicatorImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id image){ + + return weakSelf.LeeAddSelectorAndImage(tag , @selector(setBackIndicatorImage:) , image); + }; + +} + +- (LEEConfigThemeToT_Image)LeeAddBackIndicatorTransitionMaskImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id image){ + + return weakSelf.LeeAddSelectorAndImage(tag , @selector(setBackIndicatorTransitionMaskImage:) , image); + }; + +} + +- (LEEConfigThemeToT_Image)LeeAddSelectionIndicatorImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id image){ + + return weakSelf.LeeAddSelectorAndImage(tag , @selector(setSelectionIndicatorImage:) , image); + }; + +} + +- (LEEConfigThemeToT_Image)LeeAddScopeBarBackgroundImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , id image){ + + return weakSelf.LeeAddSelectorAndImage(tag , @selector(setScopeBarBackgroundImage:) , image); + }; + +} + +- (LEEConfigThemeToT_ImageAndState)LeeAddButtonImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , UIImage *image , UIControlState state){ + + return weakSelf.LeeAddSelectorAndValues(tag , @selector(setImage:forState:) , image , @(state), nil); + }; + +} + +- (LEEConfigThemeToT_ImageAndState)LeeAddButtonBackgroundImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , UIImage *image , UIControlState state){ + + return weakSelf.LeeAddSelectorAndValues(tag , @selector(setBackgroundImage:forState:) , image , @(state), nil); + }; + +} + +- (LEEConfigThemeToT_SelectorAndColor)LeeAddSelectorAndColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , SEL sel , id color){ + + id value = nil; + + if ([color isKindOfClass:NSString.class]) { + + value = [UIColor leeTheme_ColorWithHexString:color]; + + } else { + + value = color; + } + + if (value) weakSelf.LeeAddSelectorAndValueArray(tag , sel , @[value]); + + return weakSelf; + }; + +} + +- (LEEConfigThemeToT_SelectorAndImage)LeeAddSelectorAndImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , SEL sel , id image){ + + id value = nil; + + if ([image isKindOfClass:NSString.class]) { + + value = [UIImage imageNamed:image]; + + if (!value) value = [UIImage imageWithContentsOfFile:image]; + + } else { + + value = image; + } + + if (value) weakSelf.LeeAddSelectorAndValueArray(tag , sel , @[value]); + + return weakSelf; + }; + +} + +- (LEEConfigThemeToT_KeyPathAndValue)LeeAddKeyPathAndValue{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , NSString *keyPath , id value){ + + if (!value) return weakSelf; + + [LEETheme addTagToAllTags:tag]; + + NSMutableDictionary *info = weakSelf.modelThemeKeyPathConfigInfo[keyPath]; + + if (!info) info = [NSMutableDictionary dictionary]; + + [info setObject:value forKey:tag]; + + [weakSelf.modelThemeKeyPathConfigInfo setObject:info forKey:keyPath]; + + [weakSelf updateCurrentThemeConfigHandleWithTag:tag]; + + return weakSelf; + }; + +} + +- (LEEConfigThemeToT_SelectorAndValues)LeeAddSelectorAndValues{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag, SEL sel , ...){ + + if (!sel) return weakSelf; + + NSMutableArray *array = [NSMutableArray array]; + + va_list argsList; + + va_start(argsList, sel); + + id arg; + + while ((arg = va_arg(argsList, id))) { + + [array addObject:arg]; + } + + va_end(argsList); + + return weakSelf.LeeAddSelectorAndValueArray(tag, sel, array); + }; + +} + +- (LEEConfigThemeToT_SelectorAndValueArray)LeeAddSelectorAndValueArray{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag, SEL sel , NSArray *values){ + + if (!tag) return weakSelf; + + if (!sel) return weakSelf; + + [LEETheme addTagToAllTags:tag]; + + NSString *key = NSStringFromSelector(sel); + + NSMutableDictionary *info = weakSelf.modelThemeSelectorConfigInfo[key]; + + if (!info) info = [NSMutableDictionary dictionary]; + + NSMutableArray *valuesArray = info[tag]; + + if (!valuesArray) valuesArray = [NSMutableArray array]; + + [[valuesArray copy] enumerateObjectsUsingBlock:^(NSArray *valueArray, NSUInteger idx, BOOL * _Nonnull stop) { + + if ([valueArray isEqualToArray:values]) [valuesArray removeObject:valueArray]; // 过滤相同参数值的数组 + }]; + + if (values && values.count) [valuesArray addObject:values]; + + [info setObject:valuesArray forKey:tag]; + + [weakSelf.modelThemeSelectorConfigInfo setObject:info forKey:key]; + + [weakSelf updateCurrentThemeConfigHandleWithTag:tag]; + + return weakSelf; + }; + +} + +- (LEEConfigThemeToT_KeyPath)LeeRemoveKeyPath{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , NSString *keyPath){ + + NSMutableDictionary *info = weakSelf.modelThemeKeyPathConfigInfo[keyPath]; + + if (info) { + + [info removeObjectForKey:tag]; + + [weakSelf.modelThemeKeyPathConfigInfo setObject:info forKey:keyPath]; + } + + return weakSelf; + }; + +} + +- (LEEConfigThemeToT_Selector)LeeRemoveSelector{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag , SEL sel){ + + NSMutableDictionary *info = weakSelf.modelThemeSelectorConfigInfo[NSStringFromSelector(sel)]; + + if (info) { + + [info removeObjectForKey:tag]; + + [weakSelf.modelThemeSelectorConfigInfo setObject:info forKey:NSStringFromSelector(sel)]; + } + + return weakSelf; + }; + +} + +- (LEEConfigTheme)LeeClearAllConfig{ + + __weak typeof(self) weakSelf = self; + + return ^(){ + + weakSelf.modelChangingBlock = nil; + + [weakSelf.modelThemeBlockConfigInfo removeAllObjects]; + + [weakSelf.modelThemeKeyPathConfigInfo removeAllObjects]; + + [weakSelf.modelThemeSelectorConfigInfo removeAllObjects]; + + return weakSelf; + }; + +} + +- (LEEConfigThemeToTag)LeeClearAllConfig_Tag{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *tag){ + + [weakSelf.modelThemeBlockConfigInfo removeObjectForKey:tag]; + + for (id keyPath in [weakSelf.modelThemeKeyPathConfigInfo copy]) { + + weakSelf.LeeRemoveKeyPath(tag, keyPath); + } + + for (id selector in [weakSelf.modelThemeSelectorConfigInfo copy]) { + + weakSelf.LeeRemoveSelector(tag, NSSelectorFromString(selector)); + } + + return weakSelf; + }; + +} + +- (LEEConfigThemeToKeyPath)LeeClearAllConfig_KeyPath{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *keyPath){ + + [weakSelf.modelThemeKeyPathConfigInfo removeObjectForKey:keyPath]; + + return weakSelf; + }; + +} + +- (LEEConfigThemeToSelector)LeeClearAllConfig_Selector{ + + __weak typeof(self) weakSelf = self; + + return ^(SEL selector){ + + [weakSelf.modelThemeSelectorConfigInfo removeObjectForKey:NSStringFromSelector(selector)]; + + return weakSelf; + }; + +} + +#pragma mark - LazyLoading + +- (NSMutableDictionary *)modelThemeBlockConfigInfo{ + + if (!_modelThemeBlockConfigInfo) _modelThemeBlockConfigInfo = [NSMutableDictionary dictionary]; + + return _modelThemeBlockConfigInfo; +} + +- (NSMutableDictionary *)modelThemeKeyPathConfigInfo{ + + if (!_modelThemeKeyPathConfigInfo) _modelThemeKeyPathConfigInfo = [NSMutableDictionary dictionary]; + + return _modelThemeKeyPathConfigInfo; +} + +- (NSMutableDictionary *)modelThemeSelectorConfigInfo{ + + if (!_modelThemeSelectorConfigInfo) _modelThemeSelectorConfigInfo = [NSMutableDictionary dictionary]; + + return _modelThemeSelectorConfigInfo; +} + +@end + +typedef NS_ENUM(NSInteger, LEEThemeIdentifierConfigType) { + + /** 标识符设置类型 - Block */ + + LEEThemeIdentifierConfigTypeBlock, + + /** 标识符设置类型 - 路径,方法 */ + + LEEThemeIdentifierConfigTypeKeyPath, + LEEThemeIdentifierConfigTypeSelector +}; + +@implementation LEEThemeConfigModel (IdentifierModeExtend) + +- (LEEConfigThemeToIdentifierAndBlock)LeeCustomConfig{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier , LEEThemeConfigBlockToValue configBlock){ + + if (configBlock) { + + for (NSString *tag in [LEETheme shareTheme].allTags) { + + id value = [LEETheme getValueWithTag:tag Identifier:identifier]; + + if (value) { + + NSMutableDictionary *info = weakSelf.modelThemeBlockConfigInfo[tag]; + + if (!info) info = [NSMutableDictionary dictionary]; + + [info setObject:value forKey:configBlock]; + + [weakSelf.modelThemeBlockConfigInfo setObject:info forKey:tag]; + + [weakSelf updateCurrentThemeConfigHandleWithTag:tag]; + } + + } + + NSMutableDictionary *info = weakSelf.modelThemeIdentifierConfigInfo[@(LEEThemeIdentifierConfigTypeBlock)]; + + if (!info) info = [NSMutableDictionary dictionary]; + + [info setObject:identifier forKey:configBlock]; + + [weakSelf.modelThemeIdentifierConfigInfo setObject:info forKey:@(LEEThemeIdentifierConfigTypeBlock)]; + } + + return weakSelf; + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigTintColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setTintColor:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigTextColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setTextColor:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigFillColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setFillColor:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigStrokeColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setStrokeColor:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigBorderColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setBorderColor:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigShadowColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setShadowColor:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigOnTintColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setOnTintColor:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigThumbTintColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setThumbTintColor:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigSeparatorColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setSeparatorColor:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigBarTintColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setBarTintColor:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigBackgroundColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setBackgroundColor:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigPlaceholderColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigKeyPathAndIdentifier(@"_placeholderLabel.textColor" , identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigTrackTintColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setTrackTintColor:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigProgressTintColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setProgressTintColor:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigHighlightedTextColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setHighlightedTextColor:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigPageIndicatorTintColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setPageIndicatorTintColor:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigCurrentPageIndicatorTintColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setCurrentPageIndicatorTintColor:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifierAndState)LeeConfigButtonTitleColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier , UIControlState state){ + + return weakSelf.LeeConfigSelectorAndValueArray(@selector(setTitleColor:forState:), @[[LEEThemeIdentifier ident:identifier], @(state)]); + }; + +} + +- (LEEConfigThemeToIdentifierAndState)LeeConfigButtonTitleShadowColor{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier , UIControlState state){ + + return weakSelf.LeeConfigSelectorAndValueArray(@selector(setTitleShadowColor:forState:), @[[LEEThemeIdentifier ident:identifier], @(state)]); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setImage:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigTrackImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setTrackImage:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigProgressImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setProgressImage:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigShadowImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setShadowImage:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigSelectedImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setSelectedImage:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigBackgroundImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setBackgroundImage:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigBackIndicatorImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setBackIndicatorImage:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigBackIndicatorTransitionMaskImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setBackIndicatorTransitionMaskImage:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigSelectionIndicatorImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setSelectionIndicatorImage:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifier)LeeConfigScopeBarBackgroundImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndIdentifier(@selector(setScopeBarBackgroundImage:), identifier); + }; + +} + +- (LEEConfigThemeToIdentifierAndState)LeeConfigButtonImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier , UIControlState state){ + + return weakSelf.LeeConfigSelectorAndValueArray(@selector(setImage:forState:), @[[LEEThemeIdentifier ident:identifier], @(state)]); + }; + +} + +- (LEEConfigThemeToIdentifierAndState)LeeConfigButtonBackgroundImage{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier , UIControlState state){ + + weakSelf.LeeConfigSelectorAndValueArray(@selector(setBackgroundImage:forState:), @[[LEEThemeIdentifier ident:identifier], @(state)]); + + return weakSelf; + }; + +} + +- (LEEConfigThemeToKeyPathAndIdentifier)LeeConfigKeyPathAndIdentifier{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *keyPath , NSString *identifier){ + + for (NSString *tag in [LEETheme shareTheme].allTags) { + + id value = [LEETheme getValueWithTag:tag Identifier:identifier]; + + if (value) weakSelf.LeeAddKeyPathAndValue(tag, keyPath, value); + } + + NSMutableDictionary *info = weakSelf.modelThemeIdentifierConfigInfo[@(LEEThemeIdentifierConfigTypeKeyPath)]; + + if (!info) info = [NSMutableDictionary dictionary]; + + [info setObject:identifier forKey:keyPath]; + + [weakSelf.modelThemeIdentifierConfigInfo setObject:info forKey:@(LEEThemeIdentifierConfigTypeKeyPath)]; + + return weakSelf; + }; + +} + +- (LEEConfigThemeToSelectorAndIdentifier)LeeConfigSelectorAndIdentifier{ + + __weak typeof(self) weakSelf = self; + + return ^(SEL sel , NSString *identifier){ + + return weakSelf.LeeConfigSelectorAndValueArray(sel , @[[LEEThemeIdentifier ident:identifier]]); + }; + +} + +- (LEEConfigThemeToSelectorAndValues)LeeConfigSelectorAndValueArray{ + + __weak typeof(self) weakSelf = self; + + return ^(SEL sel , NSArray *values){ + + for (NSString *tag in [LEETheme shareTheme].allTags) { + + NSMutableArray *valueArray = [NSMutableArray array]; + + for (id value in values) { + + id v = value; + + if ([value isKindOfClass:LEEThemeIdentifier.class]) { + + v = [LEETheme getValueWithTag:tag Identifier:value]; + } + + if (v) [valueArray addObject:v]; + } + + if (valueArray.count == values.count && valueArray.count) weakSelf.LeeAddSelectorAndValueArray(tag, sel, valueArray); + } + + NSMutableDictionary *info = weakSelf.modelThemeIdentifierConfigInfo[@(LEEThemeIdentifierConfigTypeSelector)]; + + if (!info) info = [NSMutableDictionary dictionary]; + + if (values) [info setObject:NSStringFromSelector(sel) forKey:values]; + + [weakSelf.modelThemeIdentifierConfigInfo setObject:info forKey:@(LEEThemeIdentifierConfigTypeSelector)]; + + return weakSelf; + }; + +} + +- (LEEConfigThemeToKeyPath)LeeRemoveKeyPathIdentifier{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *keyPath){ + + id type = @(LEEThemeIdentifierConfigTypeKeyPath); + + NSMutableDictionary *info = weakSelf.modelThemeIdentifierConfigInfo[type]; + + for (id key in [info copy]) { + + if ([key isEqualToString:keyPath]) { + + for (NSString *tag in [LEETheme shareTheme].allTags) { + + if ([LEETheme getValueWithTag:tag Identifier:info[key]]) weakSelf.LeeRemoveKeyPath(tag, keyPath); + } + + [info removeObjectForKey:key]; + } + + } + + if (info) [weakSelf.modelThemeIdentifierConfigInfo setObject:info forKey:type]; + + return weakSelf; + }; + +} + +- (LEEConfigThemeToSelector)LeeRemoveSelectorIdentifier{ + + __weak typeof(self) weakSelf = self; + + return ^(SEL sel){ + + id type = @(LEEThemeIdentifierConfigTypeSelector); + + NSMutableDictionary *info = weakSelf.modelThemeIdentifierConfigInfo[type]; + + for (id key in [info copy]) { + + if ([info[key] isEqualToString:NSStringFromSelector(sel)]) { + + NSArray *values = key; + + for (id value in values) { + + if ([value isKindOfClass:LEEThemeIdentifier.class]) { + + for (NSString *tag in [LEETheme shareTheme].allTags) { + + if ([LEETheme getValueWithTag:tag Identifier:value]) weakSelf.LeeRemoveSelector(tag, NSSelectorFromString(info[key])); + } + + } + + } + + [info removeObjectForKey:key]; + } + + } + + if (info) [weakSelf.modelThemeIdentifierConfigInfo setObject:info forKey:type]; + + return weakSelf; + }; + +} + +- (LEEConfigThemeToIdentifier)LeeRemoveIdentifier{ + + __weak typeof(self) weakSelf = self; + + return ^(NSString *identifier){ + + for (id type in [weakSelf.modelThemeIdentifierConfigInfo copy]) { + + NSMutableDictionary *info = weakSelf.modelThemeIdentifierConfigInfo[type]; + + for (id key in [info copy]) { + + switch ([type integerValue]) { + + case LEEThemeIdentifierConfigTypeBlock: + { + if ([info[key] isEqualToString:identifier]) { + + for (NSString *tag in [LEETheme shareTheme].allTags) { + + id value = [LEETheme getValueWithTag:tag Identifier:identifier]; + + if (!value) continue; + + NSMutableDictionary *info = weakSelf.modelThemeBlockConfigInfo[tag]; + + if (info) { + + [info removeObjectForKey:key]; + + [weakSelf.modelThemeBlockConfigInfo setObject:info forKey:tag]; + } + + } + + [info removeObjectForKey:key]; + } + + } + break; + + case LEEThemeIdentifierConfigTypeKeyPath: + { + if ([info[key] isEqualToString:identifier]) { + + for (NSString *tag in [LEETheme shareTheme].allTags) { + + id value = [LEETheme getValueWithTag:tag Identifier:identifier]; + + if (!value) continue; + + weakSelf.LeeRemoveKeyPath(tag, key); + } + + [info removeObjectForKey:key]; + } + + } + break; + + case LEEThemeIdentifierConfigTypeSelector: + { + BOOL remove = NO; + + NSArray *values = key; + + for (id value in values) { + + if ([value isKindOfClass:LEEThemeIdentifier.class]) { + + if ([value isEqualToString:identifier]) { + + for (NSString *tag in [LEETheme shareTheme].allTags) { + + if ([LEETheme getValueWithTag:tag Identifier:value]) weakSelf.LeeRemoveSelector(tag, NSSelectorFromString(info[key])); + } + + remove = YES; + } + + } + + } + + if (remove) [info removeObjectForKey:key]; + } + break; + + default: + break; + } + + } + + if (info) [weakSelf.modelThemeIdentifierConfigInfo setObject:info forKey:type]; + } + + return weakSelf; + }; + +} + +- (LEEConfigTheme)LeeClearAllConfigOnIdentifierMode{ + + __weak typeof(self) weakSelf = self; + + return ^(){ + + for (NSNumber *type in weakSelf.modelThemeIdentifierConfigInfo) { + + NSDictionary *info = weakSelf.modelThemeIdentifierConfigInfo[type]; + + for (id key in info) { + + switch ([type integerValue]) { + + case LEEThemeIdentifierConfigTypeBlock: + { + for (NSString *tag in [LEETheme allThemeTag]) { + + NSMutableDictionary *blockInfo = weakSelf.modelThemeBlockConfigInfo[tag]; + + if (!blockInfo) blockInfo = [NSMutableDictionary dictionary]; + + for (id key in [blockInfo copy]) { + + if (![blockInfo[key] isKindOfClass:NSNull.class]) [blockInfo removeObjectForKey:key]; + } + + [weakSelf.modelThemeBlockConfigInfo setObject:blockInfo forKey:tag]; + } + + } + break; + + case LEEThemeIdentifierConfigTypeKeyPath: + { + NSString *identifier = info[key]; + + for (NSString *tag in [LEETheme allThemeTag]) { + + id value = [LEETheme getValueWithTag:tag Identifier:identifier]; + + if (!value) continue; + + weakSelf.LeeRemoveKeyPath(tag, key); + } + + } + break; + + case LEEThemeIdentifierConfigTypeSelector: + { + NSArray *values = key; + + for (NSString *tag in [LEETheme shareTheme].allTags) { + + BOOL remove = NO; + + for (id value in values) { + + if ([value isKindOfClass:LEEThemeIdentifier.class]) { + + if ([LEETheme getValueWithTag:tag Identifier:value]) remove = YES; + } + + } + + if (remove) weakSelf.LeeRemoveSelector(tag, NSSelectorFromString(info[key])); + } + + } + break; + + default: + break; + } + + } + + } + + [weakSelf.modelThemeIdentifierConfigInfo removeAllObjects]; + + return weakSelf; + }; + +} + +- (void)leeTheme_AddThemeTagNotify:(NSNotification *)notify{ + + NSString *tag = notify.userInfo[@"tag"]; + + NSDictionary *configInfo = self.modelThemeIdentifierConfigInfo; + + for (NSNumber *type in configInfo) { + + NSDictionary *info = configInfo[type]; + + for (id key in info) { + + switch ([type integerValue]) { + + case LEEThemeIdentifierConfigTypeBlock: + { + NSString *identifier = info[key]; + + id value = [LEETheme getValueWithTag:tag Identifier:identifier]; + + if (value) { + + NSMutableDictionary *blockInfo = self.modelThemeBlockConfigInfo[tag]; + + if (!blockInfo) blockInfo = [NSMutableDictionary dictionary]; + + [blockInfo setObject:value forKey:key]; + + [self.modelThemeBlockConfigInfo setObject:blockInfo forKey:tag]; + } + } + break; + + case LEEThemeIdentifierConfigTypeKeyPath: + { + NSString *identifier = info[key]; + + id value = [LEETheme getValueWithTag:tag Identifier:identifier]; + + if (value) self.LeeAddKeyPathAndValue(tag, key, value); + } + break; + + case LEEThemeIdentifierConfigTypeSelector: + { + NSArray *values = key; + + NSMutableArray *valueArray = [NSMutableArray array]; + + for (id value in values) { + + id v = value; + + if ([value isKindOfClass:LEEThemeIdentifier.class]) { + + v = [LEETheme getValueWithTag:tag Identifier:value]; + } + + if (v) [valueArray addObject:v]; + } + + if (valueArray.count == values.count && valueArray.count) self.LeeAddSelectorAndValueArray(tag, NSSelectorFromString(info[key]), valueArray); + } + break; + + default: + break; + } + + } + + } + +} + +- (NSMutableDictionary *)modelThemeIdentifierConfigInfo{ + + /** + * @{type : @{(keypath or block) : identifier}} + * + * @{type : @{values : selector}} + */ + NSMutableDictionary *dic = objc_getAssociatedObject(self, _cmd); + + if (!dic) { + + dic = [NSMutableDictionary dictionary]; + + objc_setAssociatedObject(self, _cmd, dic , OBJC_ASSOCIATION_RETAIN_NONATOMIC); + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(leeTheme_AddThemeTagNotify:) name:LEEThemeAddTagNotificaiton object:nil]; + } + + return dic; +} + +- (void)setModelThemeIdentifierConfigInfo:(NSMutableDictionary *)modelThemeIdentifierConfigInfo{ + + objc_setAssociatedObject(self, @selector(modelThemeIdentifierConfigInfo), modelThemeIdentifierConfigInfo , OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end + +@implementation LEEThemeIdentifier +{ + NSString *_backingStore; +} + ++ (LEEThemeIdentifier *)ident:(NSString *)ident{ + + return [[LEEThemeIdentifier alloc] initWithString:ident]; +} + +- (id)initWithString:(NSString *)aString +{ + if (self = [self init]) { + + _backingStore = [[NSString stringWithString:aString] copy]; + } + return self; +} + +- (NSUInteger)length{ + + return [_backingStore length]; +} + +- (unichar)characterAtIndex:(NSUInteger)index{ + + return [_backingStore characterAtIndex:index]; +} + +@end + +#pragma mark - ----------------主题设置---------------- + +@implementation NSObject (LEEThemeConfigObject) + ++ (void)load{ + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + + NSArray *selStringsArray = @[@"dealloc"]; + + [selStringsArray enumerateObjectsUsingBlock:^(NSString *selString, NSUInteger idx, BOOL *stop) { + + NSString *leeSelString = [@"lee_theme_" stringByAppendingString:selString]; + + Method originalMethod = class_getInstanceMethod(self, NSSelectorFromString(selString)); + + Method leeMethod = class_getInstanceMethod(self, NSSelectorFromString(leeSelString)); + + BOOL isAddedMethod = class_addMethod(self, NSSelectorFromString(selString), method_getImplementation(leeMethod), method_getTypeEncoding(leeMethod)); + + if (isAddedMethod) { + + class_replaceMethod(self, NSSelectorFromString(leeSelString), method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); + + } else { + + method_exchangeImplementations(originalMethod, leeMethod); + } + + }]; + + }); + +} + +- (void)lee_theme_dealloc{ + + if ([self isLeeTheme]) { + + [[NSNotificationCenter defaultCenter] removeObserver:self name:LEEThemeChangingNotificaiton object:nil]; + + objc_removeAssociatedObjects(self); + } + + [self lee_theme_dealloc]; +} + +- (BOOL)isChangeTheme{ + + return (!self.lee_theme.modelCurrentThemeTag || ![self.lee_theme.modelCurrentThemeTag isEqualToString:[LEETheme currentThemeTag]]) ? YES : NO; +} + +- (void)leeTheme_ChangeThemeConfigNotify:(NSNotification *)notify{ + + dispatch_async(dispatch_get_main_queue(), ^{ + + if ([self isChangeTheme]) { + + if (self.lee_theme.modelChangingBlock) self.lee_theme.modelChangingBlock([LEETheme currentThemeTag] , self); + + [CATransaction begin]; + + [CATransaction setDisableActions:YES]; + + [self changeThemeConfig]; + + [CATransaction commit]; + } + + }); + +} + +- (void)setInv:(NSInvocation *)inv Sig:(NSMethodSignature *)sig Obj:(id)obj Index:(NSInteger)index{ + + if (sig.numberOfArguments <= index) return; + + char *type = (char *)[sig getArgumentTypeAtIndex:index]; + + while (*type == 'r' || // const + *type == 'n' || // in + *type == 'N' || // inout + *type == 'o' || // out + *type == 'O' || // bycopy + *type == 'R' || // byref + *type == 'V') { // oneway + type++; // cutoff useless prefix + } + + BOOL unsupportedType = NO; + + switch (*type) { + case 'v': // 1: void + case 'B': // 1: bool + case 'c': // 1: char / BOOL + case 'C': // 1: unsigned char + case 's': // 2: short + case 'S': // 2: unsigned short + case 'i': // 4: int / NSInteger(32bit) + case 'I': // 4: unsigned int / NSUInteger(32bit) + case 'l': // 4: long(32bit) + case 'L': // 4: unsigned long(32bit) + { // 'char' and 'short' will be promoted to 'int'. + int value = [obj intValue]; + [inv setArgument:&value atIndex:index]; + } break; + + case 'q': // 8: long long / long(64bit) / NSInteger(64bit) + case 'Q': // 8: unsigned long long / unsigned long(64bit) / NSUInteger(64bit) + { + long long value = [obj longLongValue]; + [inv setArgument:&value atIndex:index]; + } break; + + case 'f': // 4: float / CGFloat(32bit) + { // 'float' will be promoted to 'double'. + double value = [obj doubleValue]; + float valuef = value; + [inv setArgument:&valuef atIndex:index]; + } break; + + case 'd': // 8: double / CGFloat(64bit) + { + double value = [obj doubleValue]; + [inv setArgument:&value atIndex:index]; + } break; + + case '*': // char * + case '^': // pointer + { + if ([obj isKindOfClass:UIColor.class]) obj = (id)[obj CGColor]; //CGColor转换 + if ([obj isKindOfClass:UIImage.class]) obj = (id)[obj CGImage]; //CGImage转换 + void *value = (__bridge void *)obj; + [inv setArgument:&value atIndex:index]; + } break; + + case '@': // id + { + id value = obj; + [inv setArgument:&value atIndex:index]; + } break; + + case '{': // struct + { + if (strcmp(type, @encode(CGPoint)) == 0) { + CGPoint value = [obj CGPointValue]; + [inv setArgument:&value atIndex:index]; + } else if (strcmp(type, @encode(CGSize)) == 0) { + CGSize value = [obj CGSizeValue]; + [inv setArgument:&value atIndex:index]; + } else if (strcmp(type, @encode(CGRect)) == 0) { + CGRect value = [obj CGRectValue]; + [inv setArgument:&value atIndex:index]; + } else if (strcmp(type, @encode(CGVector)) == 0) { + CGVector value = [obj CGVectorValue]; + [inv setArgument:&value atIndex:index]; + } else if (strcmp(type, @encode(CGAffineTransform)) == 0) { + CGAffineTransform value = [obj CGAffineTransformValue]; + [inv setArgument:&value atIndex:index]; + } else if (strcmp(type, @encode(CATransform3D)) == 0) { + CATransform3D value = [obj CATransform3DValue]; + [inv setArgument:&value atIndex:index]; + } else if (strcmp(type, @encode(NSRange)) == 0) { + NSRange value = [obj rangeValue]; + [inv setArgument:&value atIndex:index]; + } else if (strcmp(type, @encode(UIOffset)) == 0) { + UIOffset value = [obj UIOffsetValue]; + [inv setArgument:&value atIndex:index]; + } else if (strcmp(type, @encode(UIEdgeInsets)) == 0) { + UIEdgeInsets value = [obj UIEdgeInsetsValue]; + [inv setArgument:&value atIndex:index]; + } else { + unsupportedType = YES; + } + } break; + + case '(': // union + { + unsupportedType = YES; + } break; + + case '[': // array + { + unsupportedType = YES; + } break; + + default: // what?! + { + unsupportedType = YES; + } break; + } + + NSAssert(unsupportedType == NO, @"方法的参数类型暂不支持"); +} + +- (void)changeThemeConfig{ + + self.lee_theme.modelCurrentThemeTag = [LEETheme currentThemeTag]; + + NSString *tag = [LEETheme currentThemeTag]; + + // Block + + for (id blockKey in self.lee_theme.modelThemeBlockConfigInfo[tag]) { + + id value = self.lee_theme.modelThemeBlockConfigInfo[tag][blockKey]; + + if ([value isKindOfClass:NSNull.class]) { + + LEEThemeConfigBlock block = (LEEThemeConfigBlock)blockKey; + + if (block) block(self); + + } else { + + LEEThemeConfigBlockToValue block = (LEEThemeConfigBlockToValue)blockKey; + + if (block) block(self , value); + } + + } + + // KeyPath + + for (id keyPath in self.lee_theme.modelThemeKeyPathConfigInfo) { + + NSDictionary *info = self.lee_theme.modelThemeKeyPathConfigInfo[keyPath]; + + id value = info[tag]; + + if ([keyPath isKindOfClass:NSString.class]) { + + [self setValue:value forKeyPath:keyPath]; + } + + } + + // Selector + + for (NSString *selector in self.lee_theme.modelThemeSelectorConfigInfo) { + + NSDictionary *info = self.lee_theme.modelThemeSelectorConfigInfo[selector]; + + NSArray *valuesArray = info[tag]; + + for (NSArray *values in valuesArray) { + + SEL sel = NSSelectorFromString(selector); + + NSMethodSignature * sig = [self methodSignatureForSelector:sel]; + + if (!sig) [self doesNotRecognizeSelector:sel]; + + NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig]; + + if (!inv) [self doesNotRecognizeSelector:sel]; + + [inv setTarget:self]; + + [inv setSelector:sel]; + + if (sig.numberOfArguments == values.count + 2) { + + [values enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + + NSInteger index = idx + 2; + + [self setInv:inv Sig:sig Obj:obj Index:index]; + }]; + + [inv invoke]; + + } else { + + NSAssert(YES, @"参数个数与方法参数个数不匹配"); + } + + } + + } + +} + +- (LEEThemeConfigModel *)lee_theme{ + + LEEThemeConfigModel *model = objc_getAssociatedObject(self, _cmd); + + if (!model) { + + NSAssert(![self isKindOfClass:[LEEThemeConfigModel class]], @"是不是点多了? ( *・ω・)✄╰ひ╯ "); + + model = [LEEThemeConfigModel new]; + + objc_setAssociatedObject(self, _cmd, model , OBJC_ASSOCIATION_RETAIN_NONATOMIC); + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(leeTheme_ChangeThemeConfigNotify:) name:LEEThemeChangingNotificaiton object:nil]; + + [self setIsLeeTheme:YES]; + + __weak typeof(self) weakSelf = self; + + model.modelUpdateCurrentThemeConfig = ^{ + + if (weakSelf) [weakSelf changeThemeConfig]; + }; + + model.modelConfigThemeChangingBlock = ^{ + + if (weakSelf) weakSelf.lee_theme.modelChangingBlock([LEETheme currentThemeTag], weakSelf); + }; + + } + + return model; +} + +- (void)setLee_theme:(LEEThemeConfigModel *)lee_theme{ + + if(self) objc_setAssociatedObject(self, @selector(lee_theme), lee_theme , OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (BOOL)isLeeTheme{ + + return self ? [objc_getAssociatedObject(self, _cmd) boolValue] : NO; +} + +- (void)setIsLeeTheme:(BOOL)isLeeTheme{ + + if (self) objc_setAssociatedObject(self, @selector(isLeeTheme), @(isLeeTheme) , OBJC_ASSOCIATION_ASSIGN); +} + +@end + +#pragma mark - ----------------工具扩展---------------- + +@implementation UIColor (LEEThemeColor) + ++ (UIColor *)leeTheme_ColorWithHexString:(NSString *)hexString{ + + if (!hexString) return nil; + + NSString *colorString = [[hexString stringByReplacingOccurrencesOfString: @"#" withString: @""] uppercaseString]; + + CGFloat alpha, red, blue, green; + + switch ([colorString length]) { + case 0: + return nil; + case 3: // #RGB + alpha = 1.0f; + red = [self colorComponentFrom:colorString start: 0 length: 1]; + green = [self colorComponentFrom:colorString start: 1 length: 1]; + blue = [self colorComponentFrom:colorString start: 2 length: 1]; + break; + case 4: // #ARGB + alpha = [self colorComponentFrom:colorString start: 0 length: 1]; + red = [self colorComponentFrom:colorString start: 1 length: 1]; + green = [self colorComponentFrom:colorString start: 2 length: 1]; + blue = [self colorComponentFrom:colorString start: 3 length: 1]; + break; + case 6: // #RRGGBB + alpha = 1.0f; + red = [self colorComponentFrom:colorString start: 0 length: 2]; + green = [self colorComponentFrom:colorString start: 2 length: 2]; + blue = [self colorComponentFrom:colorString start: 4 length: 2]; + break; + case 8: // #AARRGGBB + alpha = [self colorComponentFrom:colorString start: 0 length: 2]; + red = [self colorComponentFrom:colorString start: 2 length: 2]; + green = [self colorComponentFrom:colorString start: 4 length: 2]; + blue = [self colorComponentFrom:colorString start: 6 length: 2]; + break; + default: + alpha = 0; + red = 0; + blue = 0; + green = 0; + break; + } + + return [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; +} + ++ (CGFloat)colorComponentFrom:(NSString *) string start:(NSUInteger)start length:(NSUInteger) length{ + + NSString *substring = [string substringWithRange: NSMakeRange(start, length)]; + NSString *fullHex = length == 2 ? substring : [NSString stringWithFormat: @"%@%@", substring, substring]; + unsigned hexComponent; + [[NSScanner scannerWithString: fullHex] scanHexInt: &hexComponent]; + return hexComponent / 255.0f; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/LEETheme/LEEThemeHelper.h b/SDAutoLayoutDemo/Vender/LEETheme/LEEThemeHelper.h new file mode 100644 index 0000000..c0f991a --- /dev/null +++ b/SDAutoLayoutDemo/Vender/LEETheme/LEEThemeHelper.h @@ -0,0 +1,73 @@ + +/*! + * @header LEEThemeHelper.h + * + * ┌─┐ ┌───────┐ ┌───────┐ 帅™ + * │ │ │ ┌─────┘ │ ┌─────┘ + * │ │ │ └─────┐ │ └─────┐ + * │ │ │ ┌─────┘ │ ┌─────┘ + * │ └─────┐│ └─────┐ │ └─────┐ + * └───────┘└───────┘ └───────┘ + * + * @brief LEE主题管理 + * + * @author LEE + * @copyright Copyright © 2016 - 2017年 lee. All rights reserved. + * @version V1.1.7 + */ + +FOUNDATION_EXPORT double LEEThemeVersionNumber; +FOUNDATION_EXPORT const unsigned char LEEThemeVersionString[]; + +#ifndef LEEThemeHelper_h +#define LEEThemeHelper_h + +@class LEEThemeConfigModel; + +#pragma mark - 宏 + +#define LEEColorRGBA(R , G , B , A) [UIColor colorWithRed:R/255.0f green:G/255.0f blue:B/255.0f alpha:A] + +#define LEEColorRGB(R , G , B) LEEColorRGBA(R , G , B , 1.0f) + +#define LEEColorHex(hex) [UIColor leeTheme_ColorWithHexString:hex] + +#define LEEColorFromIdentifier(tag, identifier) ({((UIColor *)([LEETheme getValueWithTag:tag Identifier:identifier]));}) + +#define LEEImageFromIdentifier(tag, identifier) ({((UIImage *)([LEETheme getValueWithTag:tag Identifier:identifier]));}) + +#define LEEValueFromIdentifier(tag, identifier) ({([LEETheme getValueWithTag:tag Identifier:identifier]);}) + +#pragma mark - typedef + +typedef void(^LEEThemeConfigBlock)(id item); +typedef void(^LEEThemeConfigBlockToValue)(id item , id value); +typedef void(^LEEThemeChangingBlock)(NSString *tag , id item); +typedef LEEThemeConfigModel *(^LEEConfigTheme)(); +typedef LEEThemeConfigModel *(^LEEConfigThemeToFloat)(CGFloat number); +typedef LEEThemeConfigModel *(^LEEConfigThemeToTag)(NSString *tag); +typedef LEEThemeConfigModel *(^LEEConfigThemeToKeyPath)(NSString *keyPath); +typedef LEEThemeConfigModel *(^LEEConfigThemeToSelector)(SEL selector); +typedef LEEThemeConfigModel *(^LEEConfigThemeToIdentifier)(NSString *identifier); +typedef LEEThemeConfigModel *(^LEEConfigThemeToChangingBlock)(LEEThemeChangingBlock); +typedef LEEThemeConfigModel *(^LEEConfigThemeToT_KeyPath)(NSString *tag , NSString *keyPath); +typedef LEEThemeConfigModel *(^LEEConfigThemeToT_Selector)(NSString *tag , SEL selector); +typedef LEEThemeConfigModel *(^LEEConfigThemeToT_Color)(NSString *tag , id color); +typedef LEEThemeConfigModel *(^LEEConfigThemeToT_Image)(NSString *tag , id image); +typedef LEEThemeConfigModel *(^LEEConfigThemeToT_Block)(NSString *tag , LEEThemeConfigBlock); +typedef LEEThemeConfigModel *(^LEEConfigThemeToTs_Block)(NSArray *tags , LEEThemeConfigBlock); +typedef LEEThemeConfigModel *(^LEEConfigThemeToKeyPathAndIdentifier)(NSString *keyPath , NSString *identifier); +typedef LEEThemeConfigModel *(^LEEConfigThemeToSelectorAndIdentifier)(SEL sel , NSString *identifier); +typedef LEEThemeConfigModel *(^LEEConfigThemeToSelectorAndIdentifierAndValueIndexAndValueArray)(SEL sel , NSString *identifier , NSInteger valueIndex , NSArray *otherValues); +typedef LEEThemeConfigModel *(^LEEConfigThemeToSelectorAndValues)(SEL sel , NSArray *values); +typedef LEEThemeConfigModel *(^LEEConfigThemeToIdentifierAndState)(NSString *identifier , UIControlState state); +typedef LEEThemeConfigModel *(^LEEConfigThemeToT_ColorAndState)(NSString *tag , UIColor *color , UIControlState state); +typedef LEEThemeConfigModel *(^LEEConfigThemeToT_ImageAndState)(NSString *tag , UIImage *image , UIControlState state); +typedef LEEThemeConfigModel *(^LEEConfigThemeToT_KeyPathAndValue)(NSString *tag , NSString *keyPath , id value); +typedef LEEThemeConfigModel *(^LEEConfigThemeToT_SelectorAndColor)(NSString *tag , SEL sel , id color); +typedef LEEThemeConfigModel *(^LEEConfigThemeToT_SelectorAndImage)(NSString *tag , SEL sel , id image); +typedef LEEThemeConfigModel *(^LEEConfigThemeToT_SelectorAndValues)(NSString *tag , SEL sel , ...); +typedef LEEThemeConfigModel *(^LEEConfigThemeToT_SelectorAndValueArray)(NSString *tag , SEL sel , NSArray *values); +typedef LEEThemeConfigModel *(^LEEConfigThemeToIdentifierAndBlock)(NSString *identifier , LEEThemeConfigBlockToValue); + +#endif /* LEEThemeHelper_h */ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmojiLabel.h b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmojiLabel.h new file mode 100755 index 0000000..40bc056 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmojiLabel.h @@ -0,0 +1,50 @@ +// +// MLEmojiLabel.h +// MLEmojiLabel +// +// Created by molon on 5/19/14. +// Copyright (c) 2014 molon. All rights reserved. +// + +#import "TTTAttributedLabel.h" + + +typedef NS_OPTIONS(NSUInteger, MLEmojiLabelLinkType) { + MLEmojiLabelLinkTypeURL = 0, + MLEmojiLabelLinkTypeEmail, + MLEmojiLabelLinkTypePhoneNumber, + MLEmojiLabelLinkTypeAt, + MLEmojiLabelLinkTypePoundSign, +}; + + +@class MLEmojiLabel; +@protocol MLEmojiLabelDelegate + +@optional +- (void)mlEmojiLabel:(MLEmojiLabel*)emojiLabel didSelectLink:(NSString*)link withType:(MLEmojiLabelLinkType)type; + + +@end + +@interface MLEmojiLabel : TTTAttributedLabel + +@property (nonatomic, assign) BOOL disableEmoji; //禁用表情 +@property (nonatomic, assign) BOOL disableThreeCommon; //禁用电话,邮箱,连接三者 + +@property (nonatomic, assign) BOOL isNeedAtAndPoundSign; //是否需要话题和@功能,默认为不需要 + +@property (nonatomic, copy) NSString *customEmojiRegex; //自定义表情正则 +@property (nonatomic, copy) NSString *customEmojiPlistName; //xxxxx.plist 格式 +@property (nonatomic, copy) NSString *customEmojiBundleName; //自定义表情图片所存储的bundleName xxxx.bundle格式 + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-property-synthesis" +@property (nonatomic, weak) id delegate; //点击连接的代理方法 +#pragma clang diagnostic pop + +@property (nonatomic, copy, readonly) id emojiText; //外部能获取text的原始副本 + +- (CGSize)preferredSizeWithMaxWidth:(CGFloat)maxWidth; + +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmojiLabel.m b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmojiLabel.m new file mode 100755 index 0000000..bf03865 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmojiLabel.m @@ -0,0 +1,716 @@ +// +// MLEmojiLabel.m +// MLEmojiLabel +// +// Created by molon on 5/19/14. +// Copyright (c) 2014 molon. All rights reserved. +// + +#import "MLEmojiLabel.h" + +#pragma mark - 正则列表 + +#define REGULAREXPRESSION_OPTION(regularExpression,regex,option) \ +\ +static inline NSRegularExpression * k##regularExpression() { \ +static NSRegularExpression *_##regularExpression = nil; \ +static dispatch_once_t onceToken; \ +dispatch_once(&onceToken, ^{ \ +_##regularExpression = [[NSRegularExpression alloc] initWithPattern:(regex) options:(option) error:nil];\ +});\ +\ +return _##regularExpression;\ +}\ + + +#define REGULAREXPRESSION(regularExpression,regex) REGULAREXPRESSION_OPTION(regularExpression,regex,NSRegularExpressionCaseInsensitive) + + +REGULAREXPRESSION(URLRegularExpression,@"((http[s]{0,1}|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)") + +REGULAREXPRESSION(PhoneNumerRegularExpression, @"\\d{3}-\\d{8}|\\d{3}-\\d{7}|\\d{4}-\\d{8}|\\d{4}-\\d{7}|1+[358]+\\d{9}|\\d{8}|\\d{7}") + +REGULAREXPRESSION(EmailRegularExpression, @"[A-Z0-9a-z\\._%+-]+@([A-Za-z0-9-]+\\.)+[A-Za-z]{2,4}") + +REGULAREXPRESSION(AtRegularExpression, @"@[\\u4e00-\\u9fa5\\w\\-]+") + + +//@"#([^\\#|.]+)#" +REGULAREXPRESSION_OPTION(PoundSignRegularExpression, @"#([\\u4e00-\\u9fa5\\w\\-]+)#", NSRegularExpressionCaseInsensitive) + +//微信的表情符其实不是这种格式,这个格式的只是让人看起来更友好。。 +//REGULAREXPRESSION(EmojiRegularExpression, @"\\[[a-zA-Z0-9\\u4e00-\\u9fa5]+\\]") + +//@"/:[\\w:~!@$&*()|+<>',?-]{1,8}" , // @"/:[\\x21-\\x2E\\x30-\\x7E]{1,8}" ,经过检测发现\w会匹配中文,好奇葩。 +REGULAREXPRESSION(SlashEmojiRegularExpression, @"/:[\\x21-\\x2E\\x30-\\x7E]{1,8}") + +const CGFloat kLineSpacing = 4.0; +const CGFloat kAscentDescentScale = 0.25; //在这里的话无意义,高度的结局都是和宽度一样 + +const CGFloat kEmojiWidthRatioWithLineHeight = 1.15;//和字体高度的比例 + +const CGFloat kEmojiOriginYOffsetRatioWithLineHeight = 0.10; //表情绘制的y坐标矫正值,和字体高度的比例,越大越往下 +NSString *const kCustomGlyphAttributeImageName = @"CustomGlyphAttributeImageName"; + +#define kEmojiReplaceCharacter @"\uFFFC" + +#define kURLActionCount 5 +NSString * const kURLActions[] = {@"url->",@"email->",@"phoneNumber->",@"at->",@"poundSign->"}; + +/** + * 搞个管理器,否则自定义plist的话,每个label都会有个副本很操蛋 + */ +@interface MLEmojiLabelRegexPlistManager : NSObject + +- (NSDictionary*)emojiDictForKey:(NSString*)key; + +@property (nonatomic, strong) NSMutableDictionary *emojiDictRecords; +@property (nonatomic, strong) NSMutableDictionary *emojiRegularExpressions; + +@end + +@implementation MLEmojiLabelRegexPlistManager + ++ (instancetype)sharedInstance { + static MLEmojiLabelRegexPlistManager *_sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _sharedInstance = [[[self class] alloc]init]; + }); + return _sharedInstance; +} + +#pragma mark - getter +- (NSMutableDictionary *)emojiDictRecords +{ + if (!_emojiDictRecords) { + _emojiDictRecords = [NSMutableDictionary new]; + } + return _emojiDictRecords; +} + +- (NSMutableDictionary *)emojiRegularExpressions +{ + if (!_emojiRegularExpressions) { + _emojiRegularExpressions = [NSMutableDictionary new]; + } + return _emojiRegularExpressions; +} + +#pragma mark - common +- (NSDictionary*)emojiDictForKey:(NSString*)key +{ + NSAssert(key&&key.length>0, @"emojiDictForKey:参数不得为空"); + + if (self.emojiDictRecords[key]) { + return self.emojiDictRecords[key]; + } + + + NSString *emojiFilePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:key]; + NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:emojiFilePath]; + NSAssert(dict,@"表情字典%@找不到",key); + self.emojiDictRecords[key] = dict; + + return self.emojiDictRecords[key]; +} + +- (NSRegularExpression *)regularExpressionForRegex:(NSString*)regex +{ + NSAssert(regex&®ex.length>0, @"regularExpressionForKey:参数不得为空"); + + if (self.emojiRegularExpressions[regex]) { + return self.emojiRegularExpressions[regex]; + } + + NSRegularExpression *re = [[NSRegularExpression alloc] initWithPattern:regex options:NSRegularExpressionCaseInsensitive error:nil]; + + NSAssert(re,@"正则%@有误",regex); + self.emojiRegularExpressions[regex] = re; + + return self.emojiRegularExpressions[regex]; +} + +@end + +@interface TTTAttributedLabel(MLEmojiLabel) + +@property (readwrite, nonatomic, strong) TTTAttributedLabelLink *activeLink; + +- (void)commonInit; +- (NSArray *)addLinksWithTextCheckingResults:(NSArray *)results + attributes:(NSDictionary *)attributes; +- (void)drawStrike:(CTFrameRef)frame + inRect:(CGRect)rect + context:(CGContextRef)c; + +@end + +@interface MLEmojiLabel() + +@property (nonatomic, weak) NSRegularExpression *customEmojiRegularExpression; +@property (nonatomic, weak) NSDictionary *customEmojiDictionary; //这玩意如果有也是在MLEmojiLabelPlistManager单例里面存着 + +@property (nonatomic, assign) BOOL ignoreSetText; + +//留个初始副本 +@property (nonatomic, copy) id emojiText; + +@end + +@implementation MLEmojiLabel + +#pragma mark - 表情包字典 ++ (NSDictionary *)emojiDictionary { + static NSDictionary *emojiDictionary = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSString *emojiFilePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"MLEmoji_ExpressionImage.plist"]; + emojiDictionary = [[NSDictionary alloc] initWithContentsOfFile:emojiFilePath]; + }); + return emojiDictionary; +} + +#pragma mark - 表情 callback +typedef struct CustomGlyphMetrics { + CGFloat ascent; + CGFloat descent; + CGFloat width; +} CustomGlyphMetrics, *CustomGlyphMetricsRef; + +static void deallocCallback(void *refCon) { + free(refCon), refCon = NULL; +} + +static CGFloat ascentCallback(void *refCon) { + CustomGlyphMetricsRef metrics = (CustomGlyphMetricsRef)refCon; + return metrics->ascent; +} + +static CGFloat descentCallback(void *refCon) { + CustomGlyphMetricsRef metrics = (CustomGlyphMetricsRef)refCon; + return metrics->descent; +} + +static CGFloat widthCallback(void *refCon) { + CustomGlyphMetricsRef metrics = (CustomGlyphMetricsRef)refCon; + return metrics->width; +} + +#pragma mark - 初始化和TTT的一些修正 +- (void)commonInit { + [super commonInit]; + + self.numberOfLines = 0; + self.font = [UIFont systemFontOfSize:14.0]; + self.textColor = [UIColor blackColor]; + self.backgroundColor = [UIColor clearColor]; + + self.lineBreakMode = NSLineBreakByCharWrapping; + + self.lineSpacing = kLineSpacing; //默认行间距 + + //链接默认样式重新设置 + NSMutableDictionary *mutableLinkAttributes = [@{(NSString *)kCTUnderlineStyleAttributeName:@(NO)}mutableCopy]; + + NSMutableDictionary *mutableActiveLinkAttributes = [@{(NSString *)kCTUnderlineStyleAttributeName:@(NO)}mutableCopy]; + + UIColor *commonLinkColor = [UIColor colorWithRed:0.112 green:0.000 blue:0.791 alpha:1.000]; + + //点击时候的背景色 + [mutableActiveLinkAttributes setValue:(__bridge id)[[UIColor colorWithWhite:0.631 alpha:1.000] CGColor] forKey:(NSString *)kTTTBackgroundFillColorAttributeName]; + + if ([NSMutableParagraphStyle class]) { + [mutableLinkAttributes setObject:commonLinkColor forKey:(NSString *)kCTForegroundColorAttributeName]; + [mutableActiveLinkAttributes setObject:commonLinkColor forKey:(NSString *)kCTForegroundColorAttributeName]; + } else { + [mutableLinkAttributes setObject:(__bridge id)[commonLinkColor CGColor] forKey:(NSString *)kCTForegroundColorAttributeName]; + [mutableActiveLinkAttributes setObject:(__bridge id)[commonLinkColor CGColor] forKey:(NSString *)kCTForegroundColorAttributeName]; + } + + self.linkAttributes = [NSDictionary dictionaryWithDictionary:mutableLinkAttributes]; + self.activeLinkAttributes = [NSDictionary dictionaryWithDictionary:mutableActiveLinkAttributes]; +} + +/** + * 如果是有attributedText的情况下,有可能会返回少那么点的,这里矫正下 + * + */ +- (CGSize)sizeThatFits:(CGSize)size { + if (!self.attributedText) { + return [super sizeThatFits:size]; + } + + CGSize rSize = [super sizeThatFits:size]; + rSize.height +=1; + return rSize; +} + + +//这里是抄TTT里的,因为他不是放在外面的 +static inline CGFloat TTTFlushFactorForTextAlignment(NSTextAlignment textAlignment) { + switch (textAlignment) { + case NSTextAlignmentCenter: + return 0.5f; + case NSTextAlignmentRight: + return 1.0f; + case NSTextAlignmentLeft: + default: + return 0.0f; + } +} + +#pragma mark - 绘制表情 +- (void)drawStrike:(CTFrameRef)frame + inRect:(CGRect)rect + context:(CGContextRef)c +{ + [super drawStrike:frame inRect:rect context:c]; + + //PS:这个是在TTT里drawFramesetter....方法最后做了修改的基础上。 + CGFloat emojiWith = self.font.lineHeight*kEmojiWidthRatioWithLineHeight; + CGFloat emojiOriginYOffset = self.font.lineHeight*kEmojiOriginYOffsetRatioWithLineHeight; + + //修正绘制offset,根据当前设置的textAlignment + CGFloat flushFactor = TTTFlushFactorForTextAlignment(self.textAlignment); + + CFArrayRef lines = CTFrameGetLines(frame); + NSInteger numberOfLines = self.numberOfLines > 0 ? MIN(self.numberOfLines, CFArrayGetCount(lines)) : CFArrayGetCount(lines); + CGPoint lineOrigins[numberOfLines]; + CTFrameGetLineOrigins(frame, CFRangeMake(0, numberOfLines), lineOrigins); + + BOOL truncateLastLine = (self.lineBreakMode == NSLineBreakByTruncatingHead || self.lineBreakMode == NSLineBreakByTruncatingMiddle || self.lineBreakMode == NSLineBreakByTruncatingTail); + CFRange textRange = CFRangeMake(0, (CFIndex)[self.attributedText length]); + + for (CFIndex lineIndex = 0; lineIndex < numberOfLines; lineIndex++) { + CTLineRef line = CFArrayGetValueAtIndex(lines, lineIndex); + + //这里其实是能获取到当前行的真实origin.x,根据textAlignment,而lineBounds.origin.x其实是默认一直为0的(不会受textAlignment影响) + CGFloat penOffset = (CGFloat)CTLineGetPenOffsetForFlush(line, flushFactor, rect.size.width); + + CFIndex truncationAttributePosition = -1; + //检测如果是最后一行,是否有替换... + if (lineIndex == numberOfLines - 1 && truncateLastLine) { + // Check if the range of text in the last line reaches the end of the full attributed string + CFRange lastLineRange = CTLineGetStringRange(line); + + if (!(lastLineRange.length == 0 && lastLineRange.location == 0) && lastLineRange.location + lastLineRange.length < textRange.location + textRange.length) { + // Get correct truncationType and attribute position + truncationAttributePosition = lastLineRange.location; + NSLineBreakMode lineBreakMode = self.lineBreakMode; + + // Multiple lines, only use UILineBreakModeTailTruncation + if (numberOfLines != 1) { + lineBreakMode = NSLineBreakByTruncatingTail; + } + + switch (lineBreakMode) { + case NSLineBreakByTruncatingHead: + break; + case NSLineBreakByTruncatingMiddle: + truncationAttributePosition += (lastLineRange.length / 2); + break; + case NSLineBreakByTruncatingTail: + default: + truncationAttributePosition += (lastLineRange.length - 1); + break; + } + + //如果要在truncationAttributePosition这个位置画表情需要忽略 + } + } + + //找到当前行的每一个要素,姑且这么叫吧。可以理解为有单独的attr属性的各个range。 + for (id glyphRun in (__bridge NSArray *)CTLineGetGlyphRuns(line)) { + //找到此要素所对应的属性 + NSDictionary *attributes = (__bridge NSDictionary *)CTRunGetAttributes((__bridge CTRunRef) glyphRun); + //判断是否有图像,如果有就绘制上去 + NSString *imageName = attributes[kCustomGlyphAttributeImageName]; + if (imageName) { + CFRange glyphRange = CTRunGetStringRange((__bridge CTRunRef)glyphRun); + if (glyphRange.location == truncationAttributePosition) { + //这里因为glyphRange的length肯定为1,所以只做这一个判断足够 + continue; + } + + CGRect runBounds = CGRectZero; + CGFloat runAscent = 0.0f; + CGFloat runDescent = 0.0f; + + runBounds.size.width = (CGFloat)CTRunGetTypographicBounds((__bridge CTRunRef)glyphRun, CFRangeMake(0, 0), &runAscent, &runDescent, NULL); + + if (runBounds.size.width!=emojiWith) { + //这一句是为了在某些情况下,例如单行省略号模式下,默认行为会将个别表情的runDelegate改变,也就改变了其大小。这时候会引起界面上错乱,这里做下检测(浮点数做等于判断似乎有点操蛋啊。。) + continue; + } + + runBounds.size.height = runAscent + runDescent; + + CGFloat xOffset = 0.0f; + switch (CTRunGetStatus((__bridge CTRunRef)glyphRun)) { + case kCTRunStatusRightToLeft: + xOffset = CTLineGetOffsetForStringIndex(line, glyphRange.location + glyphRange.length, NULL); + break; + default: + xOffset = CTLineGetOffsetForStringIndex(line, glyphRange.location, NULL); + break; + } + runBounds.origin.x = penOffset + xOffset; + runBounds.origin.y = lineOrigins[lineIndex].y; + runBounds.origin.y -= runDescent; + + NSString *imagePath = [self.customEmojiBundleName?:@"MLEmoji_Expression.bundle" stringByAppendingPathComponent:imageName]; + UIImage *image = [UIImage imageNamed:imagePath]; + runBounds.origin.y -= emojiOriginYOffset; //稍微矫正下。 + CGContextDrawImage(c, runBounds, image.CGImage); + } + } + } + +} + + +#pragma mark - main +/** + * 返回经过表情识别处理的Attributed字符串 + */ +- (NSMutableAttributedString*)mutableAttributeStringWithEmojiText:(NSAttributedString *)emojiText +{ + //获取所有表情的位置 + // NSArray *emojis = [kEmojiRegularExpression() matchesInString:emojiText + // options:NSMatchingWithTransparentBounds + // range:NSMakeRange(0, [emojiText length])]; + + NSArray *emojis = nil; + + if (self.customEmojiRegularExpression) { + //自定义表情正则 + emojis = [self.customEmojiRegularExpression matchesInString:emojiText.string + options:NSMatchingWithTransparentBounds + range:NSMakeRange(0, [emojiText length])]; + }else{ + emojis = [kSlashEmojiRegularExpression() matchesInString:emojiText.string + options:NSMatchingWithTransparentBounds + range:NSMakeRange(0, [emojiText length])]; + } + + NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] init]; + NSUInteger location = 0; + + + CGFloat emojiWith = self.font.lineHeight*kEmojiWidthRatioWithLineHeight; + for (NSTextCheckingResult *result in emojis) { + NSRange range = result.range; + NSAttributedString *attSubStr = [emojiText attributedSubstringFromRange:NSMakeRange(location, range.location - location)]; + [attrStr appendAttributedString:attSubStr]; + + location = range.location + range.length; + + NSAttributedString *emojiKey = [emojiText attributedSubstringFromRange:range]; + + NSDictionary *emojiDict = self.customEmojiRegularExpression?self.customEmojiDictionary:[MLEmojiLabel emojiDictionary]; + + //如果当前获得key后面有多余的,这个需要记录下 + NSAttributedString *otherAppendStr = nil; + + NSString *imageName = emojiDict[emojiKey.string]; + if (!self.customEmojiRegularExpression) { + //微信的表情没有结束符号,所以有可能会发现过长的只有头部才是表情的段,需要循环检测一次。微信最大表情特殊字符是8个长度,检测8次即可 + if (!imageName&&emojiKey.length>2) { + NSUInteger maxDetctIndex = emojiKey.length>8+2?8:emojiKey.length-2; + //从头开始检测是否有对应的 + for (NSUInteger i=0; iwidth = emojiWith; + metrics->ascent = 1/(1+kAscentDescentScale)*metrics->width; + metrics->descent = metrics->ascent*kAscentDescentScale; + CTRunDelegateRef delegate = CTRunDelegateCreate(&callbacks, metrics); + [attrStr addAttribute:(NSString *)kCTRunDelegateAttributeName + value:(__bridge id)delegate + range:__range]; + CFRelease(delegate); + + // 设置自定义属性,绘制的时候需要用到 + [attrStr addAttribute:kCustomGlyphAttributeImageName + value:imageName + range:__range]; + } else { + [attrStr appendAttributedString:emojiKey]; + } + } + if (location < [emojiText length]) { + NSRange range = NSMakeRange(location, [emojiText length] - location); + NSAttributedString *attrSubStr = [emojiText attributedSubstringFromRange:range]; + [attrStr appendAttributedString:attrSubStr]; + } + return attrStr; +} + + +- (void)setText:(id)text +{ + NSParameterAssert(!text || [text isKindOfClass:[NSAttributedString class]] || [text isKindOfClass:[NSString class]]); + + if (self.ignoreSetText) { + [super setText:text]; + return; + } + + if (!text) { + self.emojiText = nil; + [super setText:nil]; + return; + } + + //记录下原始的留作备份使用 + self.emojiText = text; + + NSMutableAttributedString *mutableAttributedString = nil; + + if (self.disableEmoji) { + mutableAttributedString = [text isKindOfClass:[NSAttributedString class]]?[text mutableCopy]:[[NSMutableAttributedString alloc]initWithString:text]; + //直接设置text即可,这里text可能为attrString,也可能为String,使用TTT的默认行为 + [super setText:text]; + }else{ + //如果是String,必须通过setText:afterInheritingLabelAttributesAndConfiguringWithBlock:来添加一些默认属性,例如字体颜色。这是TTT的做法,不可避免 + if([text isKindOfClass:[NSString class]]){ + mutableAttributedString = [self mutableAttributeStringWithEmojiText:[[NSAttributedString alloc] initWithString:text]]; + //这里面会调用 self setText:,所以需要做个标记避免下无限循环 + self.ignoreSetText = YES; + [super setText:mutableAttributedString afterInheritingLabelAttributesAndConfiguringWithBlock:nil]; + self.ignoreSetText = NO; + }else{ + mutableAttributedString = [self mutableAttributeStringWithEmojiText:text]; + //这里虽然会调用 + [super setText:mutableAttributedString]; + } + } + + NSRange stringRange = NSMakeRange(0, mutableAttributedString.length); + + NSRegularExpression * const regexps[] = {kURLRegularExpression(),kEmailRegularExpression(),kPhoneNumerRegularExpression(),kAtRegularExpression(),kPoundSignRegularExpression()}; + + NSMutableArray *results = [NSMutableArray array]; + + NSUInteger maxIndex = self.isNeedAtAndPoundSign?kURLActionCount:kURLActionCount-2; + for (NSUInteger i=0; i0){ + return; + } + } + + //添加链接 + NSString *actionString = [NSString stringWithFormat:@"%@%@",urlAction,[self.text substringWithRange:result.range]]; + + //这里暂时用NSTextCheckingTypeCorrection类型的传递消息吧 + //因为有自定义的类型出现,所以这样方便点。 + NSTextCheckingResult *aResult = [NSTextCheckingResult correctionCheckingResultWithRange:result.range replacementString:actionString]; + + [results addObject:aResult]; + }]; + } + + //这里直接调用父类私有方法,好处能内部只会setNeedDisplay一次。一次更新所有添加的链接 + [super addLinksWithTextCheckingResults:results attributes:self.linkAttributes]; +} + +#pragma mark - size fit result +- (CGSize)preferredSizeWithMaxWidth:(CGFloat)maxWidth +{ + maxWidth = maxWidth - self.textInsets.left - self.textInsets.right; + return [self sizeThatFits:CGSizeMake(maxWidth, CGFLOAT_MAX)]; +} + +#pragma mark - setter +- (void)setIsNeedAtAndPoundSign:(BOOL)isNeedAtAndPoundSign +{ + _isNeedAtAndPoundSign = isNeedAtAndPoundSign; + self.text = self.emojiText; //简单重新绘制处理下 +} + +- (void)setLineBreakMode:(NSLineBreakMode)lineBreakMode +{ + [super setLineBreakMode:lineBreakMode]; + self.text = self.emojiText; //简单重新绘制处理下 +} + +- (void)setDisableEmoji:(BOOL)disableEmoji +{ + _disableEmoji = disableEmoji; + self.text = self.emojiText; //简单重新绘制处理下 +} + +- (void)setDisableThreeCommon:(BOOL)disableThreeCommon +{ + _disableThreeCommon = disableThreeCommon; + self.text = self.emojiText; //简单重新绘制处理下 +} + +- (void)setCustomEmojiRegex:(NSString *)customEmojiRegex +{ + _customEmojiRegex = customEmojiRegex; + + if (customEmojiRegex&&customEmojiRegex.length>0) { + self.customEmojiRegularExpression = [[MLEmojiLabelRegexPlistManager sharedInstance]regularExpressionForRegex:customEmojiRegex]; + }else{ + self.customEmojiRegularExpression = nil; + } + + self.text = self.emojiText; //简单重新绘制处理下 +} + +- (void)setCustomEmojiPlistName:(NSString *)customEmojiPlistName +{ + if (customEmojiPlistName&&customEmojiPlistName.length>0&&![[customEmojiPlistName lowercaseString] hasSuffix:@".plist"]) { + customEmojiPlistName = [customEmojiPlistName stringByAppendingString:@".plist"]; + } + + _customEmojiPlistName = customEmojiPlistName; + + if (customEmojiPlistName&&customEmojiPlistName.length>0) { + self.customEmojiDictionary = [[MLEmojiLabelRegexPlistManager sharedInstance]emojiDictForKey:customEmojiPlistName]; + }else{ + self.customEmojiDictionary = nil; + } + + self.text = self.emojiText; //简单重新绘制处理下 +} + +- (void)setCustomEmojiBundleName:(NSString *)customEmojiBundleName +{ + if (customEmojiBundleName&&customEmojiBundleName.length>0&&![[customEmojiBundleName lowercaseString] hasSuffix:@".bundle"]) { + customEmojiBundleName = [customEmojiBundleName stringByAppendingString:@".bundle"]; + } + + _customEmojiBundleName = customEmojiBundleName; + + self.text = self.emojiText; //简单重新绘制处理下 +} + +- (void)setFont:(UIFont *)font +{ + [super setFont:font]; + self.text = self.emojiText; //简单重新绘制处理下 +} + +#pragma mark - select link override + +- (void)touchesEnded:(NSSet *)touches + withEvent:(UIEvent *)event +{ + //如果delegate实现了mlEmojiLabel自身的选择link方法 + if(self.delegate&&[self.delegate respondsToSelector:@selector(mlEmojiLabel:didSelectLink:withType:)]){ + if (self.activeLink&&self.activeLink.result.resultType==NSTextCheckingTypeCorrection) { + NSTextCheckingResult *result = self.activeLink.result; + + //判断消息类型 + for (NSUInteger i=0; i0) { + [[UIPasteboard generalPasteboard] setString:text]; + } +} + +//#pragma mark - other +//为了生成plist方便的一个方法罢了 +//- (void)initPlist +//{ +// NSString *testString = @"/::)/::~/::B/::|/:8-)/::/::,@/:,@f/::-S/:?/:,@x/:,@@/::8/:,@!/:!!!/:xx/:bye/:wipe/:dig/:handclap/:&-(/:B-)/:<@/:@>/::-O/:>-|/:P-(/::'|/:X-)/::*/:@x/:8*/:pd/:/:beer/:basketb/:oo/:coffee/:eat/:pig/:rose/:fade/:showlove/:heart/:break/:cake/:li/:bome/:kn/:footb/:ladybug/:shit/:moon/:sun/:gift/:hug/:strong/:weak/:share/:v/:@)/:jj/:@@/:bad/:lvu/:no/:ok/:love/:/:jump/:shake/:/:circle/:kotow/:turn/:skip/:oY"; +// NSMutableArray *testArray = [NSMutableArray array]; +// NSMutableDictionary *testDict = [NSMutableDictionary dictionary]; +// [kSlashEmojiRegularExpression() enumerateMatchesInString:testString options:0 range:NSMakeRange(0, testString.length) usingBlock:^(NSTextCheckingResult *result, __unused NSMatchingFlags flags, __unused BOOL *stop) { +// [testArray addObject:[testString substringWithRange:result.range]]; +// [testDict setObject:[NSString stringWithFormat:@"Expression_%u",testArray.count] forKey:[testString substringWithRange:result.range]]; +// }]; +// +// NSString *documentDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; +// NSString *doc = [NSString stringWithFormat:@"%@/expression.plist",documentDir]; +// NSLog(@"%@,length:%u",doc,testArray.count); +// if ([testArray writeToFile:doc atomically:YES]) { +// NSLog(@"归档expression.plist成功"); +// } +// doc = [NSString stringWithFormat:@"%@/expressionImage.plist",documentDir]; +// if ([testDict writeToFile:doc atomically:YES]) { +// NSLog(@"归档到expressionImage.plist成功"); +// } +// +// // NSString *testString = @"[微笑][撇嘴][色][发呆][得意][流泪][害羞][闭嘴][睡][大哭][尴尬][发怒][调皮][呲牙][惊讶][难过][酷][冷汗][抓狂][吐][偷笑][愉快][白眼][傲慢][饥饿][困][惊恐][流汗][憨笑][悠闲][奋斗][咒骂][疑问][嘘][晕][疯了][衰][骷髅][敲打][再见][擦汗][抠鼻][鼓掌][糗大了][坏笑][左哼哼][右哼哼][哈欠][鄙视][委屈][快哭了][阴险][亲亲][吓][可怜][菜刀][西瓜][啤酒][篮球][乒乓][咖啡][饭][猪头][玫瑰][凋谢][嘴唇][爱心][心碎][蛋糕][闪电][炸弹][刀][足球][瓢虫][便便][月亮][太阳][礼物][拥抱][强][弱][握手][胜利][抱拳][勾引][拳头][差劲][爱你][NO][OK][爱情][飞吻][跳跳][发抖][怄火][转圈][磕头][回头][跳绳][投降]"; +// // NSMutableArray *testArray = [NSMutableArray array]; +// // NSMutableDictionary *testDict = [NSMutableDictionary dictionary]; +// // [kEmojiRegularExpression() enumerateMatchesInString:testString options:0 range:NSMakeRange(0, testString.length) usingBlock:^(NSTextCheckingResult *result, __unused NSMatchingFlags flags, __unused BOOL *stop) { +// // [testArray addObject:[testString substringWithRange:result.range]]; +// // [testDict setObject:[NSString stringWithFormat:@"Expression_%ld",testArray.count] forKey:[testString substringWithRange:result.range]]; +// // }]; +// // NSString *documentDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; +// // NSString *doc = [NSString stringWithFormat:@"%@/expression.plist",documentDir]; +// // NSLog(@"%@,length:%ld",doc,testArray.count); +// // if ([testArray writeToFile:doc atomically:YES]) { +// // NSLog(@"归档expression.plist成功"); +// // } +// // doc = [NSString stringWithFormat:@"%@/expressionImage.plist",documentDir]; +// // if ([testDict writeToFile:doc atomically:YES]) { +// // NSLog(@"归档到expressionImage.plist成功"); +// // } +// +// +//} + +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_100@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_100@2x.png new file mode 100755 index 0000000..d111fd7 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_100@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_101@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_101@2x.png new file mode 100755 index 0000000..52cd1ea Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_101@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_102@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_102@2x.png new file mode 100755 index 0000000..f6fb7a5 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_102@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_103@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_103@2x.png new file mode 100755 index 0000000..233330b Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_103@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_104@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_104@2x.png new file mode 100755 index 0000000..9ac2644 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_104@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_105@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_105@2x.png new file mode 100755 index 0000000..d55bdba Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_105@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_10@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_10@2x.png new file mode 100755 index 0000000..617be26 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_10@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_11@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_11@2x.png new file mode 100755 index 0000000..c8e0b61 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_11@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_12@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_12@2x.png new file mode 100755 index 0000000..6aea85d Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_12@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_13@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_13@2x.png new file mode 100755 index 0000000..1b83697 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_13@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_14@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_14@2x.png new file mode 100755 index 0000000..0fd9ec7 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_14@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_15@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_15@2x.png new file mode 100755 index 0000000..7fc3891 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_15@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_16@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_16@2x.png new file mode 100755 index 0000000..ceeaf9f Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_16@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_17@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_17@2x.png new file mode 100755 index 0000000..d5d3037 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_17@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_18@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_18@2x.png new file mode 100755 index 0000000..05e5065 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_18@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_19@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_19@2x.png new file mode 100755 index 0000000..59a85e1 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_19@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_1@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_1@2x.png new file mode 100755 index 0000000..bd11416 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_1@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_20@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_20@2x.png new file mode 100755 index 0000000..d07592e Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_20@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_21@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_21@2x.png new file mode 100755 index 0000000..cfacf25 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_21@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_22@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_22@2x.png new file mode 100755 index 0000000..3d49ade Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_22@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_23@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_23@2x.png new file mode 100755 index 0000000..9789e19 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_23@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_24@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_24@2x.png new file mode 100755 index 0000000..7faad36 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_24@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_25@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_25@2x.png new file mode 100755 index 0000000..7043c77 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_25@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_26@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_26@2x.png new file mode 100755 index 0000000..62b5b8c Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_26@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_27@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_27@2x.png new file mode 100755 index 0000000..da90ab5 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_27@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_28@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_28@2x.png new file mode 100755 index 0000000..74223da Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_28@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_29@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_29@2x.png new file mode 100755 index 0000000..6565ff9 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_29@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_2@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_2@2x.png new file mode 100755 index 0000000..d3f4420 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_2@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_30@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_30@2x.png new file mode 100755 index 0000000..31edb71 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_30@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_31@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_31@2x.png new file mode 100755 index 0000000..c629263 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_31@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_32@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_32@2x.png new file mode 100755 index 0000000..7efce19 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_32@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_33@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_33@2x.png new file mode 100755 index 0000000..028dc1c Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_33@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_34@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_34@2x.png new file mode 100755 index 0000000..8d94d12 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_34@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_35@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_35@2x.png new file mode 100755 index 0000000..41763d9 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_35@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_36@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_36@2x.png new file mode 100755 index 0000000..414f660 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_36@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_37@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_37@2x.png new file mode 100755 index 0000000..425c0ee Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_37@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_38@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_38@2x.png new file mode 100755 index 0000000..4decac7 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_38@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_39@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_39@2x.png new file mode 100755 index 0000000..6b758e9 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_39@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_3@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_3@2x.png new file mode 100755 index 0000000..b040da2 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_3@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_40@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_40@2x.png new file mode 100755 index 0000000..0e62551 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_40@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_41@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_41@2x.png new file mode 100755 index 0000000..26df484 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_41@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_42@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_42@2x.png new file mode 100755 index 0000000..6f7e404 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_42@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_43@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_43@2x.png new file mode 100755 index 0000000..bd15579 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_43@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_44@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_44@2x.png new file mode 100755 index 0000000..bcf02be Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_44@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_45@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_45@2x.png new file mode 100755 index 0000000..17cc68f Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_45@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_46@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_46@2x.png new file mode 100755 index 0000000..503c4a2 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_46@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_47@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_47@2x.png new file mode 100755 index 0000000..c1db8e1 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_47@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_48@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_48@2x.png new file mode 100755 index 0000000..e9a869c Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_48@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_49@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_49@2x.png new file mode 100755 index 0000000..acf49ea Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_49@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_4@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_4@2x.png new file mode 100755 index 0000000..454a424 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_4@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_50@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_50@2x.png new file mode 100755 index 0000000..18dafe4 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_50@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_51@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_51@2x.png new file mode 100755 index 0000000..9674664 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_51@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_52@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_52@2x.png new file mode 100755 index 0000000..773e81d Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_52@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_53@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_53@2x.png new file mode 100755 index 0000000..aaeba7c Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_53@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_54@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_54@2x.png new file mode 100755 index 0000000..164392f Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_54@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_55@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_55@2x.png new file mode 100755 index 0000000..504b741 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_55@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_56@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_56@2x.png new file mode 100755 index 0000000..90eab33 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_56@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_57@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_57@2x.png new file mode 100755 index 0000000..be3715a Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_57@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_58@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_58@2x.png new file mode 100755 index 0000000..a5272a1 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_58@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_59@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_59@2x.png new file mode 100755 index 0000000..1a6b7bd Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_59@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_5@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_5@2x.png new file mode 100755 index 0000000..34546b2 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_5@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_60@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_60@2x.png new file mode 100755 index 0000000..00701bb Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_60@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_61@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_61@2x.png new file mode 100755 index 0000000..9d5ccbe Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_61@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_62@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_62@2x.png new file mode 100755 index 0000000..b018b46 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_62@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_63@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_63@2x.png new file mode 100755 index 0000000..6070599 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_63@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_64@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_64@2x.png new file mode 100755 index 0000000..7dd5dc1 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_64@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_65@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_65@2x.png new file mode 100755 index 0000000..9edeb47 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_65@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_66@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_66@2x.png new file mode 100755 index 0000000..5096cef Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_66@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_67@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_67@2x.png new file mode 100755 index 0000000..a0c07a6 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_67@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_68@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_68@2x.png new file mode 100755 index 0000000..b279000 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_68@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_69@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_69@2x.png new file mode 100755 index 0000000..ef51255 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_69@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_6@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_6@2x.png new file mode 100755 index 0000000..215159d Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_6@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_70@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_70@2x.png new file mode 100755 index 0000000..2e64191 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_70@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_71@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_71@2x.png new file mode 100755 index 0000000..65e1061 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_71@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_72@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_72@2x.png new file mode 100755 index 0000000..d583d67 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_72@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_73@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_73@2x.png new file mode 100755 index 0000000..161197a Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_73@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_74@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_74@2x.png new file mode 100755 index 0000000..259e7f8 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_74@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_75@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_75@2x.png new file mode 100755 index 0000000..a31d5f7 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_75@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_76@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_76@2x.png new file mode 100755 index 0000000..12803ea Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_76@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_77@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_77@2x.png new file mode 100755 index 0000000..27cc88a Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_77@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_78@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_78@2x.png new file mode 100755 index 0000000..2d9634f Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_78@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_79@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_79@2x.png new file mode 100755 index 0000000..e085474 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_79@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_7@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_7@2x.png new file mode 100755 index 0000000..f464598 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_7@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_80@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_80@2x.png new file mode 100755 index 0000000..8c9e2fb Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_80@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_81@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_81@2x.png new file mode 100755 index 0000000..717927a Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_81@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_82@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_82@2x.png new file mode 100755 index 0000000..38a36e6 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_82@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_83@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_83@2x.png new file mode 100755 index 0000000..9c20013 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_83@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_84@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_84@2x.png new file mode 100755 index 0000000..c0d9f8b Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_84@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_85@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_85@2x.png new file mode 100755 index 0000000..d622adf Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_85@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_86@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_86@2x.png new file mode 100755 index 0000000..e642037 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_86@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_87@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_87@2x.png new file mode 100755 index 0000000..4942d80 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_87@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_88@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_88@2x.png new file mode 100755 index 0000000..a81b326 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_88@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_89@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_89@2x.png new file mode 100755 index 0000000..1c712f0 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_89@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_8@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_8@2x.png new file mode 100755 index 0000000..1a9e035 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_8@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_90@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_90@2x.png new file mode 100755 index 0000000..35a177d Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_90@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_91@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_91@2x.png new file mode 100755 index 0000000..3dc4bc7 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_91@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_92@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_92@2x.png new file mode 100755 index 0000000..d43f499 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_92@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_93@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_93@2x.png new file mode 100755 index 0000000..c8a9e40 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_93@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_94@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_94@2x.png new file mode 100755 index 0000000..77b97b2 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_94@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_95@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_95@2x.png new file mode 100755 index 0000000..60a5f11 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_95@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_96@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_96@2x.png new file mode 100755 index 0000000..786baad Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_96@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_97@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_97@2x.png new file mode 100755 index 0000000..4893d61 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_97@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_98@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_98@2x.png new file mode 100755 index 0000000..6ce6c3d Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_98@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_99@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_99@2x.png new file mode 100755 index 0000000..48b34be Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_99@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_9@2x.png b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_9@2x.png new file mode 100755 index 0000000..870b981 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/Expression_9@2x.png differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/gifexpression.bundle/Expression_2@2x.gif b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/gifexpression.bundle/Expression_2@2x.gif new file mode 100755 index 0000000..2f1caf5 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/gifexpression.bundle/Expression_2@2x.gif differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/gifexpression.bundle/Expression_53@2x.gif b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/gifexpression.bundle/Expression_53@2x.gif new file mode 100755 index 0000000..e273d93 Binary files /dev/null and b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.bundle/gifexpression.bundle/Expression_53@2x.gif differ diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.plist b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.plist new file mode 100755 index 0000000..4e5f922 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_Expression.plist @@ -0,0 +1,106 @@ + + + + + /::) + /::~ + /::B + /::| + /:8-) + /::< + /::$ + /::X + /::Z + /::'( + /::-| + /::@ + /::P + /::D + /::O + /::( + /::+ + /:--b + /::Q + /::T + /:,@P + /:,@-D + /::d + /:,@o + /::g + /:|-) + /::! + /::L + /::> + /::,@ + /:,@f + /::-S + /:? + /:,@x + /:,@@ + /::8 + /:,@! + /:!!! + /:xx + /:bye + /:wipe + /:dig + /:handclap + /:&-( + /:B-) + /:<@ + /:@> + /::-O + /:>-| + /:P-( + /::'| + /:X-) + /::* + /:@x + /:8* + /:pd + /:<W> + /:beer + /:basketb + /:oo + /:coffee + /:eat + /:pig + /:rose + /:fade + /:showlove + /:heart + /:break + /:cake + /:li + /:bome + /:kn + /:footb + /:ladybug + /:shit + /:moon + /:sun + /:gift + /:hug + /:strong + /:weak + /:share + /:v + /:@) + /:jj + /:@@ + /:bad + /:lvu + /:no + /:ok + /:love + /:<L> + /:jump + /:shake + /:<O> + /:circle + /:kotow + /:turn + /:skip + /:oY + + diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_ExpressionImage.plist b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_ExpressionImage.plist new file mode 100755 index 0000000..6cd86e0 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLEmoji_ExpressionImage.plist @@ -0,0 +1,206 @@ + + + + + /:!!! + Expression_38 + /:&-( + Expression_44 + /:,@! + Expression_37 + /:,@-D + Expression_22 + /:,@@ + Expression_35 + /:,@P + Expression_21 + /:,@f + Expression_31 + /:,@o + Expression_24 + /:,@x + Expression_34 + /:--b + Expression_18 + /:8* + Expression_55 + /:8-) + Expression_5 + /::! + Expression_27 + /::$ + Expression_7 + /::'( + Expression_10 + /::'| + Expression_51 + /::( + Expression_16 + /::) + Expression_1 + /::* + Expression_53 + /::+ + Expression_17 + /::,@ + Expression_30 + /::-O + Expression_48 + /::-S + Expression_32 + /::-| + Expression_11 + /::8 + Expression_36 + /::< + Expression_6 + /::> + Expression_29 + /::@ + Expression_12 + /::B + Expression_3 + /::D + Expression_14 + /::L + Expression_28 + /::O + Expression_15 + /::P + Expression_13 + /::Q + Expression_19 + /::T + Expression_20 + /::X + Expression_8 + /::Z + Expression_9 + /::d + Expression_23 + /::g + Expression_25 + /::| + Expression_4 + /::~ + Expression_2 + /:<@ + Expression_46 + /:<L> + Expression_92 + /:<O> + Expression_95 + /:<W> + Expression_57 + /:>-| + Expression_49 + /:? + Expression_33 + /:@) + Expression_84 + /:@> + Expression_47 + /:@@ + Expression_86 + /:@x + Expression_54 + /:B-) + Expression_45 + /:P-( + Expression_50 + /:X-) + Expression_52 + /:bad + Expression_87 + /:basketb + Expression_59 + /:beer + Expression_58 + /:bome + Expression_71 + /:break + Expression_68 + /:bye + Expression_40 + /:cake + Expression_69 + /:circle + Expression_96 + /:coffee + Expression_61 + /:dig + Expression_42 + /:eat + Expression_62 + /:fade + Expression_65 + /:footb + Expression_73 + /:gift + Expression_78 + /:handclap + Expression_43 + /:heart + Expression_67 + /:hug + Expression_79 + /:jj + Expression_85 + /:jump + Expression_93 + /:kn + Expression_72 + /:kotow + Expression_97 + /:ladybug + Expression_74 + /:li + Expression_70 + /:love + Expression_91 + /:lvu + Expression_88 + /:moon + Expression_76 + /:no + Expression_89 + /:oY + Expression_100 + /:ok + Expression_90 + /:oo + Expression_60 + /:pd + Expression_56 + /:pig + Expression_63 + /:rose + Expression_64 + /:shake + Expression_94 + /:share + Expression_82 + /:shit + Expression_75 + /:showlove + Expression_66 + /:skip + Expression_99 + /:strong + Expression_80 + /:sun + Expression_77 + /:turn + Expression_98 + /:v + Expression_83 + /:weak + Expression_81 + /:wipe + Expression_41 + /:xx + Expression_39 + /:|-) + Expression_26 + + diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Category/NSAttributedString+MLLabel.h b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Category/NSAttributedString+MLLabel.h new file mode 100644 index 0000000..1d8e541 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Category/NSAttributedString+MLLabel.h @@ -0,0 +1,16 @@ +// +// NSAttributedString+MLLabel.h +// Pods +// +// Created by molon on 15/6/13. +// +// + +#import +#import + +@interface NSAttributedString (MLLabel) + ++ (instancetype)attributedStringWithHTML:(NSString*)htmlString; + +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Category/NSAttributedString+MLLabel.m b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Category/NSAttributedString+MLLabel.m new file mode 100644 index 0000000..202764c --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Category/NSAttributedString+MLLabel.m @@ -0,0 +1,39 @@ +// +// NSAttributedString+MLLabel.m +// Pods +// +// Created by molon on 15/6/13. +// +// + +#import "NSAttributedString+MLLabel.h" + +@implementation NSAttributedString (MLLabel) + ++ (instancetype)attributedStringWithHTML:(NSString*)htmlString +{ + NSData* htmlData = [htmlString dataUsingEncoding:NSUTF8StringEncoding]; + if (htmlData) { + __block id attributedString = nil; + dispatch_block_t block = ^{ + attributedString = [[self alloc] initWithData:htmlData + options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, + NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)} + documentAttributes:nil + error:NULL]; + }; + + //这个解析必须在主线程执行,文档上要求的 + if ([NSThread isMainThread]) { + block(); + }else{ + dispatch_sync(dispatch_get_main_queue(), block); + } + + return attributedString; + } + + return nil; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Category/NSMutableAttributedString+MLLabel.h b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Category/NSMutableAttributedString+MLLabel.h new file mode 100644 index 0000000..8d68b3f --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Category/NSMutableAttributedString+MLLabel.h @@ -0,0 +1,17 @@ +// +// NSMutableAttributedString+MLLabel.h +// MLLabel +// +// Created by molon on 15/6/5. +// Copyright (c) 2015年 molon. All rights reserved. +// + +#import + +@interface NSMutableAttributedString (MLLabel) + +- (void)removeAllNSOriginalFontAttributes; + +- (void)removeAttributes:(NSArray *)names range:(NSRange)range; + +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Category/NSMutableAttributedString+MLLabel.m b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Category/NSMutableAttributedString+MLLabel.m new file mode 100644 index 0000000..49bbdb5 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Category/NSMutableAttributedString+MLLabel.m @@ -0,0 +1,29 @@ +// +// NSMutableAttributedString+MLLabel.m +// MLLabel +// +// Created by molon on 15/6/5. +// Copyright (c) 2015年 molon. All rights reserved. +// + +#import "NSMutableAttributedString+MLLabel.h" + +@implementation NSMutableAttributedString (MLLabel) + +- (void)removeAllNSOriginalFontAttributes +{ + [self enumerateAttribute:@"NSOriginalFont" inRange:NSMakeRange(0, self.length) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) { + if (value){ + [self removeAttribute:@"NSOriginalFont" range:range]; + } + }]; +} + + +- (void)removeAttributes:(NSArray *)names range:(NSRange)range +{ + for (NSString *name in names) { + [self removeAttribute:name range:range]; + } +} +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Category/NSString+MLLabel.h b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Category/NSString+MLLabel.h new file mode 100644 index 0000000..dc1bcbf --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Category/NSString+MLLabel.h @@ -0,0 +1,24 @@ +// +// NSString+MLLabel.h +// Pods +// +// Created by molon on 15/6/13. +// +// + +#import + +@interface NSString (MLLabel) + +//这个只是由于换行符的原因所必须有的行数 +- (NSUInteger)lineCount; + +//拿到某行之前的字符串 +- (NSString*)subStringToLineIndex:(NSUInteger)lineIndex; + +//拿到某行之前的字符串的长度 +- (NSUInteger)lengthToLineIndex:(NSUInteger)lineIndex; + +//是否最后字符属于换行符 +- (BOOL)isNewlineCharacterAtEnd; +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Category/NSString+MLLabel.m b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Category/NSString+MLLabel.m new file mode 100644 index 0000000..233c5c8 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Category/NSString+MLLabel.m @@ -0,0 +1,76 @@ +// +// NSString+MLLabel.m +// Pods +// +// Created by molon on 15/6/13. +// +// + +#import "NSString+MLLabel.h" + +@implementation NSString (MLLabel) + +- (NSUInteger)lineCount +{ + if (self.length<=0) { return 0; } + + NSUInteger numberOfLines, index, stringLength = [self length]; + for (index = 0, numberOfLines = 0; index < stringLength; numberOfLines++) { + index = NSMaxRange([self lineRangeForRange:NSMakeRange(index, 0)]); + } + + if ([self isNewlineCharacterAtEnd]) { + return numberOfLines+1; + } + + return numberOfLines; +} + +- (BOOL)isNewlineCharacterAtEnd +{ + if (self.length<=0) { + return NO; + } + //检查最后是否有一个换行符 + NSCharacterSet *separator = [NSCharacterSet newlineCharacterSet]; + NSRange lastRange = [self rangeOfCharacterFromSet:separator options:NSBackwardsSearch]; + return (NSMaxRange(lastRange) == self.length); +} + +- (NSString*)subStringToLineIndex:(NSUInteger)lineIndex +{ + NSUInteger index = [self lengthToLineIndex:lineIndex]; + + return [self substringToIndex:index]; +} + +- (NSUInteger)lengthToLineIndex:(NSUInteger)lineIndex +{ + if (self.length<=0) { + return 0; + } + + NSUInteger numberOfLines, index, stringLength = [self length]; + for (index = 0, numberOfLines = 0; index < stringLength; numberOfLines++) { + NSRange lineRange = [self lineRangeForRange:NSMakeRange(index, 0)]; + index = NSMaxRange(lineRange); + + if (numberOfLines==lineIndex) { + NSString *lineString = [self substringWithRange:lineRange]; + if (![lineString isNewlineCharacterAtEnd]) { + return index; + } + //把这行对应的换行符给忽略 + if (NSMaxRange([lineString rangeOfString:@"\r\n"])==lineString.length) { + return index-2; + } + + return index - 1; + } + } + + return 0; +} + + +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/MLExpressionManager.h b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/MLExpressionManager.h new file mode 100644 index 0000000..74c00ab --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/MLExpressionManager.h @@ -0,0 +1,32 @@ +// +// MLExpressionManager.h +// Pods +// +// Created by molon on 15/6/18. +// +// + +#import + +@interface MLExpression : NSObject + +@property (readonly, nonatomic, copy) NSString *regex; +@property (readonly, nonatomic, copy) NSString *plistName; +@property (readonly, nonatomic, copy) NSString *bundleName; + ++ (instancetype)expressionWithRegex:(NSString*)regex plistName:(NSString*)plistName bundleName:(NSString*)bundleName; + +@end + +@interface MLExpressionManager : NSObject + ++ (instancetype)sharedInstance; + +//获取对应的表情attrStr ++ (NSAttributedString*)expressionAttributedStringWithString:(id)string expression:(MLExpression*)expression; +//给一个str数组,返回其对应的表情attrStr数组,顺序一致 ++ (NSArray *)expressionAttributedStringsWithStrings:(NSArray*)strings expression:(MLExpression*)expression; +//同上,但是以回调方式返回 ++ (void)expressionAttributedStringsWithStrings:(NSArray*)strings expression:(MLExpression*)expression callback:(void(^)(NSArray *result))callback; + +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/MLExpressionManager.m b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/MLExpressionManager.m new file mode 100644 index 0000000..340c2c8 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/MLExpressionManager.m @@ -0,0 +1,273 @@ +// +// MLExpressionManager.m +// Pods +// +// Created by molon on 15/6/18. +// +// + +#import "MLExpressionManager.h" +#import "MLTextAttachment.h" + +/* + 如果设置高于1.00f的话,会引起 有表情行的行距 显得比 没表情行的行距 多,显得不工整 + https://github.com/molon/MLLabel/issues/1 + 所以我们还是设置为1.00f,至于怎么解决这个问题,请参考Demo里的ClipExpressionViewController + */ +#define kExpressionLineHeightMultiple 1.00f + +@interface MLExpression() + +@property (nonatomic, copy) NSString *regex; +@property (nonatomic, copy) NSString *plistName; +@property (nonatomic, copy) NSString *bundleName; + +@property (nonatomic, strong) NSRegularExpression *expressionRegularExpression; +@property (nonatomic, strong) NSDictionary *expressionMap; + +- (BOOL)isValid; + +@end + +@interface MLExpressionManager() + +@property (nonatomic, strong) NSMutableDictionary *expressionMapRecords; +@property (nonatomic, strong) NSMutableDictionary *expressionRegularExpressionRecords; + +@end + +@implementation MLExpressionManager + ++ (instancetype)sharedInstance { + static MLExpressionManager *_sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _sharedInstance = [[[self class] alloc]init]; + }); + return _sharedInstance; +} + +#pragma mark - getter +- (NSMutableDictionary *)expressionMapRecords +{ + if (!_expressionMapRecords) { + _expressionMapRecords = [NSMutableDictionary new]; + } + return _expressionMapRecords; +} + +- (NSMutableDictionary *)expressionRegularExpressionRecords +{ + if (!_expressionRegularExpressionRecords) { + _expressionRegularExpressionRecords = [NSMutableDictionary new]; + } + return _expressionRegularExpressionRecords; +} + +#pragma mark - common +- (NSDictionary*)expressionMapWithPlistName:(NSString*)plistName +{ + NSAssert(plistName&&plistName.length>0, @"expressionMapWithRegex:参数不得为空"); + + if (self.expressionMapRecords[plistName]) { + return self.expressionMapRecords[plistName]; + } + + NSString *plistPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:plistName]; + NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:plistPath]; + NSAssert(dict,@"表情字典%@找不到,请注意大小写",plistName); + self.expressionMapRecords[plistName] = dict; + + return self.expressionMapRecords[plistName]; +} + +- (NSRegularExpression*)expressionRegularExpressionWithRegex:(NSString*)regex +{ + NSAssert(regex&®ex.length>0, @"expressionRegularExpressionWithRegex:参数不得为空"); + + if (self.expressionRegularExpressionRecords[regex]) { + return self.expressionRegularExpressionRecords[regex]; + } + + NSRegularExpression *re = [[NSRegularExpression alloc] initWithPattern:regex options:NSRegularExpressionCaseInsensitive error:nil]; + + NSAssert(re,@"正则%@有误",regex); + self.expressionRegularExpressionRecords[regex] = re; + + return self.expressionRegularExpressionRecords[regex]; +} + +//多线程转表情attrStr ++ (NSArray *)expressionAttributedStringsWithStrings:(NSArray*)strings expression:(MLExpression*)expression +{ + NSMutableDictionary *results = [NSMutableDictionary dictionaryWithCapacity:strings.count]; + + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_group_t group = dispatch_group_create(); + for (id str in strings) { + dispatch_group_async(group, queue, ^{ + NSAttributedString *result = [MLExpressionManager expressionAttributedStringWithString:str expression:expression]; + + @synchronized(results){ + results[str] = result; + } + }); + } + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + + //重新排列 + NSMutableArray *resultArr = [NSMutableArray arrayWithCapacity:results.count]; + for (id str in strings) { + [resultArr addObject:results[str]]; + } + + return resultArr; +} + ++ (void)expressionAttributedStringsWithStrings:(NSArray*)strings expression:(MLExpression*)expression callback:(void(^)(NSArray *result))callback +{ + NSMutableDictionary *results = [NSMutableDictionary dictionaryWithCapacity:strings.count]; + + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_group_t group = dispatch_group_create(); + for (id str in strings) { + dispatch_group_async(group, queue, ^{ + NSAttributedString *result = [MLExpressionManager expressionAttributedStringWithString:str expression:expression]; + + @synchronized(results){ + results[str] = result; + } + }); + } + + dispatch_group_notify(group, queue, ^{ + //重新排列 + NSMutableArray *resultArr = [NSMutableArray arrayWithCapacity:results.count]; + for (id str in strings) { + [resultArr addObject:results[str]]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + if (callback) { + callback(resultArr); + } + }); + }); +} + ++ (NSAttributedString*)expressionAttributedStringWithString:(id)string expression:(MLExpression*)expression { + NSAssert(expression&&[expression isValid], @"expression invalid"); + NSAssert([string isKindOfClass:[NSString class]]||[string isKindOfClass:[NSAttributedString class]], @"string非字符串. %@",string); + + NSAttributedString *attributedString = nil; + if ([string isKindOfClass:[NSString class]]) { + attributedString = [[NSAttributedString alloc]initWithString:string]; + }else{ + attributedString = string; + } + + if (attributedString.length<=0) { + return attributedString; + } + + NSMutableAttributedString *resultAttributedString = [NSMutableAttributedString new]; + + //处理表情 + NSArray *results = [expression.expressionRegularExpression matchesInString:attributedString.string + options:NSMatchingWithTransparentBounds + range:NSMakeRange(0, [attributedString length])]; + //遍历表情,然后找到对应图像名称,并且处理 + NSUInteger location = 0; + for (NSTextCheckingResult *result in results) { + NSRange range = result.range; + NSAttributedString *subAttrStr = [attributedString attributedSubstringFromRange:NSMakeRange(location, range.location - location)]; + //先把非表情的部分加上去 + [resultAttributedString appendAttributedString:subAttrStr]; + + //下次循环从表情的下一个位置开始 + location = NSMaxRange(range); + + NSAttributedString *expressionAttrStr = [attributedString attributedSubstringFromRange:range]; + NSString *imageName = expression.expressionMap[expressionAttrStr.string]; + if (imageName.length>0) { + //加个表情到结果中 + NSString *imagePath = [expression.bundleName stringByAppendingPathComponent:imageName]; + UIImage *image = [UIImage imageNamed:imagePath]; + + MLTextAttachment *textAttachment = [MLTextAttachment textAttachmentWithLineHeightMultiple:kExpressionLineHeightMultiple imageBlock:^UIImage *(CGRect imageBounds, NSTextContainer *textContainer, NSUInteger charIndex, MLTextAttachment *textAttachment) { + return image; + } imageAspectRatio:image.size.width/image.size.height]; + [resultAttributedString appendAttributedString:[NSAttributedString attributedStringWithAttachment:textAttachment]]; + }else{ + //找不到对应图像名称就直接加上去 + [resultAttributedString appendAttributedString:expressionAttrStr]; + } + } + + if (location < [attributedString length]) { + //到这说明最后面还有非表情字符串 + NSRange range = NSMakeRange(location, [attributedString length] - location); + NSAttributedString *subAttrStr = [attributedString attributedSubstringFromRange:range]; + [resultAttributedString appendAttributedString:subAttrStr]; + } + + return resultAttributedString; +} + +@end + + + +@implementation MLExpression + +- (BOOL)isValid +{ + return self.expressionRegularExpression&&self.expressionMap&&self.bundleName.length>0; +} + ++ (instancetype)expressionWithRegex:(NSString*)regex plistName:(NSString*)plistName bundleName:(NSString*)bundleName +{ + MLExpression *expression = [MLExpression new]; + expression.regex = regex; + expression.plistName = plistName; + expression.bundleName = bundleName; + NSAssert([expression isValid], @"此expression无效,请检查参数"); + return expression; +} + +#pragma mark - setter +- (void)setRegex:(NSString *)regex +{ + NSAssert(regex.length>0, @"regex length must >0"); + _regex = regex; + + self.expressionRegularExpression = [[MLExpressionManager sharedInstance]expressionRegularExpressionWithRegex:regex]; +} + +- (void)setPlistName:(NSString *)plistName +{ + + NSAssert(plistName.length>0, @"plistName's length must >0"); + + + if (![[plistName lowercaseString] hasSuffix:@".plist"]) { + plistName = [plistName stringByAppendingString:@".plist"]; + } + + _plistName = plistName; + + self.expressionMap = [[MLExpressionManager sharedInstance]expressionMapWithPlistName:plistName]; +} + +- (void)setBundleName:(NSString *)bundleName +{ + if (![[bundleName lowercaseString] hasSuffix:@".bundle"]) { + bundleName = [bundleName stringByAppendingString:@".bundle"]; + } + + _bundleName = bundleName; + //TODO: 这个最好验证下存在性,后期搞 +} + + +@end + diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/MLTextAttachment.h b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/MLTextAttachment.h new file mode 100644 index 0000000..bce8cf1 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/MLTextAttachment.h @@ -0,0 +1,32 @@ +// +// MLTextAttachment.h +// MLLabel +// +// Created by molon on 15/6/11. +// Copyright (c) 2015年 molon. All rights reserved. +// + +#import + +@interface MLTextAttachment : NSTextAttachment + +@property (readonly, nonatomic, assign) CGFloat width; +@property (readonly, nonatomic, assign) CGFloat height; + +/** + * 优先级比上面的高,以lineHeight为根据来决定高度 + * 宽度根据imageAspectRatio来定 + */ +@property (readonly, nonatomic, assign) CGFloat lineHeightMultiple; + +/** + * image.size.width/image.size.height + */ +@property (readonly, nonatomic, assign) CGFloat imageAspectRatio; + ++ (instancetype)textAttachmentWithWidth:(CGFloat)width height:(CGFloat)height imageBlock:(UIImage * (^)(CGRect imageBounds,NSTextContainer *textContainer,NSUInteger charIndex,MLTextAttachment *textAttachment))imageBlock; + ++ (instancetype)textAttachmentWithLineHeightMultiple:(CGFloat)lineHeightMultiple imageBlock:(UIImage * (^)(CGRect imageBounds,NSTextContainer *textContainer,NSUInteger charIndex,MLTextAttachment *textAttachment))imageBlock + imageAspectRatio:(CGFloat)imageAspectRatio; + +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/MLTextAttachment.m b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/MLTextAttachment.m new file mode 100644 index 0000000..98efbe6 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/MLTextAttachment.m @@ -0,0 +1,101 @@ +// +// MLTextAttachment.m +// MLLabel +// +// Created by molon on 15/6/11. +// Copyright (c) 2015年 molon. All rights reserved. +// + +#import "MLTextAttachment.h" + +@interface MLTextAttachment() + +@property (nonatomic, assign) CGFloat width; +@property (nonatomic, assign) CGFloat height; + +@property (nonatomic, assign) CGFloat lineHeightMultiple; +@property (nonatomic, assign) CGFloat imageAspectRatio; + +@property (nonatomic, copy) UIImage * (^imageBlock)(CGRect imageBounds,NSTextContainer *textContainer,NSUInteger charIndex,MLTextAttachment *textAttachment); + +@end + +@implementation MLTextAttachment + ++ (instancetype)textAttachmentWithWidth:(CGFloat)width height:(CGFloat)height imageBlock:(UIImage * (^)(CGRect imageBounds,NSTextContainer *textContainer,NSUInteger charIndex,MLTextAttachment *textAttachment))imageBlock +{ + MLTextAttachment *textAttachment = [MLTextAttachment new]; + textAttachment.width = width; + textAttachment.height = height; + textAttachment.imageBlock = imageBlock; + return textAttachment; +} + ++ (instancetype)textAttachmentWithLineHeightMultiple:(CGFloat)lineHeightMultiple imageBlock:(UIImage * (^)(CGRect imageBounds,NSTextContainer *textContainer,NSUInteger charIndex,MLTextAttachment *textAttachment))imageBlock + imageAspectRatio:(CGFloat)imageAspectRatio +{ + MLTextAttachment *textAttachment = [MLTextAttachment new]; + textAttachment.lineHeightMultiple = lineHeightMultiple; + textAttachment.imageBlock = imageBlock; + textAttachment.imageAspectRatio = imageAspectRatio; + return textAttachment; +} + +//重写以绘制 +- (UIImage *)imageForBounds:(CGRect)imageBounds textContainer:(NSTextContainer *)textContainer characterIndex:(NSUInteger)charIndex +{ + if (self.imageBlock) + { + return self.imageBlock(imageBounds,textContainer,charIndex,self); + } + + return [super imageForBounds:imageBounds textContainer:textContainer characterIndex:charIndex]; +} + +//重写以返回附件的大小 +- (CGRect)attachmentBoundsForTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(CGRect)lineFrag glyphPosition:(CGPoint)position characterIndex:(NSUInteger)charIndex +{ + if (self.imageBlock) + { + CGFloat width = self.width; + CGFloat height = self.height; + + // 找到其是否有设置字体,如果有,就根据字体的descender调整下位置,以及lineHeight调整大小 + UIFont *font = [textContainer.layoutManager.textStorage attribute:NSFontAttributeName + atIndex:charIndex + effectiveRange:nil]; + CGFloat baseLineHeight = (font?font.lineHeight:lineFrag.size.height); + + if (self.lineHeightMultiple>0) { + width = height = baseLineHeight*self.lineHeightMultiple; + if (self.imageAspectRatio>0) { + width = height*self.imageAspectRatio; + } + }else{ + if (width==0&&height==0) { + width = height = lineFrag.size.height; + }else if (width==0&&height!=0) { + width = height; + }else if (height==0&&width!=0) { + height = width; + } + } + + CGFloat y = font.descender; + y -= (height-baseLineHeight)/2; + + return CGRectMake(0, y, width, height); + + } + + return [super attachmentBoundsForTextContainer:textContainer proposedLineFragment:lineFrag glyphPosition:position characterIndex:charIndex]; +} + +#pragma mark - setter +- (void)setLineHeightMultiple:(CGFloat)lineHeightMultiple +{ + NSAssert(lineHeightMultiple>0, @"lineHeightMultiple必须大于0"); + + _lineHeightMultiple = lineHeightMultiple; +} +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/NSAttributedString+MLExpression.h b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/NSAttributedString+MLExpression.h new file mode 100644 index 0000000..43794e2 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/NSAttributedString+MLExpression.h @@ -0,0 +1,15 @@ +// +// NSAttributedString+MLExpression.h +// Pods +// +// Created by molon on 15/6/18. +// +// + +#import +#import "MLExpressionManager.h" + +@interface NSAttributedString (MLExpression) + +- (NSAttributedString*)expressionAttributedStringWithExpression:(MLExpression*)expression; +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/NSAttributedString+MLExpression.m b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/NSAttributedString+MLExpression.m new file mode 100644 index 0000000..4165f15 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/NSAttributedString+MLExpression.m @@ -0,0 +1,19 @@ +// +// NSAttributedString+MLExpression.m +// Pods +// +// Created by molon on 15/6/18. +// +// + +#import "NSAttributedString+MLExpression.h" + +@implementation NSAttributedString (MLExpression) + + +- (NSAttributedString*)expressionAttributedStringWithExpression:(MLExpression*)expression +{ + return [MLExpressionManager expressionAttributedStringWithString:self expression:expression]; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/NSString+MLExpression.h b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/NSString+MLExpression.h new file mode 100644 index 0000000..410c076 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/NSString+MLExpression.h @@ -0,0 +1,16 @@ +// +// NSString+MLExpression.h +// Pods +// +// Created by molon on 15/6/18. +// +// + +#import +#import "MLExpressionManager.h" + +@interface NSString (MLExpression) + +- (NSAttributedString*)expressionAttributedStringWithExpression:(MLExpression*)expression; + +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/NSString+MLExpression.m b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/NSString+MLExpression.m new file mode 100644 index 0000000..33ba99f --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Expression/NSString+MLExpression.m @@ -0,0 +1,18 @@ +// +// NSString+MLExpression.m +// Pods +// +// Created by molon on 15/6/18. +// +// + +#import "NSString+MLExpression.h" + +@implementation NSString (MLExpression) + +- (NSAttributedString*)expressionAttributedStringWithExpression:(MLExpression*)expression; +{ + return [MLExpressionManager expressionAttributedStringWithString:self expression:expression]; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Helper/MLLabel+Override.h b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Helper/MLLabel+Override.h new file mode 100644 index 0000000..93dda88 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Helper/MLLabel+Override.h @@ -0,0 +1,33 @@ +// +// MLLabel+Override.h +// MLLabel +// +// Created by molon on 15/6/6. +// Copyright (c) 2015年 molon. All rights reserved. +// + +#import "MLLabel.h" + +@class MLLabelLayoutManager; +@interface MLLabel (Override) + +@property (nonatomic, strong) NSTextStorage *textStorage; +@property (nonatomic, strong) MLLabelLayoutManager *layoutManager; +@property (nonatomic, strong) NSTextContainer *textContainer; + +@property (nonatomic, strong) NSAttributedString *lastAttributedText; +@property (nonatomic, assign) MLLastTextType lastTextType; + +//初始化 +- (void)commonInit; + +//复写这个可以最终文本改变 +- (NSMutableAttributedString*)attributedTextForTextStorageFromLabelProperties; + +//获取绘制起点 +- (CGPoint)textOffsetWithTextSize:(CGSize)textSize; + +//可以完全重绘当前label +- (void)reSetText; + +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Helper/MLLabelLayoutManager.h b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Helper/MLLabelLayoutManager.h new file mode 100644 index 0000000..abf2668 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Helper/MLLabelLayoutManager.h @@ -0,0 +1,13 @@ +// +// MLLabelLayoutManager.h +// MLLabel +// +// Created by molon on 15/6/11. +// Copyright (c) 2015年 molon. All rights reserved. +// + +#import + +@interface MLLabelLayoutManager : NSLayoutManager + +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Helper/MLLabelLayoutManager.m b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Helper/MLLabelLayoutManager.m new file mode 100644 index 0000000..4cdd6c2 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/Helper/MLLabelLayoutManager.m @@ -0,0 +1,103 @@ +// +// MLLabelLayoutManager.m +// MLLabel +// +// Created by molon on 15/6/11. +// Copyright (c) 2015年 molon. All rights reserved. +// + +#import "MLLabelLayoutManager.h" + +@interface MLLabelLayoutManager() + +@property (nonatomic, assign) CGPoint lastDrawPoint; + +@end + +@implementation MLLabelLayoutManager + +- (void)drawBackgroundForGlyphRange:(NSRange)glyphsToShow atPoint:(CGPoint)origin +{ + self.lastDrawPoint = origin; + [super drawBackgroundForGlyphRange:glyphsToShow atPoint:origin]; + self.lastDrawPoint = CGPointZero; +} + +- (void)fillBackgroundRectArray:(const CGRect *)rectArray count:(NSUInteger)rectCount forCharacterRange:(NSRange)charRange color:(UIColor *)color +{ + CGContextRef ctx = UIGraphicsGetCurrentContext(); + if (!ctx) { + [super fillBackgroundRectArray:rectArray count:rectCount forCharacterRange:charRange color:color]; + return; + } + + CGContextSaveGState(ctx); + { + [color setFill]; + + NSRange glyphRange = [self glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; + + CGPoint textOffset = self.lastDrawPoint; + + NSRange lineRange = NSMakeRange(glyphRange.location, 1); + while (NSMaxRange(lineRange)<=NSMaxRange(glyphRange)) { + + //这里可以防止这行没有用到的区域也绘制上背景色,例如收到word wrap,center alignment影响后每行文字没有占满时候 + CGRect lineBounds = [self lineFragmentUsedRectForGlyphAtIndex:lineRange.location effectiveRange:&lineRange]; + lineBounds.origin.x += textOffset.x; + lineBounds.origin.y += textOffset.y; + + //找到这行具有背景色文字区域的位置 + NSRange glyphRangeInLine = NSIntersectionRange(glyphRange,lineRange); + NSRange truncatedGlyphRange = [self truncatedGlyphRangeInLineFragmentForGlyphAtIndex:glyphRangeInLine.location]; + if (truncatedGlyphRange.location!=NSNotFound) { + //这里的glyphRangeInLine本身可能会带有被省略的区间,而我们下面计算最大行高和最小drawY的实现是不需要考虑省略的区间的,否则也可能计算有误。所以这里我们给过滤掉 + NSRange sameRange = NSIntersectionRange(glyphRangeInLine, truncatedGlyphRange); + if (sameRange.length>0&&NSMaxRange(sameRange)==NSMaxRange(glyphRangeInLine)) { + //我们这里先只处理tail模式的 + //而经过测试truncatedGlyphRangeInLineFragmentForGlyphAtIndex暂时只支持NSLineBreakByTruncatingTail模式 + //其他两种暂时也不会用,即使用,现在通过TextKit的话也没法获取 + glyphRangeInLine = NSMakeRange(glyphRangeInLine.location, sameRange.location-glyphRangeInLine.location); + } + } + + if (glyphRangeInLine.length>0) { + CGFloat startDrawY = CGFLOAT_MAX; + CGFloat maxLineHeight = 0.0f; //找到这行的 背景色区间 的文字的最小Y值和最大的文字高度 + for (NSInteger glyphIndex = glyphRangeInLine.location; glyphIndex + +typedef NS_ENUM(NSUInteger, MLLastTextType) { + MLLastTextTypeNormal, + MLLastTextTypeAttributed, +}; +// UILabel的baselineAdjustment属性功能没模拟,其他的属性都模拟了 + +@interface MLLabel : UILabel + +@property (nonatomic, assign) CGFloat lineHeightMultiple; //行高的multiple +@property (nonatomic, assign) CGFloat lineSpacing; //行间距 + +@property (nonatomic, assign) UIEdgeInsets textInsets; + +@property (nonatomic, copy) void(^doBeforeDrawingTextBlock)(CGRect rect,CGPoint beginOffset,CGSize drawSize); + +- (CGSize)preferredSizeWithMaxWidth:(CGFloat)maxWidth; + + +//方便码代码 +- (void)setDoBeforeDrawingTextBlock:(void (^)(CGRect rect,CGPoint beginOffset,CGSize drawSize))doBeforeDrawingTextBlock; +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/MLLabel.m b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/MLLabel.m new file mode 100644 index 0000000..dc1dea1 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/MLLabel.m @@ -0,0 +1,703 @@ +// +// MLLabel.m +// MLLabel +// +// Created by molon on 15/5/18. +// Copyright (c) 2015年 molon. All rights reserved. +// + +#import "MLLabel.h" +#import "NSMutableAttributedString+MLLabel.h" +#import "MLLabelLayoutManager.h" +#import "NSString+MLLabel.h" + +#define kAdjustFontSizeEveryScalingFactor (M_E / M_PI) +//总得有个极限 +static CGFloat MLFLOAT_MAX = 100000.0f; +static CGFloat ADJUST_MIN_FONT_SIZE = 1.0f; +static CGFloat ADJUST_MIN_SCALE_FACTOR = 0.01f; + +static NSArray * kStylePropertyNames() { + static NSArray *_stylePropertyNames = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + //TODO: 这个highlighted在tableview滚动到的时候会设置下(即使cell的selectStyle为None),然后就造成resetText,很鸡巴,耗费性能,这个似乎没辙,实在有必要,后期+个属性来开关 + _stylePropertyNames = @[@"font",@"textAlignment",@"textColor",@"highlighted", + @"highlightedTextColor",@"shadowColor",@"shadowOffset",@"enabled",@"lineHeightMultiple",@"lineSpacing"]; + }); + return _stylePropertyNames; +} + +@interface MLLabelStylePropertyRecord : NSObject + +@property (nonatomic, strong) UIFont *font; +@property (nonatomic, assign) NSTextAlignment textAlignment; +@property (nonatomic, strong) UIColor *textColor; +@property (nonatomic, assign) BOOL highlighted; +@property (nonatomic, strong) UIColor *highlightedTextColor; +@property (nonatomic, strong) UIColor *shadowColor; +@property (nonatomic, assign) CGSize shadowOffset; +@property (nonatomic, assign) BOOL enabled; + +@property (nonatomic, assign) CGFloat lineHeightMultiple; //行高的multiple +@property (nonatomic, assign) CGFloat lineSpacing; //行间距 + +@end +@implementation MLLabelStylePropertyRecord +@end + + +@interface MLLabel() + +@property (nonatomic, strong) NSTextStorage *textStorage; +@property (nonatomic, strong) MLLabelLayoutManager *layoutManager; +@property (nonatomic, strong) NSTextContainer *textContainer; + +@property (nonatomic, assign) MLLastTextType lastTextType; + +@property (nonatomic, strong) MLLabelStylePropertyRecord *styleRecord; + +//为什么需要这个,是因为setAttributedText之后内部可能会对其进行了改动,然后例如再次更新style属性,然后更新绘制会出问题。索性都以记录的最原始的为准。 +@property (nonatomic, copy) NSAttributedString *lastAttributedText; +//读取的时候需要 +@property (nonatomic, copy) NSString *lastText; + +@end + +@implementation MLLabel + +#pragma mark - init +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + [self commonInit]; + } + return self; +} + +- (id)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if (self) + { + [self commonInit]; + } + return self; +} + +- (void)commonInit +{ + self.lineHeightMultiple = 1.0f; + + //设置TextKit初始相关 + [self.textStorage addLayoutManager:self.layoutManager]; + [self.layoutManager addTextContainer:self.textContainer]; + + //label helper相关 + if ([super attributedText]) { + self.attributedText = [super attributedText]; + }else{ + self.text = [super text]; + } + + //kvo 监视style属性 + for (NSString *key in kStylePropertyNames()) { + [self.styleRecord setValue:[self valueForKey:key] forKey:key]; + //不直接使用NSKeyValueObservingOptionInitial来初始化赋值record,是防止无用的resettext + [self addObserver:self forKeyPath:key options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil]; + } +} + +- (void)dealloc +{ + //kvo 移除监视style属性 + for (NSString *key in kStylePropertyNames()) { + [self removeObserver:self forKeyPath:key context:nil]; + } +} + + +#pragma mark - KVO +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if ([kStylePropertyNames() containsObject:keyPath]) { + //存到记录对象里 + [_styleRecord setValue:[object valueForKey:keyPath] forKey:keyPath]; + + id old = change[NSKeyValueChangeOldKey]; + id new = change[NSKeyValueChangeNewKey]; + if ([old isEqual:new]||(!old&&!new)) { + return; + } + + [self reSetText]; + }else{ + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; + } +} + + +#pragma mark - getter +- (NSTextStorage *)textStorage +{ + if (!_textStorage) { + _textStorage = [NSTextStorage new]; + } + return _textStorage; +} + +- (MLLabelLayoutManager *)layoutManager +{ + if (!_layoutManager) { + _layoutManager = [MLLabelLayoutManager new]; + _layoutManager.allowsNonContiguousLayout = NO; + _layoutManager.delegate = self; + } + return _layoutManager; +} + +- (NSTextContainer *)textContainer +{ + if (!_textContainer) { + _textContainer = [NSTextContainer new]; + _textContainer.maximumNumberOfLines = self.numberOfLines; + _textContainer.lineBreakMode = self.lineBreakMode; + _textContainer.lineFragmentPadding = 0.0f; + _textContainer.size = self.frame.size; + } + return _textContainer; +} + +- (MLLabelStylePropertyRecord *)styleRecord +{ + if (!_styleRecord) { + _styleRecord = [MLLabelStylePropertyRecord new]; + } + return _styleRecord; +} + +- (NSAttributedString *)attributedText +{ + return _lastTextType==MLLastTextTypeAttributed?_lastAttributedText:[self attributedTextForTextStorageFromLabelProperties]; +} + +- (NSString*)text +{ + return _lastTextType==MLLastTextTypeAttributed?_lastAttributedText.string:_lastText; +} + +#pragma mark - set text +- (void)setLastTextType:(MLLastTextType)lastTextType +{ + _lastTextType = lastTextType; + //重置下 + self.lastText = nil; + self.lastAttributedText = nil; +} + +- (void)setText:(NSString *)text +{ + NSAssert(!text||[text isKindOfClass:[NSString class]], @"text must be NSString"); + + self.lastTextType = MLLastTextTypeNormal; + self.lastText = text; + + [self invalidateIntrinsicContentSize]; + // [super setText:text]; + + [_textStorage setAttributedString:[self attributedTextForTextStorageFromLabelProperties]]; + + //如果text和原本的一样的话 super 是不会触发redraw的,但是很遗憾我们的label比较灵活,验证起来很麻烦,所以还是都重绘吧 + [self setNeedsDisplay]; +} + +- (void)setAttributedText:(NSAttributedString *)attributedText +{ + NSAssert(!attributedText||[attributedText isKindOfClass:[NSAttributedString class]], @"text must be NSAttributedString"); + + self.lastTextType = MLLastTextTypeAttributed; + self.lastAttributedText = attributedText; + + [self invalidateIntrinsicContentSize]; + // [super setAttributedText:attributedText]; + + [_textStorage setAttributedString:[self attributedTextForTextStorageFromLabelProperties]]; + + //如果text和原本的一样的话 super 是不会触发redraw的,但是很遗憾我们的label比较灵活,验证起来很麻烦,所以还是都重绘吧 + [self setNeedsDisplay]; + // NSLog(@"set attr text %p",self); +} + +#pragma mark - common helper +- (CGSize)textContainerSizeWithBoundsSize:(CGSize)size +{ + //bounds改了之后,要相应的改变textContainer的size,但是要根据insets调整 + CGFloat width = fmax(0, size.width-_textInsets.left-_textInsets.right); + CGFloat height = fmax(0, size.height-_textInsets.top-_textInsets.bottom); + return CGSizeMake(width, height); +} + +- (void)reSetText +{ + if (_lastTextType == MLLastTextTypeNormal) { + self.text = _lastText; + }else{ + self.attributedText = _lastAttributedText; + } +} + +/** + * 根据label的属性来进行处理并返回给textStorage使用的 + */ +- (NSMutableAttributedString*)attributedTextForTextStorageFromLabelProperties +{ + if (_lastTextType==MLLastTextTypeNormal) { + if (!_lastText) { + return [[NSMutableAttributedString alloc]initWithString:@""]; + } + + //根据text和label默认的一些属性得到attributedText + return [[NSMutableAttributedString alloc] initWithString:_lastText attributes:[self attributesFromLabelProperties]]; + } + + if (!_lastAttributedText) { + return [[NSMutableAttributedString alloc]initWithString:@""]; + } + + //遍历并且添加Label默认的属性 + NSMutableAttributedString *newAttrStr = [[NSMutableAttributedString alloc]initWithString:_lastAttributedText.string attributes:[self attributesFromLabelProperties]]; + + [_lastAttributedText enumerateAttributesInRange:NSMakeRange(0, newAttrStr.length) options:0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { + if (attrs.count>0) { + // [newAttrStr removeAttributes:[attrs allKeys] range:range]; + [newAttrStr addAttributes:attrs range:range]; + } + }]; + return newAttrStr; +} + +- (NSDictionary *)attributesFromLabelProperties +{ + //颜色 + UIColor *color = self.styleRecord.textColor; + if (!_styleRecord.enabled) + { + color = [UIColor lightGrayColor]; + } + else if (_styleRecord.highlighted) + { + color = _styleRecord.highlightedTextColor; + } + if (!color) { + color = _styleRecord.textColor; + if (!color) { + color = [UIColor darkTextColor]; + } + } + + //阴影 + NSShadow *shadow = shadow = [[NSShadow alloc] init]; + if (_styleRecord.shadowColor) + { + shadow.shadowColor = _styleRecord.shadowColor; + shadow.shadowOffset = _styleRecord.shadowOffset; + } + else + { + shadow.shadowOffset = CGSizeMake(0, -1); + shadow.shadowColor = nil; + } + + //水平位置 + NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init]; + paragraph.alignment = _styleRecord.textAlignment; + paragraph.lineSpacing = _styleRecord.lineSpacing; + paragraph.lineHeightMultiple = _styleRecord.lineHeightMultiple; + + if (!_styleRecord.font) { + _styleRecord.font = [UIFont systemFontOfSize:17.0f]; + } + //最终 + NSDictionary *attributes = @{NSFontAttributeName : _styleRecord.font, + NSForegroundColorAttributeName : color, + NSShadowAttributeName : shadow, + NSParagraphStyleAttributeName : paragraph, + }; + return attributes; +} + + +- (CGRect)textRectForBounds:(CGRect)bounds attributedString:(NSAttributedString*)attributedString limitedToNumberOfLines:(NSInteger)numberOfLines lineCount:(NSInteger*)lineCount +{ + //这种算是特殊情况,如果为空字符串,那就没必要必要了,也忽略textInset,这样比较合理 + if (attributedString.length<=0) { + bounds.size = CGSizeZero; + return bounds; + } + + CGSize newTextContainerSize = [self textContainerSizeWithBoundsSize:bounds.size]; + if (newTextContainerSize.width<=0||newTextContainerSize.height<=0){ + CGRect textBounds = CGRectZero; + textBounds.origin = bounds.origin; + textBounds.size = CGSizeMake(fmin(_textInsets.left+_textInsets.right,CGRectGetWidth(bounds)), fmin(_textInsets.top+_textInsets.bottom,CGRectGetHeight(bounds))); + return textBounds; + } + + CGRect textBounds = CGRectZero; + @autoreleasepool { + CGSize savedTextContainerSize = _textContainer.size; + NSInteger savedTextContainerNumberOfLines = _textContainer.maximumNumberOfLines; + + _textContainer.size = newTextContainerSize; + _textContainer.maximumNumberOfLines = numberOfLines; + + NSAttributedString *savedAttributedString = nil; + if (![_textStorage isEqual:attributedString]) { + savedAttributedString = [_textStorage copy]; + [_textStorage setAttributedString:attributedString]; + } + + NSRange glyphRange = [_layoutManager glyphRangeForTextContainer:_textContainer]; + if (lineCount) { + [_layoutManager enumerateLineFragmentsForGlyphRange:glyphRange usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer *textContainer, NSRange glyphRange, BOOL *stop) { + (*lineCount)++; + }]; + //在最后字符为换行符的情况下,实际绘制出来的是会多那个一行,这里作为AppleBUG修正 + if ([_textStorage.string isNewlineCharacterAtEnd]) { + (*lineCount)++; + } + } + + textBounds = [_layoutManager usedRectForTextContainer:_textContainer]; + + //还原 + if (savedAttributedString) { + [_textStorage setAttributedString:savedAttributedString]; + } + _textContainer.size = savedTextContainerSize; + _textContainer.maximumNumberOfLines = savedTextContainerNumberOfLines; + } + //最终修正 + textBounds.size.width = fmin(ceilf(textBounds.size.width), newTextContainerSize.width); + textBounds.size.height = fmin(ceilf(textBounds.size.height), newTextContainerSize.height); + textBounds.origin = bounds.origin; + + textBounds.size = CGSizeMake(fmin(CGRectGetWidth(textBounds)+_textInsets.left+_textInsets.right,CGRectGetWidth(bounds)), fmin(CGRectGetHeight(textBounds)+_textInsets.top+_textInsets.bottom,CGRectGetHeight(bounds))); + + // NSLog(@"bounds:%@ result:%@ %p",NSStringFromCGRect(bounds),NSStringFromCGRect(textBounds),self); + return textBounds; +} + +#pragma mark - draw +- (BOOL)adjustsCurrentFontSizeToFitWidthWithScaleFactor:(CGFloat)scaleFactor numberOfLines:(NSInteger)numberOfLines originalAttributedText:(NSAttributedString*)originalAttributedText bounds:(CGRect)bounds resultAttributedString:(NSAttributedString**)resultAttributedString +{ + __block BOOL mustReturnYES = NO; + if (self.minimumScaleFactor > scaleFactor) { + scaleFactor = self.minimumScaleFactor; //这个的话 就不能在循环验证了 + mustReturnYES = YES; + } + //总得有个极限 + scaleFactor = fmax(scaleFactor, ADJUST_MIN_SCALE_FACTOR); + + //遍历并且设置一个新的字体 + NSMutableAttributedString *attrStr = [originalAttributedText mutableCopy]; + + if (scaleFactor!=1.0f) { //如果是1.0f的话就没有调整font size的必要 + [attrStr enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, attrStr.length) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) { + UIFont *font = (UIFont *)value; + if (font&&[font isKindOfClass:[UIFont class]]) { + NSString *fontName = font.fontName; + CGFloat newSize = font.pointSize*scaleFactor; + if (newSize0) { + NSInteger lineCount = 0; + currentTextSize = [self textRectForBounds:CGRectMake(0, 0, CGRectGetWidth(bounds), MLFLOAT_MAX) attributedString:attrStr limitedToNumberOfLines:0 lineCount:&lineCount].size; + //如果求行数大于设置行数,也不认为塞满了 + if (lineCount>numberOfLines) { + return NO; + } + }else{ + currentTextSize = [self textRectForBounds:CGRectMake(0, 0, CGRectGetWidth(bounds), MLFLOAT_MAX) attributedString:attrStr limitedToNumberOfLines:0 lineCount:NULL].size; + } + + //大小已经足够就认作OK + if (currentTextSize.width<=CGRectGetWidth(bounds)&¤tTextSize.height<=CGRectGetHeight(bounds)) { + if (resultAttributedString) { + (*resultAttributedString) = attrStr; + } + return YES; + } + + return NO; +} + +- (void)drawTextInRect:(CGRect)rect +{ + // NSLog(@"draw text %p",self); + //不调用super方法 + // [super drawTextInRect:rect]; //这里调用可以查看是否绘制和原来的不一致 + + //如果绘制区域本身就为0,就应该直接返回,不做多余操作。 + if (_textContainer.size.width<=0||_textContainer.size.height<=0){ + return; + } + + if (self.adjustsFontSizeToFitWidth) { + //初始scale,每次adjust都需要从头开始,因为也可能有当前font被adjust小过需要还原。 + CGFloat scaleFactor = 1.0f; + BOOL mustContinueAdjust = YES; + NSAttributedString *attributedString = [self attributedTextForTextStorageFromLabelProperties]; + + //numberOfLine>0时候可以直接尝试找寻一个preferredScale + if (self.numberOfLines>0) { + //一点点矫正,以使得内容能放到当前的size里 + + //找到当前text绘制在一行时候需要占用的宽度,其实这个值很可能不够,因为多行时候可能会因为wordwrap的关系多行+起的总宽度会多。但是这个能找到一个合适的矫正过程的开始值,大大减少矫正次数。 + + //还有一种情况就是,有可能由于字符串里带换行符的关系造成压根不可能绘制到一行,这时候应该取会显示的最长的那一行。所以这里需要先截除必然不会显示的部分。 + NSUInteger stringlineCount = [attributedString.string lineCount]; + if (stringlineCount>self.numberOfLines) { + //这里说明必然要截取 + attributedString = [attributedString attributedSubstringFromRange:NSMakeRange(0, [attributedString.string lengthToLineIndex:self.numberOfLines-1])]; + } + + CGFloat textWidth = [self textRectForBounds:CGRectMake(0, 0, MLFLOAT_MAX, MLFLOAT_MAX) attributedString:attributedString limitedToNumberOfLines:0 lineCount:NULL].size.width; + textWidth = fmax(0, textWidth-_textInsets.left-_textInsets.right); + if (textWidth>0) { + CGFloat availableWidth = _textContainer.size.width*self.numberOfLines; + if (textWidth > availableWidth) { + //这里得到的scaleFactor肯定是大于这个的是必然不满足的,目的就是找这个,以能减少下面的矫正次数。 + scaleFactor = availableWidth / textWidth; + } + }else{ + mustContinueAdjust = NO; + } + } + + if (mustContinueAdjust) { + //一点点矫正,以使得内容能放到当前的size里 + NSAttributedString *resultAttributedString = attributedString; + while (![self adjustsCurrentFontSizeToFitWidthWithScaleFactor:scaleFactor numberOfLines:self.numberOfLines originalAttributedText:attributedString bounds:self.bounds resultAttributedString:&resultAttributedString]){ + scaleFactor *= kAdjustFontSizeEveryScalingFactor; + }; + [_textStorage setAttributedString:resultAttributedString]; + } + } + + + CGPoint textOffset; + //这里根据container的size和manager布局属性以及字符串来得到实际绘制的range区间 + NSRange glyphRange = [_layoutManager glyphRangeForTextContainer:_textContainer]; + + //获取绘制区域大小 + CGRect drawBounds = [_layoutManager usedRectForTextContainer:_textContainer]; + + //因为label是默认垂直居中的,所以需要根据实际绘制区域的bounds来调整出居中的offset + textOffset = [self textOffsetWithTextSize:drawBounds.size]; + + if (_doBeforeDrawingTextBlock) { + //而实际上drawBounds的宽度可能不是我们想要的,我们想要的是_textContainer的宽度,但是高度需要是真实绘制高度 + CGSize drawSize = CGSizeMake(_textContainer.size.width, drawBounds.size.height); + _doBeforeDrawingTextBlock(rect,textOffset,drawSize); + } + + //绘制文字 + [_layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:textOffset]; + [_layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textOffset]; +} + +//这个计算出来的是绘制起点 +- (CGPoint)textOffsetWithTextSize:(CGSize)textSize +{ + CGPoint textOffset = CGPointZero; + //根据insets和默认垂直居中来计算出偏移 + textOffset.x = _textInsets.left; + CGFloat paddingHeight = (_textContainer.size.height - textSize.height) / 2.0f; + textOffset.y = paddingHeight+_textInsets.top; + + return textOffset; +} + + +//- (NSUInteger)layoutManager:(NSLayoutManager *)layoutManager +// shouldGenerateGlyphs:(const CGGlyph *)glyphs +// properties:(const NSGlyphProperty *)props +// characterIndexes:(const NSUInteger *)charIndexes +// font:(UIFont *)aFont +// forGlyphRange:(NSRange)glyphRange +//{ +// NSLog(@"shouldGenerateGlyphs: start:%ld end:%ld",*charIndexes,charIndexes[glyphRange.length-1]); +//// if (*charIndexes>=100) { +//// return 0; +//// } +// [layoutManager setGlyphs:glyphs properties:props characterIndexes:charIndexes font:aFont forGlyphRange:glyphRange]; +// return glyphRange.length; +//} + +#pragma mark - sizeThatsFit +- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines +{ + //fit实现与drawTextInRect大部分一个屌样,所以不写注释了。 + if (numberOfLines>1&&self.adjustsFontSizeToFitWidth) { + CGFloat scaleFactor = 1.0f; + NSAttributedString *attributedString = [self attributedTextForTextStorageFromLabelProperties]; + + NSUInteger stringlineCount = [attributedString.string lineCount]; + if (stringlineCount>self.numberOfLines) { + //这里说明必然要截取 + attributedString = [attributedString attributedSubstringFromRange:NSMakeRange(0, [attributedString.string lengthToLineIndex:self.numberOfLines-1])]; + } + + CGFloat textWidth = [self textRectForBounds:CGRectMake(0, 0, MLFLOAT_MAX, MLFLOAT_MAX) attributedString:attributedString limitedToNumberOfLines:0 lineCount:NULL].size.width; + textWidth = fmax(0, textWidth-_textInsets.left-_textInsets.right); + if (textWidth>0) { + CGFloat availableWidth = _textContainer.size.width*numberOfLines; + if (textWidth > availableWidth) { + //这里得到的scaleFactor肯定是大于这个的是必然不满足的,目的就是找这个,以能减少下面的矫正次数。 + scaleFactor = availableWidth / textWidth; + } + //一点点矫正,以使得内容能放到当前的size里 + NSAttributedString *resultAttributedString = attributedString; + while (![self adjustsCurrentFontSizeToFitWidthWithScaleFactor:scaleFactor numberOfLines:numberOfLines originalAttributedText:attributedString bounds:bounds resultAttributedString:&resultAttributedString]){ + scaleFactor *= kAdjustFontSizeEveryScalingFactor; + }; + + //计算当前adjust之后的合适大小,为什么不用adjust里面的 因为也可能有异常情况,例如压根adjust就没走到计算大小那一步啊什么的。 + CGRect textBounds = [self textRectForBounds:bounds attributedString:resultAttributedString limitedToNumberOfLines:numberOfLines lineCount:NULL]; + + return textBounds; + } + } + return [self textRectForBounds:bounds attributedString:_textStorage limitedToNumberOfLines:numberOfLines lineCount:NULL]; +} + +- (CGSize)sizeThatFits:(CGSize)size +{ + size = [super sizeThatFits:size]; + if (size.height>0) { + size.height++; + } + return size; +} + +- (CGSize)intrinsicContentSize { + CGSize size = [super intrinsicContentSize]; + if (size.height>0) { + size.height++; + } + return size; +} + +- (CGSize)preferredSizeWithMaxWidth:(CGFloat)maxWidth +{ + CGSize size = [self sizeThatFits:CGSizeMake(maxWidth, MLFLOAT_MAX)]; + size.width = fmin(size.width, maxWidth); //在numberOfLine为1模式下返回的可能会比maxWidth大,所以这里我们限制下 + return size; +} + +#pragma mark - set 修改container size相关 +- (void)resizeTextContainerSize +{ + if (_textContainer) { + _textContainer.size = [self textContainerSizeWithBoundsSize:self.bounds.size]; + } +} + +- (void)setFrame:(CGRect)frame +{ + [super setFrame:frame]; + [self resizeTextContainerSize]; +} + +- (void)setBounds:(CGRect)bounds +{ + [super setBounds:bounds]; + [self resizeTextContainerSize]; +} + +- (void)setTextInsets:(UIEdgeInsets)insets +{ + _textInsets = insets; + [self resizeTextContainerSize]; + + [self invalidateIntrinsicContentSize]; +} + +#pragma mark - set container相关属性 +- (void)setNumberOfLines:(NSInteger)numberOfLines +{ + BOOL isChanged = (numberOfLines!=_textContainer.maximumNumberOfLines); + + [super setNumberOfLines:numberOfLines]; + + _textContainer.maximumNumberOfLines = numberOfLines; + + if (isChanged) { + [self invalidateIntrinsicContentSize]; + [self setNeedsDisplay]; + } +} + +- (void)setLineBreakMode:(NSLineBreakMode)lineBreakMode +{ + [super setLineBreakMode:lineBreakMode]; + + _textContainer.lineBreakMode = lineBreakMode; + + [self invalidateIntrinsicContentSize]; +} + +#pragma mark - set 其他 +- (void)setMinimumScaleFactor:(CGFloat)minimumScaleFactor +{ + [super setMinimumScaleFactor:minimumScaleFactor]; + + [self invalidateIntrinsicContentSize]; + [self setNeedsDisplay]; +} + +- (void)setDoBeforeDrawingTextBlock:(void (^)(CGRect rect,CGPoint beginOffset,CGSize drawSize))doBeforeDrawingTextBlock +{ + _doBeforeDrawingTextBlock = doBeforeDrawingTextBlock; + + [self setNeedsDisplay]; +} + +#pragma mark - UIResponder +- (BOOL)canBecomeFirstResponder { + return YES; +} + +- (BOOL)canPerformAction:(SEL)action + withSender:(__unused id)sender +{ + return (action == @selector(copy:)); +} + +#pragma mark - UIResponderStandardEditActions +- (void)copy:(__unused id)sender { + [[UIPasteboard generalPasteboard] setString:self.text]; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/MLLinkLabel.h b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/MLLinkLabel.h new file mode 100644 index 0000000..92c460b --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/MLLinkLabel.h @@ -0,0 +1,135 @@ +// +// MLLinkLabel.h +// MLLabel +// +// Created by molon on 15/6/6. +// Copyright (c) 2015年 molon. All rights reserved. +// + +#import "MLLabel.h" + +typedef NS_OPTIONS(NSUInteger, MLDataDetectorTypes) { + MLDataDetectorTypeURL = 1 << 1, // 链接,不用link定义,是因为link作为统称 + MLDataDetectorTypePhoneNumber = 1 << 0, // 电话 + MLDataDetectorTypeNone = 0, // 禁用 + MLDataDetectorTypeAll = NSUIntegerMax, // 所有 + //上面4个和UIDataDetectorTypes的对应,下面是自己加的 + + MLDataDetectorTypeEmail = 1 << 4, // 邮箱 + MLDataDetectorTypeUserHandle = 1 << 5, //@ + MLDataDetectorTypeHashtag = 1 << 6, //#..# + //上面是个性化的匹配 + + + //这个是对attributedText里带有Link属性的检测,至于为什么31,预留上面空间以添加新的个性化 + //这个东西和dataDetectorTypesOfAttributedLinkValue对应起来,会对带有NSLinkAttributeName区间的value进行检测,匹配则给予对应的LinkType,找不到则为Other + MLDataDetectorTypeAttributedLink = 1 << 31, +}; + + +typedef NS_ENUM(NSUInteger, MLLinkType) { + MLLinkTypeNone = 0, + MLLinkTypeURL = 1, // 链接 + MLLinkTypePhoneNumber = 2, // 电话 + MLLinkTypeEmail = 3, // 邮箱 + MLLinkTypeUserHandle = 4, //@ + MLLinkTypeHashtag = 5, //#..# + + MLLinkTypeOther = 31, //这个一般是和MLDataDetectorTypeAttributedLink对应的,但是也可以自己随意添加啦,不过是一个标识而已,至于为什么31,随便定的,预留上面空间以添加新的个性化 +}; + +#define kDefaultLinkColorForMLLinkLabel [UIColor colorWithRed:0.061 green:0.515 blue:0.862 alpha:1.000] +#define kDefaultActiveLinkBackgroundColorForMLLinkLabel [UIColor colorWithWhite:0.215 alpha:0.300] + +@class MLLink,MLLinkLabel; + +@protocol MLLinkLabelDelegate + +- (void)didClickLink:(MLLink*)link linkText:(NSString*)linkText linkLabel:(MLLinkLabel*)linkLabel; + +@optional + +- (void)didLongPressLink:(MLLink*)link linkText:(NSString*)linkText linkLabel:(MLLinkLabel*)linkLabel; + +@end + +@interface MLLinkLabel : MLLabel + +//默认为MLDataDetectorTypeURL|MLDataDetectorTypePhoneNumber|MLDataDetectorTypeEmail|MLDataDetectorTypeAttributedLink,自动检测除了@和#话题的全部类型并且转换为链接 +@property (nonatomic, assign) MLDataDetectorTypes dataDetectorTypes; + +//这个是当dataDetectorTypes的MLDataDetectorTypeAttributedLink可用时候,自动对attributedText里Link属性value检测给予linkType的检测类型,默认为MLDataDetectorTypeNone,也就是默认最终得到的linkType为MLLinkTypeOther +@property (nonatomic, assign) MLDataDetectorTypes dataDetectorTypesOfAttributedLinkValue; + +@property (nonatomic, strong) NSDictionary *linkTextAttributes; +@property (nonatomic, strong) NSDictionary *activeLinkTextAttributes; + +//这个主要是为了不会在点击非常快速结束触摸的情况下,激活的链接样式基本没体现,这里的delay可以让其多体现那个一会,显得有反馈。 +//默认为0.3秒 +@property (nonatomic, assign) NSTimeInterval activeLinkToNilDelay; + +//是否允许在link内line break,默认为YES,即为允许,这样的话链接会能折行就折行和正常文本一样 +@property (nonatomic, assign) BOOL allowLineBreakInsideLinks; + +//优先级比delegate高 +@property (nonatomic, copy) void(^didClickLinkBlock)(MLLink *link,NSString *linkText,MLLinkLabel *label); +@property (nonatomic, copy) void(^didLongPressLinkBlock)(MLLink *link,NSString *linkText,MLLinkLabel *label); + +@property (nonatomic, weak) id delegate; //这个优先级没有block高 + +@property (nonatomic, strong, readonly) NSMutableArray *links; //可以遍历针对自定义 + +/** + * link在正式add之前可以自定义修改属性的block + */ +@property (nonatomic, copy) void(^beforeAddLinkBlock)(MLLink *link); + + +- (MLLink *)linkAtPoint:(CGPoint)location; + +/** + * 设置文本后添加link。注意如果在此之后设置了text、attributedText、dataDetectorTypes或dataDetectorTypesOfAttributedLinkValue属性的话添加的link会丢失。 + */ +- (BOOL)addLink:(MLLink*)link; +/** + * 设置文本后添加link,注意如果在此之后设置了text、attributedText、dataDetectorTypes或dataDetectorTypesOfAttributedLinkValue属性的话添加的link会丢失。 + */ +- (MLLink*)addLinkWithType:(MLLinkType)type value:(NSString*)value range:(NSRange)range; +/** + * 设置文本后添加link,注意如果在此之后设置了text、attributedText、dataDetectorTypes或dataDetectorTypesOfAttributedLinkValue属性的话添加的link会丢失。 + */ +- (NSArray*)addLinks:(NSArray*)links; + +//下面俩是为了编写代码时候外部设置block时候不需要自定义名字了,方便。 +- (void)setDidClickLinkBlock:(void (^)(MLLink *link, NSString *linkText, MLLinkLabel *label))didClickLinkBlock; +- (void)setDidLongPressLinkBlock:(void (^)(MLLink *link, NSString *linkText, MLLinkLabel *label))didLongPressLinkBlock; + +/** + * 一般用在修改了某些link的样式属性之后效果不会立马启用,使用此方法可启用。 + */ +- (void)invalidateDisplayForLinks; + +@end + +@interface MLLink : NSObject + +@property (nonatomic, assign) MLLinkType linkType; +@property (nonatomic, copy) NSString *linkValue; +@property (readonly, nonatomic, assign) NSRange linkRange; + +//可以单独设置且覆盖label的三个参数 +@property (nonatomic, strong) NSDictionary *linkTextAttributes; +@property (nonatomic, strong) NSDictionary *activeLinkTextAttributes; + +//初始化 ++ (instancetype)linkWithType:(MLLinkType)type value:(NSString*)value range:(NSRange)range; ++ (instancetype)linkWithType:(MLLinkType)type value:(NSString*)value range:(NSRange)range linkTextAttributes:(NSDictionary*)linkTextAttributes activeLinkTextAttributes:(NSDictionary*)activeLinkTextAttributes; + +@property (nonatomic, copy) void(^didClickLinkBlock)(MLLink *link,NSString *linkText,MLLinkLabel *label); +@property (nonatomic, copy) void(^didLongPressLinkBlock)(MLLink *link,NSString *linkText,MLLinkLabel *label); + +//下面俩是为了编写代码时候外部设置block时候不需要自定义名字了,方便。 +- (void)setDidClickLinkBlock:(void (^)(MLLink *link, NSString *linkText, MLLinkLabel *label))didClickLinkBlock; +- (void)setDidLongPressLinkBlock:(void (^)(MLLink *link, NSString *linkText, MLLinkLabel *label))didLongPressLinkBlock; + +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/MLLinkLabel.m b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/MLLinkLabel.m new file mode 100644 index 0000000..a3e4097 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/Classes/MLLinkLabel.m @@ -0,0 +1,529 @@ +// +// MLLinkLabel.m +// MLLabel +// +// Created by molon on 15/6/6. +// Copyright (c) 2015年 molon. All rights reserved. +// + +#import "MLLinkLabel.h" +#import "MLLabel+Override.h" +#import "NSMutableAttributedString+MLLabel.h" +#import "MLLabelLayoutManager.h" + +#define REGULAREXPRESSION_OPTION(regularExpression,regex,option) \ +\ +static NSRegularExpression * k##regularExpression() { \ +static NSRegularExpression *_##regularExpression = nil; \ +static dispatch_once_t onceToken; \ +dispatch_once(&onceToken, ^{ \ +_##regularExpression = [[NSRegularExpression alloc] initWithPattern:(regex) options:(option) error:nil];\ +});\ +\ +return _##regularExpression;\ +}\ + +#define REGULAREXPRESSION(regularExpression,regex) REGULAREXPRESSION_OPTION(regularExpression,regex,NSRegularExpressionCaseInsensitive) + + +REGULAREXPRESSION(URLRegularExpression,@"((http[s]{0,1}|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)") +REGULAREXPRESSION(PhoneNumerRegularExpression, @"\\d{3}-\\d{8}|\\d{3}-\\d{7}|\\d{4}-\\d{8}|\\d{4}-\\d{7}|1+[358]+\\d{9}|\\d{8}|\\d{7}") +REGULAREXPRESSION(EmailRegularExpression, @"[A-Z0-9a-z\\._%+-]+@([A-Za-z0-9-]+\\.)+[A-Za-z]{2,4}") +REGULAREXPRESSION(UserHandleRegularExpression, @"@[\\u4e00-\\u9fa5\\w\\-]+") +REGULAREXPRESSION(HashtagRegularExpression, @"#([\\u4e00-\\u9fa5\\w\\-]+)") + +@interface MLLink() + +@property (nonatomic, assign) NSRange linkRange; + +@end + +@implementation MLLink + ++ (instancetype)linkWithType:(MLLinkType)type value:(NSString*)value range:(NSRange)range +{ + return [MLLink linkWithType:type value:value range:range linkTextAttributes:nil activeLinkTextAttributes:nil]; +} + ++ (instancetype)linkWithType:(MLLinkType)type value:(NSString*)value range:(NSRange)range linkTextAttributes:(NSDictionary*)linkTextAttributes activeLinkTextAttributes:(NSDictionary*)activeLinkTextAttributes +{ + MLLink *link = [MLLink new]; + link.linkType = type; + link.linkValue = value; + link.linkRange = range; + link.linkTextAttributes = linkTextAttributes; + link.activeLinkTextAttributes = activeLinkTextAttributes; + return link; +} + +@end + + +@interface MLLinkLabel() + +@property (nonatomic, strong) NSMutableArray *links; +@property (nonatomic, strong) MLLink *activeLink; +@property (nonatomic, assign) BOOL dontReCreateLinks; + +@property (nonatomic, strong) UILongPressGestureRecognizer *longPressGestureRecognizer; + +@end + +@implementation MLLinkLabel + +#pragma mark - getter +- (NSMutableArray *)links +{ + if (!_links) { + _links = [NSMutableArray array]; + } + return _links; +} + +- (UILongPressGestureRecognizer *)longPressGestureRecognizer +{ + if (!_longPressGestureRecognizer) { + _longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPressGestureDidFire:)]; + _longPressGestureRecognizer.delegate = self; + } + return _longPressGestureRecognizer; +} + + +#pragma mark - setter +- (void)setActiveLink:(MLLink *)activeLink +{ + BOOL isUnChanged = (!activeLink&&!_activeLink)||[activeLink isEqual:_activeLink]; + + _activeLink = activeLink; + + if (isUnChanged) { + return; + } + + [self reSetText]; + + [CATransaction flush]; +} + +- (void)setAllowLineBreakInsideLinks:(BOOL)allowLineBreakInsideLinks +{ + if (allowLineBreakInsideLinks==_allowLineBreakInsideLinks) return; + + _allowLineBreakInsideLinks = allowLineBreakInsideLinks; + [self reSetText]; +} + +- (void)setLinkTextAttributes:(NSDictionary *)linkTextAttributes +{ + _linkTextAttributes = linkTextAttributes; + [self reSetText]; +} + +- (void)setActiveLinkTextAttributes:(NSDictionary *)activeLinkTextAttributes +{ + _activeLinkTextAttributes = activeLinkTextAttributes; + [self reSetText]; +} + +- (void)setDataDetectorTypes:(MLDataDetectorTypes)dataDetectorTypes +{ + _dataDetectorTypes = dataDetectorTypes; + [super reSetText]; +} + +- (void)setDataDetectorTypesOfAttributedLinkValue:(MLDataDetectorTypes)dataDetectorTypesOfAttributedLinkValue +{ + _dataDetectorTypesOfAttributedLinkValue = dataDetectorTypesOfAttributedLinkValue; + [super reSetText]; +} + +#pragma mark - override +- (void)reSetText +{ + //标记不重新生成链接,因为修改label的样式,例如字体啊什么的,父类会调用reSetText方法。而这时候如果依然重新生成link的话,会引起addLink方式后添加的link丢失。 + //然后此类内部所有需要重新生成链接的都是调用[super reSetText],否则只是需要重绘的调用[self reSetText] + self.dontReCreateLinks = YES; + [super reSetText]; + self.dontReCreateLinks = NO; +} + +- (void)commonInit +{ + [super commonInit]; + + self.exclusiveTouch = YES; + self.userInteractionEnabled = YES; + + self.activeLinkToNilDelay = 0.3f; + + //默认除了话题和@都检测 + _dataDetectorTypes = MLDataDetectorTypeURL|MLDataDetectorTypePhoneNumber|MLDataDetectorTypeEmail|MLDataDetectorTypeAttributedLink; + _dataDetectorTypesOfAttributedLinkValue = MLDataDetectorTypeNone; + _allowLineBreakInsideLinks = YES; + + [self addGestureRecognizer:self.longPressGestureRecognizer]; +} + +- (void)setText:(NSString *)text +{ + //先提取出来links + if (!self.dontReCreateLinks) { + self.links = [self linksWithString:text]; + _activeLink = nil; //这里不能走setter + } + + [super setText:text]; +} + +- (void)setAttributedText:(NSAttributedString *)attributedText +{ + //先提取出来links + if (!self.dontReCreateLinks) { + self.links = [self linksWithString:attributedText]; + _activeLink = nil; //这里不能走setter + } + + [super setAttributedText:attributedText]; +} + +- (NSMutableAttributedString*)attributedTextForTextStorageFromLabelProperties +{ + NSMutableAttributedString *attributedString = [super attributedTextForTextStorageFromLabelProperties]; + + //默认的链接样式不是我们想要的,去除它 + [attributedString removeAttribute:NSLinkAttributeName range:NSMakeRange(0, attributedString.length)]; + + //检测是否有链接,有的话就直接给设置链接样式 + for (MLLink *link in self.links) { + NSDictionary *attributes = nil; + if ([link isEqual:self.activeLink]) { + attributes = link.activeLinkTextAttributes?link.activeLinkTextAttributes:self.activeLinkTextAttributes; + if (!attributes) { + attributes = @{NSForegroundColorAttributeName:kDefaultLinkColorForMLLinkLabel,NSBackgroundColorAttributeName:kDefaultActiveLinkBackgroundColorForMLLinkLabel}; + } + }else{ + attributes = link.linkTextAttributes?link.linkTextAttributes:self.linkTextAttributes; + if (!attributes) { + attributes = @{NSForegroundColorAttributeName:kDefaultLinkColorForMLLinkLabel}; + } + } + // [attributedString removeAttributes:[attributes allKeys] range:link.linkRange]; + [attributedString addAttributes:attributes range:link.linkRange]; + } + + return attributedString; + +} + +#pragma mark - 正则匹配相关 +static NSArray * kAllRegexps() { + static NSArray *_allRegexps = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _allRegexps = @[kURLRegularExpression(),kPhoneNumerRegularExpression(),kEmailRegularExpression(),kUserHandleRegularExpression(),kHashtagRegularExpression()]; + }); + return _allRegexps; +} + +- (NSArray*)regexpsWithDataDetectorTypes:(MLDataDetectorTypes)dataDetectorTypes +{ + MLDataDetectorTypes const allDataDetectorTypes[] = {MLDataDetectorTypeURL,MLDataDetectorTypePhoneNumber,MLDataDetectorTypeEmail,MLDataDetectorTypeUserHandle,MLDataDetectorTypeHashtag}; + NSArray *allRegexps = kAllRegexps(); + + NSMutableArray *regexps = [NSMutableArray array]; + for (NSInteger i=0; i0?regexps:nil; +} + +//根据dataDetectorTypes和string获取其linkType +- (MLLinkType)linkTypeOfString:(NSString*)string withDataDetectorTypes:(MLDataDetectorTypes)dataDetectorTypes +{ + if (dataDetectorTypes == MLDataDetectorTypeNone) { + return MLLinkTypeOther; + } + + NSArray *allRegexps = kAllRegexps(); + NSArray *regexps = [self regexpsWithDataDetectorTypes:dataDetectorTypes]; + + NSRange textRange = NSMakeRange(0, string.length); + for (NSRegularExpression *regexp in regexps) { + NSTextCheckingResult *result = [regexp firstMatchInString:string options:NSMatchingAnchored range:textRange]; + if (result&&NSEqualRanges(result.range, textRange)) { + //这个type确定 + MLLinkType linkType = [allRegexps indexOfObject:regexp]+1; + return linkType; + } + } + + return MLLinkTypeOther; +} + +- (NSMutableArray*)linksWithString:(id)string +{ + if (self.dataDetectorTypes == MLDataDetectorTypeNone||!string) { + return nil; + } + + NSString *plainText = [string isKindOfClass:[NSAttributedString class]]?((NSAttributedString*)string).string:string; + if (plainText.length<=0) { + return nil; + } + + NSMutableArray *links = [NSMutableArray array]; + + if ((self.dataDetectorTypes&MLDataDetectorTypeAttributedLink)&&[string isKindOfClass:[NSAttributedString class]]) { + NSAttributedString *attributedString = ((NSAttributedString*)string); + [attributedString enumerateAttribute:NSLinkAttributeName inRange:NSMakeRange(0, attributedString.length) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) { + if (value) { + NSString *linkValue = nil; + if ([value isKindOfClass:[NSURL class]]) { + linkValue = [value absoluteString]; + }else if ([value isKindOfClass:[NSString class]]) { + linkValue = value; + }else if ([value isKindOfClass:[NSAttributedString class]]) { + linkValue = [value string]; + } + if (linkValue.length>0) { + MLLink *link = [MLLink linkWithType:[self linkTypeOfString:linkValue withDataDetectorTypes:self.dataDetectorTypesOfAttributedLinkValue] value:linkValue range:range]; + if (self.beforeAddLinkBlock) { + self.beforeAddLinkBlock(link); + } + [links addObject:link]; + } + } + }]; + } + + NSArray *allRegexps = kAllRegexps(); + NSArray *regexps = [self regexpsWithDataDetectorTypes:self.dataDetectorTypes]; + NSRange textRange = NSMakeRange(0, plainText.length); + for (NSRegularExpression *regexp in regexps) { + [regexp enumerateMatchesInString:plainText options:0 range:textRange usingBlock:^(NSTextCheckingResult *result, __unused NSMatchingFlags flags, __unused BOOL *stop) { + //去重处理 + for (MLLink *link in links){ + if (NSMaxRange(NSIntersectionRange(link.linkRange, result.range))>0){ + return; + } + } + + //这个刚好和MLLinkType对应 + MLLinkType linkType = [allRegexps indexOfObject:regexp]+1; + + if (linkType!=MLLinkTypeNone) { + MLLink *link = [MLLink linkWithType:linkType value:[plainText substringWithRange:result.range] range:result.range]; + if (self.beforeAddLinkBlock) { + self.beforeAddLinkBlock(link); + } + [links addObject:link]; + } + }]; + } + + return links.count>0?links:nil; + +} + +#pragma mark - 链接点击交互相关 +- (MLLink *)linkAtPoint:(CGPoint)location +{ + if (self.links.count<=0||self.textStorage.string.length == 0||self.textContainer.size.width<=0||self.textContainer.size.height<=0) + { + return nil; + } + + CGPoint textOffset; + //在执行usedRectForTextContainer之前最好还是执行下glyphRangeForTextContainer relayout + [self.layoutManager glyphRangeForTextContainer:self.textContainer]; + textOffset = [self textOffsetWithTextSize:[self.layoutManager usedRectForTextContainer:self.textContainer].size]; + + //location转换成在textContainer的绘制区域的坐标 + location.x -= textOffset.x; + location.y -= textOffset.y; + + //获取触摸的字形 + NSUInteger glyphIdx = [self.layoutManager glyphIndexForPoint:location inTextContainer:self.textContainer]; + + //apple文档上写有说 如果location的区域没字形,可能返回的是最近的字形index,所以我们再找到这个字形所处于的rect来确认 + CGRect glyphRect = [self.layoutManager boundingRectForGlyphRange:NSMakeRange(glyphIdx, 1) + inTextContainer:self.textContainer]; + if (!CGRectContainsPoint(glyphRect, location)) + return nil; + + NSUInteger charIndex = [self.layoutManager characterIndexForGlyphAtIndex:glyphIdx]; + + //找到了charIndex,然后去寻找是否这个字处于链接内部 + for (MLLink *link in self.links) { + if (NSLocationInRange(charIndex,link.linkRange)) { + return link; + } + } + + return nil; +} + + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + UITouch *touch = [touches anyObject]; + + self.activeLink = [self linkAtPoint:[touch locationInView:self]]; + + //如果已经触发了链接,就不朝上传递消息了 + if (!self.activeLink) { + [super touchesBegan:touches withEvent:event]; + } +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + //如果当前位置和之前的active的不一样的话,就认为不选那个链接了 + if (self.activeLink) { + UITouch *touch = [touches anyObject]; + + if (![self.activeLink isEqual:[self linkAtPoint:[touch locationInView:self]]]) { + self.activeLink = nil; + } + } else { + [super touchesMoved:touches withEvent:event]; + } + +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + if (self.activeLink) { + NSString *linkText = [self.textStorage.string substringWithRange:self.activeLink.linkRange]; + + //告诉外面已经点击了某链接 + if (self.activeLink.didClickLinkBlock) { + self.activeLink.didClickLinkBlock(self.activeLink,linkText,self); + }else if (self.didClickLinkBlock) { + self.didClickLinkBlock(self.activeLink,linkText,self); + }else if(self.delegate&&[self.delegate respondsToSelector:@selector(didClickLink:linkText:linkLabel:)]){ + [self.delegate didClickLink:self.activeLink linkText:linkText linkLabel:self]; + } + + [self performSelector:@selector(setActiveLink:) withObject:nil afterDelay:self.activeLinkToNilDelay]; + } else { + [super touchesEnded:touches withEvent:event]; + } + +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + if (self.activeLink) { + self.activeLink = nil; + } else { + [super touchesCancelled:touches withEvent:event]; + } +} + +#pragma mark - 长按相关 +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { + MLLink *link = [self linkAtPoint:[touch locationInView:self]]; + if (link) { + //检测是否有长按回调,没的话就不继续 + if ((self.delegate&&[self.delegate respondsToSelector:@selector(didLongPressLink:linkText:linkLabel:)]) + ||self.didLongPressLinkBlock + ||link.didLongPressLinkBlock) { + return YES; + } + } + + return NO; +} + +- (void)longPressGestureDidFire:(UILongPressGestureRecognizer *)sender { + if (sender.state==UIGestureRecognizerStateBegan) { + MLLink *link = [self linkAtPoint:[sender locationInView:self]]; + if (link) { + NSString *linkText = [self.textStorage.string substringWithRange:link.linkRange]; + + //告诉外面已经长按了某链接 + if (link.didLongPressLinkBlock) { + link.didLongPressLinkBlock(link,linkText,self); + }else if (self.didLongPressLinkBlock) { + self.didLongPressLinkBlock(link,linkText,self); + }else if (self.delegate&&[self.delegate respondsToSelector:@selector(didLongPressLink:linkText:linkLabel:)]){ + [self.delegate didLongPressLink:link linkText:linkText linkLabel:self]; + } + } + } +} + + +#pragma mark - 外部调用相关 +- (BOOL)addLink:(MLLink*)link +{ + return [self addLinks:@[link]].count>0; +} + +- (MLLink*)addLinkWithType:(MLLinkType)type value:(NSString*)value range:(NSRange)range +{ + MLLink *link = [MLLink linkWithType:type value:value range:range]; + return [self addLink:link]?link:nil; +} + +- (NSArray*)addLinks:(NSArray*)links +{ + NSMutableArray *validLinks = [NSMutableArray arrayWithCapacity:links.count]; + for (MLLink *link in links) { + if (!link||NSMaxRange(link.linkRange)>self.textStorage.length) { + continue; + } + + //检测是否此位置已经有东西占用 + for (MLLink *aLink in self.links){ + if (NSMaxRange(NSIntersectionRange(aLink.linkRange, link.linkRange))>0){ + continue; + } + } + + if (self.beforeAddLinkBlock) { + self.beforeAddLinkBlock(link); + } + + //加入它 + [self.links addObject:link]; + + [validLinks addObject:link]; + } + + //重绘 + [self reSetText]; + + return validLinks; +} + +- (void)invalidateDisplayForLinks +{ + [self reSetText]; +} + +#pragma mark - 布局相关 +-(BOOL)layoutManager:(NSLayoutManager *)layoutManager shouldBreakLineByWordBeforeCharacterAtIndex:(NSUInteger)charIndex +{ + if (self.lineBreakMode == NSLineBreakByCharWrapping) { + return NO; + } + + if (self.allowLineBreakInsideLinks) { + return YES; + } + + //让在链接区间下,尽量不break + for (MLLink *link in self.links) { + if (NSLocationInRange(charIndex,link.linkRange)) { + return NO; + } + } + return YES; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/README.md b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/README.md new file mode 100644 index 0000000..c092410 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/MLLabel/README.md @@ -0,0 +1,10 @@ +# MLLabel +UILabel with TextKit. support for Link and custom Expression. (iOS 7+) + +- Long-press handling for links + +**My library does not seek any reward, +but if you use this library, tell me if you like. :)** + +![MLLabel](https://raw.githubusercontent.com/molon/MLLabel/master/snapshot1.png) +![MLLabel](https://raw.githubusercontent.com/molon/MLLabel/master/snapshot2.png) \ No newline at end of file diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/TTTAttributedLabel/TTTAttributedLabel.h b/SDAutoLayoutDemo/Vender/MLEmojiLabel/TTTAttributedLabel/TTTAttributedLabel.h new file mode 100755 index 0000000..7e9691e --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/TTTAttributedLabel/TTTAttributedLabel.h @@ -0,0 +1,698 @@ +// TTTAttributedLabel.h +// +// Copyright (c) 2011 Mattt Thompson (http://mattt.me) +// +// 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. + +#import +#import + +//! Project version number for TTTAttributedLabel. +FOUNDATION_EXPORT double TTTAttributedLabelVersionNumber; + +//! Project version string for TTTAttributedLabel. +FOUNDATION_EXPORT const unsigned char TTTAttributedLabelVersionString[]; + +@class TTTAttributedLabelLink; + +/** + Vertical alignment for text in a label whose bounds are larger than its text bounds + */ +typedef NS_ENUM(NSInteger, TTTAttributedLabelVerticalAlignment) { + TTTAttributedLabelVerticalAlignmentCenter = 0, + TTTAttributedLabelVerticalAlignmentTop = 1, + TTTAttributedLabelVerticalAlignmentBottom = 2, +}; + +/** + Determines whether the text to which this attribute applies has a strikeout drawn through itself. + */ +extern NSString * const kTTTStrikeOutAttributeName; + +/** + The background fill color. Value must be a `CGColorRef`. Default value is `nil` (no fill). + */ +extern NSString * const kTTTBackgroundFillColorAttributeName; + +/** + The padding for the background fill. Value must be a `UIEdgeInsets`. Default value is `UIEdgeInsetsZero` (no padding). + */ +extern NSString * const kTTTBackgroundFillPaddingAttributeName; + +/** + The background stroke color. Value must be a `CGColorRef`. Default value is `nil` (no stroke). + */ +extern NSString * const kTTTBackgroundStrokeColorAttributeName; + +/** + The background stroke line width. Value must be an `NSNumber`. Default value is `1.0f`. + */ +extern NSString * const kTTTBackgroundLineWidthAttributeName; + +/** + The background corner radius. Value must be an `NSNumber`. Default value is `5.0f`. + */ +extern NSString * const kTTTBackgroundCornerRadiusAttributeName; + +@protocol TTTAttributedLabelDelegate; + +// Override UILabel @property to accept both NSString and NSAttributedString +@protocol TTTAttributedLabel +@property (nonatomic, copy) IBInspectable id text; +@end + +IB_DESIGNABLE + +/** + `TTTAttributedLabel` is a drop-in replacement for `UILabel` that supports `NSAttributedString`, as well as automatically-detected and manually-added links to URLs, addresses, phone numbers, and dates. + + ## Differences Between `TTTAttributedLabel` and `UILabel` + + For the most part, `TTTAttributedLabel` behaves just like `UILabel`. The following are notable exceptions, in which `TTTAttributedLabel` may act differently: + + - `text` - This property now takes an `id` type argument, which can either be a kind of `NSString` or `NSAttributedString` (mutable or immutable in both cases) + - `attributedText` - Do not set this property directly. Instead, pass an `NSAttributedString` to `text`. + - `lineBreakMode` - This property displays only the first line when the value is `UILineBreakModeHeadTruncation`, `UILineBreakModeTailTruncation`, or `UILineBreakModeMiddleTruncation` + - `adjustsFontsizeToFitWidth` - Supported in iOS 5 and greater, this property is effective for any value of `numberOfLines` greater than zero. In iOS 4, setting `numberOfLines` to a value greater than 1 with `adjustsFontSizeToFitWidth` set to `YES` may cause `sizeToFit` to execute indefinitely. + - `baselineAdjustment` - This property has no affect. + - `textAlignment` - This property does not support justified alignment. + - `NSTextAttachment` - This string attribute is not supported. + + Any properties affecting text or paragraph styling, such as `firstLineIndent` will only apply when text is set with an `NSString`. If the text is set with an `NSAttributedString`, these properties will not apply. + + ### NSCoding + + `TTTAttributedLabel`, like `UILabel`, conforms to `NSCoding`. However, if the build target is set to less than iOS 6.0, `linkAttributes` and `activeLinkAttributes` will not be encoded or decoded. This is due to an runtime exception thrown when attempting to copy non-object CoreText values in dictionaries. + + @warning Any properties changed on the label after setting the text will not be reflected until a subsequent call to `setText:` or `setText:afterInheritingLabelAttributesAndConfiguringWithBlock:`. This is to say, order of operations matters in this case. For example, if the label text color is originally black when the text is set, changing the text color to red will have no effect on the display of the label until the text is set once again. + + @bug Setting `attributedText` directly is not recommended, as it may cause a crash when attempting to access any links previously set. Instead, call `setText:`, passing an `NSAttributedString`. + */ +@interface TTTAttributedLabel : UILabel + +/** + * The designated initializers are @c initWithFrame: and @c initWithCoder:. + * init will not properly initialize many required properties and other configuration. + */ +- (instancetype) init NS_UNAVAILABLE; + +///----------------------------- +/// @name Accessing the Delegate +///----------------------------- + +/** + The receiver's delegate. + + @discussion A `TTTAttributedLabel` delegate responds to messages sent by tapping on links in the label. You can use the delegate to respond to links referencing a URL, address, phone number, date, or date with a specified time zone and duration. + */ +@property (nonatomic, unsafe_unretained) IBOutlet id delegate; + +///-------------------------------------------- +/// @name Detecting, Accessing, & Styling Links +///-------------------------------------------- + +/** + @deprecated Use `enabledTextCheckingTypes` property instead. + */ +@property (nonatomic, assign) NSTextCheckingTypes dataDetectorTypes DEPRECATED_ATTRIBUTE; + +/** + A bitmask of `NSTextCheckingType` which are used to automatically detect links in the label text. + + @warning You must specify `enabledTextCheckingTypes` before setting the `text`, with either `setText:` or `setText:afterInheritingLabelAttributesAndConfiguringWithBlock:`. + */ +@property (nonatomic, assign) NSTextCheckingTypes enabledTextCheckingTypes; + +/** + An array of `NSTextCheckingResult` objects for links detected or manually added to the label text. + */ +@property (readonly, nonatomic, strong) NSArray *links; + +/** + A dictionary containing the default `NSAttributedString` attributes to be applied to links detected or manually added to the label text. The default link style is blue and underlined. + + @warning You must specify `linkAttributes` before setting autodecting or manually-adding links for these attributes to be applied. + */ +@property (nonatomic, strong) NSDictionary *linkAttributes; + +/** + A dictionary containing the default `NSAttributedString` attributes to be applied to links when they are in the active state. If `nil` or an empty `NSDictionary`, active links will not be styled. The default active link style is red and underlined. + */ +@property (nonatomic, strong) NSDictionary *activeLinkAttributes; + +/** + A dictionary containing the default `NSAttributedString` attributes to be applied to links when they are in the inactive state, which is triggered by a change in `tintColor` in iOS 7 and later. If `nil` or an empty `NSDictionary`, inactive links will not be styled. The default inactive link style is gray and unadorned. + */ +@property (nonatomic, strong) NSDictionary *inactiveLinkAttributes; + +/** + The edge inset for the background of a link. The default value is `{0, -1, 0, -1}`. + */ +@property (nonatomic, assign) UIEdgeInsets linkBackgroundEdgeInset; + +/** + Indicates if links will be detected within an extended area around the touch + to emulate the link detection behaviour of UIWebView. + Default value is YES. Disable to to improve performance on long labels. + */ +@property (nonatomic, assign) BOOL extendsLinkTouchArea; + +///--------------------------------------- +/// @name Acccessing Text Style Attributes +///--------------------------------------- + +/** + The shadow blur radius for the label. A value of 0 indicates no blur, while larger values produce correspondingly larger blurring. This value must not be negative. The default value is 0. + */ +@property (nonatomic, assign) IBInspectable CGFloat shadowRadius; + +/** + The shadow blur radius for the label when the label's `highlighted` property is `YES`. A value of 0 indicates no blur, while larger values produce correspondingly larger blurring. This value must not be negative. The default value is 0. + */ +@property (nonatomic, assign) IBInspectable CGFloat highlightedShadowRadius; +/** + The shadow offset for the label when the label's `highlighted` property is `YES`. A size of {0, 0} indicates no offset, with positive values extending down and to the right. The default size is {0, 0}. + */ +@property (nonatomic, assign) IBInspectable CGSize highlightedShadowOffset; +/** + The shadow color for the label when the label's `highlighted` property is `YES`. The default value is `nil` (no shadow color). + */ +@property (nonatomic, strong) IBInspectable UIColor *highlightedShadowColor; + +/** + The amount to kern the next character. Default is standard kerning. If this attribute is set to 0.0, no kerning is done at all. + */ +@property (nonatomic, assign) IBInspectable CGFloat kern; + +///-------------------------------------------- +/// @name Acccessing Paragraph Style Attributes +///-------------------------------------------- + +/** + The distance, in points, from the leading margin of a frame to the beginning of the paragraph's first line. This value is always nonnegative, and is 0.0 by default. + */ +@property (nonatomic, assign) IBInspectable CGFloat firstLineIndent; + +/** + @deprecated Use `lineSpacing` instead. + */ +@property (nonatomic, assign) IBInspectable CGFloat leading DEPRECATED_ATTRIBUTE; + +/** + The space in points added between lines within the paragraph. This value is always nonnegative and is 0.0 by default. + */ +@property (nonatomic, assign) IBInspectable CGFloat lineSpacing; + +/** + The minimum line height within the paragraph. If the value is 0.0, the minimum line height is set to the line height of the `font`. 0.0 by default. + */ +@property (nonatomic, assign) IBInspectable CGFloat minimumLineHeight; + +/** + The maximum line height within the paragraph. If the value is 0.0, the maximum line height is set to the line height of the `font`. 0.0 by default. + */ +@property (nonatomic, assign) IBInspectable CGFloat maximumLineHeight; + +/** + The line height multiple. This value is 1.0 by default. + */ +@property (nonatomic, assign) IBInspectable CGFloat lineHeightMultiple; + +/** + The distance, in points, from the margin to the text container. This value is `UIEdgeInsetsZero` by default. + + @discussion The `UIEdgeInset` members correspond to paragraph style properties rather than a particular geometry, and can change depending on the writing direction. + + ## `UIEdgeInset` Member Correspondence With `CTParagraphStyleSpecifier` Values: + + - `top`: `kCTParagraphStyleSpecifierParagraphSpacingBefore` + - `left`: `kCTParagraphStyleSpecifierHeadIndent` + - `bottom`: `kCTParagraphStyleSpecifierParagraphSpacing` + - `right`: `kCTParagraphStyleSpecifierTailIndent` + + */ +@property (nonatomic, assign) IBInspectable UIEdgeInsets textInsets; + +/** + The vertical text alignment for the label, for when the frame size is greater than the text rect size. The vertical alignment is `TTTAttributedLabelVerticalAlignmentCenter` by default. + */ +@property (nonatomic, assign) TTTAttributedLabelVerticalAlignment verticalAlignment; + +///-------------------------------------------- +/// @name Accessing Truncation Token Appearance +///-------------------------------------------- + +/** + @deprecated Use `attributedTruncationToken` instead. + */ +@property (nonatomic, strong) NSString *truncationTokenString DEPRECATED_ATTRIBUTE; + +/** + @deprecated Use `attributedTruncationToken` instead. + */ +@property (nonatomic, strong) NSDictionary *truncationTokenStringAttributes DEPRECATED_ATTRIBUTE; + +/** + The attributed string to apply to the truncation token at the end of a truncated line. Overrides `truncationTokenStringAttributes` and `truncationTokenString`. If unspecified, attributes will fallback to `truncationTokenStringAttributes` and `truncationTokenString`. + */ +@property (nonatomic, strong) IBInspectable NSAttributedString *attributedTruncationToken; + +///-------------------------- +/// @name Long press gestures +///-------------------------- + +/** + * The long-press gesture recognizer used internally by the label. + */ +@property (nonatomic, strong, readonly) UILongPressGestureRecognizer *longPressGestureRecognizer; + +///-------------------------------------------- +/// @name Calculating Size of Attributed String +///-------------------------------------------- + +/** + Calculate and return the size that best fits an attributed string, given the specified constraints on size and number of lines. + + @param attributedString The attributed string. + @param size The maximum dimensions used to calculate size. + @param numberOfLines The maximum number of lines in the text to draw, if the constraining size cannot accomodate the full attributed string. + + @return The size that fits the attributed string within the specified constraints. + */ ++ (CGSize)sizeThatFitsAttributedString:(NSAttributedString *)attributedString + withConstraints:(CGSize)size + limitedToNumberOfLines:(NSUInteger)numberOfLines; + +///---------------------------------- +/// @name Setting the Text Attributes +///---------------------------------- + +/** + Sets the text displayed by the label. + + @param text An `NSString` or `NSAttributedString` object to be displayed by the label. If the specified text is an `NSString`, the label will display the text like a `UILabel`, inheriting the text styles of the label. If the specified text is an `NSAttributedString`, the label text styles will be overridden by the styles specified in the attributed string. + + @discussion This method overrides `UILabel -setText:` to accept both `NSString` and `NSAttributedString` objects. This string is `nil` by default. + */ +- (void)setText:(id)text; + +/** + Sets the text displayed by the label, after configuring an attributed string containing the text attributes inherited from the label in a block. + + @param text An `NSString` or `NSAttributedString` object to be displayed by the label. + @param block A block object that returns an `NSMutableAttributedString` object and takes a single argument, which is an `NSMutableAttributedString` object with the text from the first parameter, and the text attributes inherited from the label text styles. For example, if you specified the `font` of the label to be `[UIFont boldSystemFontOfSize:14]` and `textColor` to be `[UIColor redColor]`, the `NSAttributedString` argument of the block would be contain the `NSAttributedString` attribute equivalents of those properties. In this block, you can set further attributes on particular ranges. + + @discussion This string is `nil` by default. + */ +- (void)setText:(id)text +afterInheritingLabelAttributesAndConfiguringWithBlock:(NSMutableAttributedString *(^)(NSMutableAttributedString *mutableAttributedString))block; + +///------------------------------------ +/// @name Accessing the Text Attributes +///------------------------------------ + +/** + A copy of the label's current attributedText. This returns `nil` if an attributed string has never been set on the label. + + @warning Do not set this property directly. Instead, set @c text to an @c NSAttributedString. + */ +@property (readwrite, nonatomic, copy) NSAttributedString *attributedText; + +///------------------- +/// @name Adding Links +///------------------- + +/** + Adds a link. You can customize an individual link's appearance and accessibility value by creating your own @c TTTAttributedLabelLink and passing it to this method. The other methods for adding links will use the label's default attributes. + + @warning Modifying the link's attribute dictionaries must be done before calling this method. + + @param link A @c TTTAttributedLabelLink object. + */ +- (void)addLink:(TTTAttributedLabelLink *)link; + +/** + Adds a link to an @c NSTextCheckingResult. + + @param result An @c NSTextCheckingResult representing the link's location and type. + + @return The newly added link object. + */ +- (TTTAttributedLabelLink *)addLinkWithTextCheckingResult:(NSTextCheckingResult *)result; + +/** + Adds a link to an @c NSTextCheckingResult. + + @param result An @c NSTextCheckingResult representing the link's location and type. + @param attributes The attributes to be added to the text in the range of the specified link. If set, the label's @c activeAttributes and @c inactiveAttributes will be applied to the link. If `nil`, no attributes are added to the link. + + @return The newly added link object. + */ +- (TTTAttributedLabelLink *)addLinkWithTextCheckingResult:(NSTextCheckingResult *)result + attributes:(NSDictionary *)attributes; + +/** + Adds a link to a URL for a specified range in the label text. + + @param url The url to be linked to + @param range The range in the label text of the link. The range must not exceed the bounds of the receiver. + + @return The newly added link object. + */ +- (TTTAttributedLabelLink *)addLinkToURL:(NSURL *)url + withRange:(NSRange)range; + +/** + Adds a link to an address for a specified range in the label text. + + @param addressComponents A dictionary of address components for the address to be linked to + @param range The range in the label text of the link. The range must not exceed the bounds of the receiver. + + @discussion The address component dictionary keys are described in `NSTextCheckingResult`'s "Keys for Address Components." + + @return The newly added link object. + */ +- (TTTAttributedLabelLink *)addLinkToAddress:(NSDictionary *)addressComponents + withRange:(NSRange)range; + +/** + Adds a link to a phone number for a specified range in the label text. + + @param phoneNumber The phone number to be linked to. + @param range The range in the label text of the link. The range must not exceed the bounds of the receiver. + + @return The newly added link object. + */ +- (TTTAttributedLabelLink *)addLinkToPhoneNumber:(NSString *)phoneNumber + withRange:(NSRange)range; + +/** + Adds a link to a date for a specified range in the label text. + + @param date The date to be linked to. + @param range The range in the label text of the link. The range must not exceed the bounds of the receiver. + + @return The newly added link object. + */ +- (TTTAttributedLabelLink *)addLinkToDate:(NSDate *)date + withRange:(NSRange)range; + +/** + Adds a link to a date with a particular time zone and duration for a specified range in the label text. + + @param date The date to be linked to. + @param timeZone The time zone of the specified date. + @param duration The duration, in seconds from the specified date. + @param range The range in the label text of the link. The range must not exceed the bounds of the receiver. + + @return The newly added link object. + */ +- (TTTAttributedLabelLink *)addLinkToDate:(NSDate *)date + timeZone:(NSTimeZone *)timeZone + duration:(NSTimeInterval)duration + withRange:(NSRange)range; + +/** + Adds a link to transit information for a specified range in the label text. + + @param components A dictionary containing the transit components. The currently supported keys are `NSTextCheckingAirlineKey` and `NSTextCheckingFlightKey`. + @param range The range in the label text of the link. The range must not exceed the bounds of the receiver. + + @return The newly added link object. + */ +- (TTTAttributedLabelLink *)addLinkToTransitInformation:(NSDictionary *)components + withRange:(NSRange)range; + +/** + Returns whether an @c NSTextCheckingResult is found at the give point. + + @discussion This can be used together with @c UITapGestureRecognizer to tap interactions with overlapping views. + + @param point The point inside the label. + */ +- (BOOL)containslinkAtPoint:(CGPoint)point; + +@end + +/** + The `TTTAttributedLabelDelegate` protocol defines the messages sent to an attributed label delegate when links are tapped. All of the methods of this protocol are optional. + */ +@protocol TTTAttributedLabelDelegate + +///----------------------------------- +/// @name Responding to Link Selection +///----------------------------------- +@optional + +/** + Tells the delegate that the user did select a link to a URL. + + @param label The label whose link was selected. + @param url The URL for the selected link. + */ +- (void)attributedLabel:(TTTAttributedLabel *)label + didSelectLinkWithURL:(NSURL *)url; + +/** + Tells the delegate that the user did select a link to an address. + + @param label The label whose link was selected. + @param addressComponents The components of the address for the selected link. + */ +- (void)attributedLabel:(TTTAttributedLabel *)label +didSelectLinkWithAddress:(NSDictionary *)addressComponents; + +/** + Tells the delegate that the user did select a link to a phone number. + + @param label The label whose link was selected. + @param phoneNumber The phone number for the selected link. + */ +- (void)attributedLabel:(TTTAttributedLabel *)label +didSelectLinkWithPhoneNumber:(NSString *)phoneNumber; + +/** + Tells the delegate that the user did select a link to a date. + + @param label The label whose link was selected. + @param date The datefor the selected link. + */ +- (void)attributedLabel:(TTTAttributedLabel *)label + didSelectLinkWithDate:(NSDate *)date; + +/** + Tells the delegate that the user did select a link to a date with a time zone and duration. + + @param label The label whose link was selected. + @param date The date for the selected link. + @param timeZone The time zone of the date for the selected link. + @param duration The duration, in seconds from the date for the selected link. + */ +- (void)attributedLabel:(TTTAttributedLabel *)label + didSelectLinkWithDate:(NSDate *)date + timeZone:(NSTimeZone *)timeZone + duration:(NSTimeInterval)duration; + +/** + Tells the delegate that the user did select a link to transit information + + @param label The label whose link was selected. + @param components A dictionary containing the transit components. The currently supported keys are `NSTextCheckingAirlineKey` and `NSTextCheckingFlightKey`. + */ +- (void)attributedLabel:(TTTAttributedLabel *)label +didSelectLinkWithTransitInformation:(NSDictionary *)components; + +/** + Tells the delegate that the user did select a link to a text checking result. + + @discussion This method is called if no other delegate method was called, which can occur by either now implementing the method in `TTTAttributedLabelDelegate` corresponding to a particular link, or the link was added by passing an instance of a custom `NSTextCheckingResult` subclass into `-addLinkWithTextCheckingResult:`. + + @param label The label whose link was selected. + @param result The custom text checking result. + */ +- (void)attributedLabel:(TTTAttributedLabel *)label +didSelectLinkWithTextCheckingResult:(NSTextCheckingResult *)result; + +///--------------------------------- +/// @name Responding to Long Presses +///--------------------------------- + +/** + * Long-press delegate methods include the CGPoint tapped within the label's coordinate space. + * This may be useful on iPad to present a popover from a specific origin point. + */ + +/** + Tells the delegate that the user long-pressed a link to a URL. + + @param label The label whose link was long pressed. + @param url The URL for the link. + @param point the point pressed, in the label's coordinate space + */ +- (void)attributedLabel:(TTTAttributedLabel *)label +didLongPressLinkWithURL:(NSURL *)url + atPoint:(CGPoint)point; + +/** + Tells the delegate that the user long-pressed a link to an address. + + @param label The label whose link was long pressed. + @param addressComponents The components of the address for the link. + @param point the point pressed, in the label's coordinate space + */ +- (void)attributedLabel:(TTTAttributedLabel *)label +didLongPressLinkWithAddress:(NSDictionary *)addressComponents + atPoint:(CGPoint)point; + +/** + Tells the delegate that the user long-pressed a link to a phone number. + + @param label The label whose link was long pressed. + @param phoneNumber The phone number for the link. + @param point the point pressed, in the label's coordinate space + */ +- (void)attributedLabel:(TTTAttributedLabel *)label +didLongPressLinkWithPhoneNumber:(NSString *)phoneNumber + atPoint:(CGPoint)point; + + +/** + Tells the delegate that the user long-pressed a link to a date. + + @param label The label whose link was long pressed. + @param date The date for the selected link. + @param point the point pressed, in the label's coordinate space + */ +- (void)attributedLabel:(TTTAttributedLabel *)label +didLongPressLinkWithDate:(NSDate *)date + atPoint:(CGPoint)point; + + +/** + Tells the delegate that the user long-pressed a link to a date with a time zone and duration. + + @param label The label whose link was long pressed. + @param date The date for the link. + @param timeZone The time zone of the date for the link. + @param duration The duration, in seconds from the date for the link. + @param point the point pressed, in the label's coordinate space + */ +- (void)attributedLabel:(TTTAttributedLabel *)label +didLongPressLinkWithDate:(NSDate *)date + timeZone:(NSTimeZone *)timeZone + duration:(NSTimeInterval)duration + atPoint:(CGPoint)point; + + +/** + Tells the delegate that the user long-pressed a link to transit information. + + @param label The label whose link was long pressed. + @param components A dictionary containing the transit components. The currently supported keys are `NSTextCheckingAirlineKey` and `NSTextCheckingFlightKey`. + @param point the point pressed, in the label's coordinate space + */ +- (void)attributedLabel:(TTTAttributedLabel *)label +didLongPressLinkWithTransitInformation:(NSDictionary *)components + atPoint:(CGPoint)point; + +/** + Tells the delegate that the user long-pressed a link to a text checking result. + + @discussion Similar to `-attributedLabel:didSelectLinkWithTextCheckingResult:`, this method is called if a link is long pressed and the delegate does not implement the method corresponding to this type of link. + + @param label The label whose link was long pressed. + @param result The custom text checking result. + @param point the point pressed, in the label's coordinate space + */ +- (void)attributedLabel:(TTTAttributedLabel *)label +didLongPressLinkWithTextCheckingResult:(NSTextCheckingResult *)result + atPoint:(CGPoint)point; + +@end + +@interface TTTAttributedLabelLink : NSObject + +typedef void (^TTTAttributedLabelLinkBlock) (TTTAttributedLabel *, TTTAttributedLabelLink *); + +/** + An `NSTextCheckingResult` representing the link's location and type. + */ +@property (readonly, nonatomic, strong) NSTextCheckingResult *result; + +/** + A dictionary containing the @c NSAttributedString attributes to be applied to the link. + */ +@property (readonly, nonatomic, copy) NSDictionary *attributes; + +/** + A dictionary containing the @c NSAttributedString attributes to be applied to the link when it is in the active state. + */ +@property (readonly, nonatomic, copy) NSDictionary *activeAttributes; + +/** + A dictionary containing the @c NSAttributedString attributes to be applied to the link when it is in the inactive state, which is triggered by a change in `tintColor` in iOS 7 and later. + */ +@property (readonly, nonatomic, copy) NSDictionary *inactiveAttributes; + +/** + Additional information about a link for VoiceOver users. Has default values if the link's @c result is @c NSTextCheckingTypeLink, @c NSTextCheckingTypePhoneNumber, or @c NSTextCheckingTypeDate. + */ +@property (nonatomic, copy) NSString *accessibilityValue; + +/** + A block called when this link is tapped. + If non-nil, tapping on this link will call this block instead of the + @c TTTAttributedLabelDelegate tap methods, which will not be called for this link. + */ +@property (nonatomic, copy) TTTAttributedLabelLinkBlock linkTapBlock; + +/** + A block called when this link is long-pressed. + If non-nil, long pressing on this link will call this block instead of the + @c TTTAttributedLabelDelegate long press methods, which will not be called for this link. + */ +@property (nonatomic, copy) TTTAttributedLabelLinkBlock linkLongPressBlock; + +/** + Initializes a link using the attribute dictionaries specified. + + @param attributes The @c attributes property for the link. + @param activeAttributes The @c activeAttributes property for the link. + @param inactiveAttributes The @c inactiveAttributes property for the link. + @param result An @c NSTextCheckingResult representing the link's location and type. + + @return The initialized link object. + */ +- (instancetype)initWithAttributes:(NSDictionary *)attributes + activeAttributes:(NSDictionary *)activeAttributes + inactiveAttributes:(NSDictionary *)inactiveAttributes + textCheckingResult:(NSTextCheckingResult *)result; + +/** + Initializes a link using the attribute dictionaries set on a specified label. + + @param label The attributed label from which to inherit attribute dictionaries. + @param result An @c NSTextCheckingResult representing the link's location and type. + + @return The initialized link object. + */ +- (instancetype)initWithAttributesFromLabel:(TTTAttributedLabel*)label + textCheckingResult:(NSTextCheckingResult *)result; + +@end diff --git a/SDAutoLayoutDemo/Vender/MLEmojiLabel/TTTAttributedLabel/TTTAttributedLabel.m b/SDAutoLayoutDemo/Vender/MLEmojiLabel/TTTAttributedLabel/TTTAttributedLabel.m new file mode 100755 index 0000000..264a0c3 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/MLEmojiLabel/TTTAttributedLabel/TTTAttributedLabel.m @@ -0,0 +1,1887 @@ +// TTTAttributedLabel.m +// +// Copyright (c) 2011 Mattt Thompson (http://mattt.me) +// +// 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. + +#import "TTTAttributedLabel.h" + +#import +#import +#import + +#define kTTTLineBreakWordWrapTextWidthScalingFactor (M_PI / M_E) + +static CGFloat const TTTFLOAT_MAX = 100000; + +NSString * const kTTTStrikeOutAttributeName = @"TTTStrikeOutAttribute"; +NSString * const kTTTBackgroundFillColorAttributeName = @"TTTBackgroundFillColor"; +NSString * const kTTTBackgroundFillPaddingAttributeName = @"TTTBackgroundFillPadding"; +NSString * const kTTTBackgroundStrokeColorAttributeName = @"TTTBackgroundStrokeColor"; +NSString * const kTTTBackgroundLineWidthAttributeName = @"TTTBackgroundLineWidth"; +NSString * const kTTTBackgroundCornerRadiusAttributeName = @"TTTBackgroundCornerRadius"; + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000 +const NSTextAlignment TTTTextAlignmentLeft = NSTextAlignmentLeft; +const NSTextAlignment TTTTextAlignmentCenter = NSTextAlignmentCenter; +const NSTextAlignment TTTTextAlignmentRight = NSTextAlignmentRight; +const NSTextAlignment TTTTextAlignmentJustified = NSTextAlignmentJustified; +const NSTextAlignment TTTTextAlignmentNatural = NSTextAlignmentNatural; + +const NSLineBreakMode TTTLineBreakByWordWrapping = NSLineBreakByWordWrapping; +const NSLineBreakMode TTTLineBreakByCharWrapping = NSLineBreakByCharWrapping; +const NSLineBreakMode TTTLineBreakByClipping = NSLineBreakByClipping; +const NSLineBreakMode TTTLineBreakByTruncatingHead = NSLineBreakByTruncatingHead; +const NSLineBreakMode TTTLineBreakByTruncatingMiddle = NSLineBreakByTruncatingMiddle; +const NSLineBreakMode TTTLineBreakByTruncatingTail = NSLineBreakByTruncatingTail; + +typedef NSTextAlignment TTTTextAlignment; +typedef NSLineBreakMode TTTLineBreakMode; +#else +const UITextAlignment TTTTextAlignmentLeft = NSTextAlignmentLeft; +const UITextAlignment TTTTextAlignmentCenter = NSTextAlignmentCenter; +const UITextAlignment TTTTextAlignmentRight = NSTextAlignmentRight; +const UITextAlignment TTTTextAlignmentJustified = NSTextAlignmentJustified; +const UITextAlignment TTTTextAlignmentNatural = NSTextAlignmentNatural; + +const UITextAlignment TTTLineBreakByWordWrapping = NSLineBreakByWordWrapping; +const UITextAlignment TTTLineBreakByCharWrapping = NSLineBreakByCharWrapping; +const UITextAlignment TTTLineBreakByClipping = NSLineBreakByClipping; +const UITextAlignment TTTLineBreakByTruncatingHead = NSLineBreakByTruncatingHead; +const UITextAlignment TTTLineBreakByTruncatingMiddle = NSLineBreakByTruncatingMiddle; +const UITextAlignment TTTLineBreakByTruncatingTail = NSLineBreakByTruncatingTail; + +typedef UITextAlignment TTTTextAlignment; +typedef UILineBreakMode TTTLineBreakMode; +#endif + + +static inline CTTextAlignment CTTextAlignmentFromTTTTextAlignment(TTTTextAlignment alignment) { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000 + switch (alignment) { + case NSTextAlignmentLeft: return kCTLeftTextAlignment; + case NSTextAlignmentCenter: return kCTCenterTextAlignment; + case NSTextAlignmentRight: return kCTRightTextAlignment; + default: return kCTNaturalTextAlignment; + } +#else + switch (alignment) { + case UITextAlignmentLeft: return kCTLeftTextAlignment; + case UITextAlignmentCenter: return kCTCenterTextAlignment; + case UITextAlignmentRight: return kCTRightTextAlignment; + default: return kCTNaturalTextAlignment; + } +#endif +} + +static inline CTLineBreakMode CTLineBreakModeFromTTTLineBreakMode(TTTLineBreakMode lineBreakMode) { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000 + switch (lineBreakMode) { + case NSLineBreakByWordWrapping: return kCTLineBreakByWordWrapping; + case NSLineBreakByCharWrapping: return kCTLineBreakByCharWrapping; + case NSLineBreakByClipping: return kCTLineBreakByClipping; + case NSLineBreakByTruncatingHead: return kCTLineBreakByTruncatingHead; + case NSLineBreakByTruncatingTail: return kCTLineBreakByTruncatingTail; + case NSLineBreakByTruncatingMiddle: return kCTLineBreakByTruncatingMiddle; + default: return 0; + } +#else + return CTLineBreakModeFromUILineBreakMode(lineBreakMode); +#endif +} + +#if __IPHONE_OS_VERSION_MAX_ALLOWED < 60000 +static inline CTLineBreakMode CTLineBreakModeFromUILineBreakMode(UILineBreakMode lineBreakMode) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + switch (lineBreakMode) { + case UILineBreakModeWordWrap: return kCTLineBreakByWordWrapping; + case UILineBreakModeCharacterWrap: return kCTLineBreakByCharWrapping; + case UILineBreakModeClip: return kCTLineBreakByClipping; + case UILineBreakModeHeadTruncation: return kCTLineBreakByTruncatingHead; + case UILineBreakModeTailTruncation: return kCTLineBreakByTruncatingTail; + case UILineBreakModeMiddleTruncation: return kCTLineBreakByTruncatingMiddle; + default: return 0; + } +#pragma clang diagnostic pop +} +#endif + +static inline CGFLOAT_TYPE CGFloat_ceil(CGFLOAT_TYPE cgfloat) { +#if CGFLOAT_IS_DOUBLE + return ceil(cgfloat); +#else + return ceilf(cgfloat); +#endif +} + +static inline CGFLOAT_TYPE CGFloat_floor(CGFLOAT_TYPE cgfloat) { +#if CGFLOAT_IS_DOUBLE + return floor(cgfloat); +#else + return floorf(cgfloat); +#endif +} + +static inline CGFLOAT_TYPE CGFloat_round(CGFLOAT_TYPE cgfloat) { +#if CGFLOAT_IS_DOUBLE + return round(cgfloat); +#else + return roundf(cgfloat); +#endif +} + +static inline CGFLOAT_TYPE CGFloat_sqrt(CGFLOAT_TYPE cgfloat) { +#if CGFLOAT_IS_DOUBLE + return sqrt(cgfloat); +#else + return sqrtf(cgfloat); +#endif +} + +static inline CGFloat TTTFlushFactorForTextAlignment(NSTextAlignment textAlignment) { + switch (textAlignment) { + case TTTTextAlignmentCenter: + return 0.5f; + case TTTTextAlignmentRight: + return 1.0f; + case TTTTextAlignmentLeft: + default: + return 0.0f; + } +} + +static inline NSDictionary * NSAttributedStringAttributesFromLabel(TTTAttributedLabel *label) { + NSMutableDictionary *mutableAttributes = [NSMutableDictionary dictionary]; + + if ([NSMutableParagraphStyle class]) { + [mutableAttributes setObject:label.font forKey:(NSString *)kCTFontAttributeName]; + [mutableAttributes setObject:label.textColor forKey:(NSString *)kCTForegroundColorAttributeName]; + [mutableAttributes setObject:@(label.kern) forKey:(NSString *)kCTKernAttributeName]; + + NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; + paragraphStyle.alignment = label.textAlignment; + paragraphStyle.lineSpacing = label.lineSpacing; + paragraphStyle.minimumLineHeight = label.minimumLineHeight > 0 ? label.minimumLineHeight : label.font.lineHeight * label.lineHeightMultiple; + paragraphStyle.maximumLineHeight = label.maximumLineHeight > 0 ? label.maximumLineHeight : label.font.lineHeight * label.lineHeightMultiple; + paragraphStyle.lineHeightMultiple = label.lineHeightMultiple; + paragraphStyle.firstLineHeadIndent = label.firstLineIndent; + + if (label.numberOfLines == 1) { + paragraphStyle.lineBreakMode = label.lineBreakMode; + } else { + paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping; + } + + [mutableAttributes setObject:paragraphStyle forKey:(NSString *)kCTParagraphStyleAttributeName]; + } else { + CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)label.font.fontName, label.font.pointSize, NULL); + [mutableAttributes setObject:(__bridge id)font forKey:(NSString *)kCTFontAttributeName]; + CFRelease(font); + + [mutableAttributes setObject:(id)[label.textColor CGColor] forKey:(NSString *)kCTForegroundColorAttributeName]; + [mutableAttributes setObject:@(label.kern) forKey:(NSString *)kCTKernAttributeName]; + + CTTextAlignment alignment = CTTextAlignmentFromTTTTextAlignment(label.textAlignment); + CGFloat lineSpacing = label.lineSpacing; + CGFloat minimumLineHeight = label.minimumLineHeight * label.lineHeightMultiple; + CGFloat maximumLineHeight = label.maximumLineHeight * label.lineHeightMultiple; + CGFloat lineSpacingAdjustment = CGFloat_ceil(label.font.lineHeight - label.font.ascender + label.font.descender); + CGFloat lineHeightMultiple = label.lineHeightMultiple; + CGFloat firstLineIndent = label.firstLineIndent; + + CTLineBreakMode lineBreakMode = kCTLineBreakByWordWrapping; + if (label.numberOfLines == 1) { + lineBreakMode = CTLineBreakModeFromTTTLineBreakMode(label.lineBreakMode); + } + + CTParagraphStyleSetting paragraphStyles[12] = { + {.spec = kCTParagraphStyleSpecifierAlignment, .valueSize = sizeof(CTTextAlignment), .value = (const void *)&alignment}, + {.spec = kCTParagraphStyleSpecifierLineBreakMode, .valueSize = sizeof(CTLineBreakMode), .value = (const void *)&lineBreakMode}, + {.spec = kCTParagraphStyleSpecifierLineSpacing, .valueSize = sizeof(CGFloat), .value = (const void *)&lineSpacing}, + {.spec = kCTParagraphStyleSpecifierMinimumLineSpacing, .valueSize = sizeof(CGFloat), .value = (const void *)&minimumLineHeight}, + {.spec = kCTParagraphStyleSpecifierMaximumLineSpacing, .valueSize = sizeof(CGFloat), .value = (const void *)&maximumLineHeight}, + {.spec = kCTParagraphStyleSpecifierLineSpacingAdjustment, .valueSize = sizeof (CGFloat), .value = (const void *)&lineSpacingAdjustment}, + {.spec = kCTParagraphStyleSpecifierLineHeightMultiple, .valueSize = sizeof(CGFloat), .value = (const void *)&lineHeightMultiple}, + {.spec = kCTParagraphStyleSpecifierFirstLineHeadIndent, .valueSize = sizeof(CGFloat), .value = (const void *)&firstLineIndent}, + }; + + CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(paragraphStyles, 12); + + [mutableAttributes setObject:(__bridge id)paragraphStyle forKey:(NSString *)kCTParagraphStyleAttributeName]; + + CFRelease(paragraphStyle); + } + + return [NSDictionary dictionaryWithDictionary:mutableAttributes]; +} + +static inline NSAttributedString * NSAttributedStringByScalingFontSize(NSAttributedString *attributedString, CGFloat scale) { + NSMutableAttributedString *mutableAttributedString = [attributedString mutableCopy]; + [mutableAttributedString enumerateAttribute:(NSString *)kCTFontAttributeName inRange:NSMakeRange(0, [mutableAttributedString length]) options:0 usingBlock:^(id value, NSRange range, BOOL * __unused stop) { + UIFont *font = (UIFont *)value; + if (font) { + NSString *fontName; + CGFloat pointSize; + + if ([font isKindOfClass:[UIFont class]]) { + fontName = font.fontName; + pointSize = font.pointSize; + } else { + fontName = (NSString *)CFBridgingRelease(CTFontCopyName((__bridge CTFontRef)font, kCTFontPostScriptNameKey)); + pointSize = CTFontGetSize((__bridge CTFontRef)font); + } + + [mutableAttributedString removeAttribute:(NSString *)kCTFontAttributeName range:range]; + CTFontRef fontRef = CTFontCreateWithName((__bridge CFStringRef)fontName, CGFloat_floor(pointSize * scale), NULL); + [mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)fontRef range:range]; + CFRelease(fontRef); + } + }]; + + return mutableAttributedString; +} + +static inline NSAttributedString * NSAttributedStringBySettingColorFromContext(NSAttributedString *attributedString, UIColor *color) { + if (!color) { + return attributedString; + } + + NSMutableAttributedString *mutableAttributedString = [attributedString mutableCopy]; + [mutableAttributedString enumerateAttribute:(NSString *)kCTForegroundColorFromContextAttributeName inRange:NSMakeRange(0, [mutableAttributedString length]) options:0 usingBlock:^(id value, NSRange range, __unused BOOL *stop) { + BOOL usesColorFromContext = (BOOL)value; + if (usesColorFromContext) { + [mutableAttributedString setAttributes:[NSDictionary dictionaryWithObject:color forKey:(NSString *)kCTForegroundColorAttributeName] range:range]; + [mutableAttributedString removeAttribute:(NSString *)kCTForegroundColorFromContextAttributeName range:range]; + } + }]; + + return mutableAttributedString; +} + +static inline CGSize CTFramesetterSuggestFrameSizeForAttributedStringWithConstraints(CTFramesetterRef framesetter, NSAttributedString *attributedString, CGSize size, NSUInteger numberOfLines) { + CFRange rangeToSize = CFRangeMake(0, (CFIndex)[attributedString length]); + CGSize constraints = CGSizeMake(size.width, TTTFLOAT_MAX); + + if (numberOfLines == 1) { + // If there is one line, the size that fits is the full width of the line + constraints = CGSizeMake(TTTFLOAT_MAX, TTTFLOAT_MAX); + } else if (numberOfLines > 0) { + // If the line count of the label more than 1, limit the range to size to the number of lines that have been set + CGMutablePathRef path = CGPathCreateMutable(); + CGPathAddRect(path, NULL, CGRectMake(0.0f, 0.0f, constraints.width, TTTFLOAT_MAX)); + CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL); + CFArrayRef lines = CTFrameGetLines(frame); + + if (CFArrayGetCount(lines) > 0) { + NSInteger lastVisibleLineIndex = MIN((CFIndex)numberOfLines, CFArrayGetCount(lines)) - 1; + CTLineRef lastVisibleLine = CFArrayGetValueAtIndex(lines, lastVisibleLineIndex); + + CFRange rangeToLayout = CTLineGetStringRange(lastVisibleLine); + rangeToSize = CFRangeMake(0, rangeToLayout.location + rangeToLayout.length); + } + + CFRelease(frame); + CFRelease(path); + } + + CGSize suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, rangeToSize, NULL, constraints, NULL); + + return CGSizeMake(CGFloat_ceil(suggestedSize.width), CGFloat_ceil(suggestedSize.height)); +} + +@interface TTTAttributedLabel () +@property (readwrite, nonatomic, copy) NSAttributedString *inactiveAttributedText; +@property (readwrite, nonatomic, copy) NSAttributedString *renderedAttributedText; +@property (readwrite, atomic, strong) NSDataDetector *dataDetector; +@property (readwrite, nonatomic, strong) NSArray *linkModels; +@property (readwrite, nonatomic, strong) TTTAttributedLabelLink *activeLink; +@property (readwrite, nonatomic, strong) NSArray *accessibilityElements; + +- (void) longPressGestureDidFire:(UILongPressGestureRecognizer *)sender; +@end + +@implementation TTTAttributedLabel { +@private + BOOL _needsFramesetter; + CTFramesetterRef _framesetter; + CTFramesetterRef _highlightFramesetter; +} + +@dynamic text; +@synthesize attributedText = _attributedText; + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 + +#ifndef kCFCoreFoundationVersionNumber_iOS_7_0 +#define kCFCoreFoundationVersionNumber_iOS_7_0 847.2 +#endif + ++ (void)load { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0) { + Class class = [self class]; + Class superclass = class_getSuperclass(class); + + NSArray *strings = @[ + NSStringFromSelector(@selector(isAccessibilityElement)), + NSStringFromSelector(@selector(accessibilityElementCount)), + NSStringFromSelector(@selector(accessibilityElementAtIndex:)), + NSStringFromSelector(@selector(indexOfAccessibilityElement:)), + ]; + + for (NSString *string in strings) { + SEL selector = NSSelectorFromString(string); + IMP superImplementation = class_getMethodImplementation(superclass, selector); + Method method = class_getInstanceMethod(class, selector); + const char *types = method_getTypeEncoding(method); + class_replaceMethod(class, selector, superImplementation, types); + } + } + }); +} +#endif + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (!self) { + return nil; + } + + [self commonInit]; + + return self; +} + +- (void)commonInit { + self.userInteractionEnabled = YES; + self.multipleTouchEnabled = NO; + + self.textInsets = UIEdgeInsetsZero; + self.lineHeightMultiple = 1.0f; + + self.linkModels = [NSArray array]; + + self.linkBackgroundEdgeInset = UIEdgeInsetsMake(0.0f, -1.0f, 0.0f, -1.0f); + + NSMutableDictionary *mutableLinkAttributes = [NSMutableDictionary dictionary]; + [mutableLinkAttributes setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)kCTUnderlineStyleAttributeName]; + + NSMutableDictionary *mutableActiveLinkAttributes = [NSMutableDictionary dictionary]; + [mutableActiveLinkAttributes setObject:[NSNumber numberWithBool:NO] forKey:(NSString *)kCTUnderlineStyleAttributeName]; + + NSMutableDictionary *mutableInactiveLinkAttributes = [NSMutableDictionary dictionary]; + [mutableInactiveLinkAttributes setObject:[NSNumber numberWithBool:NO] forKey:(NSString *)kCTUnderlineStyleAttributeName]; + + if ([NSMutableParagraphStyle class]) { + [mutableLinkAttributes setObject:[UIColor blueColor] forKey:(NSString *)kCTForegroundColorAttributeName]; + [mutableActiveLinkAttributes setObject:[UIColor redColor] forKey:(NSString *)kCTForegroundColorAttributeName]; + [mutableInactiveLinkAttributes setObject:[UIColor grayColor] forKey:(NSString *)kCTForegroundColorAttributeName]; + } else { + [mutableLinkAttributes setObject:(__bridge id)[[UIColor blueColor] CGColor] forKey:(NSString *)kCTForegroundColorAttributeName]; + [mutableActiveLinkAttributes setObject:(__bridge id)[[UIColor redColor] CGColor] forKey:(NSString *)kCTForegroundColorAttributeName]; + [mutableInactiveLinkAttributes setObject:(__bridge id)[[UIColor grayColor] CGColor] forKey:(NSString *)kCTForegroundColorAttributeName]; + } + + self.linkAttributes = [NSDictionary dictionaryWithDictionary:mutableLinkAttributes]; + self.activeLinkAttributes = [NSDictionary dictionaryWithDictionary:mutableActiveLinkAttributes]; + self.inactiveLinkAttributes = [NSDictionary dictionaryWithDictionary:mutableInactiveLinkAttributes]; + _extendsLinkTouchArea = YES; + _longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self + action:@selector(longPressGestureDidFire:)]; + self.longPressGestureRecognizer.delegate = self; + [self addGestureRecognizer:self.longPressGestureRecognizer]; +} + +- (void)dealloc { + if (_framesetter) { + CFRelease(_framesetter); + } + + if (_highlightFramesetter) { + CFRelease(_highlightFramesetter); + } + + if (_longPressGestureRecognizer) { + [self removeGestureRecognizer:_longPressGestureRecognizer]; + } +} + +#pragma mark - + ++ (CGSize)sizeThatFitsAttributedString:(NSAttributedString *)attributedString + withConstraints:(CGSize)size + limitedToNumberOfLines:(NSUInteger)numberOfLines +{ + if (!attributedString || attributedString.length == 0) { + return CGSizeZero; + } + + CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)attributedString); + + CGSize calculatedSize = CTFramesetterSuggestFrameSizeForAttributedStringWithConstraints(framesetter, attributedString, size, numberOfLines); + + CFRelease(framesetter); + + return calculatedSize; +} + +#pragma mark - + +- (void)setAttributedText:(NSAttributedString *)text { + if ([text isEqualToAttributedString:_attributedText]) { + return; + } + + _attributedText = [text copy]; + + [self setNeedsFramesetter]; + [self setNeedsDisplay]; + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000 + if ([self respondsToSelector:@selector(invalidateIntrinsicContentSize)]) { + [self invalidateIntrinsicContentSize]; + } +#endif + + [super setText:[self.attributedText string]]; +} + +- (NSAttributedString *)renderedAttributedText { + if (!_renderedAttributedText) { + self.renderedAttributedText = NSAttributedStringBySettingColorFromContext(self.attributedText, self.textColor); + } + + return _renderedAttributedText; +} + +- (NSArray *) links { + return [_linkModels valueForKey:@"result"]; +} + +- (void)setLinkModels:(NSArray *)linkModels { + _linkModels = linkModels; + + self.accessibilityElements = nil; +} + +- (void)setNeedsFramesetter { + // Reset the rendered attributed text so it has a chance to regenerate + self.renderedAttributedText = nil; + + _needsFramesetter = YES; +} + +- (CTFramesetterRef)framesetter { + if (_needsFramesetter) { + @synchronized(self) { + CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)self.renderedAttributedText); + [self setFramesetter:framesetter]; + [self setHighlightFramesetter:nil]; + _needsFramesetter = NO; + + if (framesetter) { + CFRelease(framesetter); + } + } + } + + return _framesetter; +} + +- (void)setFramesetter:(CTFramesetterRef)framesetter { + if (framesetter) { + CFRetain(framesetter); + } + + if (_framesetter) { + CFRelease(_framesetter); + } + + _framesetter = framesetter; +} + +- (CTFramesetterRef)highlightFramesetter { + return _highlightFramesetter; +} + +- (void)setHighlightFramesetter:(CTFramesetterRef)highlightFramesetter { + if (highlightFramesetter) { + CFRetain(highlightFramesetter); + } + + if (_highlightFramesetter) { + CFRelease(_highlightFramesetter); + } + + _highlightFramesetter = highlightFramesetter; +} + +- (CGFloat)leading { + return self.lineSpacing; +} + +- (void)setLeading:(CGFloat)leading { + self.lineSpacing = leading; +} + +#pragma mark - + +- (NSTextCheckingTypes)dataDetectorTypes { + return self.enabledTextCheckingTypes; +} + +- (void)setDataDetectorTypes:(NSTextCheckingTypes)dataDetectorTypes { + self.enabledTextCheckingTypes = dataDetectorTypes; +} + +- (void)setEnabledTextCheckingTypes:(NSTextCheckingTypes)enabledTextCheckingTypes { + if (self.enabledTextCheckingTypes == enabledTextCheckingTypes) { + return; + } + + _enabledTextCheckingTypes = enabledTextCheckingTypes; + + if (self.enabledTextCheckingTypes) { + self.dataDetector = [NSDataDetector dataDetectorWithTypes:self.enabledTextCheckingTypes error:nil]; + } else { + self.dataDetector = nil; + } +} + +- (void)addLink:(TTTAttributedLabelLink *)link { + [self addLinks:@[link]]; +} + +- (void)addLinks:(NSArray *)links { + NSMutableArray *mutableLinkModels = [NSMutableArray arrayWithArray:self.linkModels]; + + NSMutableAttributedString *mutableAttributedString = [self.attributedText mutableCopy]; + + for (TTTAttributedLabelLink *link in links) { + if (link.attributes) { + [mutableAttributedString addAttributes:link.attributes range:link.result.range]; + } + } + + self.attributedText = mutableAttributedString; + [self setNeedsDisplay]; + + [mutableLinkModels addObjectsFromArray:links]; + + self.linkModels = [NSArray arrayWithArray:mutableLinkModels]; +} + +- (TTTAttributedLabelLink *)addLinkWithTextCheckingResult:(NSTextCheckingResult *)result + attributes:(NSDictionary *)attributes +{ + return [self addLinksWithTextCheckingResults:@[result] attributes:attributes].firstObject; +} + +- (NSArray *)addLinksWithTextCheckingResults:(NSArray *)results + attributes:(NSDictionary *)attributes +{ + NSMutableArray *links = [NSMutableArray array]; + + for (NSTextCheckingResult *result in results) { + NSDictionary *activeAttributes = attributes ? self.activeLinkAttributes : nil; + NSDictionary *inactiveAttributes = attributes ? self.inactiveLinkAttributes : nil; + + TTTAttributedLabelLink *link = [[TTTAttributedLabelLink alloc] initWithAttributes:attributes + activeAttributes:activeAttributes + inactiveAttributes:inactiveAttributes + textCheckingResult:result]; + + [links addObject:link]; + } + + [self addLinks:links]; + + return links; +} + +- (TTTAttributedLabelLink *)addLinkWithTextCheckingResult:(NSTextCheckingResult *)result { + return [self addLinkWithTextCheckingResult:result attributes:self.linkAttributes]; +} + +- (TTTAttributedLabelLink *)addLinkToURL:(NSURL *)url + withRange:(NSRange)range +{ + return [self addLinkWithTextCheckingResult:[NSTextCheckingResult linkCheckingResultWithRange:range URL:url]]; +} + +- (TTTAttributedLabelLink *)addLinkToAddress:(NSDictionary *)addressComponents + withRange:(NSRange)range +{ + return [self addLinkWithTextCheckingResult:[NSTextCheckingResult addressCheckingResultWithRange:range components:addressComponents]]; +} + +- (TTTAttributedLabelLink *)addLinkToPhoneNumber:(NSString *)phoneNumber + withRange:(NSRange)range +{ + return [self addLinkWithTextCheckingResult:[NSTextCheckingResult phoneNumberCheckingResultWithRange:range phoneNumber:phoneNumber]]; +} + +- (TTTAttributedLabelLink *)addLinkToDate:(NSDate *)date + withRange:(NSRange)range +{ + return [self addLinkWithTextCheckingResult:[NSTextCheckingResult dateCheckingResultWithRange:range date:date]]; +} + +- (TTTAttributedLabelLink *)addLinkToDate:(NSDate *)date + timeZone:(NSTimeZone *)timeZone + duration:(NSTimeInterval)duration + withRange:(NSRange)range +{ + return [self addLinkWithTextCheckingResult:[NSTextCheckingResult dateCheckingResultWithRange:range date:date timeZone:timeZone duration:duration]]; +} + +- (TTTAttributedLabelLink *)addLinkToTransitInformation:(NSDictionary *)components + withRange:(NSRange)range +{ + return [self addLinkWithTextCheckingResult:[NSTextCheckingResult transitInformationCheckingResultWithRange:range components:components]]; +} + +#pragma mark - + +- (BOOL)containslinkAtPoint:(CGPoint)point { + return [self linkAtPoint:point] != nil; +} + +- (TTTAttributedLabelLink *)linkAtPoint:(CGPoint)point { + + // Stop quickly if none of the points to be tested are in the bounds. + if (!CGRectContainsPoint(CGRectInset(self.bounds, -15.f, -15.f), point) || self.links.count == 0) { + return nil; + } + + TTTAttributedLabelLink *result = [self linkAtCharacterIndex:[self characterIndexAtPoint:point]]; + + if (!result && self.extendsLinkTouchArea) { + result = [self linkAtRadius:2.5f aroundPoint:point] + ?: [self linkAtRadius:5.f aroundPoint:point] + ?: [self linkAtRadius:7.5f aroundPoint:point] + ?: [self linkAtRadius:12.5f aroundPoint:point] + ?: [self linkAtRadius:15.f aroundPoint:point]; + } + + return result; +} + +- (TTTAttributedLabelLink *)linkAtRadius:(const CGFloat)radius aroundPoint:(CGPoint)point { + const CGFloat diagonal = CGFloat_sqrt(2 * radius * radius); + const CGPoint deltas[] = { + CGPointMake(0, -radius), CGPointMake(0, radius), // Above and below + CGPointMake(-radius, 0), CGPointMake(radius, 0), // Beside + CGPointMake(-diagonal, -diagonal), CGPointMake(-diagonal, diagonal), + CGPointMake(diagonal, diagonal), CGPointMake(diagonal, -diagonal) // Diagonal + }; + const size_t count = sizeof(deltas) / sizeof(CGPoint); + + TTTAttributedLabelLink *link = nil; + + for (NSInteger i = 0; i < count && link.result == nil; i ++) { + CGPoint currentPoint = CGPointMake(point.x + deltas[i].x, point.y + deltas[i].y); + link = [self linkAtCharacterIndex:[self characterIndexAtPoint:currentPoint]]; + } + + return link; +} + +- (TTTAttributedLabelLink *)linkAtCharacterIndex:(CFIndex)idx { + // Do not enumerate if the index is outside of the bounds of the text. + if (!NSLocationInRange((NSUInteger)idx, NSMakeRange(0, self.attributedText.length))) { + return nil; + } + + NSEnumerator *enumerator = [self.linkModels reverseObjectEnumerator]; + TTTAttributedLabelLink *link = nil; + while ((link = [enumerator nextObject])) { + if (NSLocationInRange((NSUInteger)idx, link.result.range)) { + return link; + } + } + + return nil; +} + +- (CFIndex)characterIndexAtPoint:(CGPoint)p { + if (!CGRectContainsPoint(self.bounds, p)) { + return NSNotFound; + } + + CGRect textRect = [self textRectForBounds:self.bounds limitedToNumberOfLines:self.numberOfLines]; + if (!CGRectContainsPoint(textRect, p)) { + return NSNotFound; + } + + // Offset tap coordinates by textRect origin to make them relative to the origin of frame + p = CGPointMake(p.x - textRect.origin.x, p.y - textRect.origin.y); + // Convert tap coordinates (start at top left) to CT coordinates (start at bottom left) + p = CGPointMake(p.x, textRect.size.height - p.y); + + CGMutablePathRef path = CGPathCreateMutable(); + CGPathAddRect(path, NULL, textRect); + CTFrameRef frame = CTFramesetterCreateFrame([self framesetter], CFRangeMake(0, (CFIndex)[self.attributedText length]), path, NULL); + if (frame == NULL) { + CFRelease(path); + return NSNotFound; + } + + CFArrayRef lines = CTFrameGetLines(frame); + NSInteger numberOfLines = self.numberOfLines > 0 ? MIN(self.numberOfLines, CFArrayGetCount(lines)) : CFArrayGetCount(lines); + if (numberOfLines == 0) { + CFRelease(frame); + CFRelease(path); + return NSNotFound; + } + + CFIndex idx = NSNotFound; + + CGPoint lineOrigins[numberOfLines]; + CTFrameGetLineOrigins(frame, CFRangeMake(0, numberOfLines), lineOrigins); + + for (CFIndex lineIndex = 0; lineIndex < numberOfLines; lineIndex++) { + CGPoint lineOrigin = lineOrigins[lineIndex]; + CTLineRef line = CFArrayGetValueAtIndex(lines, lineIndex); + + // Get bounding information of line + CGFloat ascent = 0.0f, descent = 0.0f, leading = 0.0f; + CGFloat width = (CGFloat)CTLineGetTypographicBounds(line, &ascent, &descent, &leading); + CGFloat yMin = (CGFloat)floor(lineOrigin.y - descent); + CGFloat yMax = (CGFloat)ceil(lineOrigin.y + ascent); + + // Apply penOffset using flushFactor for horizontal alignment to set lineOrigin since this is the horizontal offset from drawFramesetter + CGFloat flushFactor = TTTFlushFactorForTextAlignment(self.textAlignment); + CGFloat penOffset = (CGFloat)CTLineGetPenOffsetForFlush(line, flushFactor, textRect.size.width); + lineOrigin.x = penOffset; + + // Check if we've already passed the line + if (p.y > yMax) { + break; + } + // Check if the point is within this line vertically + if (p.y >= yMin) { + // Check if the point is within this line horizontally + if (p.x >= lineOrigin.x && p.x <= lineOrigin.x + width) { + // Convert CT coordinates to line-relative coordinates + CGPoint relativePoint = CGPointMake(p.x - lineOrigin.x, p.y - lineOrigin.y); + idx = CTLineGetStringIndexForPosition(line, relativePoint); + break; + } + } + } + + CFRelease(frame); + CFRelease(path); + + return idx; +} + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 +- (CGRect)boundingRectForCharacterRange:(NSRange)range { + NSMutableAttributedString *mutableAttributedString = [self.attributedText mutableCopy]; + + NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:mutableAttributedString]; + + NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; + [textStorage addLayoutManager:layoutManager]; + + NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:self.bounds.size]; + [layoutManager addTextContainer:textContainer]; + + NSRange glyphRange; + [layoutManager characterRangeForGlyphRange:range actualGlyphRange:&glyphRange]; + + return [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer]; +} +#endif + +- (void)drawFramesetter:(CTFramesetterRef)framesetter + attributedString:(NSAttributedString *)attributedString + textRange:(CFRange)textRange + inRect:(CGRect)rect + context:(CGContextRef)c +{ + CGMutablePathRef path = CGPathCreateMutable(); + CGPathAddRect(path, NULL, rect); + CTFrameRef frame = CTFramesetterCreateFrame(framesetter, textRange, path, NULL); + + [self drawBackground:frame inRect:rect context:c]; + + CFArrayRef lines = CTFrameGetLines(frame); + NSInteger numberOfLines = self.numberOfLines > 0 ? MIN(self.numberOfLines, CFArrayGetCount(lines)) : CFArrayGetCount(lines); + BOOL truncateLastLine = (self.lineBreakMode == TTTLineBreakByTruncatingHead || self.lineBreakMode == TTTLineBreakByTruncatingMiddle || self.lineBreakMode == TTTLineBreakByTruncatingTail); + + CGPoint lineOrigins[numberOfLines]; + CTFrameGetLineOrigins(frame, CFRangeMake(0, numberOfLines), lineOrigins); + + for (CFIndex lineIndex = 0; lineIndex < numberOfLines; lineIndex++) { + CGPoint lineOrigin = lineOrigins[lineIndex]; + CGContextSetTextPosition(c, lineOrigin.x, lineOrigin.y); + CTLineRef line = CFArrayGetValueAtIndex(lines, lineIndex); + + CGFloat descent = 0.0f; + CTLineGetTypographicBounds((CTLineRef)line, NULL, &descent, NULL); + + // Adjust pen offset for flush depending on text alignment + CGFloat flushFactor = TTTFlushFactorForTextAlignment(self.textAlignment); + + if (lineIndex == numberOfLines - 1 && truncateLastLine) { + // Check if the range of text in the last line reaches the end of the full attributed string + CFRange lastLineRange = CTLineGetStringRange(line); + + if (!(lastLineRange.length == 0 && lastLineRange.location == 0) && lastLineRange.location + lastLineRange.length < textRange.location + textRange.length) { + // Get correct truncationType and attribute position + CTLineTruncationType truncationType; + CFIndex truncationAttributePosition = lastLineRange.location; + TTTLineBreakMode lineBreakMode = self.lineBreakMode; + + // Multiple lines, only use UILineBreakModeTailTruncation + if (numberOfLines != 1) { + lineBreakMode = TTTLineBreakByTruncatingTail; + } + + switch (lineBreakMode) { + case TTTLineBreakByTruncatingHead: + truncationType = kCTLineTruncationStart; + break; + case TTTLineBreakByTruncatingMiddle: + truncationType = kCTLineTruncationMiddle; + truncationAttributePosition += (lastLineRange.length / 2); + break; + case TTTLineBreakByTruncatingTail: + default: + truncationType = kCTLineTruncationEnd; + truncationAttributePosition += (lastLineRange.length - 1); + break; + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + NSAttributedString *attributedTruncationString = self.attributedTruncationToken; + if (!attributedTruncationString) { + NSString *truncationTokenString = self.truncationTokenString; + if (!truncationTokenString) { + truncationTokenString = @"\u2026"; // Unicode Character 'HORIZONTAL ELLIPSIS' (U+2026) + } + + NSDictionary *truncationTokenStringAttributes = self.truncationTokenStringAttributes; + if (!truncationTokenStringAttributes) { + truncationTokenStringAttributes = [attributedString attributesAtIndex:(NSUInteger)truncationAttributePosition effectiveRange:NULL]; + } + + attributedTruncationString = [[NSAttributedString alloc] initWithString:truncationTokenString attributes:truncationTokenStringAttributes]; + } + CTLineRef truncationToken = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)attributedTruncationString); +#pragma clang diagnostic pop + + // Append truncationToken to the string + // because if string isn't too long, CT won't add the truncationToken on its own. + // There is no chance of a double truncationToken because CT only adds the + // token if it removes characters (and the one we add will go first) + NSMutableAttributedString *truncationString = [[NSMutableAttributedString alloc] initWithAttributedString: + [attributedString attributedSubstringFromRange: + NSMakeRange((NSUInteger)lastLineRange.location, + (NSUInteger)lastLineRange.length)]]; + if (lastLineRange.length > 0) { + // Remove any newline at the end (we don't want newline space between the text and the truncation token). There can only be one, because the second would be on the next line. + unichar lastCharacter = [[truncationString string] characterAtIndex:(NSUInteger)(lastLineRange.length - 1)]; + if ([[NSCharacterSet newlineCharacterSet] characterIsMember:lastCharacter]) { + [truncationString deleteCharactersInRange:NSMakeRange((NSUInteger)(lastLineRange.length - 1), 1)]; + } + } + [truncationString appendAttributedString:attributedTruncationString]; + CTLineRef truncationLine = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)truncationString); + + // Truncate the line in case it is too long. + CTLineRef truncatedLine = CTLineCreateTruncatedLine(truncationLine, rect.size.width, truncationType, truncationToken); + if (!truncatedLine) { + // If the line is not as wide as the truncationToken, truncatedLine is NULL + truncatedLine = CFRetain(truncationToken); + } + + CGFloat penOffset = (CGFloat)CTLineGetPenOffsetForFlush(truncatedLine, flushFactor, rect.size.width); + CGContextSetTextPosition(c, penOffset, lineOrigin.y - descent - self.font.descender); + + CTLineDraw(truncatedLine, c); + + NSRange linkRange; + if ([attributedTruncationString attribute:NSLinkAttributeName atIndex:0 effectiveRange:&linkRange]) { + NSRange tokenRange = [truncationString.string rangeOfString:attributedTruncationString.string]; + NSRange tokenLinkRange = NSMakeRange((NSUInteger)(lastLineRange.location+lastLineRange.length)-tokenRange.length, (NSUInteger)tokenRange.length); + + [self addLinkToURL:[attributedTruncationString attribute:NSLinkAttributeName atIndex:0 effectiveRange:&linkRange] withRange:tokenLinkRange]; + } + + CFRelease(truncatedLine); + CFRelease(truncationLine); + CFRelease(truncationToken); + } else { + CGFloat penOffset = (CGFloat)CTLineGetPenOffsetForFlush(line, flushFactor, rect.size.width); + CGContextSetTextPosition(c, penOffset, lineOrigin.y - descent - self.font.descender); + CTLineDraw(line, c); + } + } else { + CGFloat penOffset = (CGFloat)CTLineGetPenOffsetForFlush(line, flushFactor, rect.size.width); + CGContextSetTextPosition(c, penOffset, lineOrigin.y - descent - self.font.descender); + CTLineDraw(line, c); + } + } + + [self drawStrike:frame inRect:rect context:c]; + + CFRelease(frame); + CFRelease(path); +} + +- (void)drawBackground:(CTFrameRef)frame + inRect:(CGRect)rect + context:(CGContextRef)c +{ + NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frame); + CGPoint origins[[lines count]]; + CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins); + + CFIndex lineIndex = 0; + for (id line in lines) { + CGFloat ascent = 0.0f, descent = 0.0f, leading = 0.0f; + CGFloat width = (CGFloat)CTLineGetTypographicBounds((__bridge CTLineRef)line, &ascent, &descent, &leading) ; + + for (id glyphRun in (__bridge NSArray *)CTLineGetGlyphRuns((__bridge CTLineRef)line)) { + NSDictionary *attributes = (__bridge NSDictionary *)CTRunGetAttributes((__bridge CTRunRef) glyphRun); + CGColorRef strokeColor = (__bridge CGColorRef)[attributes objectForKey:kTTTBackgroundStrokeColorAttributeName]; + CGColorRef fillColor = (__bridge CGColorRef)[attributes objectForKey:kTTTBackgroundFillColorAttributeName]; + UIEdgeInsets fillPadding = [[attributes objectForKey:kTTTBackgroundFillPaddingAttributeName] UIEdgeInsetsValue]; + CGFloat cornerRadius = [[attributes objectForKey:kTTTBackgroundCornerRadiusAttributeName] floatValue]; + CGFloat lineWidth = [[attributes objectForKey:kTTTBackgroundLineWidthAttributeName] floatValue]; + + if (strokeColor || fillColor) { + CGRect runBounds = CGRectZero; + CGFloat runAscent = 0.0f; + CGFloat runDescent = 0.0f; + + runBounds.size.width = (CGFloat)CTRunGetTypographicBounds((__bridge CTRunRef)glyphRun, CFRangeMake(0, 0), &runAscent, &runDescent, NULL) + fillPadding.left + fillPadding.right; + runBounds.size.height = runAscent + runDescent + fillPadding.top + fillPadding.bottom; + + CGFloat xOffset = 0.0f; + CFRange glyphRange = CTRunGetStringRange((__bridge CTRunRef)glyphRun); + switch (CTRunGetStatus((__bridge CTRunRef)glyphRun)) { + case kCTRunStatusRightToLeft: + xOffset = CTLineGetOffsetForStringIndex((__bridge CTLineRef)line, glyphRange.location + glyphRange.length, NULL); + break; + default: + xOffset = CTLineGetOffsetForStringIndex((__bridge CTLineRef)line, glyphRange.location, NULL); + break; + } + + runBounds.origin.x = origins[lineIndex].x + rect.origin.x + xOffset - fillPadding.left - rect.origin.x; + runBounds.origin.y = origins[lineIndex].y + rect.origin.y - fillPadding.bottom - rect.origin.y; + runBounds.origin.y -= runDescent; + + // Don't draw higlightedLinkBackground too far to the right + if (CGRectGetWidth(runBounds) > width) { + runBounds.size.width = width; + } + + CGPathRef path = [[UIBezierPath bezierPathWithRoundedRect:CGRectInset(UIEdgeInsetsInsetRect(runBounds, self.linkBackgroundEdgeInset), lineWidth, lineWidth) cornerRadius:cornerRadius] CGPath]; + + CGContextSetLineJoin(c, kCGLineJoinRound); + + if (fillColor) { + CGContextSetFillColorWithColor(c, fillColor); + CGContextAddPath(c, path); + CGContextFillPath(c); + } + + if (strokeColor) { + CGContextSetStrokeColorWithColor(c, strokeColor); + CGContextAddPath(c, path); + CGContextStrokePath(c); + } + } + } + + lineIndex++; + } +} + +- (void)drawStrike:(CTFrameRef)frame + inRect:(__unused CGRect)rect + context:(CGContextRef)c +{ + NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frame); + CGPoint origins[[lines count]]; + CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins); + + CFIndex lineIndex = 0; + for (id line in lines) { + CGFloat ascent = 0.0f, descent = 0.0f, leading = 0.0f; + CGFloat width = (CGFloat)CTLineGetTypographicBounds((__bridge CTLineRef)line, &ascent, &descent, &leading) ; + + for (id glyphRun in (__bridge NSArray *)CTLineGetGlyphRuns((__bridge CTLineRef)line)) { + NSDictionary *attributes = (__bridge NSDictionary *)CTRunGetAttributes((__bridge CTRunRef) glyphRun); + BOOL strikeOut = [[attributes objectForKey:kTTTStrikeOutAttributeName] boolValue]; + NSInteger superscriptStyle = [[attributes objectForKey:(id)kCTSuperscriptAttributeName] integerValue]; + + if (strikeOut) { + CGRect runBounds = CGRectZero; + CGFloat runAscent = 0.0f; + CGFloat runDescent = 0.0f; + + runBounds.size.width = (CGFloat)CTRunGetTypographicBounds((__bridge CTRunRef)glyphRun, CFRangeMake(0, 0), &runAscent, &runDescent, NULL); + runBounds.size.height = runAscent + runDescent; + + CGFloat xOffset = 0.0f; + CFRange glyphRange = CTRunGetStringRange((__bridge CTRunRef)glyphRun); + switch (CTRunGetStatus((__bridge CTRunRef)glyphRun)) { + case kCTRunStatusRightToLeft: + xOffset = CTLineGetOffsetForStringIndex((__bridge CTLineRef)line, glyphRange.location + glyphRange.length, NULL); + break; + default: + xOffset = CTLineGetOffsetForStringIndex((__bridge CTLineRef)line, glyphRange.location, NULL); + break; + } + runBounds.origin.x = origins[lineIndex].x + xOffset; + runBounds.origin.y = origins[lineIndex].y; + runBounds.origin.y -= runDescent; + + // Don't draw strikeout too far to the right + if (CGRectGetWidth(runBounds) > width) { + runBounds.size.width = width; + } + + switch (superscriptStyle) { + case 1: + runBounds.origin.y -= runAscent * 0.47f; + break; + case -1: + runBounds.origin.y += runAscent * 0.25f; + break; + default: + break; + } + + // Use text color, or default to black + id color = [attributes objectForKey:(id)kCTForegroundColorAttributeName]; + if (color) { + if ([color isKindOfClass:[UIColor class]]) { + CGContextSetStrokeColorWithColor(c, [color CGColor]); + } else { + CGContextSetStrokeColorWithColor(c, (__bridge CGColorRef)color); + } + } else { + CGContextSetGrayStrokeColor(c, 0.0f, 1.0); + } + + CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)self.font.fontName, self.font.pointSize, NULL); + CGContextSetLineWidth(c, CTFontGetUnderlineThickness(font)); + CFRelease(font); + + CGFloat y = CGFloat_round(runBounds.origin.y + runBounds.size.height / 2.0f); + CGContextMoveToPoint(c, runBounds.origin.x, y); + CGContextAddLineToPoint(c, runBounds.origin.x + runBounds.size.width, y); + + CGContextStrokePath(c); + } + } + + lineIndex++; + } +} + +#pragma mark - TTTAttributedLabel + +- (void)setText:(id)text { + NSParameterAssert(!text || [text isKindOfClass:[NSAttributedString class]] || [text isKindOfClass:[NSString class]]); + + if ([text isKindOfClass:[NSString class]]) { + [self setText:text afterInheritingLabelAttributesAndConfiguringWithBlock:nil]; + return; + } + + self.attributedText = text; + self.activeLink = nil; + + self.linkModels = [NSArray array]; + if (self.attributedText && self.enabledTextCheckingTypes) { +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 50000 + __unsafe_unretained __typeof(self)weakSelf = self; +#else + __weak __typeof(self)weakSelf = self; +#endif + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + __strong __typeof(weakSelf)strongSelf = weakSelf; + + NSDataDetector *dataDetector = strongSelf.dataDetector; + if (dataDetector && [dataDetector respondsToSelector:@selector(matchesInString:options:range:)]) { + NSArray *results = [dataDetector matchesInString:[(NSAttributedString *)text string] options:0 range:NSMakeRange(0, [(NSAttributedString *)text length])]; + if ([results count] > 0) { + dispatch_sync(dispatch_get_main_queue(), ^{ + if ([[strongSelf.attributedText string] isEqualToString:[(NSAttributedString *)text string]]) { + [strongSelf addLinksWithTextCheckingResults:results attributes:strongSelf.linkAttributes]; + } + }); + } + } + }); + } + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 + if (&NSLinkAttributeName) { + [self.attributedText enumerateAttribute:NSLinkAttributeName inRange:NSMakeRange(0, self.attributedText.length) options:0 usingBlock:^(id value, __unused NSRange range, __unused BOOL *stop) { + if (value) { + NSURL *URL = [value isKindOfClass:[NSString class]] ? [NSURL URLWithString:value] : value; + [self addLinkToURL:URL withRange:range]; + } + }]; + } +#endif +} + +- (void)setText:(id)text +afterInheritingLabelAttributesAndConfiguringWithBlock:(NSMutableAttributedString * (^)(NSMutableAttributedString *mutableAttributedString))block +{ + NSMutableAttributedString *mutableAttributedString = nil; + if ([text isKindOfClass:[NSString class]]) { + mutableAttributedString = [[NSMutableAttributedString alloc] initWithString:text attributes:NSAttributedStringAttributesFromLabel(self)]; + } else { + mutableAttributedString = [[NSMutableAttributedString alloc] initWithAttributedString:text]; + [mutableAttributedString addAttributes:NSAttributedStringAttributesFromLabel(self) range:NSMakeRange(0, [mutableAttributedString length])]; + } + + if (block) { + mutableAttributedString = block(mutableAttributedString); + } + + [self setText:mutableAttributedString]; +} + +- (void)setActiveLink:(TTTAttributedLabelLink *)activeLink { + _activeLink = activeLink; + + NSDictionary *activeAttributes = activeLink.activeAttributes ?: self.activeLinkAttributes; + + if (_activeLink && activeAttributes.count > 0) { + if (!self.inactiveAttributedText) { + self.inactiveAttributedText = [self.attributedText copy]; + } + + NSMutableAttributedString *mutableAttributedString = [self.inactiveAttributedText mutableCopy]; + if (self.activeLink.result.range.length > 0 && NSLocationInRange(NSMaxRange(self.activeLink.result.range) - 1, NSMakeRange(0, [self.inactiveAttributedText length]))) { + [mutableAttributedString addAttributes:activeAttributes range:self.activeLink.result.range]; + } + + self.attributedText = mutableAttributedString; + [self setNeedsDisplay]; + + [CATransaction flush]; + } else if (self.inactiveAttributedText) { + self.attributedText = self.inactiveAttributedText; + self.inactiveAttributedText = nil; + + [self setNeedsDisplay]; + } +} + +#pragma mark - UILabel + +- (void)setHighlighted:(BOOL)highlighted { + [super setHighlighted:highlighted]; + [self setNeedsDisplay]; +} + +// Fixes crash when loading from a UIStoryboard +- (UIColor *)textColor { + UIColor *color = [super textColor]; + if (!color) { + color = [UIColor blackColor]; + } + + return color; +} + +- (void)setTextColor:(UIColor *)textColor { + UIColor *oldTextColor = self.textColor; + [super setTextColor:textColor]; + + // Redraw to allow any ColorFromContext attributes a chance to update + if (textColor != oldTextColor) { + [self setNeedsFramesetter]; + [self setNeedsDisplay]; + } +} + +- (CGRect)textRectForBounds:(CGRect)bounds + limitedToNumberOfLines:(NSInteger)numberOfLines +{ + bounds = UIEdgeInsetsInsetRect(bounds, self.textInsets); + if (!self.attributedText) { + return [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines]; + } + + CGRect textRect = bounds; + + // Calculate height with a minimum of double the font pointSize, to ensure that CTFramesetterSuggestFrameSizeWithConstraints doesn't return CGSizeZero, as it would if textRect height is insufficient. + textRect.size.height = MAX(self.font.lineHeight * MAX(2, numberOfLines), bounds.size.height); + + // Adjust the text to be in the center vertically, if the text size is smaller than bounds + CGSize textSize = CTFramesetterSuggestFrameSizeWithConstraints([self framesetter], CFRangeMake(0, (CFIndex)[self.attributedText length]), NULL, textRect.size, NULL); + textSize = CGSizeMake(CGFloat_ceil(textSize.width), CGFloat_ceil(textSize.height)); // Fix for iOS 4, CTFramesetterSuggestFrameSizeWithConstraints sometimes returns fractional sizes + + if (textSize.height < bounds.size.height) { + CGFloat yOffset = 0.0f; + switch (self.verticalAlignment) { + case TTTAttributedLabelVerticalAlignmentCenter: + yOffset = CGFloat_floor((bounds.size.height - textSize.height) / 2.0f); + break; + case TTTAttributedLabelVerticalAlignmentBottom: + yOffset = bounds.size.height - textSize.height; + break; + case TTTAttributedLabelVerticalAlignmentTop: + default: + break; + } + + textRect.origin.y += yOffset; + } + + return textRect; +} + +- (void)drawTextInRect:(CGRect)rect { + CGRect insetRect = UIEdgeInsetsInsetRect(rect, self.textInsets); + if (!self.attributedText) { + [super drawTextInRect:insetRect]; + return; + } + + NSAttributedString *originalAttributedText = nil; + + // Adjust the font size to fit width, if necessarry + if (self.adjustsFontSizeToFitWidth && self.numberOfLines > 0) { + // Use infinite width to find the max width, which will be compared to availableWidth if needed. + CGSize maxSize = (self.numberOfLines > 1) ? CGSizeMake(TTTFLOAT_MAX, TTTFLOAT_MAX) : CGSizeZero; + + CGFloat textWidth = [self sizeThatFits:maxSize].width; + CGFloat availableWidth = self.frame.size.width * self.numberOfLines; + if (self.numberOfLines > 1 && self.lineBreakMode == TTTLineBreakByWordWrapping) { + textWidth *= kTTTLineBreakWordWrapTextWidthScalingFactor; + } + + if (textWidth > availableWidth && textWidth > 0.0f) { + originalAttributedText = [self.attributedText copy]; + + CGFloat scaleFactor = availableWidth / textWidth; + if ([self respondsToSelector:@selector(minimumScaleFactor)] && self.minimumScaleFactor > scaleFactor) { + scaleFactor = self.minimumScaleFactor; + } + + self.attributedText = NSAttributedStringByScalingFontSize(self.attributedText, scaleFactor); + } + } + + CGContextRef c = UIGraphicsGetCurrentContext(); + CGContextSaveGState(c); + { + CGContextSetTextMatrix(c, CGAffineTransformIdentity); + + // Inverts the CTM to match iOS coordinates (otherwise text draws upside-down; Mac OS's system is different) + CGContextTranslateCTM(c, 0.0f, insetRect.size.height); + CGContextScaleCTM(c, 1.0f, -1.0f); + + CFRange textRange = CFRangeMake(0, (CFIndex)[self.attributedText length]); + + // First, get the text rect (which takes vertical centering into account) + CGRect textRect = [self textRectForBounds:rect limitedToNumberOfLines:self.numberOfLines]; + + // CoreText draws its text aligned to the bottom, so we move the CTM here to take our vertical offsets into account + CGContextTranslateCTM(c, insetRect.origin.x, insetRect.size.height - textRect.origin.y - textRect.size.height); + + // Second, trace the shadow before the actual text, if we have one + if (self.shadowColor && !self.highlighted) { + CGContextSetShadowWithColor(c, self.shadowOffset, self.shadowRadius, [self.shadowColor CGColor]); + } else if (self.highlightedShadowColor) { + CGContextSetShadowWithColor(c, self.highlightedShadowOffset, self.highlightedShadowRadius, [self.highlightedShadowColor CGColor]); + } + + // Finally, draw the text or highlighted text itself (on top of the shadow, if there is one) + if (self.highlightedTextColor && self.highlighted) { + NSMutableAttributedString *highlightAttributedString = [self.renderedAttributedText mutableCopy]; + [highlightAttributedString addAttribute:(__bridge NSString *)kCTForegroundColorAttributeName value:(id)[self.highlightedTextColor CGColor] range:NSMakeRange(0, highlightAttributedString.length)]; + + if (![self highlightFramesetter]) { + CTFramesetterRef highlightFramesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)highlightAttributedString); + [self setHighlightFramesetter:highlightFramesetter]; + CFRelease(highlightFramesetter); + } + + [self drawFramesetter:[self highlightFramesetter] attributedString:highlightAttributedString textRange:textRange inRect:textRect context:c]; + } else { + [self drawFramesetter:[self framesetter] attributedString:self.renderedAttributedText textRange:textRange inRect:textRect context:c]; + } + + // If we adjusted the font size, set it back to its original size + if (originalAttributedText) { + // Use ivar directly to avoid clearing out framesetter and renderedAttributedText + _attributedText = originalAttributedText; + } + } + CGContextRestoreGState(c); +} + +#pragma mark - UIAccessibilityElement + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 + +- (BOOL)isAccessibilityElement { + return NO; +} + +- (NSInteger)accessibilityElementCount { + return (NSInteger)[[self accessibilityElements] count]; +} + +- (id)accessibilityElementAtIndex:(NSInteger)index { + return [[self accessibilityElements] objectAtIndex:(NSUInteger)index]; +} + +- (NSInteger)indexOfAccessibilityElement:(id)element { + return (NSInteger)[[self accessibilityElements] indexOfObject:element]; +} + +- (NSArray *)accessibilityElements { + if (!_accessibilityElements) { + @synchronized(self) { + NSMutableArray *mutableAccessibilityItems = [NSMutableArray array]; + + for (TTTAttributedLabelLink *link in self.linkModels) { + + if (link.result.range.location == NSNotFound) { + continue; + } + + NSString *sourceText = [self.text isKindOfClass:[NSString class]] ? self.text : [(NSAttributedString *)self.text string]; + + NSString *accessibilityLabel = [sourceText substringWithRange:link.result.range]; + NSString *accessibilityValue = link.accessibilityValue; + + if (accessibilityLabel) { + UIAccessibilityElement *linkElement = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self]; + linkElement.accessibilityTraits = UIAccessibilityTraitLink; + linkElement.accessibilityFrame = [self convertRect:[self boundingRectForCharacterRange:link.result.range] toView:self.window]; + linkElement.accessibilityLabel = accessibilityLabel; + + if (![accessibilityLabel isEqualToString:accessibilityValue]) { + linkElement.accessibilityValue = accessibilityValue; + } + + [mutableAccessibilityItems addObject:linkElement]; + } + } + + UIAccessibilityElement *baseElement = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self]; + baseElement.accessibilityLabel = [super accessibilityLabel]; + baseElement.accessibilityHint = [super accessibilityHint]; + baseElement.accessibilityValue = [super accessibilityValue]; + baseElement.accessibilityFrame = [self convertRect:self.bounds toView:self.window]; + baseElement.accessibilityTraits = [super accessibilityTraits]; + + [mutableAccessibilityItems addObject:baseElement]; + + self.accessibilityElements = [NSArray arrayWithArray:mutableAccessibilityItems]; + } + } + + return _accessibilityElements; +} +#endif + +#pragma mark - UIView + +- (CGSize)sizeThatFits:(CGSize)size { + if (!self.attributedText) { + return [super sizeThatFits:size]; + } else { + NSMutableAttributedString *fullString = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText]; + + if (self.attributedTruncationToken) { + [fullString appendAttributedString:self.attributedTruncationToken]; + } + + NSAttributedString *string = [[NSAttributedString alloc] initWithAttributedString:fullString]; + + CGSize labelSize = CTFramesetterSuggestFrameSizeForAttributedStringWithConstraints([self framesetter], string, size, (NSUInteger)self.numberOfLines); + labelSize.width += self.textInsets.left + self.textInsets.right; + labelSize.height += self.textInsets.top + self.textInsets.bottom; + + return labelSize; + } +} + +- (CGSize)intrinsicContentSize { + // There's an implicit width from the original UILabel implementation + return [self sizeThatFits:[super intrinsicContentSize]]; +} + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 +- (void)tintColorDidChange { + if (!self.inactiveLinkAttributes || [self.inactiveLinkAttributes count] == 0) { + return; + } + + BOOL isInactive = (self.tintAdjustmentMode == UIViewTintAdjustmentModeDimmed); + + NSMutableAttributedString *mutableAttributedString = [self.attributedText mutableCopy]; + for (TTTAttributedLabelLink *link in self.linkModels) { + NSDictionary *attributesToRemove = isInactive ? link.attributes : link.inactiveAttributes; + NSDictionary *attributesToAdd = isInactive ? link.inactiveAttributes : link.attributes; + + [attributesToRemove enumerateKeysAndObjectsUsingBlock:^(NSString *name, __unused id value, __unused BOOL *stop) { + if (NSMaxRange(link.result.range) <= mutableAttributedString.length) { + [mutableAttributedString removeAttribute:name range:link.result.range]; + } + }]; + + if (attributesToAdd) { + if (NSMaxRange(link.result.range) <= mutableAttributedString.length) { + [mutableAttributedString addAttributes:attributesToAdd range:link.result.range]; + } + } + } + + self.attributedText = mutableAttributedString; + + [self setNeedsDisplay]; +} +#endif + +- (UIView *)hitTest:(CGPoint)point + withEvent:(UIEvent *)event +{ + if (![self linkAtPoint:point] || !self.userInteractionEnabled || self.hidden || self.alpha < 0.01) { + return [super hitTest:point withEvent:event]; + } + + return self; +} + +#pragma mark - UIResponder + +- (BOOL)canBecomeFirstResponder { + return YES; +} + +- (BOOL)canPerformAction:(SEL)action + withSender:(__unused id)sender +{ + return (action == @selector(copy:)); +} + +- (void)touchesBegan:(NSSet *)touches + withEvent:(UIEvent *)event +{ + UITouch *touch = [touches anyObject]; + + self.activeLink = [self linkAtPoint:[touch locationInView:self]]; + + if (!self.activeLink) { + [super touchesBegan:touches withEvent:event]; + } +} + +- (void)touchesMoved:(NSSet *)touches + withEvent:(UIEvent *)event +{ + if (self.activeLink) { + UITouch *touch = [touches anyObject]; + + if (self.activeLink != [self linkAtPoint:[touch locationInView:self]]) { + self.activeLink = nil; + } + } else { + [super touchesMoved:touches withEvent:event]; + } +} + +- (void)touchesEnded:(NSSet *)touches + withEvent:(UIEvent *)event +{ + if (self.activeLink) { + if (self.activeLink.linkTapBlock) { + self.activeLink.linkTapBlock(self, self.activeLink); + self.activeLink = nil; + return; + } + + NSTextCheckingResult *result = self.activeLink.result; + self.activeLink = nil; + + switch (result.resultType) { + case NSTextCheckingTypeLink: + if ([self.delegate respondsToSelector:@selector(attributedLabel:didSelectLinkWithURL:)]) { + [self.delegate attributedLabel:self didSelectLinkWithURL:result.URL]; + return; + } + break; + case NSTextCheckingTypeAddress: + if ([self.delegate respondsToSelector:@selector(attributedLabel:didSelectLinkWithAddress:)]) { + [self.delegate attributedLabel:self didSelectLinkWithAddress:result.addressComponents]; + return; + } + break; + case NSTextCheckingTypePhoneNumber: + if ([self.delegate respondsToSelector:@selector(attributedLabel:didSelectLinkWithPhoneNumber:)]) { + [self.delegate attributedLabel:self didSelectLinkWithPhoneNumber:result.phoneNumber]; + return; + } + break; + case NSTextCheckingTypeDate: + if (result.timeZone && [self.delegate respondsToSelector:@selector(attributedLabel:didSelectLinkWithDate:timeZone:duration:)]) { + [self.delegate attributedLabel:self didSelectLinkWithDate:result.date timeZone:result.timeZone duration:result.duration]; + return; + } else if ([self.delegate respondsToSelector:@selector(attributedLabel:didSelectLinkWithDate:)]) { + [self.delegate attributedLabel:self didSelectLinkWithDate:result.date]; + return; + } + break; + case NSTextCheckingTypeTransitInformation: + if ([self.delegate respondsToSelector:@selector(attributedLabel:didSelectLinkWithTransitInformation:)]) { + [self.delegate attributedLabel:self didSelectLinkWithTransitInformation:result.components]; + return; + } + default: + break; + } + + // Fallback to `attributedLabel:didSelectLinkWithTextCheckingResult:` if no other delegate method matched. + if ([self.delegate respondsToSelector:@selector(attributedLabel:didSelectLinkWithTextCheckingResult:)]) { + [self.delegate attributedLabel:self didSelectLinkWithTextCheckingResult:result]; + } + } else { + [super touchesEnded:touches withEvent:event]; + } +} + +- (void)touchesCancelled:(NSSet *)touches + withEvent:(UIEvent *)event +{ + if (self.activeLink) { + self.activeLink = nil; + } else { + [super touchesCancelled:touches withEvent:event]; + } +} + +#pragma mark - UIGestureRecognizerDelegate + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { + return [self containslinkAtPoint:[touch locationInView:self]]; +} + +#pragma mark - UILongPressGestureRecognizer + +- (void)longPressGestureDidFire:(UILongPressGestureRecognizer *)sender { + switch (sender.state) { + case UIGestureRecognizerStateBegan: { + CGPoint touchPoint = [sender locationInView:self]; + TTTAttributedLabelLink *link = [self linkAtPoint:touchPoint]; + + if (link) { + if (link.linkLongPressBlock) { + link.linkLongPressBlock(self, link); + return; + } + + NSTextCheckingResult *result = link.result; + + if (!result) { + return; + } + + switch (result.resultType) { + case NSTextCheckingTypeLink: + if ([self.delegate respondsToSelector:@selector(attributedLabel:didLongPressLinkWithURL:atPoint:)]) { + [self.delegate attributedLabel:self didLongPressLinkWithURL:result.URL atPoint:touchPoint]; + return; + } + break; + case NSTextCheckingTypeAddress: + if ([self.delegate respondsToSelector:@selector(attributedLabel:didLongPressLinkWithAddress:atPoint:)]) { + [self.delegate attributedLabel:self didLongPressLinkWithAddress:result.addressComponents atPoint:touchPoint]; + return; + } + break; + case NSTextCheckingTypePhoneNumber: + if ([self.delegate respondsToSelector:@selector(attributedLabel:didLongPressLinkWithPhoneNumber:atPoint:)]) { + [self.delegate attributedLabel:self didLongPressLinkWithPhoneNumber:result.phoneNumber atPoint:touchPoint]; + return; + } + break; + case NSTextCheckingTypeDate: + if (result.timeZone && [self.delegate respondsToSelector:@selector(attributedLabel:didLongPressLinkWithDate:timeZone:duration:atPoint:)]) { + [self.delegate attributedLabel:self didLongPressLinkWithDate:result.date timeZone:result.timeZone duration:result.duration atPoint:touchPoint]; + return; + } else if ([self.delegate respondsToSelector:@selector(attributedLabel:didLongPressLinkWithDate:atPoint:)]) { + [self.delegate attributedLabel:self didLongPressLinkWithDate:result.date atPoint:touchPoint]; + return; + } + break; + case NSTextCheckingTypeTransitInformation: + if ([self.delegate respondsToSelector:@selector(attributedLabel:didLongPressLinkWithTransitInformation:atPoint:)]) { + [self.delegate attributedLabel:self didLongPressLinkWithTransitInformation:result.components atPoint:touchPoint]; + return; + } + default: + break; + } + + // Fallback to `attributedLabel:didLongPressLinkWithTextCheckingResult:atPoint:` if no other delegate method matched. + if ([self.delegate respondsToSelector:@selector(attributedLabel:didLongPressLinkWithTextCheckingResult:atPoint:)]) { + [self.delegate attributedLabel:self didLongPressLinkWithTextCheckingResult:result atPoint:touchPoint]; + } + } + break; + } + default: + break; + } +} + +#pragma mark - UIResponderStandardEditActions + +- (void)copy:(__unused id)sender { + [[UIPasteboard generalPasteboard] setString:self.text]; +} + +#pragma mark - NSCoding + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:@(self.enabledTextCheckingTypes) forKey:NSStringFromSelector(@selector(enabledTextCheckingTypes))]; + + [coder encodeObject:self.linkModels forKey:NSStringFromSelector(@selector(linkModels))]; + if ([NSMutableParagraphStyle class]) { + [coder encodeObject:self.linkAttributes forKey:NSStringFromSelector(@selector(linkAttributes))]; + [coder encodeObject:self.activeLinkAttributes forKey:NSStringFromSelector(@selector(activeLinkAttributes))]; + [coder encodeObject:self.inactiveLinkAttributes forKey:NSStringFromSelector(@selector(inactiveLinkAttributes))]; + } + [coder encodeObject:@(self.shadowRadius) forKey:NSStringFromSelector(@selector(shadowRadius))]; + [coder encodeObject:@(self.highlightedShadowRadius) forKey:NSStringFromSelector(@selector(highlightedShadowRadius))]; + [coder encodeCGSize:self.highlightedShadowOffset forKey:NSStringFromSelector(@selector(highlightedShadowOffset))]; + [coder encodeObject:self.highlightedShadowColor forKey:NSStringFromSelector(@selector(highlightedShadowColor))]; + [coder encodeObject:@(self.kern) forKey:NSStringFromSelector(@selector(kern))]; + [coder encodeObject:@(self.firstLineIndent) forKey:NSStringFromSelector(@selector(firstLineIndent))]; + [coder encodeObject:@(self.lineSpacing) forKey:NSStringFromSelector(@selector(lineSpacing))]; + [coder encodeObject:@(self.lineHeightMultiple) forKey:NSStringFromSelector(@selector(lineHeightMultiple))]; + [coder encodeUIEdgeInsets:self.textInsets forKey:NSStringFromSelector(@selector(textInsets))]; + [coder encodeInteger:self.verticalAlignment forKey:NSStringFromSelector(@selector(verticalAlignment))]; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [coder encodeObject:self.truncationTokenString forKey:NSStringFromSelector(@selector(truncationTokenString))]; +#pragma clang diagnostic pop + + [coder encodeObject:NSStringFromUIEdgeInsets(self.linkBackgroundEdgeInset) forKey:NSStringFromSelector(@selector(linkBackgroundEdgeInset))]; + [coder encodeObject:self.attributedText forKey:NSStringFromSelector(@selector(attributedText))]; + [coder encodeObject:self.text forKey:NSStringFromSelector(@selector(text))]; +} + +- (id)initWithCoder:(NSCoder *)coder { + self = [super initWithCoder:coder]; + if (!self) { + return nil; + } + + [self commonInit]; + + if ([coder containsValueForKey:NSStringFromSelector(@selector(enabledTextCheckingTypes))]) { + self.enabledTextCheckingTypes = [[coder decodeObjectForKey:NSStringFromSelector(@selector(enabledTextCheckingTypes))] unsignedLongLongValue]; + } + + if ([NSMutableParagraphStyle class]) { + if ([coder containsValueForKey:NSStringFromSelector(@selector(linkAttributes))]) { + self.linkAttributes = [coder decodeObjectForKey:NSStringFromSelector(@selector(linkAttributes))]; + } + + if ([coder containsValueForKey:NSStringFromSelector(@selector(activeLinkAttributes))]) { + self.activeLinkAttributes = [coder decodeObjectForKey:NSStringFromSelector(@selector(activeLinkAttributes))]; + } + + if ([coder containsValueForKey:NSStringFromSelector(@selector(inactiveLinkAttributes))]) { + self.inactiveLinkAttributes = [coder decodeObjectForKey:NSStringFromSelector(@selector(inactiveLinkAttributes))]; + } + } + + if ([coder containsValueForKey:NSStringFromSelector(@selector(links))]) { + NSArray *oldLinks = [coder decodeObjectForKey:NSStringFromSelector(@selector(links))]; + [self addLinksWithTextCheckingResults:oldLinks attributes:nil]; + } + + if ([coder containsValueForKey:NSStringFromSelector(@selector(linkModels))]) { + self.linkModels = [coder decodeObjectForKey:NSStringFromSelector(@selector(linkModels))]; + } + + if ([coder containsValueForKey:NSStringFromSelector(@selector(shadowRadius))]) { + self.shadowRadius = [[coder decodeObjectForKey:NSStringFromSelector(@selector(shadowRadius))] floatValue]; + } + + if ([coder containsValueForKey:NSStringFromSelector(@selector(highlightedShadowRadius))]) { + self.highlightedShadowRadius = [[coder decodeObjectForKey:NSStringFromSelector(@selector(highlightedShadowRadius))] floatValue]; + } + + if ([coder containsValueForKey:NSStringFromSelector(@selector(highlightedShadowOffset))]) { + self.highlightedShadowOffset = [coder decodeCGSizeForKey:NSStringFromSelector(@selector(highlightedShadowOffset))]; + } + + if ([coder containsValueForKey:NSStringFromSelector(@selector(highlightedShadowColor))]) { + self.highlightedShadowColor = [coder decodeObjectForKey:NSStringFromSelector(@selector(highlightedShadowColor))]; + } + + if ([coder containsValueForKey:NSStringFromSelector(@selector(kern))]) { + self.kern = [[coder decodeObjectForKey:NSStringFromSelector(@selector(kern))] floatValue]; + } + + if ([coder containsValueForKey:NSStringFromSelector(@selector(firstLineIndent))]) { + self.firstLineIndent = [[coder decodeObjectForKey:NSStringFromSelector(@selector(firstLineIndent))] floatValue]; + } + + if ([coder containsValueForKey:NSStringFromSelector(@selector(lineSpacing))]) { + self.lineSpacing = [[coder decodeObjectForKey:NSStringFromSelector(@selector(lineSpacing))] floatValue]; + } + + if ([coder containsValueForKey:NSStringFromSelector(@selector(minimumLineHeight))]) { + self.minimumLineHeight = [[coder decodeObjectForKey:NSStringFromSelector(@selector(minimumLineHeight))] floatValue]; + } + + if ([coder containsValueForKey:NSStringFromSelector(@selector(maximumLineHeight))]) { + self.maximumLineHeight = [[coder decodeObjectForKey:NSStringFromSelector(@selector(maximumLineHeight))] floatValue]; + } + + if ([coder containsValueForKey:NSStringFromSelector(@selector(lineHeightMultiple))]) { + self.lineHeightMultiple = [[coder decodeObjectForKey:NSStringFromSelector(@selector(lineHeightMultiple))] floatValue]; + } + + if ([coder containsValueForKey:NSStringFromSelector(@selector(textInsets))]) { + self.textInsets = [coder decodeUIEdgeInsetsForKey:NSStringFromSelector(@selector(textInsets))]; + } + + if ([coder containsValueForKey:NSStringFromSelector(@selector(verticalAlignment))]) { + self.verticalAlignment = [coder decodeIntegerForKey:NSStringFromSelector(@selector(verticalAlignment))]; + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + if ([coder containsValueForKey:NSStringFromSelector(@selector(truncationTokenString))]) { + self.truncationTokenString = [coder decodeObjectForKey:NSStringFromSelector(@selector(truncationTokenString))]; + } +#pragma clang diagnostic pop + + if ([coder containsValueForKey:NSStringFromSelector(@selector(linkBackgroundEdgeInset))]) { + self.linkBackgroundEdgeInset = UIEdgeInsetsFromString([coder decodeObjectForKey:NSStringFromSelector(@selector(linkBackgroundEdgeInset))]); + } + + if ([coder containsValueForKey:NSStringFromSelector(@selector(attributedText))]) { + self.attributedText = [coder decodeObjectForKey:NSStringFromSelector(@selector(attributedText))]; + } else { + self.text = super.text; + } + + return self; +} + +@end + +#pragma mark - TTTAttributedLabelLink + +@implementation TTTAttributedLabelLink + +- (instancetype)initWithAttributes:(NSDictionary *)attributes + activeAttributes:(NSDictionary *)activeAttributes + inactiveAttributes:(NSDictionary *)inactiveAttributes + textCheckingResult:(NSTextCheckingResult *)result { + + if ((self = [super init])) { + _result = result; + _attributes = [attributes copy]; + _activeAttributes = [activeAttributes copy]; + _inactiveAttributes = [inactiveAttributes copy]; + } + + return self; +} + +- (instancetype)initWithAttributesFromLabel:(TTTAttributedLabel*)label + textCheckingResult:(NSTextCheckingResult *)result { + + return [self initWithAttributes:label.linkAttributes + activeAttributes:label.activeLinkAttributes + inactiveAttributes:label.inactiveLinkAttributes + textCheckingResult:result]; +} + +#pragma mark - Accessibility + +- (NSString *) accessibilityValue { + if ([_accessibilityValue length] == 0) { + switch (self.result.resultType) { + case NSTextCheckingTypeLink: + _accessibilityValue = self.result.URL.absoluteString; + break; + case NSTextCheckingTypePhoneNumber: + _accessibilityValue = self.result.phoneNumber; + break; + case NSTextCheckingTypeDate: + _accessibilityValue = [NSDateFormatter localizedStringFromDate:self.result.date + dateStyle:NSDateFormatterLongStyle + timeStyle:NSDateFormatterLongStyle]; + break; + default: + break; + } + } + + return _accessibilityValue; +} + +#pragma mark - NSCoding + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:self.result forKey:NSStringFromSelector(@selector(result))]; + [aCoder encodeObject:self.attributes forKey:NSStringFromSelector(@selector(attributes))]; + [aCoder encodeObject:self.activeAttributes forKey:NSStringFromSelector(@selector(activeAttributes))]; + [aCoder encodeObject:self.inactiveAttributes forKey:NSStringFromSelector(@selector(inactiveAttributes))]; + [aCoder encodeObject:self.accessibilityValue forKey:NSStringFromSelector(@selector(accessibilityValue))]; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + if ((self = [super init])) { + _result = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(result))]; + _attributes = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(attributes))]; + _activeAttributes = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(activeAttributes))]; + _inactiveAttributes = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(inactiveAttributes))]; + self.accessibilityValue = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(accessibilityValue))]; + } + + return self; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TAAbstractDotView.h b/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TAAbstractDotView.h new file mode 100755 index 0000000..bcf91f3 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TAAbstractDotView.h @@ -0,0 +1,24 @@ +// +// TAAbstractDotView.h +// TAPageControl +// +// Created by Tanguy Aladenise on 2015-01-22. +// Copyright (c) 2015 Tanguy Aladenise. All rights reserved. +// + +#import + + +@interface TAAbstractDotView : UIView + + +/** + * A method call let view know which state appearance it should take. Active meaning it's current page. Inactive not the current page. + * + * @param active BOOL to tell if view is active or not + */ +- (void)changeActivityState:(BOOL)active; + + +@end + diff --git a/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TAAbstractDotView.m b/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TAAbstractDotView.m new file mode 100755 index 0000000..d39c4b5 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TAAbstractDotView.m @@ -0,0 +1,30 @@ +// +// TAAbstractDotView.m +// TAPageControl +// +// Created by Tanguy Aladenise on 2015-01-22. +// Copyright (c) 2015 Tanguy Aladenise. All rights reserved. +// + +#import "TAAbstractDotView.h" + + +@implementation TAAbstractDotView + + +- (id)init +{ + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:[NSString stringWithFormat:@"You must override %@ in %@", NSStringFromSelector(_cmd), self.class] + userInfo:nil]; +} + + +- (void)changeActivityState:(BOOL)active +{ + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:[NSString stringWithFormat:@"You must override %@ in %@", NSStringFromSelector(_cmd), self.class] + userInfo:nil]; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TAAnimatedDotView.h b/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TAAnimatedDotView.h new file mode 100755 index 0000000..7214e06 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TAAnimatedDotView.h @@ -0,0 +1,15 @@ +// +// TAAnimatedDotView.h +// TAPageControl +// +// Created by Tanguy Aladenise on 2015-01-22. +// Copyright (c) 2015 Tanguy Aladenise. All rights reserved. +// + +#import "TAAbstractDotView.h" + +@interface TAAnimatedDotView : TAAbstractDotView + +@property (nonatomic, strong) UIColor *dotColor; + +@end diff --git a/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TAAnimatedDotView.m b/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TAAnimatedDotView.m new file mode 100755 index 0000000..e6202bb --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TAAnimatedDotView.m @@ -0,0 +1,88 @@ +// +// TAAnimatedDotView.m +// TAPageControl +// +// Created by Tanguy Aladenise on 2015-01-22. +// Copyright (c) 2015 Tanguy Aladenise. All rights reserved. +// + +#import "TAAnimatedDotView.h" + +static CGFloat const kAnimateDuration = 1; + +@implementation TAAnimatedDotView + +- (instancetype)init +{ + self = [super init]; + if (self) { + [self initialization]; + } + + return self; +} + + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + [self initialization]; + } + return self; +} + + +- (id)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if (self) { + [self initialization]; + } + + return self; +} + +- (void)setDotColor:(UIColor *)dotColor +{ + _dotColor = dotColor; + self.layer.borderColor = dotColor.CGColor; +} + +- (void)initialization +{ + _dotColor = [UIColor whiteColor]; + self.backgroundColor = [UIColor clearColor]; + self.layer.cornerRadius = CGRectGetWidth(self.frame) / 2; + self.layer.borderColor = [UIColor whiteColor].CGColor; + self.layer.borderWidth = 2; +} + + +- (void)changeActivityState:(BOOL)active +{ + if (active) { + [self animateToActiveState]; + } else { + [self animateToDeactiveState]; + } +} + + +- (void)animateToActiveState +{ + [UIView animateWithDuration:kAnimateDuration delay:0 usingSpringWithDamping:.5 initialSpringVelocity:-20 options:UIViewAnimationOptionCurveLinear animations:^{ + self.backgroundColor = _dotColor; + self.transform = CGAffineTransformMakeScale(1.4, 1.4); + } completion:nil]; +} + +- (void)animateToDeactiveState +{ + [UIView animateWithDuration:kAnimateDuration delay:0 usingSpringWithDamping:.5 initialSpringVelocity:0 options:UIViewAnimationOptionCurveLinear animations:^{ + self.backgroundColor = [UIColor clearColor]; + self.transform = CGAffineTransformIdentity; + } completion:nil]; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TADotView.h b/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TADotView.h new file mode 100755 index 0000000..cf984cc --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TADotView.h @@ -0,0 +1,13 @@ +// +// TADotView.h +// TAPageControl +// +// Created by Tanguy Aladenise on 2015-01-22. +// Copyright (c) 2015 Tanguy Aladenise. All rights reserved. +// + +#import "TAAbstractDotView.h" + +@interface TADotView : TAAbstractDotView + +@end diff --git a/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TADotView.m b/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TADotView.m new file mode 100755 index 0000000..092170a --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TADotView.m @@ -0,0 +1,63 @@ +// +// TADotView.m +// TAPageControl +// +// Created by Tanguy Aladenise on 2015-01-22. +// Copyright (c) 2015 Tanguy Aladenise. All rights reserved. +// + +#import "TADotView.h" + +@implementation TADotView + + +- (instancetype)init +{ + self = [super init]; + if (self) { + [self initialization]; + } + + return self; +} + + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + [self initialization]; + } + return self; +} + + +- (id)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if (self) { + [self initialization]; + } + + return self; +} + +- (void)initialization +{ + self.backgroundColor = [UIColor clearColor]; + self.layer.cornerRadius = CGRectGetWidth(self.frame) / 2; + self.layer.borderColor = [UIColor whiteColor].CGColor; + self.layer.borderWidth = 2; +} + + +- (void)changeActivityState:(BOOL)active +{ + if (active) { + self.backgroundColor = [UIColor whiteColor]; + } else { + self.backgroundColor = [UIColor clearColor]; + } +} + +@end diff --git a/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TAPageControl.h b/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TAPageControl.h new file mode 100755 index 0000000..366ff47 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TAPageControl.h @@ -0,0 +1,106 @@ +// +// TAPageControl.h +// TAPageControl +// +// Created by Tanguy Aladenise on 2015-01-21. +// Copyright (c) 2015 Tanguy Aladenise. All rights reserved. +// + +#import + +@protocol TAPageControlDelegate; + + +@interface TAPageControl : UIControl + + +/** + * Dot view customization properties + */ + +/** + * The Class of your custom UIView, make sure to respect the TAAbstractDotView class. + */ +@property (nonatomic) Class dotViewClass; + + +/** + * UIImage to represent a dot. + */ +@property (nonatomic) UIImage *dotImage; + + +/** + * UIImage to represent current page dot. + */ +@property (nonatomic) UIImage *currentDotImage; + + +/** + * Dot size for dot views. Default is 8 by 8. + */ +@property (nonatomic) CGSize dotSize; + + +@property (nonatomic, strong) UIColor *dotColor; + +/** + * Spacing between two dot views. Default is 8. + */ +@property (nonatomic) NSInteger spacingBetweenDots; + + +/** + * Page control setup properties + */ + + +/** + * Delegate for TAPageControl + */ +@property(nonatomic,assign) id delegate; + + +/** + * Number of pages for control. Default is 0. + */ +@property (nonatomic) NSInteger numberOfPages; + + +/** + * Current page on which control is active. Default is 0. + */ +@property (nonatomic) NSInteger currentPage; + + +/** + * Hide the control if there is only one page. Default is NO. + */ +@property (nonatomic) BOOL hidesForSinglePage; + + +/** + * Let the control know if should grow bigger by keeping center, or just get longer (right side expanding). By default YES. + */ +@property (nonatomic) BOOL shouldResizeFromCenter; + + +/** + * Return the minimum size required to display control properly for the given page count. + * + * @param pageCount Number of dots that will require display + * + * @return The CGSize being the minimum size required. + */ +- (CGSize)sizeForNumberOfPages:(NSInteger)pageCount; + + +@end + + +@protocol TAPageControlDelegate + +@optional +- (void)TAPageControl:(TAPageControl *)pageControl didSelectPageAtIndex:(NSInteger)index; + +@end diff --git a/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TAPageControl.m b/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TAPageControl.m new file mode 100755 index 0000000..4475517 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDCycleScrollView/PageControl/TAPageControl.m @@ -0,0 +1,364 @@ +// +// TAPageControl.m +// TAPageControl +// +// Created by Tanguy Aladenise on 2015-01-21. +// Copyright (c) 2015 Tanguy Aladenise. All rights reserved. +// + +#import "TAPageControl.h" +#import "TAAbstractDotView.h" +#import "TAAnimatedDotView.h" +#import "TADotView.h" + +/** + * Default number of pages for initialization + */ +static NSInteger const kDefaultNumberOfPages = 0; + +/** + * Default current page for initialization + */ +static NSInteger const kDefaultCurrentPage = 0; + +/** + * Default setting for hide for single page feature. For initialization + */ +static BOOL const kDefaultHideForSinglePage = NO; + +/** + * Default setting for shouldResizeFromCenter. For initialiation + */ +static BOOL const kDefaultShouldResizeFromCenter = YES; + +/** + * Default spacing between dots + */ +static NSInteger const kDefaultSpacingBetweenDots = 8; + +/** + * Default dot size + */ +static CGSize const kDefaultDotSize = {8, 8}; + + +@interface TAPageControl() + + +/** + * Array of dot views for reusability and touch events. + */ +@property (strong, nonatomic) NSMutableArray *dots; + + +@end + +@implementation TAPageControl + + +#pragma mark - Lifecycle + + +- (id)init +{ + self = [super init]; + if (self) { + [self initialization]; + } + + return self; +} + + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + [self initialization]; + } + return self; +} + + +- (id)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if (self) { + [self initialization]; + } + + return self; +} + + +/** + * Default setup when initiating control + */ +- (void)initialization +{ + self.dotViewClass = [TAAnimatedDotView class]; + self.spacingBetweenDots = kDefaultSpacingBetweenDots; + self.numberOfPages = kDefaultNumberOfPages; + self.currentPage = kDefaultCurrentPage; + self.hidesForSinglePage = kDefaultHideForSinglePage; + self.shouldResizeFromCenter = kDefaultShouldResizeFromCenter; +} + + +#pragma mark - Touch event + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + UITouch *touch = [touches anyObject]; + if (touch.view != self) { + NSInteger index = [self.dots indexOfObject:touch.view]; + if ([self.delegate respondsToSelector:@selector(TAPageControl:didSelectPageAtIndex:)]) { + [self.delegate TAPageControl:self didSelectPageAtIndex:index]; + } + } +} + +#pragma mark - Layout + + +/** + * Resizes and moves the receiver view so it just encloses its subviews. + */ +- (void)sizeToFit +{ + [self updateFrame:YES]; +} + + +- (CGSize)sizeForNumberOfPages:(NSInteger)pageCount +{ + return CGSizeMake((self.dotSize.width + self.spacingBetweenDots) * pageCount - self.spacingBetweenDots , self.dotSize.height); +} + + +/** + * Will update dots display and frame. Reuse existing views or instantiate one if required. Update their position in case frame changed. + */ +- (void)updateDots +{ + if (self.numberOfPages == 0) { + return; + } + + for (NSInteger i = 0; i < self.numberOfPages; i++) { + + UIView *dot; + if (i < self.dots.count) { + dot = [self.dots objectAtIndex:i]; + } else { + dot = [self generateDotView]; + } + + [self updateDotFrame:dot atIndex:i]; + } + + [self changeActivity:YES atIndex:self.currentPage]; + + [self hideForSinglePage]; +} + + +/** + * Update frame control to fit current number of pages. It will apply required size if authorize and required. + * + * @param overrideExistingFrame BOOL to allow frame to be overriden. Meaning the required size will be apply no mattter what. + */ +- (void)updateFrame:(BOOL)overrideExistingFrame +{ + CGPoint center = self.center; + CGSize requiredSize = [self sizeForNumberOfPages:self.numberOfPages]; + + // We apply requiredSize only if authorize to and necessary + if (overrideExistingFrame || ((CGRectGetWidth(self.frame) < requiredSize.width || CGRectGetHeight(self.frame) < requiredSize.height) && !overrideExistingFrame)) { + self.frame = CGRectMake(CGRectGetMinX(self.frame), CGRectGetMinY(self.frame), requiredSize.width, requiredSize.height); + if (self.shouldResizeFromCenter) { + self.center = center; + } + } + + [self resetDotViews]; +} + + +/** + * Update the frame of a specific dot at a specific index + * + * @param dot Dot view + * @param index Page index of dot + */ +- (void)updateDotFrame:(UIView *)dot atIndex:(NSInteger)index +{ + // Dots are always centered within view + CGFloat x = (self.dotSize.width + self.spacingBetweenDots) * index + ( (CGRectGetWidth(self.frame) - [self sizeForNumberOfPages:self.numberOfPages].width) / 2); + CGFloat y = (CGRectGetHeight(self.frame) - self.dotSize.height) / 2; + + dot.frame = CGRectMake(x, y, self.dotSize.width, self.dotSize.height); +} + + +#pragma mark - Utils + + +/** + * Generate a dot view and add it to the collection + * + * @return The UIView object representing a dot + */ +- (UIView *)generateDotView +{ + UIView *dotView; + + if (self.dotViewClass) { + dotView = [[self.dotViewClass alloc] initWithFrame:CGRectMake(0, 0, self.dotSize.width, self.dotSize.height)]; + if ([dotView isKindOfClass:[TAAnimatedDotView class]] && self.dotColor) { + ((TAAnimatedDotView *)dotView).dotColor = self.dotColor; + } + } else { + dotView = [[UIImageView alloc] initWithImage:self.dotImage]; + dotView.frame = CGRectMake(0, 0, self.dotSize.width, self.dotSize.height); + } + + if (dotView) { + [self addSubview:dotView]; + [self.dots addObject:dotView]; + } + + dotView.userInteractionEnabled = YES; + return dotView; +} + + +/** + * Change activity state of a dot view. Current/not currrent. + * + * @param active Active state to apply + * @param index Index of dot for state update + */ +- (void)changeActivity:(BOOL)active atIndex:(NSInteger)index +{ + if (self.dotViewClass) { + TAAbstractDotView *abstractDotView = (TAAbstractDotView *)[self.dots objectAtIndex:index]; + if ([abstractDotView respondsToSelector:@selector(changeActivityState:)]) { + [abstractDotView changeActivityState:active]; + } else { + NSLog(@"Custom view : %@ must implement an 'changeActivityState' method or you can subclass %@ to help you.", self.dotViewClass, [TAAbstractDotView class]); + } + } else if (self.dotImage && self.currentDotImage) { + UIImageView *dotView = (UIImageView *)[self.dots objectAtIndex:index]; + dotView.image = (active) ? self.currentDotImage : self.dotImage; + } +} + + +- (void)resetDotViews +{ + for (UIView *dotView in self.dots) { + [dotView removeFromSuperview]; + } + + [self.dots removeAllObjects]; + [self updateDots]; +} + + +- (void)hideForSinglePage +{ + if (self.dots.count == 1 && self.hidesForSinglePage) { + self.hidden = YES; + } else { + self.hidden = NO; + } +} + +#pragma mark - Setters + + +- (void)setNumberOfPages:(NSInteger)numberOfPages +{ + _numberOfPages = numberOfPages; + + // Update dot position to fit new number of pages + [self resetDotViews]; +} + + +- (void)setSpacingBetweenDots:(NSInteger)spacingBetweenDots +{ + _spacingBetweenDots = spacingBetweenDots; + + [self resetDotViews]; +} + + +- (void)setCurrentPage:(NSInteger)currentPage +{ + // If no pages, no current page to treat. + if (self.numberOfPages == 0 || currentPage == _currentPage) { + _currentPage = currentPage; + return; + } + + // Pre set + [self changeActivity:NO atIndex:_currentPage]; + _currentPage = currentPage; + // Post set + [self changeActivity:YES atIndex:_currentPage]; +} + + +- (void)setDotImage:(UIImage *)dotImage +{ + _dotImage = dotImage; + [self resetDotViews]; + self.dotViewClass = nil; +} + + +- (void)setCurrentDotImage:(UIImage *)currentDotimage +{ + _currentDotImage = currentDotimage; + [self resetDotViews]; + self.dotViewClass = nil; +} + + +- (void)setDotViewClass:(Class)dotViewClass +{ + _dotViewClass = dotViewClass; + self.dotSize = CGSizeZero; + [self resetDotViews]; +} + + +#pragma mark - Getters + + +- (NSMutableArray *)dots +{ + if (!_dots) { + _dots = [[NSMutableArray alloc] init]; + } + + return _dots; +} + + +- (CGSize)dotSize +{ + // Dot size logic depending on the source of the dot view + if (self.dotImage && CGSizeEqualToSize(_dotSize, CGSizeZero)) { + _dotSize = self.dotImage.size; + } else if (self.dotViewClass && CGSizeEqualToSize(_dotSize, CGSizeZero)) { + _dotSize = kDefaultDotSize; + return _dotSize; + } + + return _dotSize; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/SDCycleScrollView/SDCollectionViewCell.h b/SDAutoLayoutDemo/Vender/SDCycleScrollView/SDCollectionViewCell.h new file mode 100644 index 0000000..4d307ab --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDCycleScrollView/SDCollectionViewCell.h @@ -0,0 +1,47 @@ +// +// SDCollectionViewCell.h +// SDCycleScrollView +// +// Created by aier on 15-3-22. +// Copyright (c) 2015年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * 🌟🌟🌟 新建SDCycleScrollView交流QQ群:185534916 🌟🌟🌟 + * + * 在您使用此自动轮播库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 + * 帮您解决问题。 + * 新浪微博:GSD_iOS + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios + * + * 另(我的自动布局库SDAutoLayout): + * 一行代码搞定自动布局!支持Cell和Tableview高度自适应,Label和ScrollView内容自适应,致力于 + * 做最简单易用的AutoLayout库。 + * 视频教程:http://www.letv.com/ptv/vplay/24038772.html + * 用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * GitHub:https://github.com/gsdios/SDAutoLayout + ********************************************************************************* + + */ + + + +#import + +@interface SDCollectionViewCell : UICollectionViewCell + +@property (weak, nonatomic) UIImageView *imageView; +@property (copy, nonatomic) NSString *title; + +@property (nonatomic, strong) UIColor *titleLabelTextColor; +@property (nonatomic, strong) UIFont *titleLabelTextFont; +@property (nonatomic, strong) UIColor *titleLabelBackgroundColor; +@property (nonatomic, assign) CGFloat titleLabelHeight; + +@property (nonatomic, assign) BOOL hasConfigured; + +@end diff --git a/SDAutoLayoutDemo/Vender/SDCycleScrollView/SDCollectionViewCell.m b/SDAutoLayoutDemo/Vender/SDCycleScrollView/SDCollectionViewCell.m new file mode 100644 index 0000000..cd3cfda --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDCycleScrollView/SDCollectionViewCell.m @@ -0,0 +1,106 @@ +// +// SDCollectionViewCell.m +// SDCycleScrollView +// +// Created by aier on 15-3-22. +// Copyright (c) 2015年 GSD. All rights reserved. +// + + +/* + + ********************************************************************************* + * + * 🌟🌟🌟 新建SDCycleScrollView交流QQ群:185534916 🌟🌟🌟 + * + * 在您使用此自动轮播库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 + * 帮您解决问题。 + * 新浪微博:GSD_iOS + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios + * + * 另(我的自动布局库SDAutoLayout): + * 一行代码搞定自动布局!支持Cell和Tableview高度自适应,Label和ScrollView内容自适应,致力于 + * 做最简单易用的AutoLayout库。 + * 视频教程:http://www.letv.com/ptv/vplay/24038772.html + * 用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * GitHub:https://github.com/gsdios/SDAutoLayout + ********************************************************************************* + + */ + + +#import "SDCollectionViewCell.h" +#import "UIView+SDExtension.h" + +@implementation SDCollectionViewCell +{ + __weak UILabel *_titleLabel; +} + + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + [self setupImageView]; + [self setupTitleLabel]; + } + + return self; +} + +- (void)setTitleLabelBackgroundColor:(UIColor *)titleLabelBackgroundColor +{ + _titleLabelBackgroundColor = titleLabelBackgroundColor; + _titleLabel.backgroundColor = titleLabelBackgroundColor; +} + +- (void)setTitleLabelTextColor:(UIColor *)titleLabelTextColor +{ + _titleLabelTextColor = titleLabelTextColor; + _titleLabel.textColor = titleLabelTextColor; +} + +- (void)setTitleLabelTextFont:(UIFont *)titleLabelTextFont +{ + _titleLabelTextFont = titleLabelTextFont; + _titleLabel.font = titleLabelTextFont; +} + +- (void)setupImageView +{ + UIImageView *imageView = [[UIImageView alloc] init]; + _imageView = imageView; + [self addSubview:imageView]; +} + +- (void)setupTitleLabel +{ + UILabel *titleLabel = [[UILabel alloc] init]; + _titleLabel = titleLabel; + _titleLabel.hidden = YES; + [self addSubview:titleLabel]; +} + +- (void)setTitle:(NSString *)title +{ + _title = [title copy]; + _titleLabel.text = [NSString stringWithFormat:@" %@", title]; +} + + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + _imageView.frame = self.bounds; + + CGFloat titleLabelW = self.sd_width; + CGFloat titleLabelH = _titleLabelHeight; + CGFloat titleLabelX = 0; + CGFloat titleLabelY = self.sd_height - titleLabelH; + _titleLabel.frame = CGRectMake(titleLabelX, titleLabelY, titleLabelW, titleLabelH); + _titleLabel.hidden = !_titleLabel.text; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/SDCycleScrollView/SDCycleScrollView.h b/SDAutoLayoutDemo/Vender/SDCycleScrollView/SDCycleScrollView.h new file mode 100644 index 0000000..ee695f7 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDCycleScrollView/SDCycleScrollView.h @@ -0,0 +1,167 @@ +// +// SDCycleScrollView.h +// SDCycleScrollView +// +// Created by aier on 15-3-22. +// Copyright (c) 2015年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * 🌟🌟🌟 新建SDCycleScrollView交流QQ群:185534916 🌟🌟🌟 + * + * 在您使用此自动轮播库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 + * 帮您解决问题。 + * 新浪微博:GSD_iOS + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios + * + * 另(我的自动布局库SDAutoLayout): + * 一行代码搞定自动布局!支持Cell和Tableview高度自适应,Label和ScrollView内容自适应,致力于 + * 做最简单易用的AutoLayout库。 + * 视频教程:http://www.letv.com/ptv/vplay/24038772.html + * 用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * GitHub:https://github.com/gsdios/SDAutoLayout + ********************************************************************************* + + */ + + +#import + +typedef enum { + SDCycleScrollViewPageContolAlimentRight, + SDCycleScrollViewPageContolAlimentCenter +} SDCycleScrollViewPageContolAliment; + +typedef enum { + SDCycleScrollViewPageContolStyleClassic, // 系统自带经典样式 + SDCycleScrollViewPageContolStyleAnimated, // 动画效果pagecontrol + SDCycleScrollViewPageContolStyleNone // 不显示pagecontrol +} SDCycleScrollViewPageContolStyle; + +@class SDCycleScrollView; + +@protocol SDCycleScrollViewDelegate + +@optional + +/** 点击图片回调 */ +- (void)cycleScrollView:(SDCycleScrollView *)cycleScrollView didSelectItemAtIndex:(NSInteger)index; + +/** 图片滚动回调 */ +- (void)cycleScrollView:(SDCycleScrollView *)cycleScrollView didScrollToIndex:(NSInteger)index; + +@end + +@interface SDCycleScrollView : UIView + + + +// >>>>>>>>>>>>>>>>>>>>>>>>>> 数据源接口 + +/** 本地图片数组 */ +@property (nonatomic, strong) NSArray *localizationImageNamesGroup; + +/** 网络图片 url string 数组 */ +@property (nonatomic, strong) NSArray *imageURLStringsGroup; + +/** 每张图片对应要显示的文字数组 */ +@property (nonatomic, strong) NSArray *titlesGroup; + + + + + +// >>>>>>>>>>>>>>>>>>>>>>>>> 滚动控制接口 + +/** 自动滚动间隔时间,默认2s */ +@property (nonatomic, assign) CGFloat autoScrollTimeInterval; + +/** 是否无限循环,默认Yes */ +@property(nonatomic,assign) BOOL infiniteLoop; + +/** 是否自动滚动,默认Yes */ +@property(nonatomic,assign) BOOL autoScroll; + +@property (nonatomic, weak) id delegate; + +/** block监听点击方式 */ +@property (nonatomic, copy) void (^clickItemOperationBlock)(NSInteger currentIndex); + + +// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 自定义样式接口 + +/** 轮播图片的ContentMode */ +@property (nonatomic, assign) UIViewContentMode bannerImageViewContentMode; + +/** 占位图,用于网络未加载到图片时 */ +@property (nonatomic, strong) UIImage *placeholderImage; + + + +/** 是否显示分页控件 */ +@property (nonatomic, assign) BOOL showPageControl; + +/** 是否在只有一张图时隐藏pagecontrol,默认为YES */ +@property(nonatomic) BOOL hidesForSinglePage; + +/** pagecontrol 样式,默认为动画样式 */ +@property (nonatomic, assign) SDCycleScrollViewPageContolStyle pageControlStyle; + +/** 分页控件位置 */ +@property (nonatomic, assign) SDCycleScrollViewPageContolAliment pageControlAliment; + +/** 分页控件小圆标大小 */ +@property (nonatomic, assign) CGSize pageControlDotSize; + +/** 当前分页控件小圆标颜色 */ +@property (nonatomic, strong) UIColor *currentPageDotColor; + +/** 其他分页控件小圆标颜色 */ +@property (nonatomic, strong) UIColor *pageDotColor; + +/** 当前分页控件小圆标图片 */ +@property (nonatomic, strong) UIImage *currentPageDotImage; + +/** 其他分页控件小圆标图片 */ +@property (nonatomic, strong) UIImage *pageDotImage; + + + +/** 轮播文字label字体颜色 */ +@property (nonatomic, strong) UIColor *titleLabelTextColor; + +/** 轮播文字label字体大小 */ +@property (nonatomic, strong) UIFont *titleLabelTextFont; + +/** 轮播文字label背景颜色 */ +@property (nonatomic, strong) UIColor *titleLabelBackgroundColor; + +/** 轮播文字label高度 */ +@property (nonatomic, assign) CGFloat titleLabelHeight; + + + +// 网络图片轮播初始化方式 + ++ (instancetype)cycleScrollViewWithFrame:(CGRect)frame delegate:(id)delegate placeholderImage:(UIImage *)placeholderImage; + ++ (instancetype)cycleScrollViewWithFrame:(CGRect)frame imageURLStringsGroup:(NSArray *)imageURLStringsGroup; + + +// 本地图片轮播初始化方式 ++ (instancetype)cycleScrollViewWithFrame:(CGRect)frame imageNamesGroup:(NSArray *)imageNamesGroup; + + +// >>>>>>>>>>>>>>>>>>>>>>>>> 清除缓存接口 + +/** 清除图片缓存(此次升级后统一使用SDWebImage管理图片加载和缓存) */ ++ (void)clearImagesCache; + +/** 清除图片缓存(兼容旧版本方法) */ +- (void)clearCache; + +@end diff --git a/SDAutoLayoutDemo/Vender/SDCycleScrollView/SDCycleScrollView.m b/SDAutoLayoutDemo/Vender/SDCycleScrollView/SDCycleScrollView.m new file mode 100644 index 0000000..b6075dd --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDCycleScrollView/SDCycleScrollView.m @@ -0,0 +1,534 @@ +// +// SDCycleScrollView.m +// SDCycleScrollView +// +// Created by aier on 15-3-22. +// Copyright (c) 2015年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * 🌟🌟🌟 新建SDCycleScrollView交流QQ群:185534916 🌟🌟🌟 + * + * 在您使用此自动轮播库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 + * 帮您解决问题。 + * 新浪微博:GSD_iOS + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios + * + * 另(我的自动布局库SDAutoLayout): + * 一行代码搞定自动布局!支持Cell和Tableview高度自适应,Label和ScrollView内容自适应,致力于 + * 做最简单易用的AutoLayout库。 + * 视频教程:http://www.letv.com/ptv/vplay/24038772.html + * 用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * GitHub:https://github.com/gsdios/SDAutoLayout + ********************************************************************************* + + */ + + +#import "SDCycleScrollView.h" +#import "SDCollectionViewCell.h" +#import "UIView+SDExtension.h" +#import "TAPageControl.h" +#import "UIImageView+WebCache.h" +#import "SDImageCache.h" + + + +NSString * const ID = @"cycleCell"; + +@interface SDCycleScrollView () + + +@property (nonatomic, weak) UICollectionView *mainView; // 显示图片的collectionView +@property (nonatomic, weak) UICollectionViewFlowLayout *flowLayout; +@property (nonatomic, strong) NSArray *imageURLsGroup; +@property (nonatomic, weak) NSTimer *timer; +@property (nonatomic, assign) NSInteger totalItemsCount; +@property (nonatomic, weak) UIControl *pageControl; + +@property (nonatomic, weak) UIImageView *backgroundImageView; // 当imageURLs为空时的背景图 + +@property (nonatomic, assign) NSInteger networkFailedRetryCount; + +@end + +@implementation SDCycleScrollView + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + [self initialization]; + [self setupMainView]; + } + return self; +} + +- (void)awakeFromNib +{ + [self initialization]; + [self setupMainView]; +} + +- (void)initialization +{ + _pageControlAliment = SDCycleScrollViewPageContolAlimentCenter; + _autoScrollTimeInterval = 2.0; + _titleLabelTextColor = [UIColor whiteColor]; + _titleLabelTextFont= [UIFont systemFontOfSize:14]; + _titleLabelBackgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5]; + _titleLabelHeight = 30; + _autoScroll = YES; + _infiniteLoop = YES; + _showPageControl = YES; + _pageControlDotSize = CGSizeMake(10, 10); + _pageControlStyle = SDCycleScrollViewPageContolStyleClassic; + _hidesForSinglePage = YES; + _currentPageDotColor = [UIColor whiteColor]; + _pageDotColor = [UIColor lightGrayColor]; + _bannerImageViewContentMode = UIViewContentModeScaleToFill; + + self.backgroundColor = [UIColor lightGrayColor]; + +} + ++ (instancetype)cycleScrollViewWithFrame:(CGRect)frame imageNamesGroup:(NSArray *)imageNamesGroup +{ + SDCycleScrollView *cycleScrollView = [[self alloc] initWithFrame:frame]; + cycleScrollView.localizationImageNamesGroup = [NSMutableArray arrayWithArray:imageNamesGroup]; + return cycleScrollView; +} + ++ (instancetype)cycleScrollViewWithFrame:(CGRect)frame imageURLStringsGroup:(NSArray *)imageURLsGroup +{ + SDCycleScrollView *cycleScrollView = [[self alloc] initWithFrame:frame]; + cycleScrollView.imageURLStringsGroup = [NSMutableArray arrayWithArray:imageURLsGroup]; + return cycleScrollView; +} + ++ (instancetype)cycleScrollViewWithFrame:(CGRect)frame delegate:(id)delegate placeholderImage:(UIImage *)placeholderImage +{ + SDCycleScrollView *cycleScrollView = [[self alloc] initWithFrame:frame]; + cycleScrollView.delegate = delegate; + cycleScrollView.placeholderImage = placeholderImage; + + return cycleScrollView; +} + +// 设置显示图片的collectionView +- (void)setupMainView +{ + UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init]; + flowLayout.minimumLineSpacing = 0; + flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + _flowLayout = flowLayout; + + UICollectionView *mainView = [[UICollectionView alloc] initWithFrame:self.bounds collectionViewLayout:flowLayout]; + mainView.backgroundColor = [UIColor clearColor]; + mainView.pagingEnabled = YES; + mainView.showsHorizontalScrollIndicator = NO; + mainView.showsVerticalScrollIndicator = NO; + [mainView registerClass:[SDCollectionViewCell class] forCellWithReuseIdentifier:ID]; + mainView.dataSource = self; + mainView.delegate = self; + [self addSubview:mainView]; + _mainView = mainView; +} + + +#pragma mark - properties + +- (void)setPlaceholderImage:(UIImage *)placeholderImage +{ + _placeholderImage = placeholderImage; + + if (!self.backgroundImageView) { + UIImageView *bgImageView = [UIImageView new]; + [self insertSubview:bgImageView belowSubview:self.mainView]; + self.backgroundImageView = bgImageView; + } + + self.backgroundImageView.image = placeholderImage; +} + +- (void)setPageControlDotSize:(CGSize)pageControlDotSize +{ + _pageControlDotSize = pageControlDotSize; + [self setupPageControl]; + if ([self.pageControl isKindOfClass:[TAPageControl class]]) { + TAPageControl *pageContol = (TAPageControl *)_pageControl; + pageContol.dotSize = pageControlDotSize; + } +} + +- (void)setShowPageControl:(BOOL)showPageControl +{ + _showPageControl = showPageControl; + + _pageControl.hidden = !showPageControl; +} + +- (void)setCurrentPageDotColor:(UIColor *)currentPageDotColor +{ + _currentPageDotColor = currentPageDotColor; + if ([self.pageControl isKindOfClass:[TAPageControl class]]) { + TAPageControl *pageControl = (TAPageControl *)_pageControl; + pageControl.dotColor = currentPageDotColor; + } else { + UIPageControl *pageControl = (UIPageControl *)_pageControl; + pageControl.currentPageIndicatorTintColor = currentPageDotColor; + } + +} + +- (void)setPageDotColor:(UIColor *)pageDotColor +{ + _pageDotColor = pageDotColor; + + if ([self.pageDotColor isKindOfClass:[UIPageControl class]]) { + UIPageControl *pageControl = (UIPageControl *)_pageControl; + pageControl.pageIndicatorTintColor = pageDotColor; + } +} + +- (void)setCurrentPageDotImage:(UIImage *)currentPageDotImage +{ + _currentPageDotImage = currentPageDotImage; + + [self setCustomPageControlDotImage:currentPageDotImage isCurrentPageDot:YES]; +} + +- (void)setPageDotImage:(UIImage *)pageDotImage +{ + _pageDotImage = pageDotImage; + + [self setCustomPageControlDotImage:pageDotImage isCurrentPageDot:NO]; +} + +- (void)setCustomPageControlDotImage:(UIImage *)image isCurrentPageDot:(BOOL)isCurrentPageDot +{ + if (!image || !self.pageControl) return; + + if ([self.pageControl isKindOfClass:[TAPageControl class]]) { + TAPageControl *pageControl = (TAPageControl *)_pageControl; + if (isCurrentPageDot) { + pageControl.currentDotImage = image; + } else { + pageControl.dotImage = image; + } + } else { + UIPageControl *pageControl = (UIPageControl *)_pageControl; + if (isCurrentPageDot) { + [pageControl setValue:image forKey:@"_currentPageImage"]; + } else { + [pageControl setValue:image forKey:@"_pageImage"]; + } + } +} + +-(void)setAutoScroll:(BOOL)autoScroll{ + _autoScroll = autoScroll; + [_timer invalidate]; + _timer = nil; + + if (_autoScroll) { + [self setupTimer]; + } +} + +- (void)setAutoScrollTimeInterval:(CGFloat)autoScrollTimeInterval +{ + _autoScrollTimeInterval = autoScrollTimeInterval; + + [self setAutoScroll:self.autoScroll]; +} + +- (void)setPageControlStyle:(SDCycleScrollViewPageContolStyle)pageControlStyle +{ + _pageControlStyle = pageControlStyle; + + [self setupPageControl]; +} + +- (void)setImageURLsGroup:(NSArray *)imageURLsGroup +{ + _imageURLsGroup = imageURLsGroup; + + _totalItemsCount = self.infiniteLoop ? self.imageURLsGroup.count * 100 : self.imageURLsGroup.count; + + if (imageURLsGroup.count != 1) { + self.mainView.scrollEnabled = YES; + [self setAutoScroll:self.autoScroll]; + } else { + self.mainView.scrollEnabled = NO; + } + + [self setupPageControl]; + [self.mainView reloadData]; +} + +- (void)setImageURLStringsGroup:(NSArray *)imageURLStringsGroup +{ + _imageURLStringsGroup = imageURLStringsGroup; + + NSMutableArray *temp = [NSMutableArray new]; + [_imageURLStringsGroup enumerateObjectsUsingBlock:^(NSString * obj, NSUInteger idx, BOOL * stop) { + NSURL *url; + if ([obj isKindOfClass:[NSString class]]) { + url = [NSURL URLWithString:obj]; + } else if ([obj isKindOfClass:[NSURL class]]) { + url = (NSURL *)obj; + } + if (url) { + [temp addObject:url]; + } + }]; + self.imageURLsGroup = [temp copy]; +} + +- (void)setLocalizationImageNamesGroup:(NSArray *)localizationImageNamesGroup +{ + _localizationImageNamesGroup = localizationImageNamesGroup; + + NSMutableArray *temp = [NSMutableArray new]; + [_localizationImageNamesGroup enumerateObjectsUsingBlock:^(NSString * obj, NSUInteger idx, BOOL * stop) { + NSURL *url = [[NSBundle mainBundle] URLForResource:obj withExtension:nil]; + if (url) { + [temp addObject:url]; + } + }]; + self.imageURLsGroup = [temp copy]; +} + +#pragma mark - actions + + +- (void)setupPageControl +{ + if (_pageControl) [_pageControl removeFromSuperview]; // 重新加载数据时调整 + + if ((self.imageURLsGroup.count <= 1) && self.hidesForSinglePage) { + return; + } + + switch (self.pageControlStyle) { + case SDCycleScrollViewPageContolStyleAnimated: + { + TAPageControl *pageControl = [[TAPageControl alloc] init]; + pageControl.numberOfPages = self.imageURLsGroup.count; + pageControl.dotColor = self.currentPageDotColor; + pageControl.userInteractionEnabled = NO; + [self addSubview:pageControl]; + _pageControl = pageControl; + } + break; + + case SDCycleScrollViewPageContolStyleClassic: + { + UIPageControl *pageControl = [[UIPageControl alloc] init]; + pageControl.numberOfPages = self.imageURLsGroup.count; + pageControl.currentPageIndicatorTintColor = self.currentPageDotColor; + pageControl.pageIndicatorTintColor = self.pageDotColor; + pageControl.userInteractionEnabled = NO; + [self addSubview:pageControl]; + _pageControl = pageControl; + } + break; + + default: + break; + } + + // 重设pagecontroldot图片 + self.currentPageDotImage = self.currentPageDotImage; + self.pageDotImage = self.pageDotImage; +} + + +- (void)automaticScroll +{ + if (0 == _totalItemsCount) return; + int currentIndex = _mainView.contentOffset.x / _flowLayout.itemSize.width; + int targetIndex = currentIndex + 1; + if (targetIndex == _totalItemsCount) { + if (self.infiniteLoop) { + targetIndex = _totalItemsCount * 0.5; + }else{ + return; + } + [_mainView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:targetIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:NO]; + } + [_mainView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:targetIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:YES]; +} + +- (void)setupTimer +{ + NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:self.autoScrollTimeInterval target:self selector:@selector(automaticScroll) userInfo:nil repeats:YES]; + _timer = timer; + [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; +} + +- (void)clearCache +{ + [[self class] clearImagesCache]; +} + ++ (void)clearImagesCache +{ + [[[SDWebImageManager sharedManager] imageCache] clearDisk]; +} + +#pragma mark - life circles + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + _flowLayout.itemSize = self.frame.size; + + _mainView.frame = self.bounds; + if (_mainView.contentOffset.x == 0 && _totalItemsCount) { + int targetIndex = 0; + if (self.infiniteLoop) { + targetIndex = _totalItemsCount * 0.5; + }else{ + targetIndex = 0; + } + [_mainView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:targetIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:NO]; + } + + CGSize size = CGSizeZero; + if ([self.pageControl isKindOfClass:[TAPageControl class]]) { + TAPageControl *pageControl = (TAPageControl *)_pageControl; + size = [pageControl sizeForNumberOfPages:self.imageURLsGroup.count]; + } else { + size = CGSizeMake(self.imageURLsGroup.count * self.pageControlDotSize.width * 1.2, self.pageControlDotSize.height); + } + CGFloat x = (self.sd_width - size.width) * 0.5; + if (self.pageControlAliment == SDCycleScrollViewPageContolAlimentRight) { + x = self.mainView.sd_width - size.width - 10; + } + CGFloat y = self.mainView.sd_height - size.height - 10; + + if ([self.pageControl isKindOfClass:[TAPageControl class]]) { + TAPageControl *pageControl = (TAPageControl *)_pageControl; + [pageControl sizeToFit]; + } + + self.pageControl.frame = CGRectMake(x, y, size.width, size.height); + self.pageControl.hidden = !_showPageControl; + + if (self.backgroundImageView) { + self.backgroundImageView.frame = self.bounds; + } + +} + +//解决当父View释放时,当前视图因为被Timer强引用而不能释放的问题 +- (void)willMoveToSuperview:(UIView *)newSuperview +{ + if (!newSuperview) { + [_timer invalidate]; + _timer = nil; + } +} + +//解决当timer释放后 回调scrollViewDidScroll时访问野指针导致崩溃 +- (void)dealloc { + _mainView.delegate = nil; + _mainView.dataSource = nil; +} + +#pragma mark - public actions + + +#pragma mark - UICollectionViewDataSource + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section +{ + return _totalItemsCount; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath +{ + SDCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath]; + long itemIndex = indexPath.item % self.imageURLsGroup.count; + + NSURL *url = self.imageURLsGroup[itemIndex]; + [cell.imageView sd_setImageWithURL:url placeholderImage:self.placeholderImage]; + + if (_titlesGroup.count) { + cell.title = _titlesGroup[itemIndex]; + } + + if (!cell.hasConfigured) { + cell.titleLabelBackgroundColor = self.titleLabelBackgroundColor; + cell.titleLabelHeight = self.titleLabelHeight; + cell.titleLabelTextColor = self.titleLabelTextColor; + cell.titleLabelTextFont = self.titleLabelTextFont; + cell.hasConfigured = YES; + cell.imageView.contentMode = self.bannerImageViewContentMode; + } + + return cell; +} + +- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath +{ + if ([self.delegate respondsToSelector:@selector(cycleScrollView:didSelectItemAtIndex:)]) { + [self.delegate cycleScrollView:self didSelectItemAtIndex:indexPath.item % self.imageURLsGroup.count]; + } + if (self.clickItemOperationBlock) { + self.clickItemOperationBlock(indexPath.item % self.imageURLsGroup.count); + } +} + + +#pragma mark - UIScrollViewDelegate + +- (void)scrollViewDidScroll:(UIScrollView *)scrollView +{ + int itemIndex = (scrollView.contentOffset.x + self.mainView.sd_width * 0.5) / self.mainView.sd_width; + if (!self.imageURLsGroup.count) return; // 解决清除timer时偶尔会出现的问题 + int indexOnPageControl = itemIndex % self.imageURLsGroup.count; + + if ([self.pageControl isKindOfClass:[TAPageControl class]]) { + TAPageControl *pageControl = (TAPageControl *)_pageControl; + pageControl.currentPage = indexOnPageControl; + } else { + UIPageControl *pageControl = (UIPageControl *)_pageControl; + pageControl.currentPage = indexOnPageControl; + } +} + +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView +{ + if (self.autoScroll) { + [_timer invalidate]; + _timer = nil; + } +} + +- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate +{ + if (self.autoScroll) { + [self setupTimer]; + } +} + +- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView +{ + int itemIndex = (scrollView.contentOffset.x + self.mainView.sd_width * 0.5) / self.mainView.sd_width; + if (!self.imageURLsGroup.count) return; // 解决清除timer时偶尔会出现的问题 + int indexOnPageControl = itemIndex % self.imageURLsGroup.count; + + if ([self.delegate respondsToSelector:@selector(cycleScrollView:didScrollToIndex:)]) { + [self.delegate cycleScrollView:self didScrollToIndex:indexOnPageControl]; + } +} + + +@end diff --git a/SDAutoLayoutDemo/Vender/SDCycleScrollView/UIView+SDExtension.h b/SDAutoLayoutDemo/Vender/SDCycleScrollView/UIView+SDExtension.h new file mode 100644 index 0000000..7fa220e --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDCycleScrollView/UIView+SDExtension.h @@ -0,0 +1,44 @@ +// +// UIView+SDExtension.h +// SDRefreshView +// +// Created by aier on 15-2-23. +// Copyright (c) 2015年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * 🌟🌟🌟 新建SDCycleScrollView交流QQ群:185534916 🌟🌟🌟 + * + * 在您使用此自动轮播库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 + * 帮您解决问题。 + * 新浪微博:GSD_iOS + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios + * + * 另(我的自动布局库SDAutoLayout): + * 一行代码搞定自动布局!支持Cell和Tableview高度自适应,Label和ScrollView内容自适应,致力于 + * 做最简单易用的AutoLayout库。 + * 视频教程:http://www.letv.com/ptv/vplay/24038772.html + * 用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * GitHub:https://github.com/gsdios/SDAutoLayout + ********************************************************************************* + + */ + +#import + +#define SDColorCreater(r, g, b, a) [UIColor colorWithRed:(r / 255.0) green:(g / 255.0) blue:(b / 255.0) alpha:a] + + +@interface UIView (SDExtension) + +@property (nonatomic, assign) CGFloat sd_height; +@property (nonatomic, assign) CGFloat sd_width; + +@property (nonatomic, assign) CGFloat sd_y; +@property (nonatomic, assign) CGFloat sd_x; + +@end diff --git a/SDAutoLayoutDemo/Vender/SDCycleScrollView/UIView+SDExtension.m b/SDAutoLayoutDemo/Vender/SDCycleScrollView/UIView+SDExtension.m new file mode 100644 index 0000000..1be615d --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDCycleScrollView/UIView+SDExtension.m @@ -0,0 +1,107 @@ +// +// UIView+SDExtension.m +// SDRefreshView +// +// Created by aier on 15-2-23. +// Copyright (c) 2015年 GSD. All rights reserved. +// + +/* + + ********************************************************************************* + * + * 🌟🌟🌟 新建SDCycleScrollView交流QQ群:185534916 🌟🌟🌟 + * + * 在您使用此自动轮播库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 + * 帮您解决问题。 + * 新浪微博:GSD_iOS + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios + * + * 另(我的自动布局库SDAutoLayout): + * 一行代码搞定自动布局!支持Cell和Tableview高度自适应,Label和ScrollView内容自适应,致力于 + * 做最简单易用的AutoLayout库。 + * 视频教程:http://www.letv.com/ptv/vplay/24038772.html + * 用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * GitHub:https://github.com/gsdios/SDAutoLayout + ********************************************************************************* + + */ + +/* + + ********************************************************************************* + * + * 在您使用此自动轮播库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并 + * 帮您解决问题。 + * 新浪微博:GSD_iOS + * Email : gsdios@126.com + * GitHub: https://github.com/gsdios + * + * 另(我的自动布局库SDAutoLayout): + * 一行代码搞定自动布局!支持Cell和Tableview高度自适应,Label和ScrollView内容自适应,致力于 + * 做最简单易用的AutoLayout库。 + * 视频教程:http://www.letv.com/ptv/vplay/24038772.html + * 用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md + * GitHub:https://github.com/gsdios/SDAutoLayout + ********************************************************************************* + + */ + + +#import "UIView+SDExtension.h" + +@implementation UIView (SDExtension) + +- (CGFloat)sd_height +{ + return self.frame.size.height; +} + +- (void)setSd_height:(CGFloat)sd_height +{ + CGRect temp = self.frame; + temp.size.height = sd_height; + self.frame = temp; +} + +- (CGFloat)sd_width +{ + return self.frame.size.width; +} + +- (void)setSd_width:(CGFloat)sd_width +{ + CGRect temp = self.frame; + temp.size.width = sd_width; + self.frame = temp; +} + + +- (CGFloat)sd_y +{ + return self.frame.origin.y; +} + +- (void)setSd_y:(CGFloat)sd_y +{ + CGRect temp = self.frame; + temp.origin.y = sd_y; + self.frame = temp; +} + +- (CGFloat)sd_x +{ + return self.frame.origin.x; +} + +- (void)setSd_x:(CGFloat)sd_x +{ + CGRect temp = self.frame; + temp.origin.x = sd_x; + self.frame = temp; +} + + + +@end diff --git a/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDBrowserImageView.h b/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDBrowserImageView.h new file mode 100755 index 0000000..e5d88e5 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDBrowserImageView.h @@ -0,0 +1,27 @@ +// +// SDBrowserImageView.h +// SDPhotoBrowser +// +// Created by aier on 15-2-6. +// Copyright (c) 2015年 GSD. All rights reserved. +// + +#import +#import "SDWaitingView.h" + + +@interface SDBrowserImageView : UIImageView + +@property (nonatomic, assign) CGFloat progress; +@property (nonatomic, assign, readonly) BOOL isScaled; +@property (nonatomic, assign) BOOL hasLoadedImage; + +- (void)eliminateScale; // 清除缩放 + +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder; + +- (void)doubleTapToZommWithScale:(CGFloat)scale; + +- (void)clear; + +@end diff --git a/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDBrowserImageView.m b/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDBrowserImageView.m new file mode 100755 index 0000000..03d6567 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDBrowserImageView.m @@ -0,0 +1,236 @@ +// +// SDBrowserImageView.m +// SDPhotoBrowser +// +// Created by aier on 15-2-6. +// Copyright (c) 2015年 GSD. All rights reserved. +// + +#import "SDBrowserImageView.h" +#import "UIImageView+WebCache.h" +#import "SDPhotoBrowserConfig.h" + +@implementation SDBrowserImageView +{ + __weak SDWaitingView *_waitingView; + BOOL _didCheckSize; + UIScrollView *_scroll; + UIImageView *_scrollImageView; + UIScrollView *_zoomingScroolView; + UIImageView *_zoomingImageView; + CGFloat _totalScale; +} + + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + self.userInteractionEnabled = YES; + self.contentMode = UIViewContentModeScaleAspectFit; + _totalScale = 1.0; + + // 捏合手势缩放图片 + UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(zoomImage:)]; + pinch.delegate = self; + [self addGestureRecognizer:pinch]; + + + + } + return self; +} + +- (BOOL)isScaled +{ + return 1.0 != _totalScale; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + _waitingView.center = CGPointMake(self.frame.size.width * 0.5, self.frame.size.height * 0.5); + + CGSize imageSize = self.image.size; + + if (self.bounds.size.width * (imageSize.height / imageSize.width) > self.bounds.size.height) { + if (!_scroll) { + UIScrollView *scroll = [[UIScrollView alloc] init]; + scroll.backgroundColor = [UIColor whiteColor]; + UIImageView *imageView = [[UIImageView alloc] init]; + imageView.image = self.image; + _scrollImageView = imageView; + [scroll addSubview:imageView]; + scroll.backgroundColor = SDPhotoBrowserBackgrounColor; + _scroll = scroll; + [self addSubview:scroll]; + if (_waitingView) { + [self bringSubviewToFront:_waitingView]; + } + } + _scroll.frame = self.bounds; + + CGFloat imageViewH = self.bounds.size.width * (imageSize.height / imageSize.width); + + _scrollImageView.bounds = CGRectMake(0, 0, _scroll.frame.size.width, imageViewH); + _scrollImageView.center = CGPointMake(_scroll.frame.size.width * 0.5, _scrollImageView.frame.size.height * 0.5); + _scroll.contentSize = CGSizeMake(0, _scrollImageView.bounds.size.height); + + } else { + if (_scroll) [_scroll removeFromSuperview]; // 防止旋转时适配的scrollView的影响 + } + +} + + + +- (void)setProgress:(CGFloat)progress +{ + _progress = progress; + _waitingView.progress = progress; + +} + +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder +{ + SDWaitingView *waiting = [[SDWaitingView alloc] init]; + waiting.bounds = CGRectMake(0, 0, 100, 100); + waiting.mode = SDWaitingViewProgressMode; + _waitingView = waiting; + [self addSubview:waiting]; + + + __weak SDBrowserImageView *imageViewWeak = self; + + [self sd_setImageWithURL:url placeholderImage:placeholder options:SDWebImageRetryFailed progress:^(NSInteger receivedSize, NSInteger expectedSize) { + imageViewWeak.progress = (CGFloat)receivedSize / expectedSize; + + } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + [imageViewWeak removeWaitingView]; + + + if (error) { + UILabel *label = [[UILabel alloc] init]; + label.bounds = CGRectMake(0, 0, 160, 30); + label.center = CGPointMake(imageViewWeak.bounds.size.width * 0.5, imageViewWeak.bounds.size.height * 0.5); + label.text = @"图片加载失败"; + label.font = [UIFont systemFontOfSize:16]; + label.textColor = [UIColor whiteColor]; + label.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.8]; + label.layer.cornerRadius = 5; + label.clipsToBounds = YES; + label.textAlignment = NSTextAlignmentCenter; + [imageViewWeak addSubview:label]; + } else { + _scrollImageView.image = image; + [_scrollImageView setNeedsDisplay]; + } + + }]; +} + +- (void)zoomImage:(UIPinchGestureRecognizer *)recognizer +{ + [self prepareForImageViewScaling]; + CGFloat scale = recognizer.scale; + CGFloat temp = _totalScale + (scale - 1); + [self setTotalScale:temp]; + recognizer.scale = 1.0; +} + +- (void)setTotalScale:(CGFloat)totalScale +{ + if ((_totalScale < 0.5 && totalScale < _totalScale) || (_totalScale > 2.0 && totalScale > _totalScale)) return; // 最大缩放 2倍,最小0.5倍 + + [self zoomWithScale:totalScale]; +} + +- (void)zoomWithScale:(CGFloat)scale +{ + _totalScale = scale; + + _zoomingImageView.transform = CGAffineTransformMakeScale(scale, scale); + + if (scale > 1) { + CGFloat contentW = _zoomingImageView.frame.size.width; + CGFloat contentH = MAX(_zoomingImageView.frame.size.height, self.frame.size.height); + + _zoomingImageView.center = CGPointMake(contentW * 0.5, contentH * 0.5); + _zoomingScroolView.contentSize = CGSizeMake(contentW, contentH); + + + CGPoint offset = _zoomingScroolView.contentOffset; + offset.x = (contentW - _zoomingScroolView.frame.size.width) * 0.5; +// offset.y = (contentH - _zoomingImageView.frame.size.height) * 0.5; + _zoomingScroolView.contentOffset = offset; + + } else { + _zoomingScroolView.contentSize = _zoomingScroolView.frame.size; + _zoomingScroolView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0); + _zoomingImageView.center = _zoomingScroolView.center; + } +} + +- (void)doubleTapToZommWithScale:(CGFloat)scale +{ + [self prepareForImageViewScaling]; + [UIView animateWithDuration:0.5 animations:^{ + [self zoomWithScale:scale]; + } completion:^(BOOL finished) { + if (scale == 1) { + [self clear]; + } + }]; +} + +- (void)prepareForImageViewScaling +{ + if (!_zoomingScroolView) { + _zoomingScroolView = [[UIScrollView alloc] initWithFrame:self.bounds]; + _zoomingScroolView.backgroundColor = SDPhotoBrowserBackgrounColor; + _zoomingScroolView.contentSize = self.bounds.size; + UIImageView *zoomingImageView = [[UIImageView alloc] initWithImage:self.image]; + CGSize imageSize = zoomingImageView.image.size; + CGFloat imageViewH = self.bounds.size.height; + if (imageSize.width > 0) { + imageViewH = self.bounds.size.width * (imageSize.height / imageSize.width); + } + zoomingImageView.bounds = CGRectMake(0, 0, self.bounds.size.width, imageViewH); + zoomingImageView.center = _zoomingScroolView.center; + zoomingImageView.contentMode = UIViewContentModeScaleAspectFit; + _zoomingImageView = zoomingImageView; + [_zoomingScroolView addSubview:zoomingImageView]; + [self addSubview:_zoomingScroolView]; + } +} + +- (void)scaleImage:(CGFloat)scale +{ + [self prepareForImageViewScaling]; + [self setTotalScale:scale]; +} + +// 清除缩放 +- (void)eliminateScale +{ + [self clear]; + _totalScale = 1.0; +} + +- (void)clear +{ + [_zoomingScroolView removeFromSuperview]; + _zoomingScroolView = nil; + _zoomingImageView = nil; + +} + +- (void)removeWaitingView +{ + [_waitingView removeFromSuperview]; +} + + + + +@end diff --git a/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDPhotoBrowser.h b/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDPhotoBrowser.h new file mode 100755 index 0000000..9fe04fe --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDPhotoBrowser.h @@ -0,0 +1,37 @@ +// +// SDPhotoBrowser.h +// photobrowser +// +// Created by aier on 15-2-3. +// Copyright (c) 2015年 aier. All rights reserved. +// + +#import + + +@class SDButton, SDPhotoBrowser; + +@protocol SDPhotoBrowserDelegate + +@required + +- (UIImage *)photoBrowser:(SDPhotoBrowser *)browser placeholderImageForIndex:(NSInteger)index; + +@optional + +- (NSURL *)photoBrowser:(SDPhotoBrowser *)browser highQualityImageURLForIndex:(NSInteger)index; + +@end + + +@interface SDPhotoBrowser : UIView + +@property (nonatomic, weak) UIView *sourceImagesContainerView; +@property (nonatomic, assign) NSInteger currentImageIndex; +@property (nonatomic, assign) NSInteger imageCount; + +@property (nonatomic, weak) id delegate; + +- (void)show; + +@end diff --git a/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDPhotoBrowser.m b/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDPhotoBrowser.m new file mode 100755 index 0000000..6b8f6b5 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDPhotoBrowser.m @@ -0,0 +1,346 @@ +// +// SDPhotoBrowser.m +// photobrowser +// +// Created by aier on 15-2-3. +// Copyright (c) 2015年 aier. All rights reserved. +// + +#import "SDPhotoBrowser.h" +#import "UIImageView+WebCache.h" +#import "SDBrowserImageView.h" + + +// ============在这里方便配置样式相关设置=========== + +// || +// || +// || +// \\// +// \/ + +#import "SDPhotoBrowserConfig.h" + +// ============================================= + +@implementation SDPhotoBrowser +{ + UIScrollView *_scrollView; + BOOL _hasShowedFistView; + UILabel *_indexLabel; + UIButton *_saveButton; + UIActivityIndicatorView *_indicatorView; + BOOL _willDisappear; +} + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + self.backgroundColor = SDPhotoBrowserBackgrounColor; + } + return self; +} + + +- (void)didMoveToSuperview +{ + [self setupScrollView]; + + [self setupToolbars]; +} + +- (void)dealloc +{ + [[UIApplication sharedApplication].keyWindow removeObserver:self forKeyPath:@"frame"]; +} + +- (void)setupToolbars +{ + // 1. 序标 + UILabel *indexLabel = [[UILabel alloc] init]; + indexLabel.bounds = CGRectMake(0, 0, 80, 30); + indexLabel.textAlignment = NSTextAlignmentCenter; + indexLabel.textColor = [UIColor whiteColor]; + indexLabel.font = [UIFont boldSystemFontOfSize:20]; + indexLabel.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5]; + indexLabel.layer.cornerRadius = indexLabel.bounds.size.height * 0.5; + indexLabel.clipsToBounds = YES; + if (self.imageCount > 1) { + indexLabel.text = [NSString stringWithFormat:@"1/%ld", (long)self.imageCount]; + } + _indexLabel = indexLabel; + [self addSubview:indexLabel]; + + // 2.保存按钮 + UIButton *saveButton = [[UIButton alloc] init]; + [saveButton setTitle:@"保存" forState:UIControlStateNormal]; + [saveButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + saveButton.backgroundColor = [UIColor colorWithRed:0.1f green:0.1f blue:0.1f alpha:0.90f]; + saveButton.layer.cornerRadius = 5; + saveButton.clipsToBounds = YES; + [saveButton addTarget:self action:@selector(saveImage) forControlEvents:UIControlEventTouchUpInside]; + _saveButton = saveButton; + [self addSubview:saveButton]; +} + +- (void)saveImage +{ + int index = _scrollView.contentOffset.x / _scrollView.bounds.size.width; + UIImageView *currentImageView = _scrollView.subviews[index]; + + UIImageWriteToSavedPhotosAlbum(currentImageView.image, self, @selector(image:didFinishSavingWithError:contextInfo:), NULL); + + UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] init]; + indicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge; + indicator.center = self.center; + _indicatorView = indicator; + [[UIApplication sharedApplication].keyWindow addSubview:indicator]; + [indicator startAnimating]; +} + +- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo; +{ + [_indicatorView removeFromSuperview]; + + UILabel *label = [[UILabel alloc] init]; + label.textColor = [UIColor whiteColor]; + label.backgroundColor = [UIColor colorWithRed:0.1f green:0.1f blue:0.1f alpha:0.90f]; + label.layer.cornerRadius = 5; + label.clipsToBounds = YES; + label.bounds = CGRectMake(0, 0, 150, 30); + label.center = self.center; + label.textAlignment = NSTextAlignmentCenter; + label.font = [UIFont boldSystemFontOfSize:17]; + [[UIApplication sharedApplication].keyWindow addSubview:label]; + [[UIApplication sharedApplication].keyWindow bringSubviewToFront:label]; + if (error) { + label.text = SDPhotoBrowserSaveImageFailText; + } else { + label.text = SDPhotoBrowserSaveImageSuccessText; + } + [label performSelector:@selector(removeFromSuperview) withObject:nil afterDelay:1.0]; +} + +- (void)setupScrollView +{ + _scrollView = [[UIScrollView alloc] init]; + _scrollView.delegate = self; + _scrollView.showsHorizontalScrollIndicator = NO; + _scrollView.showsVerticalScrollIndicator = NO; + _scrollView.pagingEnabled = YES; + [self addSubview:_scrollView]; + + for (int i = 0; i < self.imageCount; i++) { + SDBrowserImageView *imageView = [[SDBrowserImageView alloc] init]; + imageView.tag = i; + + // 单击图片 + UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(photoClick:)]; + + // 双击放大图片 + UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageViewDoubleTaped:)]; + doubleTap.numberOfTapsRequired = 2; + + [singleTap requireGestureRecognizerToFail:doubleTap]; + + [imageView addGestureRecognizer:singleTap]; + [imageView addGestureRecognizer:doubleTap]; + [_scrollView addSubview:imageView]; + } + + [self setupImageOfImageViewForIndex:self.currentImageIndex]; + +} + +// 加载图片 +- (void)setupImageOfImageViewForIndex:(NSInteger)index +{ + SDBrowserImageView *imageView = _scrollView.subviews[index]; + self.currentImageIndex = index; + if (imageView.hasLoadedImage) return; + if ([self highQualityImageURLForIndex:index]) { + [imageView setImageWithURL:[self highQualityImageURLForIndex:index] placeholderImage:[self placeholderImageForIndex:index]]; + } else { + imageView.image = [self placeholderImageForIndex:index]; + } + imageView.hasLoadedImage = YES; +} + +- (void)photoClick:(UITapGestureRecognizer *)recognizer +{ + _scrollView.hidden = YES; + _willDisappear = YES; + + SDBrowserImageView *currentImageView = (SDBrowserImageView *)recognizer.view; + NSInteger currentIndex = currentImageView.tag; + + UIView *sourceView = self.sourceImagesContainerView.subviews[currentIndex]; + CGRect targetTemp = [self.sourceImagesContainerView convertRect:sourceView.frame toView:self]; + + UIImageView *tempView = [[UIImageView alloc] init]; + tempView.contentMode = sourceView.contentMode; + tempView.clipsToBounds = YES; + tempView.image = currentImageView.image; + CGFloat h = (self.bounds.size.width / currentImageView.image.size.width) * currentImageView.image.size.height; + + if (!currentImageView.image) { // 防止 因imageview的image加载失败 导致 崩溃 + h = self.bounds.size.height; + } + + tempView.bounds = CGRectMake(0, 0, self.bounds.size.width, h); + tempView.center = self.center; + + [self addSubview:tempView]; + + _saveButton.hidden = YES; + + [UIView animateWithDuration:SDPhotoBrowserHideImageAnimationDuration animations:^{ + tempView.frame = targetTemp; + self.backgroundColor = [UIColor clearColor]; + _indexLabel.alpha = 0.1; + } completion:^(BOOL finished) { + [self removeFromSuperview]; + }]; +} + +- (void)imageViewDoubleTaped:(UITapGestureRecognizer *)recognizer +{ + SDBrowserImageView *imageView = (SDBrowserImageView *)recognizer.view; + CGFloat scale; + if (imageView.isScaled) { + scale = 1.0; + } else { + scale = 2.0; + } + + SDBrowserImageView *view = (SDBrowserImageView *)recognizer.view; + + [view doubleTapToZommWithScale:scale]; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + CGRect rect = self.bounds; + rect.size.width += SDPhotoBrowserImageViewMargin * 2; + + _scrollView.bounds = rect; + _scrollView.center = self.center; + + CGFloat y = 0; + CGFloat w = _scrollView.frame.size.width - SDPhotoBrowserImageViewMargin * 2; + CGFloat h = _scrollView.frame.size.height; + + + + [_scrollView.subviews enumerateObjectsUsingBlock:^(SDBrowserImageView *obj, NSUInteger idx, BOOL *stop) { + CGFloat x = SDPhotoBrowserImageViewMargin + idx * (SDPhotoBrowserImageViewMargin * 2 + w); + obj.frame = CGRectMake(x, y, w, h); + }]; + + _scrollView.contentSize = CGSizeMake(_scrollView.subviews.count * _scrollView.frame.size.width, 0); + _scrollView.contentOffset = CGPointMake(self.currentImageIndex * _scrollView.frame.size.width, 0); + + + if (!_hasShowedFistView) { + [self showFirstImage]; + } + + _indexLabel.center = CGPointMake(self.bounds.size.width * 0.5, 35); + _saveButton.frame = CGRectMake(30, self.bounds.size.height - 70, 50, 25); +} + +- (void)show +{ + UIWindow *window = [UIApplication sharedApplication].keyWindow; + self.frame = window.bounds; + [window addObserver:self forKeyPath:@"frame" options:0 context:nil]; + [window addSubview:self]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(UIView *)object change:(NSDictionary *)change context:(void *)context +{ + if ([keyPath isEqualToString:@"frame"]) { + self.frame = object.bounds; + SDBrowserImageView *currentImageView = _scrollView.subviews[_currentImageIndex]; + if ([currentImageView isKindOfClass:[SDBrowserImageView class]]) { + [currentImageView clear]; + } + } +} + +- (void)showFirstImage +{ + UIView *sourceView = self.sourceImagesContainerView.subviews[self.currentImageIndex]; + CGRect rect = [self.sourceImagesContainerView convertRect:sourceView.frame toView:self]; + + UIImageView *tempView = [[UIImageView alloc] init]; + tempView.image = [self placeholderImageForIndex:self.currentImageIndex]; + + [self addSubview:tempView]; + + CGRect targetTemp = [_scrollView.subviews[self.currentImageIndex] bounds]; + + tempView.frame = rect; + tempView.contentMode = [_scrollView.subviews[self.currentImageIndex] contentMode]; + _scrollView.hidden = YES; + + + [UIView animateWithDuration:SDPhotoBrowserShowImageAnimationDuration animations:^{ + tempView.center = self.center; + tempView.bounds = (CGRect){CGPointZero, targetTemp.size}; + } completion:^(BOOL finished) { + _hasShowedFistView = YES; + [tempView removeFromSuperview]; + _scrollView.hidden = NO; + }]; +} + +- (UIImage *)placeholderImageForIndex:(NSInteger)index +{ + if ([self.delegate respondsToSelector:@selector(photoBrowser:placeholderImageForIndex:)]) { + return [self.delegate photoBrowser:self placeholderImageForIndex:index]; + } + return nil; +} + +- (NSURL *)highQualityImageURLForIndex:(NSInteger)index +{ + if ([self.delegate respondsToSelector:@selector(photoBrowser:highQualityImageURLForIndex:)]) { + return [self.delegate photoBrowser:self highQualityImageURLForIndex:index]; + } + return nil; +} + +#pragma mark - scrollview代理方法 + +- (void)scrollViewDidScroll:(UIScrollView *)scrollView +{ + int index = (scrollView.contentOffset.x + _scrollView.bounds.size.width * 0.5) / _scrollView.bounds.size.width; + + // 有过缩放的图片在拖动一定距离后清除缩放 + CGFloat margin = 150; + CGFloat x = scrollView.contentOffset.x; + if ((x - index * self.bounds.size.width) > margin || (x - index * self.bounds.size.width) < - margin) { + SDBrowserImageView *imageView = _scrollView.subviews[index]; + if (imageView.isScaled) { + [UIView animateWithDuration:0.5 animations:^{ + imageView.transform = CGAffineTransformIdentity; + } completion:^(BOOL finished) { + [imageView eliminateScale]; + }]; + } + } + + + if (!_willDisappear) { + _indexLabel.text = [NSString stringWithFormat:@"%d/%ld", index + 1, (long)self.imageCount]; + } + [self setupImageOfImageViewForIndex:index]; +} + + + +@end diff --git a/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDPhotoBrowserConfig.h b/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDPhotoBrowserConfig.h new file mode 100755 index 0000000..443b558 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDPhotoBrowserConfig.h @@ -0,0 +1,42 @@ +// +// SDPhotoBrowserConfig.h +// SDPhotoBrowser +// +// Created by aier on 15-2-9. +// Copyright (c) 2015年 GSD. All rights reserved. +// + + +typedef enum { + SDWaitingViewModeLoopDiagram, // 环形 + SDWaitingViewModePieDiagram // 饼型 +} SDWaitingViewMode; + +// 图片保存成功提示文字 +#define SDPhotoBrowserSaveImageSuccessText @" ^_^ 保存成功 "; + +// 图片保存失败提示文字 +#define SDPhotoBrowserSaveImageFailText @" >_< 保存失败 "; + +// browser背景颜色 +#define SDPhotoBrowserBackgrounColor [UIColor colorWithRed:0 green:0 blue:0 alpha:0.95] + +// browser中图片间的margin +#define SDPhotoBrowserImageViewMargin 10 + +// browser中显示图片动画时长 +#define SDPhotoBrowserShowImageAnimationDuration 0.4f + +// browser中显示图片动画时长 +#define SDPhotoBrowserHideImageAnimationDuration 0.4f + +// 图片下载进度指示进度显示样式(SDWaitingViewModeLoopDiagram 环形,SDWaitingViewModePieDiagram 饼型) +#define SDWaitingViewProgressMode SDWaitingViewModeLoopDiagram + +// 图片下载进度指示器背景色 +#define SDWaitingViewBackgroundColor [UIColor colorWithRed:0 green:0 blue:0 alpha:0.7] + +// 图片下载进度指示器内部控件间的间距 +#define SDWaitingViewItemMargin 10 + + diff --git a/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDWaitingView.h b/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDWaitingView.h new file mode 100755 index 0000000..d688e1e --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDWaitingView.h @@ -0,0 +1,17 @@ +// +// SDWaitingView.h +// SDPhotoBrowser +// +// Created by aier on 15-2-6. +// Copyright (c) 2015年 GSD. All rights reserved. +// + +#import +#import "SDPhotoBrowserConfig.h" + +@interface SDWaitingView : UIView + +@property (nonatomic, assign) CGFloat progress; +@property (nonatomic, assign) int mode; + +@end diff --git a/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDWaitingView.m b/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDWaitingView.m new file mode 100755 index 0000000..2841e03 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDPhotoBrowser/SDWaitingView.m @@ -0,0 +1,88 @@ +// +// SDWaitingView.m +// SDPhotoBrowser +// +// Created by aier on 15-2-6. +// Copyright (c) 2015年 GSD. All rights reserved. +// + +#import "SDWaitingView.h" + +//// 图片下载进度指示器背景色 +//#define SDWaitingViewBackgroundColor [UIColor colorWithRed:0 green:0 blue:0 alpha:0.7] +// +//// 图片下载进度指示器内部控件间的间距 +// +//#define SDWaitingViewItemMargin 10 + + +@implementation SDWaitingView + + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + self.backgroundColor = SDWaitingViewBackgroundColor; + self.layer.cornerRadius = 5; + self.clipsToBounds = YES; + self.mode = SDWaitingViewModeLoopDiagram; + } + return self; +} + +- (void)setProgress:(CGFloat)progress +{ + _progress = progress; + [self setNeedsDisplay]; + if (progress >= 1) { + [self removeFromSuperview]; + } +} + +- (void)drawRect:(CGRect)rect +{ + CGContextRef ctx = UIGraphicsGetCurrentContext(); + + CGFloat xCenter = rect.size.width * 0.5; + CGFloat yCenter = rect.size.height * 0.5; + [[UIColor whiteColor] set]; + + switch (self.mode) { + case SDWaitingViewModePieDiagram: + { + CGFloat radius = MIN(rect.size.width * 0.5, rect.size.height * 0.5) - SDWaitingViewItemMargin; + + + CGFloat w = radius * 2 + SDWaitingViewItemMargin; + CGFloat h = w; + CGFloat x = (rect.size.width - w) * 0.5; + CGFloat y = (rect.size.height - h) * 0.5; + CGContextAddEllipseInRect(ctx, CGRectMake(x, y, w, h)); + CGContextFillPath(ctx); + + [SDWaitingViewBackgroundColor set]; + CGContextMoveToPoint(ctx, xCenter, yCenter); + CGContextAddLineToPoint(ctx, xCenter, 0); + CGFloat to = - M_PI * 0.5 + self.progress * M_PI * 2 + 0.001; // 初始值 + CGContextAddArc(ctx, xCenter, yCenter, radius, - M_PI * 0.5, to, 1); + CGContextClosePath(ctx); + + CGContextFillPath(ctx); + } + break; + + default: + { + CGContextSetLineWidth(ctx, 15); + CGContextSetLineCap(ctx, kCGLineCapRound); + CGFloat to = - M_PI * 0.5 + self.progress * M_PI * 2 + 0.05; // 初始值0.05 + CGFloat radius = MIN(rect.size.width, rect.size.height) * 0.5 - SDWaitingViewItemMargin; + CGContextAddArc(ctx, xCenter, yCenter, radius, - M_PI * 0.5, to, 0); + CGContextStrokePath(ctx); + } + break; + } +} + +@end diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/SDRefresh.h" b/SDAutoLayoutDemo/Vender/SDRefeshView/SDRefresh.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/SDRefresh.h" rename to SDAutoLayoutDemo/Vender/SDRefeshView/SDRefresh.h diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/SDRefreshFooterView.h" b/SDAutoLayoutDemo/Vender/SDRefeshView/SDRefreshFooterView.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/SDRefreshFooterView.h" rename to SDAutoLayoutDemo/Vender/SDRefeshView/SDRefreshFooterView.h diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/SDRefreshFooterView.m" b/SDAutoLayoutDemo/Vender/SDRefeshView/SDRefreshFooterView.m similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/SDRefreshFooterView.m" rename to SDAutoLayoutDemo/Vender/SDRefeshView/SDRefreshFooterView.m diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/SDRefreshHeaderView.h" b/SDAutoLayoutDemo/Vender/SDRefeshView/SDRefreshHeaderView.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/SDRefreshHeaderView.h" rename to SDAutoLayoutDemo/Vender/SDRefeshView/SDRefreshHeaderView.h diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/SDRefreshHeaderView.m" b/SDAutoLayoutDemo/Vender/SDRefeshView/SDRefreshHeaderView.m similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/SDRefreshHeaderView.m" rename to SDAutoLayoutDemo/Vender/SDRefeshView/SDRefreshHeaderView.m diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/SDRefreshView.h" b/SDAutoLayoutDemo/Vender/SDRefeshView/SDRefreshView.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/SDRefreshView.h" rename to SDAutoLayoutDemo/Vender/SDRefeshView/SDRefreshView.h diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/SDRefreshView.m" b/SDAutoLayoutDemo/Vender/SDRefeshView/SDRefreshView.m similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/SDRefreshView.m" rename to SDAutoLayoutDemo/Vender/SDRefeshView/SDRefreshView.m diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/UIView+SDExtension.h" b/SDAutoLayoutDemo/Vender/SDRefeshView/UIView+SDExtension.h similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/UIView+SDExtension.h" rename to SDAutoLayoutDemo/Vender/SDRefeshView/UIView+SDExtension.h diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/UIView+SDExtension.m" b/SDAutoLayoutDemo/Vender/SDRefeshView/UIView+SDExtension.m similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/UIView+SDExtension.m" rename to SDAutoLayoutDemo/Vender/SDRefeshView/UIView+SDExtension.m diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/sdRefeshView_arrow@2x.png" b/SDAutoLayoutDemo/Vender/SDRefeshView/sdRefeshView_arrow@2x.png similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/SDRefeshView/sdRefeshView_arrow@2x.png" rename to SDAutoLayoutDemo/Vender/SDRefeshView/sdRefeshView_arrow@2x.png diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/MKAnnotationView+WebCache.h b/SDAutoLayoutDemo/Vender/SDWebImage/MKAnnotationView+WebCache.h new file mode 100755 index 0000000..f61f3c0 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/MKAnnotationView+WebCache.h @@ -0,0 +1,124 @@ +// +// MKAnnotationView+WebCache.h +// SDWebImage +// +// Created by Olivier Poitrey on 14/03/12. +// Copyright (c) 2012 Dailymotion. All rights reserved. +// + +#import "MapKit/MapKit.h" +#import "SDWebImageManager.h" + +/** + * Integrates SDWebImage async downloading and caching of remote images with MKAnnotationView. + */ +@interface MKAnnotationView (WebCache) + +/** + * Get the current image URL. + * + * Note that because of the limitations of categories this property can get out of sync + * if you use sd_setImage: directly. + */ +- (NSURL *)sd_imageURL; + +/** + * Set the imageView `image` with an `url`. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + */ +- (void)sd_setImageWithURL:(NSURL *)url; + +/** + * Set the imageView `image` with an `url` and a placeholder. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @see sd_setImageWithURL:placeholderImage:options: + */ +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder; + +/** + * Set the imageView `image` with an `url`, placeholder and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + */ + +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options; + +/** + * Set the imageView `image` with an `url`. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the imageView `image` with an `url`, placeholder. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the imageView `image` with an `url`, placeholder and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Cancel the current download + */ +- (void)sd_cancelCurrentImageLoad; + +@end + + +@interface MKAnnotationView (WebCacheDeprecated) + +- (NSURL *)imageURL __deprecated_msg("Use `sd_imageURL`"); + +- (void)setImageWithURL:(NSURL *)url __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:`"); +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:`"); +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:options:`"); + +- (void)setImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:completed:`"); +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:completed:`"); +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:options:completed:`"); + +- (void)cancelCurrentImageLoad __deprecated_msg("Use `sd_cancelCurrentImageLoad`"); + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/MKAnnotationView+WebCache.m b/SDAutoLayoutDemo/Vender/SDWebImage/MKAnnotationView+WebCache.m new file mode 100755 index 0000000..5d4875c --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/MKAnnotationView+WebCache.m @@ -0,0 +1,126 @@ +// +// MKAnnotationView+WebCache.m +// SDWebImage +// +// Created by Olivier Poitrey on 14/03/12. +// Copyright (c) 2012 Dailymotion. All rights reserved. +// + +#import "MKAnnotationView+WebCache.h" +#import "objc/runtime.h" +#import "UIView+WebCacheOperation.h" + +static char imageURLKey; + +@implementation MKAnnotationView (WebCache) + +- (NSURL *)sd_imageURL { + return objc_getAssociatedObject(self, &imageURLKey); +} + +- (void)sd_setImageWithURL:(NSURL *)url { + [self sd_setImageWithURL:url placeholderImage:nil options:0 completed:nil]; +} + +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder { + [self sd_setImageWithURL:url placeholderImage:placeholder options:0 completed:nil]; +} + +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { + [self sd_setImageWithURL:url placeholderImage:placeholder options:options completed:nil]; +} + +- (void)sd_setImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setImageWithURL:url placeholderImage:nil options:0 completed:completedBlock]; +} + +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setImageWithURL:url placeholderImage:placeholder options:0 completed:completedBlock]; +} + +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_cancelCurrentImageLoad]; + + objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + self.image = placeholder; + + if (url) { + __weak __typeof(self)wself = self; + id operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { + if (!wself) return; + dispatch_main_sync_safe(^{ + __strong MKAnnotationView *sself = wself; + if (!sself) return; + if (image) { + sself.image = image; + } + if (completedBlock && finished) { + completedBlock(image, error, cacheType, url); + } + }); + }]; + [self sd_setImageLoadOperation:operation forKey:@"MKAnnotationViewImage"]; + } else { + dispatch_main_async_safe(^{ + NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}]; + if (completedBlock) { + completedBlock(nil, error, SDImageCacheTypeNone, url); + } + }); + } +} + +- (void)sd_cancelCurrentImageLoad { + [self sd_cancelImageLoadOperationWithKey:@"MKAnnotationViewImage"]; +} + +@end + + +@implementation MKAnnotationView (WebCacheDeprecated) + +- (NSURL *)imageURL { + return [self sd_imageURL]; +} + +- (void)setImageWithURL:(NSURL *)url { + [self sd_setImageWithURL:url placeholderImage:nil options:0 completed:nil]; +} + +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder { + [self sd_setImageWithURL:url placeholderImage:placeholder options:0 completed:nil]; +} + +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { + [self sd_setImageWithURL:url placeholderImage:placeholder options:options completed:nil]; +} + +- (void)setImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setImageWithURL:url placeholderImage:nil options:0 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setImageWithURL:url placeholderImage:placeholder options:0 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setImageWithURL:url placeholderImage:placeholder options:options completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)cancelCurrentImageLoad { + [self sd_cancelCurrentImageLoad]; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/NSData+ImageContentType.h b/SDAutoLayoutDemo/Vender/SDWebImage/NSData+ImageContentType.h new file mode 100755 index 0000000..69c76dc --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/NSData+ImageContentType.h @@ -0,0 +1,26 @@ +// +// Created by Fabrice Aneche on 06/01/14. +// Copyright (c) 2014 Dailymotion. All rights reserved. +// + +#import + +@interface NSData (ImageContentType) + +/** + * Compute the content type for an image data + * + * @param data the input data + * + * @return the content type as string (i.e. image/jpeg, image/gif) + */ ++ (NSString *)sd_contentTypeForImageData:(NSData *)data; + +@end + + +@interface NSData (ImageContentTypeDeprecated) + ++ (NSString *)contentTypeForImageData:(NSData *)data __deprecated_msg("Use `sd_contentTypeForImageData:`"); + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/NSData+ImageContentType.m b/SDAutoLayoutDemo/Vender/SDWebImage/NSData+ImageContentType.m new file mode 100755 index 0000000..0941cfa --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/NSData+ImageContentType.m @@ -0,0 +1,49 @@ +// +// Created by Fabrice Aneche on 06/01/14. +// Copyright (c) 2014 Dailymotion. All rights reserved. +// + +#import "NSData+ImageContentType.h" + + +@implementation NSData (ImageContentType) + ++ (NSString *)sd_contentTypeForImageData:(NSData *)data { + uint8_t c; + [data getBytes:&c length:1]; + switch (c) { + case 0xFF: + return @"image/jpeg"; + case 0x89: + return @"image/png"; + case 0x47: + return @"image/gif"; + case 0x49: + case 0x4D: + return @"image/tiff"; + case 0x52: + // R as RIFF for WEBP + if ([data length] < 12) { + return nil; + } + + NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding]; + if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) { + return @"image/webp"; + } + + return nil; + } + return nil; +} + +@end + + +@implementation NSData (ImageContentTypeDeprecated) + ++ (NSString *)contentTypeForImageData:(NSData *)data { + return [self sd_contentTypeForImageData:data]; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/SDImageCache.h b/SDAutoLayoutDemo/Vender/SDWebImage/SDImageCache.h new file mode 100755 index 0000000..9577726 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/SDImageCache.h @@ -0,0 +1,272 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import +#import "SDWebImageCompat.h" + +typedef NS_ENUM(NSInteger, SDImageCacheType) { + /** + * The image wasn't available the SDWebImage caches, but was downloaded from the web. + */ + SDImageCacheTypeNone, + /** + * The image was obtained from the disk cache. + */ + SDImageCacheTypeDisk, + /** + * The image was obtained from the memory cache. + */ + SDImageCacheTypeMemory +}; + +typedef void(^SDWebImageQueryCompletedBlock)(UIImage *image, SDImageCacheType cacheType); + +typedef void(^SDWebImageCheckCacheCompletionBlock)(BOOL isInCache); + +typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger totalSize); + +/** + * SDImageCache maintains a memory cache and an optional disk cache. Disk cache write operations are performed + * asynchronous so it doesn’t add unnecessary latency to the UI. + */ +@interface SDImageCache : NSObject + +/** + * Decompressing images that are downloaded and cached can improve performance but can consume lot of memory. + * Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption. + */ +@property (assign, nonatomic) BOOL shouldDecompressImages; + +/** + * disable iCloud backup [defaults to YES] + */ +@property (assign, nonatomic) BOOL shouldDisableiCloud; + +/** + * use memory cache [defaults to YES] + */ +@property (assign, nonatomic) BOOL shouldCacheImagesInMemory; + +/** + * The maximum "total cost" of the in-memory image cache. The cost function is the number of pixels held in memory. + */ +@property (assign, nonatomic) NSUInteger maxMemoryCost; + +/** + * The maximum number of objects the cache should hold. + */ +@property (assign, nonatomic) NSUInteger maxMemoryCountLimit; + +/** + * The maximum length of time to keep an image in the cache, in seconds + */ +@property (assign, nonatomic) NSInteger maxCacheAge; + +/** + * The maximum size of the cache, in bytes. + */ +@property (assign, nonatomic) NSUInteger maxCacheSize; + +/** + * Returns global shared cache instance + * + * @return SDImageCache global instance + */ ++ (SDImageCache *)sharedImageCache; + +/** + * Init a new cache store with a specific namespace + * + * @param ns The namespace to use for this cache store + */ +- (id)initWithNamespace:(NSString *)ns; + +/** + * Init a new cache store with a specific namespace and directory + * + * @param ns The namespace to use for this cache store + * @param directory Directory to cache disk images in + */ +- (id)initWithNamespace:(NSString *)ns diskCacheDirectory:(NSString *)directory; + +-(NSString *)makeDiskCachePath:(NSString*)fullNamespace; + +/** + * Add a read-only cache path to search for images pre-cached by SDImageCache + * Useful if you want to bundle pre-loaded images with your app + * + * @param path The path to use for this read-only cache path + */ +- (void)addReadOnlyCachePath:(NSString *)path; + +/** + * Store an image into memory and disk cache at the given key. + * + * @param image The image to store + * @param key The unique image cache key, usually it's image absolute URL + */ +- (void)storeImage:(UIImage *)image forKey:(NSString *)key; + +/** + * Store an image into memory and optionally disk cache at the given key. + * + * @param image The image to store + * @param key The unique image cache key, usually it's image absolute URL + * @param toDisk Store the image to disk cache if YES + */ +- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk; + +/** + * Store an image into memory and optionally disk cache at the given key. + * + * @param image The image to store + * @param recalculate BOOL indicates if imageData can be used or a new data should be constructed from the UIImage + * @param imageData The image data as returned by the server, this representation will be used for disk storage + * instead of converting the given image object into a storable/compressed image format in order + * to save quality and CPU + * @param key The unique image cache key, usually it's image absolute URL + * @param toDisk Store the image to disk cache if YES + */ +- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk; + +/** + * Query the disk cache asynchronously. + * + * @param key The unique key used to store the wanted image + */ +- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock; + +/** + * Query the memory cache synchronously. + * + * @param key The unique key used to store the wanted image + */ +- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key; + +/** + * Query the disk cache synchronously after checking the memory cache. + * + * @param key The unique key used to store the wanted image + */ +- (UIImage *)imageFromDiskCacheForKey:(NSString *)key; + +/** + * Remove the image from memory and disk cache synchronously + * + * @param key The unique image cache key + */ +- (void)removeImageForKey:(NSString *)key; + + +/** + * Remove the image from memory and disk cache asynchronously + * + * @param key The unique image cache key + * @param completion An block that should be executed after the image has been removed (optional) + */ +- (void)removeImageForKey:(NSString *)key withCompletion:(SDWebImageNoParamsBlock)completion; + +/** + * Remove the image from memory and optionally disk cache asynchronously + * + * @param key The unique image cache key + * @param fromDisk Also remove cache entry from disk if YES + */ +- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk; + +/** + * Remove the image from memory and optionally disk cache asynchronously + * + * @param key The unique image cache key + * @param fromDisk Also remove cache entry from disk if YES + * @param completion An block that should be executed after the image has been removed (optional) + */ +- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(SDWebImageNoParamsBlock)completion; + +/** + * Clear all memory cached images + */ +- (void)clearMemory; + +/** + * Clear all disk cached images. Non-blocking method - returns immediately. + * @param completion An block that should be executed after cache expiration completes (optional) + */ +- (void)clearDiskOnCompletion:(SDWebImageNoParamsBlock)completion; + +/** + * Clear all disk cached images + * @see clearDiskOnCompletion: + */ +- (void)clearDisk; + +/** + * Remove all expired cached image from disk. Non-blocking method - returns immediately. + * @param completionBlock An block that should be executed after cache expiration completes (optional) + */ +- (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock; + +/** + * Remove all expired cached image from disk + * @see cleanDiskWithCompletionBlock: + */ +- (void)cleanDisk; + +/** + * Get the size used by the disk cache + */ +- (NSUInteger)getSize; + +/** + * Get the number of images in the disk cache + */ +- (NSUInteger)getDiskCount; + +/** + * Asynchronously calculate the disk cache's size. + */ +- (void)calculateSizeWithCompletionBlock:(SDWebImageCalculateSizeBlock)completionBlock; + +/** + * Async check if image exists in disk cache already (does not load the image) + * + * @param key the key describing the url + * @param completionBlock the block to be executed when the check is done. + * @note the completion block will be always executed on the main queue + */ +- (void)diskImageExistsWithKey:(NSString *)key completion:(SDWebImageCheckCacheCompletionBlock)completionBlock; + +/** + * Check if image exists in disk cache already (does not load the image) + * + * @param key the key describing the url + * + * @return YES if an image exists for the given key + */ +- (BOOL)diskImageExistsWithKey:(NSString *)key; + +/** + * Get the cache path for a certain key (needs the cache path root folder) + * + * @param key the key (can be obtained from url using cacheKeyForURL) + * @param path the cache path root folder + * + * @return the cache path + */ +- (NSString *)cachePathForKey:(NSString *)key inPath:(NSString *)path; + +/** + * Get the default cache path for a certain key + * + * @param key the key (can be obtained from url using cacheKeyForURL) + * + * @return the default cache path + */ +- (NSString *)defaultCachePathForKey:(NSString *)key; + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/SDImageCache.m b/SDAutoLayoutDemo/Vender/SDWebImage/SDImageCache.m new file mode 100755 index 0000000..9ad41ee --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/SDImageCache.m @@ -0,0 +1,623 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDImageCache.h" +#import "SDWebImageDecoder.h" +#import "UIImage+MultiFormat.h" +#import + +// See https://github.com/rs/SDWebImage/pull/1141 for discussion +@interface AutoPurgeCache : NSCache +@end + +@implementation AutoPurgeCache + +- (id)init +{ + self = [super init]; + if (self) { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; + } + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; + +} + +@end + +static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week +// PNG signature bytes and data (below) +static unsigned char kPNGSignatureBytes[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}; +static NSData *kPNGSignatureData = nil; + +BOOL ImageDataHasPNGPreffix(NSData *data); + +BOOL ImageDataHasPNGPreffix(NSData *data) { + NSUInteger pngSignatureLength = [kPNGSignatureData length]; + if ([data length] >= pngSignatureLength) { + if ([[data subdataWithRange:NSMakeRange(0, pngSignatureLength)] isEqualToData:kPNGSignatureData]) { + return YES; + } + } + + return NO; +} + +FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { + return image.size.height * image.size.width * image.scale * image.scale; +} + +@interface SDImageCache () + +@property (strong, nonatomic) NSCache *memCache; +@property (strong, nonatomic) NSString *diskCachePath; +@property (strong, nonatomic) NSMutableArray *customPaths; +@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t ioQueue; + +@end + + +@implementation SDImageCache { + NSFileManager *_fileManager; +} + ++ (SDImageCache *)sharedImageCache { + static dispatch_once_t once; + static id instance; + dispatch_once(&once, ^{ + instance = [self new]; + }); + return instance; +} + +- (id)init { + return [self initWithNamespace:@"default"]; +} + +- (id)initWithNamespace:(NSString *)ns { + NSString *path = [self makeDiskCachePath:ns]; + return [self initWithNamespace:ns diskCacheDirectory:path]; +} + +- (id)initWithNamespace:(NSString *)ns diskCacheDirectory:(NSString *)directory { + if ((self = [super init])) { + NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns]; + + // initialise PNG signature data + kPNGSignatureData = [NSData dataWithBytes:kPNGSignatureBytes length:8]; + + // Create IO serial queue + _ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL); + + // Init default values + _maxCacheAge = kDefaultCacheMaxCacheAge; + + // Init the memory cache + _memCache = [[AutoPurgeCache alloc] init]; + _memCache.name = fullNamespace; + + // Init the disk cache + if (directory != nil) { + _diskCachePath = [directory stringByAppendingPathComponent:fullNamespace]; + } else { + NSString *path = [self makeDiskCachePath:ns]; + _diskCachePath = path; + } + + // Set decompression to YES + _shouldDecompressImages = YES; + + // memory cache enabled + _shouldCacheImagesInMemory = YES; + + // Disable iCloud + _shouldDisableiCloud = YES; + + dispatch_sync(_ioQueue, ^{ + _fileManager = [NSFileManager new]; + }); + +#if TARGET_OS_IPHONE + // Subscribe to app events + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(clearMemory) + name:UIApplicationDidReceiveMemoryWarningNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(cleanDisk) + name:UIApplicationWillTerminateNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(backgroundCleanDisk) + name:UIApplicationDidEnterBackgroundNotification + object:nil]; +#endif + } + + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + SDDispatchQueueRelease(_ioQueue); +} + +- (void)addReadOnlyCachePath:(NSString *)path { + if (!self.customPaths) { + self.customPaths = [NSMutableArray new]; + } + + if (![self.customPaths containsObject:path]) { + [self.customPaths addObject:path]; + } +} + +- (NSString *)cachePathForKey:(NSString *)key inPath:(NSString *)path { + NSString *filename = [self cachedFileNameForKey:key]; + return [path stringByAppendingPathComponent:filename]; +} + +- (NSString *)defaultCachePathForKey:(NSString *)key { + return [self cachePathForKey:key inPath:self.diskCachePath]; +} + +#pragma mark SDImageCache (private) + +- (NSString *)cachedFileNameForKey:(NSString *)key { + const char *str = [key UTF8String]; + if (str == NULL) { + str = ""; + } + unsigned char r[CC_MD5_DIGEST_LENGTH]; + CC_MD5(str, (CC_LONG)strlen(str), r); + NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%@", + r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], + r[11], r[12], r[13], r[14], r[15], [[key pathExtension] isEqualToString:@""] ? @"" : [NSString stringWithFormat:@".%@", [key pathExtension]]]; + + return filename; +} + +#pragma mark ImageCache + +// Init the disk cache +-(NSString *)makeDiskCachePath:(NSString*)fullNamespace{ + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); + return [paths[0] stringByAppendingPathComponent:fullNamespace]; +} + +- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk { + if (!image || !key) { + return; + } + // if memory cache is enabled + if (self.shouldCacheImagesInMemory) { + NSUInteger cost = SDCacheCostForImage(image); + [self.memCache setObject:image forKey:key cost:cost]; + } + + if (toDisk) { + dispatch_async(self.ioQueue, ^{ + NSData *data = imageData; + + if (image && (recalculate || !data)) { +#if TARGET_OS_IPHONE + // We need to determine if the image is a PNG or a JPEG + // PNGs are easier to detect because they have a unique signature (http://www.w3.org/TR/PNG-Structure.html) + // The first eight bytes of a PNG file always contain the following (decimal) values: + // 137 80 78 71 13 10 26 10 + + // If the imageData is nil (i.e. if trying to save a UIImage directly or the image was transformed on download) + // and the image has an alpha channel, we will consider it PNG to avoid losing the transparency + int alphaInfo = CGImageGetAlphaInfo(image.CGImage); + BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone || + alphaInfo == kCGImageAlphaNoneSkipFirst || + alphaInfo == kCGImageAlphaNoneSkipLast); + BOOL imageIsPng = hasAlpha; + + // But if we have an image data, we will look at the preffix + if ([imageData length] >= [kPNGSignatureData length]) { + imageIsPng = ImageDataHasPNGPreffix(imageData); + } + + if (imageIsPng) { + data = UIImagePNGRepresentation(image); + } + else { + data = UIImageJPEGRepresentation(image, (CGFloat)1.0); + } +#else + data = [NSBitmapImageRep representationOfImageRepsInArray:image.representations usingType: NSJPEGFileType properties:nil]; +#endif + } + + if (data) { + if (![_fileManager fileExistsAtPath:_diskCachePath]) { + [_fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL]; + } + + // get cache Path for image key + NSString *cachePathForKey = [self defaultCachePathForKey:key]; + // transform to NSUrl + NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey]; + + [_fileManager createFileAtPath:cachePathForKey contents:data attributes:nil]; + + // disable iCloud backup + if (self.shouldDisableiCloud) { + [fileURL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:nil]; + } + } + }); + } +} + +- (void)storeImage:(UIImage *)image forKey:(NSString *)key { + [self storeImage:image recalculateFromImage:YES imageData:nil forKey:key toDisk:YES]; +} + +- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk { + [self storeImage:image recalculateFromImage:YES imageData:nil forKey:key toDisk:toDisk]; +} + +- (BOOL)diskImageExistsWithKey:(NSString *)key { + BOOL exists = NO; + + // this is an exception to access the filemanager on another queue than ioQueue, but we are using the shared instance + // from apple docs on NSFileManager: The methods of the shared NSFileManager object can be called from multiple threads safely. + exists = [[NSFileManager defaultManager] fileExistsAtPath:[self defaultCachePathForKey:key]]; + + return exists; +} + +- (void)diskImageExistsWithKey:(NSString *)key completion:(SDWebImageCheckCacheCompletionBlock)completionBlock { + dispatch_async(_ioQueue, ^{ + BOOL exists = [_fileManager fileExistsAtPath:[self defaultCachePathForKey:key]]; + if (completionBlock) { + dispatch_async(dispatch_get_main_queue(), ^{ + completionBlock(exists); + }); + } + }); +} + +- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key { + return [self.memCache objectForKey:key]; +} + +- (UIImage *)imageFromDiskCacheForKey:(NSString *)key { + + // First check the in-memory cache... + UIImage *image = [self imageFromMemoryCacheForKey:key]; + if (image) { + return image; + } + + // Second check the disk cache... + UIImage *diskImage = [self diskImageForKey:key]; + if (diskImage && self.shouldCacheImagesInMemory) { + NSUInteger cost = SDCacheCostForImage(diskImage); + [self.memCache setObject:diskImage forKey:key cost:cost]; + } + + return diskImage; +} + +- (NSData *)diskImageDataBySearchingAllPathsForKey:(NSString *)key { + NSString *defaultPath = [self defaultCachePathForKey:key]; + NSData *data = [NSData dataWithContentsOfFile:defaultPath]; + if (data) { + return data; + } + + NSArray *customPaths = [self.customPaths copy]; + for (NSString *path in customPaths) { + NSString *filePath = [self cachePathForKey:key inPath:path]; + NSData *imageData = [NSData dataWithContentsOfFile:filePath]; + if (imageData) { + return imageData; + } + } + + return nil; +} + +- (UIImage *)diskImageForKey:(NSString *)key { + NSData *data = [self diskImageDataBySearchingAllPathsForKey:key]; + if (data) { + UIImage *image = [UIImage sd_imageWithData:data]; + image = [self scaledImageForKey:key image:image]; + if (self.shouldDecompressImages) { + image = [UIImage decodedImageWithImage:image]; + } + return image; + } + else { + return nil; + } +} + +- (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image { + return SDScaledImageForKey(key, image); +} + +- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock { + if (!doneBlock) { + return nil; + } + + if (!key) { + doneBlock(nil, SDImageCacheTypeNone); + return nil; + } + + // First check the in-memory cache... + UIImage *image = [self imageFromMemoryCacheForKey:key]; + if (image) { + doneBlock(image, SDImageCacheTypeMemory); + return nil; + } + + NSOperation *operation = [NSOperation new]; + dispatch_async(self.ioQueue, ^{ + if (operation.isCancelled) { + return; + } + + @autoreleasepool { + UIImage *diskImage = [self diskImageForKey:key]; + if (diskImage && self.shouldCacheImagesInMemory) { + NSUInteger cost = SDCacheCostForImage(diskImage); + [self.memCache setObject:diskImage forKey:key cost:cost]; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + doneBlock(diskImage, SDImageCacheTypeDisk); + }); + } + }); + + return operation; +} + +- (void)removeImageForKey:(NSString *)key { + [self removeImageForKey:key withCompletion:nil]; +} + +- (void)removeImageForKey:(NSString *)key withCompletion:(SDWebImageNoParamsBlock)completion { + [self removeImageForKey:key fromDisk:YES withCompletion:completion]; +} + +- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk { + [self removeImageForKey:key fromDisk:fromDisk withCompletion:nil]; +} + +- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(SDWebImageNoParamsBlock)completion { + + if (key == nil) { + return; + } + + if (self.shouldCacheImagesInMemory) { + [self.memCache removeObjectForKey:key]; + } + + if (fromDisk) { + dispatch_async(self.ioQueue, ^{ + [_fileManager removeItemAtPath:[self defaultCachePathForKey:key] error:nil]; + + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(); + }); + } + }); + } else if (completion){ + completion(); + } + +} + +- (void)setMaxMemoryCost:(NSUInteger)maxMemoryCost { + self.memCache.totalCostLimit = maxMemoryCost; +} + +- (NSUInteger)maxMemoryCost { + return self.memCache.totalCostLimit; +} + +- (NSUInteger)maxMemoryCountLimit { + return self.memCache.countLimit; +} + +- (void)setMaxMemoryCountLimit:(NSUInteger)maxCountLimit { + self.memCache.countLimit = maxCountLimit; +} + +- (void)clearMemory { + [self.memCache removeAllObjects]; +} + +- (void)clearDisk { + [self clearDiskOnCompletion:nil]; +} + +- (void)clearDiskOnCompletion:(SDWebImageNoParamsBlock)completion +{ + dispatch_async(self.ioQueue, ^{ + [_fileManager removeItemAtPath:self.diskCachePath error:nil]; + [_fileManager createDirectoryAtPath:self.diskCachePath + withIntermediateDirectories:YES + attributes:nil + error:NULL]; + + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(); + }); + } + }); +} + +- (void)cleanDisk { + [self cleanDiskWithCompletionBlock:nil]; +} + +- (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock { + dispatch_async(self.ioQueue, ^{ + NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES]; + NSArray *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey]; + + // This enumerator prefetches useful properties for our cache files. + NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL + includingPropertiesForKeys:resourceKeys + options:NSDirectoryEnumerationSkipsHiddenFiles + errorHandler:NULL]; + + NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge]; + NSMutableDictionary *cacheFiles = [NSMutableDictionary dictionary]; + NSUInteger currentCacheSize = 0; + + // Enumerate all of the files in the cache directory. This loop has two purposes: + // + // 1. Removing files that are older than the expiration date. + // 2. Storing file attributes for the size-based cleanup pass. + NSMutableArray *urlsToDelete = [[NSMutableArray alloc] init]; + for (NSURL *fileURL in fileEnumerator) { + NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:NULL]; + + // Skip directories. + if ([resourceValues[NSURLIsDirectoryKey] boolValue]) { + continue; + } + + // Remove files that are older than the expiration date; + NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey]; + if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) { + [urlsToDelete addObject:fileURL]; + continue; + } + + // Store a reference to this file and account for its total size. + NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey]; + currentCacheSize += [totalAllocatedSize unsignedIntegerValue]; + [cacheFiles setObject:resourceValues forKey:fileURL]; + } + + for (NSURL *fileURL in urlsToDelete) { + [_fileManager removeItemAtURL:fileURL error:nil]; + } + + // If our remaining disk cache exceeds a configured maximum size, perform a second + // size-based cleanup pass. We delete the oldest files first. + if (self.maxCacheSize > 0 && currentCacheSize > self.maxCacheSize) { + // Target half of our maximum cache size for this cleanup pass. + const NSUInteger desiredCacheSize = self.maxCacheSize / 2; + + // Sort the remaining cache files by their last modification time (oldest first). + NSArray *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent + usingComparator:^NSComparisonResult(id obj1, id obj2) { + return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]]; + }]; + + // Delete files until we fall below our desired cache size. + for (NSURL *fileURL in sortedFiles) { + if ([_fileManager removeItemAtURL:fileURL error:nil]) { + NSDictionary *resourceValues = cacheFiles[fileURL]; + NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey]; + currentCacheSize -= [totalAllocatedSize unsignedIntegerValue]; + + if (currentCacheSize < desiredCacheSize) { + break; + } + } + } + } + if (completionBlock) { + dispatch_async(dispatch_get_main_queue(), ^{ + completionBlock(); + }); + } + }); +} + +- (void)backgroundCleanDisk { + Class UIApplicationClass = NSClassFromString(@"UIApplication"); + if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) { + return; + } + UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)]; + __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{ + // Clean up any unfinished task business by marking where you + // stopped or ending the task outright. + [application endBackgroundTask:bgTask]; + bgTask = UIBackgroundTaskInvalid; + }]; + + // Start the long-running task and return immediately. + [self cleanDiskWithCompletionBlock:^{ + [application endBackgroundTask:bgTask]; + bgTask = UIBackgroundTaskInvalid; + }]; +} + +- (NSUInteger)getSize { + __block NSUInteger size = 0; + dispatch_sync(self.ioQueue, ^{ + NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtPath:self.diskCachePath]; + for (NSString *fileName in fileEnumerator) { + NSString *filePath = [self.diskCachePath stringByAppendingPathComponent:fileName]; + NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil]; + size += [attrs fileSize]; + } + }); + return size; +} + +- (NSUInteger)getDiskCount { + __block NSUInteger count = 0; + dispatch_sync(self.ioQueue, ^{ + NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtPath:self.diskCachePath]; + count = [[fileEnumerator allObjects] count]; + }); + return count; +} + +- (void)calculateSizeWithCompletionBlock:(SDWebImageCalculateSizeBlock)completionBlock { + NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES]; + + dispatch_async(self.ioQueue, ^{ + NSUInteger fileCount = 0; + NSUInteger totalSize = 0; + + NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL + includingPropertiesForKeys:@[NSFileSize] + options:NSDirectoryEnumerationSkipsHiddenFiles + errorHandler:NULL]; + + for (NSURL *fileURL in fileEnumerator) { + NSNumber *fileSize; + [fileURL getResourceValue:&fileSize forKey:NSURLFileSizeKey error:NULL]; + totalSize += [fileSize unsignedIntegerValue]; + fileCount += 1; + } + + if (completionBlock) { + dispatch_async(dispatch_get_main_queue(), ^{ + completionBlock(fileCount, totalSize); + }); + } + }); +} + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageCompat.h b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageCompat.h new file mode 100755 index 0000000..3c21b41 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageCompat.h @@ -0,0 +1,72 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * (c) Jamie Pinkham + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import + +#ifdef __OBJC_GC__ +#error SDWebImage does not support Objective-C Garbage Collection +#endif + +#if __IPHONE_OS_VERSION_MIN_REQUIRED != 20000 && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0 +#error SDWebImage doesn't support Deployment Target version < 5.0 +#endif + +#if !TARGET_OS_IPHONE +#import +#ifndef UIImage +#define UIImage NSImage +#endif +#ifndef UIImageView +#define UIImageView NSImageView +#endif +#else + +#import + +#endif + +#ifndef NS_ENUM +#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type +#endif + +#ifndef NS_OPTIONS +#define NS_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type +#endif + +#if OS_OBJECT_USE_OBJC + #undef SDDispatchQueueRelease + #undef SDDispatchQueueSetterSementics + #define SDDispatchQueueRelease(q) + #define SDDispatchQueueSetterSementics strong +#else +#undef SDDispatchQueueRelease +#undef SDDispatchQueueSetterSementics +#define SDDispatchQueueRelease(q) (dispatch_release(q)) +#define SDDispatchQueueSetterSementics assign +#endif + +extern UIImage *SDScaledImageForKey(NSString *key, UIImage *image); + +typedef void(^SDWebImageNoParamsBlock)(); + +extern NSString *const SDWebImageErrorDomain; + +#define dispatch_main_sync_safe(block)\ + if ([NSThread isMainThread]) {\ + block();\ + } else {\ + dispatch_sync(dispatch_get_main_queue(), block);\ + } + +#define dispatch_main_async_safe(block)\ + if ([NSThread isMainThread]) {\ + block();\ + } else {\ + dispatch_async(dispatch_get_main_queue(), block);\ + } diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageCompat.m b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageCompat.m new file mode 100755 index 0000000..9a011bc --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageCompat.m @@ -0,0 +1,51 @@ +// +// SDWebImageCompat.m +// SDWebImage +// +// Created by Olivier Poitrey on 11/12/12. +// Copyright (c) 2012 Dailymotion. All rights reserved. +// + +#import "SDWebImageCompat.h" + +#if !__has_feature(objc_arc) +#error SDWebImage is ARC only. Either turn on ARC for the project or use -fobjc-arc flag +#endif + +inline UIImage *SDScaledImageForKey(NSString *key, UIImage *image) { + if (!image) { + return nil; + } + + if ([image.images count] > 0) { + NSMutableArray *scaledImages = [NSMutableArray array]; + + for (UIImage *tempImage in image.images) { + [scaledImages addObject:SDScaledImageForKey(key, tempImage)]; + } + + return [UIImage animatedImageWithImages:scaledImages duration:image.duration]; + } + else { + if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) { + CGFloat scale = [UIScreen mainScreen].scale; + if (key.length >= 8) { + NSRange range = [key rangeOfString:@"@2x."]; + if (range.location != NSNotFound) { + scale = 2.0; + } + + range = [key rangeOfString:@"@3x."]; + if (range.location != NSNotFound) { + scale = 3.0; + } + } + + UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation]; + image = scaledImage; + } + return image; + } +} + +NSString *const SDWebImageErrorDomain = @"SDWebImageErrorDomain"; diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageDecoder.h b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageDecoder.h new file mode 100755 index 0000000..0176a7b --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageDecoder.h @@ -0,0 +1,18 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * Created by james on 9/28/11. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import +#import "SDWebImageCompat.h" + +@interface UIImage (ForceDecode) + ++ (UIImage *)decodedImageWithImage:(UIImage *)image; + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageDecoder.m b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageDecoder.m new file mode 100755 index 0000000..b78217a --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageDecoder.m @@ -0,0 +1,69 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * Created by james on 9/28/11. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImageDecoder.h" + +@implementation UIImage (ForceDecode) + ++ (UIImage *)decodedImageWithImage:(UIImage *)image { + // while downloading huge amount of images + // autorelease the bitmap context + // and all vars to help system to free memory + // when there are memory warning. + // on iOS7, do not forget to call + // [[SDImageCache sharedImageCache] clearMemory]; + @autoreleasepool{ + // do not decode animated images + if (image.images) { return image; } + + CGImageRef imageRef = image.CGImage; + + CGImageAlphaInfo alpha = CGImageGetAlphaInfo(imageRef); + BOOL anyAlpha = (alpha == kCGImageAlphaFirst || + alpha == kCGImageAlphaLast || + alpha == kCGImageAlphaPremultipliedFirst || + alpha == kCGImageAlphaPremultipliedLast); + + if (anyAlpha) { return image; } + + size_t width = CGImageGetWidth(imageRef); + size_t height = CGImageGetHeight(imageRef); + + // current + CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(CGImageGetColorSpace(imageRef)); + CGColorSpaceRef colorspaceRef = CGImageGetColorSpace(imageRef); + + bool unsupportedColorSpace = (imageColorSpaceModel == 0 || imageColorSpaceModel == -1 || imageColorSpaceModel == kCGColorSpaceModelIndexed); + if (unsupportedColorSpace) + colorspaceRef = CGColorSpaceCreateDeviceRGB(); + + CGContextRef context = CGBitmapContextCreate(NULL, width, + height, + CGImageGetBitsPerComponent(imageRef), + 0, + colorspaceRef, + kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst); + + // Draw the image into the context and retrieve the new image, which will now have an alpha layer + CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); + CGImageRef imageRefWithAlpha = CGBitmapContextCreateImage(context); + UIImage *imageWithAlpha = [UIImage imageWithCGImage:imageRefWithAlpha scale:image.scale orientation:image.imageOrientation]; + + if (unsupportedColorSpace) + CGColorSpaceRelease(colorspaceRef); + + CGContextRelease(context); + CGImageRelease(imageRefWithAlpha); + + return imageWithAlpha; + } +} + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageDownloader.h b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageDownloader.h new file mode 100755 index 0000000..b64fb13 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageDownloader.h @@ -0,0 +1,191 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import +#import "SDWebImageCompat.h" +#import "SDWebImageOperation.h" + +typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) { + SDWebImageDownloaderLowPriority = 1 << 0, + SDWebImageDownloaderProgressiveDownload = 1 << 1, + + /** + * By default, request prevent the of NSURLCache. With this flag, NSURLCache + * is used with default policies. + */ + SDWebImageDownloaderUseNSURLCache = 1 << 2, + + /** + * Call completion block with nil image/imageData if the image was read from NSURLCache + * (to be combined with `SDWebImageDownloaderUseNSURLCache`). + */ + + SDWebImageDownloaderIgnoreCachedResponse = 1 << 3, + /** + * In iOS 4+, continue the download of the image if the app goes to background. This is achieved by asking the system for + * extra time in background to let the request finish. If the background task expires the operation will be cancelled. + */ + + SDWebImageDownloaderContinueInBackground = 1 << 4, + + /** + * Handles cookies stored in NSHTTPCookieStore by setting + * NSMutableURLRequest.HTTPShouldHandleCookies = YES; + */ + SDWebImageDownloaderHandleCookies = 1 << 5, + + /** + * Enable to allow untrusted SSL certificates. + * Useful for testing purposes. Use with caution in production. + */ + SDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6, + + /** + * Put the image in the high priority queue. + */ + SDWebImageDownloaderHighPriority = 1 << 7, +}; + +typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) { + /** + * Default value. All download operations will execute in queue style (first-in-first-out). + */ + SDWebImageDownloaderFIFOExecutionOrder, + + /** + * All download operations will execute in stack style (last-in-first-out). + */ + SDWebImageDownloaderLIFOExecutionOrder +}; + +extern NSString *const SDWebImageDownloadStartNotification; +extern NSString *const SDWebImageDownloadStopNotification; + +typedef void(^SDWebImageDownloaderProgressBlock)(NSInteger receivedSize, NSInteger expectedSize); + +typedef void(^SDWebImageDownloaderCompletedBlock)(UIImage *image, NSData *data, NSError *error, BOOL finished); + +typedef NSDictionary *(^SDWebImageDownloaderHeadersFilterBlock)(NSURL *url, NSDictionary *headers); + +/** + * Asynchronous downloader dedicated and optimized for image loading. + */ +@interface SDWebImageDownloader : NSObject + +/** + * Decompressing images that are downloaded and cached can improve performance but can consume lot of memory. + * Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption. + */ +@property (assign, nonatomic) BOOL shouldDecompressImages; + +@property (assign, nonatomic) NSInteger maxConcurrentDownloads; + +/** + * Shows the current amount of downloads that still need to be downloaded + */ +@property (readonly, nonatomic) NSUInteger currentDownloadCount; + + +/** + * The timeout value (in seconds) for the download operation. Default: 15.0. + */ +@property (assign, nonatomic) NSTimeInterval downloadTimeout; + + +/** + * Changes download operations execution order. Default value is `SDWebImageDownloaderFIFOExecutionOrder`. + */ +@property (assign, nonatomic) SDWebImageDownloaderExecutionOrder executionOrder; + +/** + * Singleton method, returns the shared instance + * + * @return global shared instance of downloader class + */ ++ (SDWebImageDownloader *)sharedDownloader; + +/** + * Set the default URL credential to be set for request operations. + */ +@property (strong, nonatomic) NSURLCredential *urlCredential; + +/** + * Set username + */ +@property (strong, nonatomic) NSString *username; + +/** + * Set password + */ +@property (strong, nonatomic) NSString *password; + +/** + * Set filter to pick headers for downloading image HTTP request. + * + * This block will be invoked for each downloading image request, returned + * NSDictionary will be used as headers in corresponding HTTP request. + */ +@property (nonatomic, copy) SDWebImageDownloaderHeadersFilterBlock headersFilter; + +/** + * Set a value for a HTTP header to be appended to each download HTTP request. + * + * @param value The value for the header field. Use `nil` value to remove the header. + * @param field The name of the header field to set. + */ +- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field; + +/** + * Returns the value of the specified HTTP header field. + * + * @return The value associated with the header field field, or `nil` if there is no corresponding header field. + */ +- (NSString *)valueForHTTPHeaderField:(NSString *)field; + +/** + * Sets a subclass of `SDWebImageDownloaderOperation` as the default + * `NSOperation` to be used each time SDWebImage constructs a request + * operation to download an image. + * + * @param operationClass The subclass of `SDWebImageDownloaderOperation` to set + * as default. Passing `nil` will revert to `SDWebImageDownloaderOperation`. + */ +- (void)setOperationClass:(Class)operationClass; + +/** + * Creates a SDWebImageDownloader async downloader instance with a given URL + * + * The delegate will be informed when the image is finish downloaded or an error has happen. + * + * @see SDWebImageDownloaderDelegate + * + * @param url The URL to the image to download + * @param options The options to be used for this download + * @param progressBlock A block called repeatedly while the image is downloading + * @param completedBlock A block called once the download is completed. + * If the download succeeded, the image parameter is set, in case of error, + * error parameter is set with the error. The last parameter is always YES + * if SDWebImageDownloaderProgressiveDownload isn't use. With the + * SDWebImageDownloaderProgressiveDownload option, this block is called + * repeatedly with the partial image object and the finished argument set to NO + * before to be called a last time with the full image and finished argument + * set to YES. In case of error, the finished argument is always YES. + * + * @return A cancellable SDWebImageOperation + */ +- (id )downloadImageWithURL:(NSURL *)url + options:(SDWebImageDownloaderOptions)options + progress:(SDWebImageDownloaderProgressBlock)progressBlock + completed:(SDWebImageDownloaderCompletedBlock)completedBlock; + +/** + * Sets the download queue suspension state + */ +- (void)setSuspended:(BOOL)suspended; + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageDownloader.m b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageDownloader.m new file mode 100755 index 0000000..1fdcfc9 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageDownloader.m @@ -0,0 +1,232 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImageDownloader.h" +#import "SDWebImageDownloaderOperation.h" +#import + +static NSString *const kProgressCallbackKey = @"progress"; +static NSString *const kCompletedCallbackKey = @"completed"; + +@interface SDWebImageDownloader () + +@property (strong, nonatomic) NSOperationQueue *downloadQueue; +@property (weak, nonatomic) NSOperation *lastAddedOperation; +@property (assign, nonatomic) Class operationClass; +@property (strong, nonatomic) NSMutableDictionary *URLCallbacks; +@property (strong, nonatomic) NSMutableDictionary *HTTPHeaders; +// This queue is used to serialize the handling of the network responses of all the download operation in a single queue +@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t barrierQueue; + +@end + +@implementation SDWebImageDownloader + ++ (void)initialize { + // Bind SDNetworkActivityIndicator if available (download it here: http://github.com/rs/SDNetworkActivityIndicator ) + // To use it, just add #import "SDNetworkActivityIndicator.h" in addition to the SDWebImage import + if (NSClassFromString(@"SDNetworkActivityIndicator")) { + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + id activityIndicator = [NSClassFromString(@"SDNetworkActivityIndicator") performSelector:NSSelectorFromString(@"sharedActivityIndicator")]; +#pragma clang diagnostic pop + + // Remove observer in case it was previously added. + [[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStartNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStopNotification object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:activityIndicator + selector:NSSelectorFromString(@"startActivity") + name:SDWebImageDownloadStartNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:activityIndicator + selector:NSSelectorFromString(@"stopActivity") + name:SDWebImageDownloadStopNotification object:nil]; + } +} + ++ (SDWebImageDownloader *)sharedDownloader { + static dispatch_once_t once; + static id instance; + dispatch_once(&once, ^{ + instance = [self new]; + }); + return instance; +} + +- (id)init { + if ((self = [super init])) { + _operationClass = [SDWebImageDownloaderOperation class]; + _shouldDecompressImages = YES; + _executionOrder = SDWebImageDownloaderFIFOExecutionOrder; + _downloadQueue = [NSOperationQueue new]; + _downloadQueue.maxConcurrentOperationCount = 6; + _URLCallbacks = [NSMutableDictionary new]; +#ifdef SD_WEBP + _HTTPHeaders = [@{@"Accept": @"image/webp,image/*;q=0.8"} mutableCopy]; +#else + _HTTPHeaders = [@{@"Accept": @"image/*;q=0.8"} mutableCopy]; +#endif + _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT); + _downloadTimeout = 15.0; + } + return self; +} + +- (void)dealloc { + [self.downloadQueue cancelAllOperations]; + SDDispatchQueueRelease(_barrierQueue); +} + +- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field { + if (value) { + self.HTTPHeaders[field] = value; + } + else { + [self.HTTPHeaders removeObjectForKey:field]; + } +} + +- (NSString *)valueForHTTPHeaderField:(NSString *)field { + return self.HTTPHeaders[field]; +} + +- (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads { + _downloadQueue.maxConcurrentOperationCount = maxConcurrentDownloads; +} + +- (NSUInteger)currentDownloadCount { + return _downloadQueue.operationCount; +} + +- (NSInteger)maxConcurrentDownloads { + return _downloadQueue.maxConcurrentOperationCount; +} + +- (void)setOperationClass:(Class)operationClass { + _operationClass = operationClass ?: [SDWebImageDownloaderOperation class]; +} + +- (id )downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock { + __block SDWebImageDownloaderOperation *operation; + __weak __typeof(self)wself = self; + + [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^{ + NSTimeInterval timeoutInterval = wself.downloadTimeout; + if (timeoutInterval == 0.0) { + timeoutInterval = 15.0; + } + + // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval]; + request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies); + request.HTTPShouldUsePipelining = YES; + if (wself.headersFilter) { + request.allHTTPHeaderFields = wself.headersFilter(url, [wself.HTTPHeaders copy]); + } + else { + request.allHTTPHeaderFields = wself.HTTPHeaders; + } + operation = [[wself.operationClass alloc] initWithRequest:request + options:options + progress:^(NSInteger receivedSize, NSInteger expectedSize) { + SDWebImageDownloader *sself = wself; + if (!sself) return; + __block NSArray *callbacksForURL; + dispatch_sync(sself.barrierQueue, ^{ + callbacksForURL = [sself.URLCallbacks[url] copy]; + }); + for (NSDictionary *callbacks in callbacksForURL) { + dispatch_async(dispatch_get_main_queue(), ^{ + SDWebImageDownloaderProgressBlock callback = callbacks[kProgressCallbackKey]; + if (callback) callback(receivedSize, expectedSize); + }); + } + } + completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) { + SDWebImageDownloader *sself = wself; + if (!sself) return; + __block NSArray *callbacksForURL; + dispatch_barrier_sync(sself.barrierQueue, ^{ + callbacksForURL = [sself.URLCallbacks[url] copy]; + if (finished) { + [sself.URLCallbacks removeObjectForKey:url]; + } + }); + for (NSDictionary *callbacks in callbacksForURL) { + SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey]; + if (callback) callback(image, data, error, finished); + } + } + cancelled:^{ + SDWebImageDownloader *sself = wself; + if (!sself) return; + dispatch_barrier_async(sself.barrierQueue, ^{ + [sself.URLCallbacks removeObjectForKey:url]; + }); + }]; + operation.shouldDecompressImages = wself.shouldDecompressImages; + + if (wself.urlCredential) { + operation.credential = wself.urlCredential; + } else if (wself.username && wself.password) { + operation.credential = [NSURLCredential credentialWithUser:wself.username password:wself.password persistence:NSURLCredentialPersistenceForSession]; + } + + if (options & SDWebImageDownloaderHighPriority) { + operation.queuePriority = NSOperationQueuePriorityHigh; + } else if (options & SDWebImageDownloaderLowPriority) { + operation.queuePriority = NSOperationQueuePriorityLow; + } + + [wself.downloadQueue addOperation:operation]; + if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) { + // Emulate LIFO execution order by systematically adding new operations as last operation's dependency + [wself.lastAddedOperation addDependency:operation]; + wself.lastAddedOperation = operation; + } + }]; + + return operation; +} + +- (void)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock forURL:(NSURL *)url createCallback:(SDWebImageNoParamsBlock)createCallback { + // The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data. + if (url == nil) { + if (completedBlock != nil) { + completedBlock(nil, nil, nil, NO); + } + return; + } + + dispatch_barrier_sync(self.barrierQueue, ^{ + BOOL first = NO; + if (!self.URLCallbacks[url]) { + self.URLCallbacks[url] = [NSMutableArray new]; + first = YES; + } + + // Handle single download of simultaneous download request for the same URL + NSMutableArray *callbacksForURL = self.URLCallbacks[url]; + NSMutableDictionary *callbacks = [NSMutableDictionary new]; + if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy]; + if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy]; + [callbacksForURL addObject:callbacks]; + self.URLCallbacks[url] = callbacksForURL; + + if (first) { + createCallback(); + } + }); +} + +- (void)setSuspended:(BOOL)suspended { + [self.downloadQueue setSuspended:suspended]; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageDownloaderOperation.h b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageDownloaderOperation.h new file mode 100755 index 0000000..dd48b22 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageDownloaderOperation.h @@ -0,0 +1,78 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import +#import "SDWebImageDownloader.h" +#import "SDWebImageOperation.h" + +extern NSString *const SDWebImageDownloadStartNotification; +extern NSString *const SDWebImageDownloadReceiveResponseNotification; +extern NSString *const SDWebImageDownloadStopNotification; +extern NSString *const SDWebImageDownloadFinishNotification; + +@interface SDWebImageDownloaderOperation : NSOperation + +/** + * The request used by the operation's connection. + */ +@property (strong, nonatomic, readonly) NSURLRequest *request; + + +@property (assign, nonatomic) BOOL shouldDecompressImages; + +/** + * Whether the URL connection should consult the credential storage for authenticating the connection. `YES` by default. + * + * This is the value that is returned in the `NSURLConnectionDelegate` method `-connectionShouldUseCredentialStorage:`. + */ +@property (nonatomic, assign) BOOL shouldUseCredentialStorage; + +/** + * The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`. + * + * This will be overridden by any shared credentials that exist for the username or password of the request URL, if present. + */ +@property (nonatomic, strong) NSURLCredential *credential; + +/** + * The SDWebImageDownloaderOptions for the receiver. + */ +@property (assign, nonatomic, readonly) SDWebImageDownloaderOptions options; + +/** + * The expected size of data. + */ +@property (assign, nonatomic) NSInteger expectedSize; + +/** + * The response returned by the operation's connection. + */ +@property (strong, nonatomic) NSURLResponse *response; + +/** + * Initializes a `SDWebImageDownloaderOperation` object + * + * @see SDWebImageDownloaderOperation + * + * @param request the URL request + * @param options downloader options + * @param progressBlock the block executed when a new chunk of data arrives. + * @note the progress block is executed on a background queue + * @param completedBlock the block executed when the download is done. + * @note the completed block is executed on the main queue for success. If errors are found, there is a chance the block will be executed on a background queue + * @param cancelBlock the block executed if the download (operation) is cancelled + * + * @return the initialized instance + */ +- (id)initWithRequest:(NSURLRequest *)request + options:(SDWebImageDownloaderOptions)options + progress:(SDWebImageDownloaderProgressBlock)progressBlock + completed:(SDWebImageDownloaderCompletedBlock)completedBlock + cancelled:(SDWebImageNoParamsBlock)cancelBlock; + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageDownloaderOperation.m b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageDownloaderOperation.m new file mode 100755 index 0000000..5a8bd11 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageDownloaderOperation.m @@ -0,0 +1,466 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImageDownloaderOperation.h" +#import "SDWebImageDecoder.h" +#import "UIImage+MultiFormat.h" +#import +#import "SDWebImageManager.h" + +NSString *const SDWebImageDownloadStartNotification = @"SDWebImageDownloadStartNotification"; +NSString *const SDWebImageDownloadReceiveResponseNotification = @"SDWebImageDownloadReceiveResponseNotification"; +NSString *const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNotification"; +NSString *const SDWebImageDownloadFinishNotification = @"SDWebImageDownloadFinishNotification"; + +@interface SDWebImageDownloaderOperation () + +@property (copy, nonatomic) SDWebImageDownloaderProgressBlock progressBlock; +@property (copy, nonatomic) SDWebImageDownloaderCompletedBlock completedBlock; +@property (copy, nonatomic) SDWebImageNoParamsBlock cancelBlock; + +@property (assign, nonatomic, getter = isExecuting) BOOL executing; +@property (assign, nonatomic, getter = isFinished) BOOL finished; +@property (strong, nonatomic) NSMutableData *imageData; +@property (strong, nonatomic) NSURLConnection *connection; +@property (strong, atomic) NSThread *thread; + +#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 +@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskId; +#endif + +@end + +@implementation SDWebImageDownloaderOperation { + size_t width, height; + UIImageOrientation orientation; + BOOL responseFromCached; +} + +@synthesize executing = _executing; +@synthesize finished = _finished; + +- (id)initWithRequest:(NSURLRequest *)request + options:(SDWebImageDownloaderOptions)options + progress:(SDWebImageDownloaderProgressBlock)progressBlock + completed:(SDWebImageDownloaderCompletedBlock)completedBlock + cancelled:(SDWebImageNoParamsBlock)cancelBlock { + if ((self = [super init])) { + _request = request; + _shouldDecompressImages = YES; + _shouldUseCredentialStorage = YES; + _options = options; + _progressBlock = [progressBlock copy]; + _completedBlock = [completedBlock copy]; + _cancelBlock = [cancelBlock copy]; + _executing = NO; + _finished = NO; + _expectedSize = 0; + responseFromCached = YES; // Initially wrong until `connection:willCacheResponse:` is called or not called + } + return self; +} + +- (void)start { + @synchronized (self) { + if (self.isCancelled) { + self.finished = YES; + [self reset]; + return; + } + +#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 + Class UIApplicationClass = NSClassFromString(@"UIApplication"); + BOOL hasApplication = UIApplicationClass && [UIApplicationClass respondsToSelector:@selector(sharedApplication)]; + if (hasApplication && [self shouldContinueWhenAppEntersBackground]) { + __weak __typeof__ (self) wself = self; + UIApplication * app = [UIApplicationClass performSelector:@selector(sharedApplication)]; + self.backgroundTaskId = [app beginBackgroundTaskWithExpirationHandler:^{ + __strong __typeof (wself) sself = wself; + + if (sself) { + [sself cancel]; + + [app endBackgroundTask:sself.backgroundTaskId]; + sself.backgroundTaskId = UIBackgroundTaskInvalid; + } + }]; + } +#endif + + self.executing = YES; + self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO]; + self.thread = [NSThread currentThread]; + } + + [self.connection start]; + + if (self.connection) { + if (self.progressBlock) { + self.progressBlock(0, NSURLResponseUnknownLength); + } + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self]; + }); + + if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_5_1) { + // Make sure to run the runloop in our background thread so it can process downloaded data + // Note: we use a timeout to work around an issue with NSURLConnection cancel under iOS 5 + // not waking up the runloop, leading to dead threads (see https://github.com/rs/SDWebImage/issues/466) + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false); + } + else { + CFRunLoopRun(); + } + + if (!self.isFinished) { + [self.connection cancel]; + [self connection:self.connection didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorTimedOut userInfo:@{NSURLErrorFailingURLErrorKey : self.request.URL}]]; + } + } + else { + if (self.completedBlock) { + self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}], YES); + } + } + +#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 + Class UIApplicationClass = NSClassFromString(@"UIApplication"); + if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) { + return; + } + if (self.backgroundTaskId != UIBackgroundTaskInvalid) { + UIApplication * app = [UIApplication performSelector:@selector(sharedApplication)]; + [app endBackgroundTask:self.backgroundTaskId]; + self.backgroundTaskId = UIBackgroundTaskInvalid; + } +#endif +} + +- (void)cancel { + @synchronized (self) { + if (self.thread) { + [self performSelector:@selector(cancelInternalAndStop) onThread:self.thread withObject:nil waitUntilDone:NO]; + } + else { + [self cancelInternal]; + } + } +} + +- (void)cancelInternalAndStop { + if (self.isFinished) return; + [self cancelInternal]; + CFRunLoopStop(CFRunLoopGetCurrent()); +} + +- (void)cancelInternal { + if (self.isFinished) return; + [super cancel]; + if (self.cancelBlock) self.cancelBlock(); + + if (self.connection) { + [self.connection cancel]; + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self]; + }); + + // As we cancelled the connection, its callback won't be called and thus won't + // maintain the isFinished and isExecuting flags. + if (self.isExecuting) self.executing = NO; + if (!self.isFinished) self.finished = YES; + } + + [self reset]; +} + +- (void)done { + self.finished = YES; + self.executing = NO; + [self reset]; +} + +- (void)reset { + self.cancelBlock = nil; + self.completedBlock = nil; + self.progressBlock = nil; + self.connection = nil; + self.imageData = nil; + self.thread = nil; +} + +- (void)setFinished:(BOOL)finished { + [self willChangeValueForKey:@"isFinished"]; + _finished = finished; + [self didChangeValueForKey:@"isFinished"]; +} + +- (void)setExecuting:(BOOL)executing { + [self willChangeValueForKey:@"isExecuting"]; + _executing = executing; + [self didChangeValueForKey:@"isExecuting"]; +} + +- (BOOL)isConcurrent { + return YES; +} + +#pragma mark NSURLConnection (delegate) + +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { + + //'304 Not Modified' is an exceptional one + if (![response respondsToSelector:@selector(statusCode)] || ([((NSHTTPURLResponse *)response) statusCode] < 400 && [((NSHTTPURLResponse *)response) statusCode] != 304)) { + NSInteger expected = response.expectedContentLength > 0 ? (NSInteger)response.expectedContentLength : 0; + self.expectedSize = expected; + if (self.progressBlock) { + self.progressBlock(0, expected); + } + + self.imageData = [[NSMutableData alloc] initWithCapacity:expected]; + self.response = response; + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadReceiveResponseNotification object:self]; + }); + } + else { + NSUInteger code = [((NSHTTPURLResponse *)response) statusCode]; + + //This is the case when server returns '304 Not Modified'. It means that remote image is not changed. + //In case of 304 we need just cancel the operation and return cached image from the cache. + if (code == 304) { + [self cancelInternal]; + } else { + [self.connection cancel]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self]; + }); + + if (self.completedBlock) { + self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:[((NSHTTPURLResponse *)response) statusCode] userInfo:nil], YES); + } + CFRunLoopStop(CFRunLoopGetCurrent()); + [self done]; + } +} + +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { + [self.imageData appendData:data]; + + if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0 && self.completedBlock) { + // The following code is from http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/ + // Thanks to the author @Nyx0uf + + // Get the total bytes downloaded + const NSInteger totalSize = self.imageData.length; + + // Update the data source, we must pass ALL the data, not just the new bytes + CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)self.imageData, NULL); + + if (width + height == 0) { + CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL); + if (properties) { + NSInteger orientationValue = -1; + CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight); + if (val) CFNumberGetValue(val, kCFNumberLongType, &height); + val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth); + if (val) CFNumberGetValue(val, kCFNumberLongType, &width); + val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation); + if (val) CFNumberGetValue(val, kCFNumberNSIntegerType, &orientationValue); + CFRelease(properties); + + // When we draw to Core Graphics, we lose orientation information, + // which means the image below born of initWithCGIImage will be + // oriented incorrectly sometimes. (Unlike the image born of initWithData + // in connectionDidFinishLoading.) So save it here and pass it on later. + orientation = [[self class] orientationFromPropertyValue:(orientationValue == -1 ? 1 : orientationValue)]; + } + + } + + if (width + height > 0 && totalSize < self.expectedSize) { + // Create the image + CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL); + +#ifdef TARGET_OS_IPHONE + // Workaround for iOS anamorphic image + if (partialImageRef) { + const size_t partialHeight = CGImageGetHeight(partialImageRef); + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8, width * 4, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst); + CGColorSpaceRelease(colorSpace); + if (bmContext) { + CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = width, .size.height = partialHeight}, partialImageRef); + CGImageRelease(partialImageRef); + partialImageRef = CGBitmapContextCreateImage(bmContext); + CGContextRelease(bmContext); + } + else { + CGImageRelease(partialImageRef); + partialImageRef = nil; + } + } +#endif + + if (partialImageRef) { + UIImage *image = [UIImage imageWithCGImage:partialImageRef scale:1 orientation:orientation]; + NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL]; + UIImage *scaledImage = [self scaledImageForKey:key image:image]; + if (self.shouldDecompressImages) { + image = [UIImage decodedImageWithImage:scaledImage]; + } + else { + image = scaledImage; + } + CGImageRelease(partialImageRef); + dispatch_main_sync_safe(^{ + if (self.completedBlock) { + self.completedBlock(image, nil, nil, NO); + } + }); + } + } + + CFRelease(imageSource); + } + + if (self.progressBlock) { + self.progressBlock(self.imageData.length, self.expectedSize); + } +} + ++ (UIImageOrientation)orientationFromPropertyValue:(NSInteger)value { + switch (value) { + case 1: + return UIImageOrientationUp; + case 3: + return UIImageOrientationDown; + case 8: + return UIImageOrientationLeft; + case 6: + return UIImageOrientationRight; + case 2: + return UIImageOrientationUpMirrored; + case 4: + return UIImageOrientationDownMirrored; + case 5: + return UIImageOrientationLeftMirrored; + case 7: + return UIImageOrientationRightMirrored; + default: + return UIImageOrientationUp; + } +} + +- (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image { + return SDScaledImageForKey(key, image); +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection { + SDWebImageDownloaderCompletedBlock completionBlock = self.completedBlock; + @synchronized(self) { + CFRunLoopStop(CFRunLoopGetCurrent()); + self.thread = nil; + self.connection = nil; + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self]; + [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadFinishNotification object:self]; + }); + } + + if (![[NSURLCache sharedURLCache] cachedResponseForRequest:_request]) { + responseFromCached = NO; + } + + if (completionBlock) { + if (self.options & SDWebImageDownloaderIgnoreCachedResponse && responseFromCached) { + completionBlock(nil, nil, nil, YES); + } else if (self.imageData) { + UIImage *image = [UIImage sd_imageWithData:self.imageData]; + NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL]; + image = [self scaledImageForKey:key image:image]; + + // Do not force decoding animated GIFs + if (!image.images) { + if (self.shouldDecompressImages) { + image = [UIImage decodedImageWithImage:image]; + } + } + if (CGSizeEqualToSize(image.size, CGSizeZero)) { + completionBlock(nil, nil, [NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}], YES); + } + else { + completionBlock(image, self.imageData, nil, YES); + } + } else { + completionBlock(nil, nil, [NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}], YES); + } + } + self.completionBlock = nil; + [self done]; +} + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { + @synchronized(self) { + CFRunLoopStop(CFRunLoopGetCurrent()); + self.thread = nil; + self.connection = nil; + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self]; + }); + } + + if (self.completedBlock) { + self.completedBlock(nil, nil, error, YES); + } + self.completionBlock = nil; + [self done]; +} + +- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse { + responseFromCached = NO; // If this method is called, it means the response wasn't read from cache + if (self.request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData) { + // Prevents caching of responses + return nil; + } + else { + return cachedResponse; + } +} + +- (BOOL)shouldContinueWhenAppEntersBackground { + return self.options & SDWebImageDownloaderContinueInBackground; +} + +- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection __unused *)connection { + return self.shouldUseCredentialStorage; +} + +- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{ + if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { + if (!(self.options & SDWebImageDownloaderAllowInvalidSSLCertificates) && + [challenge.sender respondsToSelector:@selector(performDefaultHandlingForAuthenticationChallenge:)]) { + [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge]; + } else { + NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; + [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; + } + } else { + if ([challenge previousFailureCount] == 0) { + if (self.credential) { + [[challenge sender] useCredential:self.credential forAuthenticationChallenge:challenge]; + } else { + [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge]; + } + } else { + [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge]; + } + } +} + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageManager.h b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageManager.h new file mode 100755 index 0000000..18e578a --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageManager.h @@ -0,0 +1,299 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImageCompat.h" +#import "SDWebImageOperation.h" +#import "SDWebImageDownloader.h" +#import "SDImageCache.h" + +typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) { + /** + * By default, when a URL fail to be downloaded, the URL is blacklisted so the library won't keep trying. + * This flag disable this blacklisting. + */ + SDWebImageRetryFailed = 1 << 0, + + /** + * By default, image downloads are started during UI interactions, this flags disable this feature, + * leading to delayed download on UIScrollView deceleration for instance. + */ + SDWebImageLowPriority = 1 << 1, + + /** + * This flag disables on-disk caching + */ + SDWebImageCacheMemoryOnly = 1 << 2, + + /** + * This flag enables progressive download, the image is displayed progressively during download as a browser would do. + * By default, the image is only displayed once completely downloaded. + */ + SDWebImageProgressiveDownload = 1 << 3, + + /** + * Even if the image is cached, respect the HTTP response cache control, and refresh the image from remote location if needed. + * The disk caching will be handled by NSURLCache instead of SDWebImage leading to slight performance degradation. + * This option helps deal with images changing behind the same request URL, e.g. Facebook graph api profile pics. + * If a cached image is refreshed, the completion block is called once with the cached image and again with the final image. + * + * Use this flag only if you can't make your URLs static with embedded cache busting parameter. + */ + SDWebImageRefreshCached = 1 << 4, + + /** + * In iOS 4+, continue the download of the image if the app goes to background. This is achieved by asking the system for + * extra time in background to let the request finish. If the background task expires the operation will be cancelled. + */ + SDWebImageContinueInBackground = 1 << 5, + + /** + * Handles cookies stored in NSHTTPCookieStore by setting + * NSMutableURLRequest.HTTPShouldHandleCookies = YES; + */ + SDWebImageHandleCookies = 1 << 6, + + /** + * Enable to allow untrusted SSL certificates. + * Useful for testing purposes. Use with caution in production. + */ + SDWebImageAllowInvalidSSLCertificates = 1 << 7, + + /** + * By default, image are loaded in the order they were queued. This flag move them to + * the front of the queue and is loaded immediately instead of waiting for the current queue to be loaded (which + * could take a while). + */ + SDWebImageHighPriority = 1 << 8, + + /** + * By default, placeholder images are loaded while the image is loading. This flag will delay the loading + * of the placeholder image until after the image has finished loading. + */ + SDWebImageDelayPlaceholder = 1 << 9, + + /** + * We usually don't call transformDownloadedImage delegate method on animated images, + * as most transformation code would mangle it. + * Use this flag to transform them anyway. + */ + SDWebImageTransformAnimatedImage = 1 << 10, + + /** + * By default, image is added to the imageView after download. But in some cases, we want to + * have the hand before setting the image (apply a filter or add it with cross-fade animation for instance) + * Use this flag if you want to manually set the image in the completion when success + */ + SDWebImageAvoidAutoSetImage = 1 << 11 +}; + +typedef void(^SDWebImageCompletionBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL); + +typedef void(^SDWebImageCompletionWithFinishedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL); + +typedef NSString *(^SDWebImageCacheKeyFilterBlock)(NSURL *url); + + +@class SDWebImageManager; + +@protocol SDWebImageManagerDelegate + +@optional + +/** + * Controls which image should be downloaded when the image is not found in the cache. + * + * @param imageManager The current `SDWebImageManager` + * @param imageURL The url of the image to be downloaded + * + * @return Return NO to prevent the downloading of the image on cache misses. If not implemented, YES is implied. + */ +- (BOOL)imageManager:(SDWebImageManager *)imageManager shouldDownloadImageForURL:(NSURL *)imageURL; + +/** + * Allows to transform the image immediately after it has been downloaded and just before to cache it on disk and memory. + * NOTE: This method is called from a global queue in order to not to block the main thread. + * + * @param imageManager The current `SDWebImageManager` + * @param image The image to transform + * @param imageURL The url of the image to transform + * + * @return The transformed image object. + */ +- (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL; + +@end + +/** + * The SDWebImageManager is the class behind the UIImageView+WebCache category and likes. + * It ties the asynchronous downloader (SDWebImageDownloader) with the image cache store (SDImageCache). + * You can use this class directly to benefit from web image downloading with caching in another context than + * a UIView. + * + * Here is a simple example of how to use SDWebImageManager: + * + * @code + +SDWebImageManager *manager = [SDWebImageManager sharedManager]; +[manager downloadImageWithURL:imageURL + options:0 + progress:nil + completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { + if (image) { + // do something with image + } + }]; + + * @endcode + */ +@interface SDWebImageManager : NSObject + +@property (weak, nonatomic) id delegate; + +@property (strong, nonatomic, readonly) SDImageCache *imageCache; +@property (strong, nonatomic, readonly) SDWebImageDownloader *imageDownloader; + +/** + * The cache filter is a block used each time SDWebImageManager need to convert an URL into a cache key. This can + * be used to remove dynamic part of an image URL. + * + * The following example sets a filter in the application delegate that will remove any query-string from the + * URL before to use it as a cache key: + * + * @code + +[[SDWebImageManager sharedManager] setCacheKeyFilter:^(NSURL *url) { + url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path]; + return [url absoluteString]; +}]; + + * @endcode + */ +@property (nonatomic, copy) SDWebImageCacheKeyFilterBlock cacheKeyFilter; + +/** + * Returns global SDWebImageManager instance. + * + * @return SDWebImageManager shared instance + */ ++ (SDWebImageManager *)sharedManager; + +/** + * Downloads the image at the given URL if not present in cache or return the cached version otherwise. + * + * @param url The URL to the image + * @param options A mask to specify options to use for this request + * @param progressBlock A block called while image is downloading + * @param completedBlock A block called when operation has been completed. + * + * This parameter is required. + * + * This block has no return value and takes the requested UIImage as first parameter. + * In case of error the image parameter is nil and the second parameter may contain an NSError. + * + * The third parameter is an `SDImageCacheType` enum indicating if the image was retrieved from the local cache + * or from the memory cache or from the network. + * + * The last parameter is set to NO when the SDWebImageProgressiveDownload option is used and the image is + * downloading. This block is thus called repeatedly with a partial image. When image is fully downloaded, the + * block is called a last time with the full image and the last parameter set to YES. + * + * @return Returns an NSObject conforming to SDWebImageOperation. Should be an instance of SDWebImageDownloaderOperation + */ +- (id )downloadImageWithURL:(NSURL *)url + options:(SDWebImageOptions)options + progress:(SDWebImageDownloaderProgressBlock)progressBlock + completed:(SDWebImageCompletionWithFinishedBlock)completedBlock; + +/** + * Saves image to cache for given URL + * + * @param image The image to cache + * @param url The URL to the image + * + */ + +- (void)saveImageToCache:(UIImage *)image forURL:(NSURL *)url; + +/** + * Cancel all current operations + */ +- (void)cancelAll; + +/** + * Check one or more operations running + */ +- (BOOL)isRunning; + +/** + * Check if image has already been cached + * + * @param url image url + * + * @return if the image was already cached + */ +- (BOOL)cachedImageExistsForURL:(NSURL *)url; + +/** + * Check if image has already been cached on disk only + * + * @param url image url + * + * @return if the image was already cached (disk only) + */ +- (BOOL)diskImageExistsForURL:(NSURL *)url; + +/** + * Async check if image has already been cached + * + * @param url image url + * @param completionBlock the block to be executed when the check is finished + * + * @note the completion block is always executed on the main queue + */ +- (void)cachedImageExistsForURL:(NSURL *)url + completion:(SDWebImageCheckCacheCompletionBlock)completionBlock; + +/** + * Async check if image has already been cached on disk only + * + * @param url image url + * @param completionBlock the block to be executed when the check is finished + * + * @note the completion block is always executed on the main queue + */ +- (void)diskImageExistsForURL:(NSURL *)url + completion:(SDWebImageCheckCacheCompletionBlock)completionBlock; + + +/** + *Return the cache key for a given URL + */ +- (NSString *)cacheKeyForURL:(NSURL *)url; + +@end + + +#pragma mark - Deprecated + +typedef void(^SDWebImageCompletedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType) __deprecated_msg("Block type deprecated. Use `SDWebImageCompletionBlock`"); +typedef void(^SDWebImageCompletedWithFinishedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) __deprecated_msg("Block type deprecated. Use `SDWebImageCompletionWithFinishedBlock`"); + + +@interface SDWebImageManager (Deprecated) + +/** + * Downloads the image at the given URL if not present in cache or return the cached version otherwise. + * + * @deprecated This method has been deprecated. Use `downloadImageWithURL:options:progress:completed:` + */ +- (id )downloadWithURL:(NSURL *)url + options:(SDWebImageOptions)options + progress:(SDWebImageDownloaderProgressBlock)progressBlock + completed:(SDWebImageCompletedWithFinishedBlock)completedBlock __deprecated_msg("Method deprecated. Use `downloadImageWithURL:options:progress:completed:`"); + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageManager.m b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageManager.m new file mode 100755 index 0000000..6ca4713 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageManager.m @@ -0,0 +1,362 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImageManager.h" +#import + +@interface SDWebImageCombinedOperation : NSObject + +@property (assign, nonatomic, getter = isCancelled) BOOL cancelled; +@property (copy, nonatomic) SDWebImageNoParamsBlock cancelBlock; +@property (strong, nonatomic) NSOperation *cacheOperation; + +@end + +@interface SDWebImageManager () + +@property (strong, nonatomic, readwrite) SDImageCache *imageCache; +@property (strong, nonatomic, readwrite) SDWebImageDownloader *imageDownloader; +@property (strong, nonatomic) NSMutableSet *failedURLs; +@property (strong, nonatomic) NSMutableArray *runningOperations; + +@end + +@implementation SDWebImageManager + ++ (id)sharedManager { + static dispatch_once_t once; + static id instance; + dispatch_once(&once, ^{ + instance = [self new]; + }); + return instance; +} + +- (id)init { + if ((self = [super init])) { + _imageCache = [self createCache]; + _imageDownloader = [SDWebImageDownloader sharedDownloader]; + _failedURLs = [NSMutableSet new]; + _runningOperations = [NSMutableArray new]; + } + return self; +} + +- (SDImageCache *)createCache { + return [SDImageCache sharedImageCache]; +} + +- (NSString *)cacheKeyForURL:(NSURL *)url { + if (self.cacheKeyFilter) { + return self.cacheKeyFilter(url); + } + else { + return [url absoluteString]; + } +} + +- (BOOL)cachedImageExistsForURL:(NSURL *)url { + NSString *key = [self cacheKeyForURL:url]; + if ([self.imageCache imageFromMemoryCacheForKey:key] != nil) return YES; + return [self.imageCache diskImageExistsWithKey:key]; +} + +- (BOOL)diskImageExistsForURL:(NSURL *)url { + NSString *key = [self cacheKeyForURL:url]; + return [self.imageCache diskImageExistsWithKey:key]; +} + +- (void)cachedImageExistsForURL:(NSURL *)url + completion:(SDWebImageCheckCacheCompletionBlock)completionBlock { + NSString *key = [self cacheKeyForURL:url]; + + BOOL isInMemoryCache = ([self.imageCache imageFromMemoryCacheForKey:key] != nil); + + if (isInMemoryCache) { + // making sure we call the completion block on the main queue + dispatch_async(dispatch_get_main_queue(), ^{ + if (completionBlock) { + completionBlock(YES); + } + }); + return; + } + + [self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) { + // the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch + if (completionBlock) { + completionBlock(isInDiskCache); + } + }]; +} + +- (void)diskImageExistsForURL:(NSURL *)url + completion:(SDWebImageCheckCacheCompletionBlock)completionBlock { + NSString *key = [self cacheKeyForURL:url]; + + [self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) { + // the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch + if (completionBlock) { + completionBlock(isInDiskCache); + } + }]; +} + +- (id )downloadImageWithURL:(NSURL *)url + options:(SDWebImageOptions)options + progress:(SDWebImageDownloaderProgressBlock)progressBlock + completed:(SDWebImageCompletionWithFinishedBlock)completedBlock { + // Invoking this method without a completedBlock is pointless + NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead"); + + // Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, XCode won't + // throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString. + if ([url isKindOfClass:NSString.class]) { + url = [NSURL URLWithString:(NSString *)url]; + } + + // Prevents app crashing on argument type error like sending NSNull instead of NSURL + if (![url isKindOfClass:NSURL.class]) { + url = nil; + } + + __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new]; + __weak SDWebImageCombinedOperation *weakOperation = operation; + + BOOL isFailedUrl = NO; + @synchronized (self.failedURLs) { + isFailedUrl = [self.failedURLs containsObject:url]; + } + + if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) { + dispatch_main_sync_safe(^{ + NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]; + completedBlock(nil, error, SDImageCacheTypeNone, YES, url); + }); + return operation; + } + + @synchronized (self.runningOperations) { + [self.runningOperations addObject:operation]; + } + NSString *key = [self cacheKeyForURL:url]; + + operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) { + if (operation.isCancelled) { + @synchronized (self.runningOperations) { + [self.runningOperations removeObject:operation]; + } + + return; + } + + if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) { + if (image && options & SDWebImageRefreshCached) { + dispatch_main_sync_safe(^{ + // If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image + // AND try to re-download it in order to let a chance to NSURLCache to refresh it from server. + completedBlock(image, nil, cacheType, YES, url); + }); + } + + // download if no image or requested to refresh anyway, and download allowed by delegate + SDWebImageDownloaderOptions downloaderOptions = 0; + if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority; + if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload; + if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache; + if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground; + if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies; + if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates; + if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority; + if (image && options & SDWebImageRefreshCached) { + // force progressive off if image already cached but forced refreshing + downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload; + // ignore image read from NSURLCache if image if cached but force refreshing + downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse; + } + id subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) { + if (weakOperation.isCancelled) { + // Do nothing if the operation was cancelled + // See #699 for more details + // if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data + } + else if (error) { + dispatch_main_sync_safe(^{ + if (!weakOperation.isCancelled) { + completedBlock(nil, error, SDImageCacheTypeNone, finished, url); + } + }); + + if ( error.code != NSURLErrorNotConnectedToInternet + && error.code != NSURLErrorCancelled + && error.code != NSURLErrorTimedOut + && error.code != NSURLErrorInternationalRoamingOff + && error.code != NSURLErrorDataNotAllowed + && error.code != NSURLErrorCannotFindHost + && error.code != NSURLErrorCannotConnectToHost) { + @synchronized (self.failedURLs) { + [self.failedURLs addObject:url]; + } + } + } + else { + if ((options & SDWebImageRetryFailed)) { + @synchronized (self.failedURLs) { + [self.failedURLs removeObject:url]; + } + } + + BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly); + + if (options & SDWebImageRefreshCached && image && !downloadedImage) { + // Image refresh hit the NSURLCache cache, do not call the completion block + } + else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url]; + + if (transformedImage && finished) { + BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage]; + [self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : data) forKey:key toDisk:cacheOnDisk]; + } + + dispatch_main_sync_safe(^{ + if (!weakOperation.isCancelled) { + completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url); + } + }); + }); + } + else { + if (downloadedImage && finished) { + [self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk]; + } + + dispatch_main_sync_safe(^{ + if (!weakOperation.isCancelled) { + completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url); + } + }); + } + } + + if (finished) { + @synchronized (self.runningOperations) { + [self.runningOperations removeObject:operation]; + } + } + }]; + operation.cancelBlock = ^{ + [subOperation cancel]; + + @synchronized (self.runningOperations) { + [self.runningOperations removeObject:weakOperation]; + } + }; + } + else if (image) { + dispatch_main_sync_safe(^{ + if (!weakOperation.isCancelled) { + completedBlock(image, nil, cacheType, YES, url); + } + }); + @synchronized (self.runningOperations) { + [self.runningOperations removeObject:operation]; + } + } + else { + // Image not in cache and download disallowed by delegate + dispatch_main_sync_safe(^{ + if (!weakOperation.isCancelled) { + completedBlock(nil, nil, SDImageCacheTypeNone, YES, url); + } + }); + @synchronized (self.runningOperations) { + [self.runningOperations removeObject:operation]; + } + } + }]; + + return operation; +} + +- (void)saveImageToCache:(UIImage *)image forURL:(NSURL *)url { + if (image && url) { + NSString *key = [self cacheKeyForURL:url]; + [self.imageCache storeImage:image forKey:key toDisk:YES]; + } +} + +- (void)cancelAll { + @synchronized (self.runningOperations) { + NSArray *copiedOperations = [self.runningOperations copy]; + [copiedOperations makeObjectsPerformSelector:@selector(cancel)]; + [self.runningOperations removeObjectsInArray:copiedOperations]; + } +} + +- (BOOL)isRunning { + BOOL isRunning = NO; + @synchronized(self.runningOperations) { + isRunning = (self.runningOperations.count > 0); + } + return isRunning; +} + +@end + + +@implementation SDWebImageCombinedOperation + +- (void)setCancelBlock:(SDWebImageNoParamsBlock)cancelBlock { + // check if the operation is already cancelled, then we just call the cancelBlock + if (self.isCancelled) { + if (cancelBlock) { + cancelBlock(); + } + _cancelBlock = nil; // don't forget to nil the cancelBlock, otherwise we will get crashes + } else { + _cancelBlock = [cancelBlock copy]; + } +} + +- (void)cancel { + self.cancelled = YES; + if (self.cacheOperation) { + [self.cacheOperation cancel]; + self.cacheOperation = nil; + } + if (self.cancelBlock) { + self.cancelBlock(); + + // TODO: this is a temporary fix to #809. + // Until we can figure the exact cause of the crash, going with the ivar instead of the setter +// self.cancelBlock = nil; + _cancelBlock = nil; + } +} + +@end + + +@implementation SDWebImageManager (Deprecated) + +// deprecated method, uses the non deprecated method +// adapter for the completion block +- (id )downloadWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedWithFinishedBlock)completedBlock { + return [self downloadImageWithURL:url + options:options + progress:progressBlock + completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType, finished); + } + }]; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageOperation.h b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageOperation.h new file mode 100755 index 0000000..71094ee --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImageOperation.h @@ -0,0 +1,15 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import + +@protocol SDWebImageOperation + +- (void)cancel; + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImagePrefetcher.h b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImagePrefetcher.h new file mode 100755 index 0000000..6c31b15 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImagePrefetcher.h @@ -0,0 +1,108 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import +#import "SDWebImageManager.h" + +@class SDWebImagePrefetcher; + +@protocol SDWebImagePrefetcherDelegate + +@optional + +/** + * Called when an image was prefetched. + * + * @param imagePrefetcher The current image prefetcher + * @param imageURL The image url that was prefetched + * @param finishedCount The total number of images that were prefetched (successful or not) + * @param totalCount The total number of images that were to be prefetched + */ +- (void)imagePrefetcher:(SDWebImagePrefetcher *)imagePrefetcher didPrefetchURL:(NSURL *)imageURL finishedCount:(NSUInteger)finishedCount totalCount:(NSUInteger)totalCount; + +/** + * Called when all images are prefetched. + * @param imagePrefetcher The current image prefetcher + * @param totalCount The total number of images that were prefetched (whether successful or not) + * @param skippedCount The total number of images that were skipped + */ +- (void)imagePrefetcher:(SDWebImagePrefetcher *)imagePrefetcher didFinishWithTotalCount:(NSUInteger)totalCount skippedCount:(NSUInteger)skippedCount; + +@end + +typedef void(^SDWebImagePrefetcherProgressBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfTotalUrls); +typedef void(^SDWebImagePrefetcherCompletionBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfSkippedUrls); + +/** + * Prefetch some URLs in the cache for future use. Images are downloaded in low priority. + */ +@interface SDWebImagePrefetcher : NSObject + +/** + * The web image manager + */ +@property (strong, nonatomic, readonly) SDWebImageManager *manager; + +/** + * Maximum number of URLs to prefetch at the same time. Defaults to 3. + */ +@property (nonatomic, assign) NSUInteger maxConcurrentDownloads; + +/** + * SDWebImageOptions for prefetcher. Defaults to SDWebImageLowPriority. + */ +@property (nonatomic, assign) SDWebImageOptions options; + +/** + * Queue options for Prefetcher. Defaults to Main Queue. + */ +@property (nonatomic, assign) dispatch_queue_t prefetcherQueue; + +@property (weak, nonatomic) id delegate; + +/** + * Return the global image prefetcher instance. + */ ++ (SDWebImagePrefetcher *)sharedImagePrefetcher; + +/** + * Allows you to instantiate a prefetcher with any arbitrary image manager. + */ +- (id)initWithImageManager:(SDWebImageManager *)manager; + +/** + * Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching, + * currently one image is downloaded at a time, + * and skips images for failed downloads and proceed to the next image in the list + * + * @param urls list of URLs to prefetch + */ +- (void)prefetchURLs:(NSArray *)urls; + +/** + * Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching, + * currently one image is downloaded at a time, + * and skips images for failed downloads and proceed to the next image in the list + * + * @param urls list of URLs to prefetch + * @param progressBlock block to be called when progress updates; + * first parameter is the number of completed (successful or not) requests, + * second parameter is the total number of images originally requested to be prefetched + * @param completionBlock block to be called when prefetching is completed + * first param is the number of completed (successful or not) requests, + * second parameter is the number of skipped requests + */ +- (void)prefetchURLs:(NSArray *)urls progress:(SDWebImagePrefetcherProgressBlock)progressBlock completed:(SDWebImagePrefetcherCompletionBlock)completionBlock; + +/** + * Remove and cancel queued list + */ +- (void)cancelPrefetching; + + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImagePrefetcher.m b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImagePrefetcher.m new file mode 100755 index 0000000..a2ad8a8 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/SDWebImagePrefetcher.m @@ -0,0 +1,151 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImagePrefetcher.h" + +@interface SDWebImagePrefetcher () + +@property (strong, nonatomic) SDWebImageManager *manager; +@property (strong, nonatomic) NSArray *prefetchURLs; +@property (assign, nonatomic) NSUInteger requestedCount; +@property (assign, nonatomic) NSUInteger skippedCount; +@property (assign, nonatomic) NSUInteger finishedCount; +@property (assign, nonatomic) NSTimeInterval startedTime; +@property (copy, nonatomic) SDWebImagePrefetcherCompletionBlock completionBlock; +@property (copy, nonatomic) SDWebImagePrefetcherProgressBlock progressBlock; + +@end + +@implementation SDWebImagePrefetcher + ++ (SDWebImagePrefetcher *)sharedImagePrefetcher { + static dispatch_once_t once; + static id instance; + dispatch_once(&once, ^{ + instance = [self new]; + }); + return instance; +} + +- (id)init { + return [self initWithImageManager:[SDWebImageManager new]]; +} + +- (id)initWithImageManager:(SDWebImageManager *)manager { + if ((self = [super init])) { + _manager = manager; + _options = SDWebImageLowPriority; + _prefetcherQueue = dispatch_get_main_queue(); + self.maxConcurrentDownloads = 3; + } + return self; +} + +- (void)setMaxConcurrentDownloads:(NSUInteger)maxConcurrentDownloads { + self.manager.imageDownloader.maxConcurrentDownloads = maxConcurrentDownloads; +} + +- (NSUInteger)maxConcurrentDownloads { + return self.manager.imageDownloader.maxConcurrentDownloads; +} + +- (void)startPrefetchingAtIndex:(NSUInteger)index { + if (index >= self.prefetchURLs.count) return; + self.requestedCount++; + [self.manager downloadImageWithURL:self.prefetchURLs[index] options:self.options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { + if (!finished) return; + self.finishedCount++; + + if (image) { + if (self.progressBlock) { + self.progressBlock(self.finishedCount,[self.prefetchURLs count]); + } + } + else { + if (self.progressBlock) { + self.progressBlock(self.finishedCount,[self.prefetchURLs count]); + } + // Add last failed + self.skippedCount++; + } + if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didPrefetchURL:finishedCount:totalCount:)]) { + [self.delegate imagePrefetcher:self + didPrefetchURL:self.prefetchURLs[index] + finishedCount:self.finishedCount + totalCount:self.prefetchURLs.count + ]; + } + else if (self.finishedCount == self.requestedCount) { + [self reportStatus]; + if (self.completionBlock) { + self.completionBlock(self.finishedCount, self.skippedCount); + self.completionBlock = nil; + } + self.progressBlock = nil; + } + }]; +} + +- (void)reportStatus { + NSUInteger total = [self.prefetchURLs count]; + if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didFinishWithTotalCount:skippedCount:)]) { + [self.delegate imagePrefetcher:self + didFinishWithTotalCount:(total - self.skippedCount) + skippedCount:self.skippedCount + ]; + } +} + +- (void)prefetchURLs:(NSArray *)urls { + [self prefetchURLs:urls progress:nil completed:nil]; +} + +- (void)prefetchURLs:(NSArray *)urls progress:(SDWebImagePrefetcherProgressBlock)progressBlock completed:(SDWebImagePrefetcherCompletionBlock)completionBlock { + [self cancelPrefetching]; // Prevent duplicate prefetch request + self.startedTime = CFAbsoluteTimeGetCurrent(); + self.prefetchURLs = urls; + self.completionBlock = completionBlock; + self.progressBlock = progressBlock; + + __weak SDWebImagePrefetcher *weakSelf = self; + + if (urls.count == 0) { + if (completionBlock) { + completionBlock(0,0); + } + } else { + // http://oleb.net/blog/2013/07/parallelize-for-loops-gcd-dispatch_apply/ + // Optimize the maxConcurrentdownloads for effeciency. Since caching operations are involved that are non-trivial using + // dispatch_apply might be helpful. + + NSInteger maxNumberOfImages = self.prefetchURLs.count; + + dispatch_apply(maxNumberOfImages/self.maxConcurrentDownloads, self.prefetcherQueue, ^(size_t index) { + size_t i = index * self.maxConcurrentDownloads; + size_t stop = i + self.maxConcurrentDownloads; + do { + [weakSelf startPrefetchingAtIndex:i++]; + } while (i < stop); + }); + + // Download remaining images. + for (size_t i = maxNumberOfImages - (maxNumberOfImages % self.maxConcurrentDownloads); i < (size_t)maxNumberOfImages; i++) { + [self startPrefetchingAtIndex:i]; + } + } +} + +- (void)cancelPrefetching { + self.prefetchURLs = nil; + self.skippedCount = 0; + self.requestedCount = 0; + self.finishedCount = 0; + [self.manager cancelAll]; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/UIButton+WebCache.h b/SDAutoLayoutDemo/Vender/SDWebImage/UIButton+WebCache.h new file mode 100755 index 0000000..ecf5ced --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/UIButton+WebCache.h @@ -0,0 +1,229 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImageCompat.h" +#import "SDWebImageManager.h" + +/** + * Integrates SDWebImage async downloading and caching of remote images with UIButtonView. + */ +@interface UIButton (WebCache) + +/** + * Get the current image URL. + */ +- (NSURL *)sd_currentImageURL; + +/** + * Get the image URL for a control state. + * + * @param state Which state you want to know the URL for. The values are described in UIControlState. + */ +- (NSURL *)sd_imageURLForState:(UIControlState)state; + +/** + * Set the imageView `image` with an `url`. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + */ +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state; + +/** + * Set the imageView `image` with an `url` and a placeholder. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param placeholder The image to be set initially, until the image request finishes. + * @see sd_setImageWithURL:placeholderImage:options: + */ +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder; + +/** + * Set the imageView `image` with an `url`, placeholder and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + */ +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options; + +/** + * Set the imageView `image` with an `url`. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the imageView `image` with an `url`, placeholder. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param placeholder The image to be set initially, until the image request finishes. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the imageView `image` with an `url`, placeholder and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the backgroundImageView `image` with an `url`. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + */ +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state; + +/** + * Set the backgroundImageView `image` with an `url` and a placeholder. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param placeholder The image to be set initially, until the image request finishes. + * @see sd_setImageWithURL:placeholderImage:options: + */ +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder; + +/** + * Set the backgroundImageView `image` with an `url`, placeholder and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + */ +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options; + +/** + * Set the backgroundImageView `image` with an `url`. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the backgroundImageView `image` with an `url`, placeholder. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param placeholder The image to be set initially, until the image request finishes. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the backgroundImageView `image` with an `url`, placeholder and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Cancel the current image download + */ +- (void)sd_cancelImageLoadForState:(UIControlState)state; + +/** + * Cancel the current backgroundImage download + */ +- (void)sd_cancelBackgroundImageLoadForState:(UIControlState)state; + +@end + + +@interface UIButton (WebCacheDeprecated) + +- (NSURL *)currentImageURL __deprecated_msg("Use `sd_currentImageURL`"); +- (NSURL *)imageURLForState:(UIControlState)state __deprecated_msg("Use `sd_imageURLForState:`"); + +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:`"); +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:placeholderImage:`"); +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:placeholderImage:options:`"); + +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:completed:`"); +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:placeholderImage:completed:`"); +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:placeholderImage:options:completed:`"); + +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:`"); +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:placeholderImage:`"); +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:placeholderImage:options:`"); + +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:completed:`"); +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:placeholderImage:completed:`"); +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:placeholderImage:options:completed:`"); + +- (void)cancelCurrentImageLoad __deprecated_msg("Use `sd_cancelImageLoadForState:`"); +- (void)cancelBackgroundImageLoadForState:(UIControlState)state __deprecated_msg("Use `sd_cancelBackgroundImageLoadForState:`"); + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/UIButton+WebCache.m b/SDAutoLayoutDemo/Vender/SDWebImage/UIButton+WebCache.m new file mode 100755 index 0000000..5bf6620 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/UIButton+WebCache.m @@ -0,0 +1,270 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "UIButton+WebCache.h" +#import "objc/runtime.h" +#import "UIView+WebCacheOperation.h" + +static char imageURLStorageKey; + +@implementation UIButton (WebCache) + +- (NSURL *)sd_currentImageURL { + NSURL *url = self.imageURLStorage[@(self.state)]; + + if (!url) { + url = self.imageURLStorage[@(UIControlStateNormal)]; + } + + return url; +} + +- (NSURL *)sd_imageURLForState:(UIControlState)state { + return self.imageURLStorage[@(state)]; +} + +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state { + [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil]; +} + +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder { + [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil]; +} + +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { + [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil]; +} + +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock]; +} + +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock]; +} + +- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock { + + [self setImage:placeholder forState:state]; + [self sd_cancelImageLoadForState:state]; + + if (!url) { + [self.imageURLStorage removeObjectForKey:@(state)]; + + dispatch_main_async_safe(^{ + NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}]; + if (completedBlock) { + completedBlock(nil, error, SDImageCacheTypeNone, url); + } + }); + + return; + } + + self.imageURLStorage[@(state)] = url; + + __weak __typeof(self)wself = self; + id operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { + if (!wself) return; + dispatch_main_sync_safe(^{ + __strong UIButton *sself = wself; + if (!sself) return; + if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) + { + completedBlock(image, error, cacheType, url); + return; + } + else if (image) { + [sself setImage:image forState:state]; + } + if (completedBlock && finished) { + completedBlock(image, error, cacheType, url); + } + }); + }]; + [self sd_setImageLoadOperation:operation forState:state]; +} + +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil]; +} + +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil]; +} + +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil]; +} + +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock]; +} + +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock]; +} + +- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_cancelImageLoadForState:state]; + + [self setBackgroundImage:placeholder forState:state]; + + if (url) { + __weak __typeof(self)wself = self; + id operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { + if (!wself) return; + dispatch_main_sync_safe(^{ + __strong UIButton *sself = wself; + if (!sself) return; + if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) + { + completedBlock(image, error, cacheType, url); + return; + } + else if (image) { + [sself setBackgroundImage:image forState:state]; + } + if (completedBlock && finished) { + completedBlock(image, error, cacheType, url); + } + }); + }]; + [self sd_setBackgroundImageLoadOperation:operation forState:state]; + } else { + dispatch_main_async_safe(^{ + NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}]; + if (completedBlock) { + completedBlock(nil, error, SDImageCacheTypeNone, url); + } + }); + } +} + +- (void)sd_setImageLoadOperation:(id)operation forState:(UIControlState)state { + [self sd_setImageLoadOperation:operation forKey:[NSString stringWithFormat:@"UIButtonImageOperation%@", @(state)]]; +} + +- (void)sd_cancelImageLoadForState:(UIControlState)state { + [self sd_cancelImageLoadOperationWithKey:[NSString stringWithFormat:@"UIButtonImageOperation%@", @(state)]]; +} + +- (void)sd_setBackgroundImageLoadOperation:(id)operation forState:(UIControlState)state { + [self sd_setImageLoadOperation:operation forKey:[NSString stringWithFormat:@"UIButtonBackgroundImageOperation%@", @(state)]]; +} + +- (void)sd_cancelBackgroundImageLoadForState:(UIControlState)state { + [self sd_cancelImageLoadOperationWithKey:[NSString stringWithFormat:@"UIButtonBackgroundImageOperation%@", @(state)]]; +} + +- (NSMutableDictionary *)imageURLStorage { + NSMutableDictionary *storage = objc_getAssociatedObject(self, &imageURLStorageKey); + if (!storage) + { + storage = [NSMutableDictionary dictionary]; + objc_setAssociatedObject(self, &imageURLStorageKey, storage, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + + return storage; +} + +@end + + +@implementation UIButton (WebCacheDeprecated) + +- (NSURL *)currentImageURL { + return [self sd_currentImageURL]; +} + +- (NSURL *)imageURLForState:(UIControlState)state { + return [self sd_imageURLForState:state]; +} + +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state { + [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil]; +} + +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder { + [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil]; +} + +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { + [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil]; +} + +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:options completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil]; +} + +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil]; +} + +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil]; +} + +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:options completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)cancelCurrentImageLoad { + // in a backwards compatible manner, cancel for current state + [self sd_cancelImageLoadForState:self.state]; +} + +- (void)cancelBackgroundImageLoadForState:(UIControlState)state { + [self sd_cancelBackgroundImageLoadForState:state]; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/UIImage+GIF.h b/SDAutoLayoutDemo/Vender/SDWebImage/UIImage+GIF.h new file mode 100755 index 0000000..084f424 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/UIImage+GIF.h @@ -0,0 +1,19 @@ +// +// UIImage+GIF.h +// LBGIFImage +// +// Created by Laurin Brandner on 06.01.12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import + +@interface UIImage (GIF) + ++ (UIImage *)sd_animatedGIFNamed:(NSString *)name; + ++ (UIImage *)sd_animatedGIFWithData:(NSData *)data; + +- (UIImage *)sd_animatedImageByScalingAndCroppingToSize:(CGSize)size; + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/UIImage+GIF.m b/SDAutoLayoutDemo/Vender/SDWebImage/UIImage+GIF.m new file mode 100755 index 0000000..e1ded2a --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/UIImage+GIF.m @@ -0,0 +1,158 @@ +// +// UIImage+GIF.m +// LBGIFImage +// +// Created by Laurin Brandner on 06.01.12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "UIImage+GIF.h" +#import + +@implementation UIImage (GIF) + ++ (UIImage *)sd_animatedGIFWithData:(NSData *)data { + if (!data) { + return nil; + } + + CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); + + size_t count = CGImageSourceGetCount(source); + + UIImage *animatedImage; + + if (count <= 1) { + animatedImage = [[UIImage alloc] initWithData:data]; + } + else { + NSMutableArray *images = [NSMutableArray array]; + + NSTimeInterval duration = 0.0f; + + for (size_t i = 0; i < count; i++) { + CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL); + + duration += [self sd_frameDurationAtIndex:i source:source]; + + [images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]]; + + CGImageRelease(image); + } + + if (!duration) { + duration = (1.0f / 10.0f) * count; + } + + animatedImage = [UIImage animatedImageWithImages:images duration:duration]; + } + + CFRelease(source); + + return animatedImage; +} + ++ (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source { + float frameDuration = 0.1f; + CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil); + NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties; + NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary]; + + NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime]; + if (delayTimeUnclampedProp) { + frameDuration = [delayTimeUnclampedProp floatValue]; + } + else { + + NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime]; + if (delayTimeProp) { + frameDuration = [delayTimeProp floatValue]; + } + } + + // Many annoying ads specify a 0 duration to make an image flash as quickly as possible. + // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify + // a duration of <= 10 ms. See and + // for more information. + + if (frameDuration < 0.011f) { + frameDuration = 0.100f; + } + + CFRelease(cfFrameProperties); + return frameDuration; +} + ++ (UIImage *)sd_animatedGIFNamed:(NSString *)name { + CGFloat scale = [UIScreen mainScreen].scale; + + if (scale > 1.0f) { + NSString *retinaPath = [[NSBundle mainBundle] pathForResource:[name stringByAppendingString:@"@2x"] ofType:@"gif"]; + + NSData *data = [NSData dataWithContentsOfFile:retinaPath]; + + if (data) { + return [UIImage sd_animatedGIFWithData:data]; + } + + NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"]; + + data = [NSData dataWithContentsOfFile:path]; + + if (data) { + return [UIImage sd_animatedGIFWithData:data]; + } + + return [UIImage imageNamed:name]; + } + else { + NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"]; + + NSData *data = [NSData dataWithContentsOfFile:path]; + + if (data) { + return [UIImage sd_animatedGIFWithData:data]; + } + + return [UIImage imageNamed:name]; + } +} + +- (UIImage *)sd_animatedImageByScalingAndCroppingToSize:(CGSize)size { + if (CGSizeEqualToSize(self.size, size) || CGSizeEqualToSize(size, CGSizeZero)) { + return self; + } + + CGSize scaledSize = size; + CGPoint thumbnailPoint = CGPointZero; + + CGFloat widthFactor = size.width / self.size.width; + CGFloat heightFactor = size.height / self.size.height; + CGFloat scaleFactor = (widthFactor > heightFactor) ? widthFactor : heightFactor; + scaledSize.width = self.size.width * scaleFactor; + scaledSize.height = self.size.height * scaleFactor; + + if (widthFactor > heightFactor) { + thumbnailPoint.y = (size.height - scaledSize.height) * 0.5; + } + else if (widthFactor < heightFactor) { + thumbnailPoint.x = (size.width - scaledSize.width) * 0.5; + } + + NSMutableArray *scaledImages = [NSMutableArray array]; + + for (UIImage *image in self.images) { + UIGraphicsBeginImageContextWithOptions(size, NO, 0.0); + + [image drawInRect:CGRectMake(thumbnailPoint.x, thumbnailPoint.y, scaledSize.width, scaledSize.height)]; + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + + [scaledImages addObject:newImage]; + + UIGraphicsEndImageContext(); + } + + return [UIImage animatedImageWithImages:scaledImages duration:self.duration]; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/UIImage+MultiFormat.h b/SDAutoLayoutDemo/Vender/SDWebImage/UIImage+MultiFormat.h new file mode 100755 index 0000000..186ebc0 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/UIImage+MultiFormat.h @@ -0,0 +1,15 @@ +// +// UIImage+MultiFormat.h +// SDWebImage +// +// Created by Olivier Poitrey on 07/06/13. +// Copyright (c) 2013 Dailymotion. All rights reserved. +// + +#import + +@interface UIImage (MultiFormat) + ++ (UIImage *)sd_imageWithData:(NSData *)data; + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/UIImage+MultiFormat.m b/SDAutoLayoutDemo/Vender/SDWebImage/UIImage+MultiFormat.m new file mode 100755 index 0000000..a830754 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/UIImage+MultiFormat.m @@ -0,0 +1,118 @@ +// +// UIImage+MultiFormat.m +// SDWebImage +// +// Created by Olivier Poitrey on 07/06/13. +// Copyright (c) 2013 Dailymotion. All rights reserved. +// + +#import "UIImage+MultiFormat.h" +#import "UIImage+GIF.h" +#import "NSData+ImageContentType.h" +#import + +#ifdef SD_WEBP +#import "UIImage+WebP.h" +#endif + +@implementation UIImage (MultiFormat) + ++ (UIImage *)sd_imageWithData:(NSData *)data { + if (!data) { + return nil; + } + + UIImage *image; + NSString *imageContentType = [NSData sd_contentTypeForImageData:data]; + if ([imageContentType isEqualToString:@"image/gif"]) { + image = [UIImage sd_animatedGIFWithData:data]; + } +#ifdef SD_WEBP + else if ([imageContentType isEqualToString:@"image/webp"]) + { + image = [UIImage sd_imageWithWebPData:data]; + } +#endif + else { + image = [[UIImage alloc] initWithData:data]; + UIImageOrientation orientation = [self sd_imageOrientationFromImageData:data]; + if (orientation != UIImageOrientationUp) { + image = [UIImage imageWithCGImage:image.CGImage + scale:image.scale + orientation:orientation]; + } + } + + + return image; +} + + ++(UIImageOrientation)sd_imageOrientationFromImageData:(NSData *)imageData { + UIImageOrientation result = UIImageOrientationUp; + CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL); + if (imageSource) { + CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL); + if (properties) { + CFTypeRef val; + int exifOrientation; + val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation); + if (val) { + CFNumberGetValue(val, kCFNumberIntType, &exifOrientation); + result = [self sd_exifOrientationToiOSOrientation:exifOrientation]; + } // else - if it's not set it remains at up + CFRelease((CFTypeRef) properties); + } else { + //NSLog(@"NO PROPERTIES, FAIL"); + } + CFRelease(imageSource); + } + return result; +} + +#pragma mark EXIF orientation tag converter +// Convert an EXIF image orientation to an iOS one. +// reference see here: http://sylvana.net/jpegcrop/exif_orientation.html ++ (UIImageOrientation) sd_exifOrientationToiOSOrientation:(int)exifOrientation { + UIImageOrientation orientation = UIImageOrientationUp; + switch (exifOrientation) { + case 1: + orientation = UIImageOrientationUp; + break; + + case 3: + orientation = UIImageOrientationDown; + break; + + case 8: + orientation = UIImageOrientationLeft; + break; + + case 6: + orientation = UIImageOrientationRight; + break; + + case 2: + orientation = UIImageOrientationUpMirrored; + break; + + case 4: + orientation = UIImageOrientationDownMirrored; + break; + + case 5: + orientation = UIImageOrientationLeftMirrored; + break; + + case 7: + orientation = UIImageOrientationRightMirrored; + break; + default: + break; + } + return orientation; +} + + + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/UIImage+WebP.h b/SDAutoLayoutDemo/Vender/SDWebImage/UIImage+WebP.h new file mode 100755 index 0000000..b791e6f --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/UIImage+WebP.h @@ -0,0 +1,26 @@ +// +// UIImage+WebP.h +// SDWebImage +// +// Created by Olivier Poitrey on 07/06/13. +// Copyright (c) 2013 Dailymotion. All rights reserved. +// + +#ifdef SD_WEBP + +#import + +// Fix for issue #416 Undefined symbols for architecture armv7 since WebP introduction when deploying to device +void WebPInitPremultiplyNEON(void); + +void WebPInitUpsamplersNEON(void); + +void VP8DspInitNEON(void); + +@interface UIImage (WebP) + ++ (UIImage *)sd_imageWithWebPData:(NSData *)data; + +@end + +#endif diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/UIImage+WebP.m b/SDAutoLayoutDemo/Vender/SDWebImage/UIImage+WebP.m new file mode 100755 index 0000000..f27a1b6 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/UIImage+WebP.m @@ -0,0 +1,78 @@ +// +// UIImage+WebP.m +// SDWebImage +// +// Created by Olivier Poitrey on 07/06/13. +// Copyright (c) 2013 Dailymotion. All rights reserved. +// + +#ifdef SD_WEBP +#import "UIImage+WebP.h" + +#if !COCOAPODS +#import "webp/decode.h" +#else +#import "libwebp/webp/decode.h" +#endif + +// Callback for CGDataProviderRelease +static void FreeImageData(void *info, const void *data, size_t size) +{ + free((void *)data); +} + +@implementation UIImage (WebP) + ++ (UIImage *)sd_imageWithWebPData:(NSData *)data { + WebPDecoderConfig config; + if (!WebPInitDecoderConfig(&config)) { + return nil; + } + + if (WebPGetFeatures(data.bytes, data.length, &config.input) != VP8_STATUS_OK) { + return nil; + } + + config.output.colorspace = config.input.has_alpha ? MODE_rgbA : MODE_RGB; + config.options.use_threads = 1; + + // Decode the WebP image data into a RGBA value array. + if (WebPDecode(data.bytes, data.length, &config) != VP8_STATUS_OK) { + return nil; + } + + int width = config.input.width; + int height = config.input.height; + if (config.options.use_scaling) { + width = config.options.scaled_width; + height = config.options.scaled_height; + } + + // Construct a UIImage from the decoded RGBA value array. + CGDataProviderRef provider = + CGDataProviderCreateWithData(NULL, config.output.u.RGBA.rgba, config.output.u.RGBA.size, FreeImageData); + CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); + CGBitmapInfo bitmapInfo = config.input.has_alpha ? kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast : 0; + size_t components = config.input.has_alpha ? 4 : 3; + CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault; + CGImageRef imageRef = CGImageCreate(width, height, 8, components * 8, components * width, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent); + + CGColorSpaceRelease(colorSpaceRef); + CGDataProviderRelease(provider); + + UIImage *image = [[UIImage alloc] initWithCGImage:imageRef]; + CGImageRelease(imageRef); + + return image; +} + +@end + +#if !COCOAPODS +// Functions to resolve some undefined symbols when using WebP and force_load flag +void WebPInitPremultiplyNEON(void) {} +void WebPInitUpsamplersNEON(void) {} +void VP8DspInitNEON(void) {} +#endif + +#endif diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/UIImageView+HighlightedWebCache.h b/SDAutoLayoutDemo/Vender/SDWebImage/UIImageView+HighlightedWebCache.h new file mode 100755 index 0000000..c1d8fea --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/UIImageView+HighlightedWebCache.h @@ -0,0 +1,100 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import +#import "SDWebImageCompat.h" +#import "SDWebImageManager.h" + +/** + * Integrates SDWebImage async downloading and caching of remote images with UIImageView for highlighted state. + */ +@interface UIImageView (HighlightedWebCache) + +/** + * Set the imageView `highlightedImage` with an `url`. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + */ +- (void)sd_setHighlightedImageWithURL:(NSURL *)url; + +/** + * Set the imageView `highlightedImage` with an `url` and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + */ +- (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options; + +/** + * Set the imageView `highlightedImage` with an `url`. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setHighlightedImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the imageView `highlightedImage` with an `url` and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the imageView `highlightedImage` with an `url` and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + * @param progressBlock A block called while image is downloading + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Cancel the current download + */ +- (void)sd_cancelCurrentHighlightedImageLoad; + +@end + + +@interface UIImageView (HighlightedWebCacheDeprecated) + +- (void)setHighlightedImageWithURL:(NSURL *)url __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:`"); +- (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:options:`"); +- (void)setHighlightedImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:completed:`"); +- (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:options:completed:`"); +- (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setHighlightedImageWithURL:options:progress:completed:`"); + +- (void)cancelCurrentHighlightedImageLoad __deprecated_msg("Use `sd_cancelCurrentHighlightedImageLoad`"); + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/UIImageView+HighlightedWebCache.m b/SDAutoLayoutDemo/Vender/SDWebImage/UIImageView+HighlightedWebCache.m new file mode 100755 index 0000000..921134e --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/UIImageView+HighlightedWebCache.m @@ -0,0 +1,112 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "UIImageView+HighlightedWebCache.h" +#import "UIView+WebCacheOperation.h" + +#define UIImageViewHighlightedWebCacheOperationKey @"highlightedImage" + +@implementation UIImageView (HighlightedWebCache) + +- (void)sd_setHighlightedImageWithURL:(NSURL *)url { + [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:nil]; +} + +- (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options { + [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:nil]; +} + +- (void)sd_setHighlightedImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:completedBlock]; +} + +- (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:completedBlock]; +} + +- (void)sd_setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_cancelCurrentHighlightedImageLoad]; + + if (url) { + __weak __typeof(self)wself = self; + id operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { + if (!wself) return; + dispatch_main_sync_safe (^ + { + if (!wself) return; + if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) + { + completedBlock(image, error, cacheType, url); + return; + } + else if (image) { + wself.highlightedImage = image; + [wself setNeedsLayout]; + } + if (completedBlock && finished) { + completedBlock(image, error, cacheType, url); + } + }); + }]; + [self sd_setImageLoadOperation:operation forKey:UIImageViewHighlightedWebCacheOperationKey]; + } else { + dispatch_main_async_safe(^{ + NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}]; + if (completedBlock) { + completedBlock(nil, error, SDImageCacheTypeNone, url); + } + }); + } +} + +- (void)sd_cancelCurrentHighlightedImageLoad { + [self sd_cancelImageLoadOperationWithKey:UIImageViewHighlightedWebCacheOperationKey]; +} + +@end + + +@implementation UIImageView (HighlightedWebCacheDeprecated) + +- (void)setHighlightedImageWithURL:(NSURL *)url { + [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:nil]; +} + +- (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options { + [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:nil]; +} + +- (void)setHighlightedImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setHighlightedImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setHighlightedImageWithURL:url options:0 progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)cancelCurrentHighlightedImageLoad { + [self sd_cancelCurrentHighlightedImageLoad]; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/UIImageView+WebCache.h b/SDAutoLayoutDemo/Vender/SDWebImage/UIImageView+WebCache.h new file mode 100755 index 0000000..eeb7460 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/UIImageView+WebCache.h @@ -0,0 +1,213 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImageCompat.h" +#import "SDWebImageManager.h" + +/** + * Integrates SDWebImage async downloading and caching of remote images with UIImageView. + * + * Usage with a UITableViewCell sub-class: + * + * @code + +#import + +... + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + static NSString *MyIdentifier = @"MyIdentifier"; + + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier]; + + if (cell == nil) { + cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] + autorelease]; + } + + // Here we use the provided sd_setImageWithURL: method to load the web image + // Ensure you use a placeholder image otherwise cells will be initialized with no image + [cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://example.com/image.jpg"] + placeholderImage:[UIImage imageNamed:@"placeholder"]]; + + cell.textLabel.text = @"My Text"; + return cell; +} + + * @endcode + */ +@interface UIImageView (WebCache) + +/** + * Get the current image URL. + * + * Note that because of the limitations of categories this property can get out of sync + * if you use sd_setImage: directly. + */ +- (NSURL *)sd_imageURL; + +/** + * Set the imageView `image` with an `url`. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + */ +- (void)sd_setImageWithURL:(NSURL *)url; + +/** + * Set the imageView `image` with an `url` and a placeholder. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @see sd_setImageWithURL:placeholderImage:options: + */ +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder; + +/** + * Set the imageView `image` with an `url`, placeholder and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + */ +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options; + +/** + * Set the imageView `image` with an `url`. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the imageView `image` with an `url`, placeholder. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the imageView `image` with an `url`, placeholder and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the imageView `image` with an `url`, placeholder and custom options. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + * @param progressBlock A block called while image is downloading + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Set the imageView `image` with an `url` and optionally a placeholder image. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + * @param progressBlock A block called while image is downloading + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ +- (void)sd_setImageWithPreviousCachedImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock; + +/** + * Download an array of images and starts them in an animation loop + * + * @param arrayOfURLs An array of NSURL + */ +- (void)sd_setAnimationImagesWithURLs:(NSArray *)arrayOfURLs; + +/** + * Cancel the current download + */ +- (void)sd_cancelCurrentImageLoad; + +- (void)sd_cancelCurrentAnimationImagesLoad; + +/** + * Show activity UIActivityIndicatorView + */ +- (void)setShowActivityIndicatorView:(BOOL)show; + +/** + * set desired UIActivityIndicatorViewStyle + * + * @param style The style of the UIActivityIndicatorView + */ +- (void)setIndicatorStyle:(UIActivityIndicatorViewStyle)style; + +@end + + +@interface UIImageView (WebCacheDeprecated) + +- (NSURL *)imageURL __deprecated_msg("Use `sd_imageURL`"); + +- (void)setImageWithURL:(NSURL *)url __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:`"); +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:`"); +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:options`"); + +- (void)setImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:completed:`"); +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:completed:`"); +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:options:completed:`"); +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:options:progress:completed:`"); + +- (void)setAnimationImagesWithURLs:(NSArray *)arrayOfURLs __deprecated_msg("Use `sd_setAnimationImagesWithURLs:`"); + +- (void)cancelCurrentArrayLoad __deprecated_msg("Use `sd_cancelCurrentAnimationImagesLoad`"); + +- (void)cancelCurrentImageLoad __deprecated_msg("Use `sd_cancelCurrentImageLoad`"); + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/UIImageView+WebCache.m b/SDAutoLayoutDemo/Vender/SDWebImage/UIImageView+WebCache.m new file mode 100755 index 0000000..ec0d2e9 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/UIImageView+WebCache.m @@ -0,0 +1,277 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "UIImageView+WebCache.h" +#import "objc/runtime.h" +#import "UIView+WebCacheOperation.h" + +static char imageURLKey; +static char TAG_ACTIVITY_INDICATOR; +static char TAG_ACTIVITY_STYLE; +static char TAG_ACTIVITY_SHOW; + +@implementation UIImageView (WebCache) + +- (void)sd_setImageWithURL:(NSURL *)url { + [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil]; +} + +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder { + [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil]; +} + +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { + [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil]; +} + +- (void)sd_setImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:completedBlock]; +} + +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:completedBlock]; +} + +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:completedBlock]; +} + +- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock { + [self sd_cancelCurrentImageLoad]; + objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + + if (!(options & SDWebImageDelayPlaceholder)) { + dispatch_main_async_safe(^{ + self.image = placeholder; + }); + } + + if (url) { + + // check if activityView is enabled or not + if ([self showActivityIndicatorView]) { + [self addActivityIndicator]; + } + + __weak __typeof(self)wself = self; + id operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { + [wself removeActivityIndicator]; + if (!wself) return; + dispatch_main_sync_safe(^{ + if (!wself) return; + if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) + { + completedBlock(image, error, cacheType, url); + return; + } + else if (image) { + wself.image = image; + [wself setNeedsLayout]; + } else { + if ((options & SDWebImageDelayPlaceholder)) { + wself.image = placeholder; + [wself setNeedsLayout]; + } + } + if (completedBlock && finished) { + completedBlock(image, error, cacheType, url); + } + }); + }]; + [self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"]; + } else { + dispatch_main_async_safe(^{ + [self removeActivityIndicator]; + NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}]; + if (completedBlock) { + completedBlock(nil, error, SDImageCacheTypeNone, url); + } + }); + } +} + +- (void)sd_setImageWithPreviousCachedImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock { + NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:url]; + UIImage *lastPreviousCachedImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:key]; + + [self sd_setImageWithURL:url placeholderImage:lastPreviousCachedImage ?: placeholder options:options progress:progressBlock completed:completedBlock]; +} + +- (NSURL *)sd_imageURL { + return objc_getAssociatedObject(self, &imageURLKey); +} + +- (void)sd_setAnimationImagesWithURLs:(NSArray *)arrayOfURLs { + [self sd_cancelCurrentAnimationImagesLoad]; + __weak __typeof(self)wself = self; + + NSMutableArray *operationsArray = [[NSMutableArray alloc] init]; + + for (NSURL *logoImageURL in arrayOfURLs) { + id operation = [SDWebImageManager.sharedManager downloadImageWithURL:logoImageURL options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { + if (!wself) return; + dispatch_main_sync_safe(^{ + __strong UIImageView *sself = wself; + [sself stopAnimating]; + if (sself && image) { + NSMutableArray *currentImages = [[sself animationImages] mutableCopy]; + if (!currentImages) { + currentImages = [[NSMutableArray alloc] init]; + } + [currentImages addObject:image]; + + sself.animationImages = currentImages; + [sself setNeedsLayout]; + } + [sself startAnimating]; + }); + }]; + [operationsArray addObject:operation]; + } + + [self sd_setImageLoadOperation:[NSArray arrayWithArray:operationsArray] forKey:@"UIImageViewAnimationImages"]; +} + +- (void)sd_cancelCurrentImageLoad { + [self sd_cancelImageLoadOperationWithKey:@"UIImageViewImageLoad"]; +} + +- (void)sd_cancelCurrentAnimationImagesLoad { + [self sd_cancelImageLoadOperationWithKey:@"UIImageViewAnimationImages"]; +} + + +#pragma mark - +- (UIActivityIndicatorView *)activityIndicator { + return (UIActivityIndicatorView *)objc_getAssociatedObject(self, &TAG_ACTIVITY_INDICATOR); +} + +- (void)setActivityIndicator:(UIActivityIndicatorView *)activityIndicator { + objc_setAssociatedObject(self, &TAG_ACTIVITY_INDICATOR, activityIndicator, OBJC_ASSOCIATION_RETAIN); +} + +- (void)setShowActivityIndicatorView:(BOOL)show{ + objc_setAssociatedObject(self, &TAG_ACTIVITY_SHOW, [NSNumber numberWithBool:show], OBJC_ASSOCIATION_RETAIN); +} + +- (BOOL)showActivityIndicatorView{ + return [objc_getAssociatedObject(self, &TAG_ACTIVITY_SHOW) boolValue]; +} + +- (void)setIndicatorStyle:(UIActivityIndicatorViewStyle)style{ + objc_setAssociatedObject(self, &TAG_ACTIVITY_STYLE, [NSNumber numberWithInt:style], OBJC_ASSOCIATION_RETAIN); +} + +- (int)getIndicatorStyle{ + return [objc_getAssociatedObject(self, &TAG_ACTIVITY_STYLE) intValue]; +} + +- (void)addActivityIndicator { + if (!self.activityIndicator) { + self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:[self getIndicatorStyle]]; + self.activityIndicator.translatesAutoresizingMaskIntoConstraints = NO; + + dispatch_main_async_safe(^{ + [self addSubview:self.activityIndicator]; + + [self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeCenterX + multiplier:1.0 + constant:0.0]]; + [self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator + attribute:NSLayoutAttributeCenterY + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeCenterY + multiplier:1.0 + constant:0.0]]; + }); + } + + dispatch_main_async_safe(^{ + [self.activityIndicator startAnimating]; + }); + +} + +- (void)removeActivityIndicator { + if (self.activityIndicator) { + [self.activityIndicator removeFromSuperview]; + self.activityIndicator = nil; + } +} + +@end + + +@implementation UIImageView (WebCacheDeprecated) + +- (NSURL *)imageURL { + return [self sd_imageURL]; +} + +- (void)setImageWithURL:(NSURL *)url { + [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil]; +} + +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder { + [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil]; +} + +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { + [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil]; +} + +- (void)setImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock { + [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { + if (completedBlock) { + completedBlock(image, error, cacheType); + } + }]; +} + +- (void)cancelCurrentArrayLoad { + [self sd_cancelCurrentAnimationImagesLoad]; +} + +- (void)cancelCurrentImageLoad { + [self sd_cancelCurrentImageLoad]; +} + +- (void)setAnimationImagesWithURLs:(NSArray *)arrayOfURLs { + [self sd_setAnimationImagesWithURLs:arrayOfURLs]; +} + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/UIView+WebCacheOperation.h b/SDAutoLayoutDemo/Vender/SDWebImage/UIView+WebCacheOperation.h new file mode 100755 index 0000000..6719036 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/UIView+WebCacheOperation.h @@ -0,0 +1,36 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import +#import "SDWebImageManager.h" + +@interface UIView (WebCacheOperation) + +/** + * Set the image load operation (storage in a UIView based dictionary) + * + * @param operation the operation + * @param key key for storing the operation + */ +- (void)sd_setImageLoadOperation:(id)operation forKey:(NSString *)key; + +/** + * Cancel all operations for the current UIView and key + * + * @param key key for identifying the operations + */ +- (void)sd_cancelImageLoadOperationWithKey:(NSString *)key; + +/** + * Just remove the operations corresponding to the current UIView and key without cancelling them + * + * @param key key for identifying the operations + */ +- (void)sd_removeImageLoadOperationWithKey:(NSString *)key; + +@end diff --git a/SDAutoLayoutDemo/Vender/SDWebImage/UIView+WebCacheOperation.m b/SDAutoLayoutDemo/Vender/SDWebImage/UIView+WebCacheOperation.m new file mode 100755 index 0000000..9219478 --- /dev/null +++ b/SDAutoLayoutDemo/Vender/SDWebImage/UIView+WebCacheOperation.m @@ -0,0 +1,55 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "UIView+WebCacheOperation.h" +#import "objc/runtime.h" + +static char loadOperationKey; + +@implementation UIView (WebCacheOperation) + +- (NSMutableDictionary *)operationDictionary { + NSMutableDictionary *operations = objc_getAssociatedObject(self, &loadOperationKey); + if (operations) { + return operations; + } + operations = [NSMutableDictionary dictionary]; + objc_setAssociatedObject(self, &loadOperationKey, operations, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return operations; +} + +- (void)sd_setImageLoadOperation:(id)operation forKey:(NSString *)key { + [self sd_cancelImageLoadOperationWithKey:key]; + NSMutableDictionary *operationDictionary = [self operationDictionary]; + [operationDictionary setObject:operation forKey:key]; +} + +- (void)sd_cancelImageLoadOperationWithKey:(NSString *)key { + // Cancel in progress downloader from queue + NSMutableDictionary *operationDictionary = [self operationDictionary]; + id operations = [operationDictionary objectForKey:key]; + if (operations) { + if ([operations isKindOfClass:[NSArray class]]) { + for (id operation in operations) { + if (operation) { + [operation cancel]; + } + } + } else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){ + [(id) operations cancel]; + } + [operationDictionary removeObjectForKey:key]; + } +} + +- (void)sd_removeImageLoadOperationWithKey:(NSString *)key { + NSMutableDictionary *operationDictionary = [self operationDictionary]; + [operationDictionary removeObjectForKey:key]; +} + +@end diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/main.m" b/SDAutoLayoutDemo/main.m similarity index 100% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/main.m" rename to SDAutoLayoutDemo/main.m diff --git a/SDAutoLayoutDemoTests/Fixtures/news_sample.json b/SDAutoLayoutDemoTests/Fixtures/news_sample.json new file mode 100644 index 0000000..8e345e1 --- /dev/null +++ b/SDAutoLayoutDemoTests/Fixtures/news_sample.json @@ -0,0 +1,23 @@ +{ + "headline/T1348647853363": [ + { + "title": "测试新闻标题", + "digest": "测试摘要", + "ptime": "2020-01-01 12:00:00", + "docid": "TEST001", + "imgsrc": "http://example.com/a.jpg", + "hasHead": 0, + "imgType": 0 + }, + { + "title": "多图新闻", + "digest": "摘要", + "ptime": "2020-01-02 08:00:00", + "docid": "TEST002", + "imgextra": [ + { "imgsrc": "http://example.com/1.jpg" }, + { "imgsrc": "http://example.com/2.jpg" } + ] + } + ] +} diff --git "a/SDAutoLayout \346\265\213\350\257\225 DemoTests/Info.plist" b/SDAutoLayoutDemoTests/Info.plist similarity index 85% rename from "SDAutoLayout \346\265\213\350\257\225 DemoTests/Info.plist" rename to SDAutoLayoutDemoTests/Info.plist index 0d7f005..6c6c23c 100644 --- "a/SDAutoLayout \346\265\213\350\257\225 DemoTests/Info.plist" +++ b/SDAutoLayoutDemoTests/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - --.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -16,8 +16,6 @@ BNDL CFBundleShortVersionString 1.0 - CFBundleSignature - ???? CFBundleVersion 1 diff --git a/SDAutoLayoutDemoTests/SDAutoLayoutDemoTests.m b/SDAutoLayoutDemoTests/SDAutoLayoutDemoTests.m new file mode 100644 index 0000000..e6f7608 --- /dev/null +++ b/SDAutoLayoutDemoTests/SDAutoLayoutDemoTests.m @@ -0,0 +1,109 @@ +// +// SDAutoLayoutDemoTests.m +// SDAutoLayoutDemoTests +// + +#import +#import +#import +#import + +#import "ThreeModel.h" +#import "ThreeBaseCell.h" +#import "XYString.h" + +@interface SDAutoLayoutDemoTests : XCTestCase +@end + +@implementation SDAutoLayoutDemoTests + +#pragma mark - CocoaPods / AFNetworking 4 + +- (void)testAFHTTPSessionManagerFactoryExists { + AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; + XCTAssertNotNil(manager); + XCTAssertTrue([manager isKindOfClass:[AFHTTPSessionManager class]]); +} + +- (void)testAFNetworkingPodsDoNotImportPrivateIn6Header { + NSString *podsRoot = [[NSProcessInfo processInfo].environment objectForKey:@"PODS_ROOT"]; + if (podsRoot.length == 0) { + podsRoot = [[[NSBundle bundleForClass:[self class]] bundlePath] + stringByDeletingLastPathComponent]; + podsRoot = [podsRoot stringByAppendingPathComponent:@"../../Pods"]; + } + NSArray *files = @[ + @"AFNetworking/AFNetworking/AFHTTPSessionManager.m", + @"AFNetworking/AFNetworking/AFNetworkReachabilityManager.m", + ]; + for (NSString *rel in files) { + NSString *path = [podsRoot stringByAppendingPathComponent:rel]; + if (![[NSFileManager defaultManager] fileExistsAtPath:path]) { + continue; + } + NSString *source = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; + XCTAssertNotNil(source, @"应能读取 %@", rel); + XCTAssertFalse([source containsString:@"netinet6/in6.h"], + @"%@ 不应再 import 私有头 netinet6/in6.h", rel); + } +} + +- (void)testMJRefreshConfigUsesDefaultFactory { + MJRefreshConfig *config = MJRefreshConfig.defaultConfig; + XCTAssertNotNil(config); +} + +#pragma mark - DemoVC10 数据链路 + +- (void)testDemoVC10ArticleURLFormat { + NSInteger page = 10; + NSString *url = [NSString stringWithFormat:@"http://c.m.163.com/nc/article/%@/%ld-20.html", + @"headline/T1348647853363", (long)page]; + XCTAssertEqualObjects(url, + @"http://c.m.163.com/nc/article/headline/T1348647853363/10-20.html"); +} + +- (void)testXYStringParsesJSONDictionary { + NSString *json = @"{\"headline/T1348647853363\":[{\"title\":\"t\"}]}"; + NSDictionary *dict = [XYString getObjectFromJsonString:json]; + XCTAssertTrue([dict isKindOfClass:[NSDictionary class]]); + XCTAssertNotNil(dict[@"headline/T1348647853363"]); +} + +- (void)testThreeModelParsesFromFixture { + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + NSURL *url = [bundle URLForResource:@"news_sample" withExtension:@"json"]; + XCTAssertNotNil(url); + NSData *data = [NSData dataWithContentsOfURL:url]; + XCTAssertNotNil(data); + NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; + XCTAssertNotNil(dict); + NSString *key = dict.allKeys.firstObject; + NSArray *items = dict[key]; + NSArray *models = [ThreeModel mj_objectArrayWithKeyValuesArray:items]; + XCTAssertEqual(models.count, 2U); + XCTAssertEqualObjects(models.firstObject.title, @"测试新闻标题"); + XCTAssertEqualObjects(models.firstObject.docid, @"TEST001"); +} + +- (void)testThreeBaseCellIdentifierMapping { + ThreeModel *plain = [ThreeModel new]; + plain.hasHead = @(NO); + plain.imgType = nil; + plain.imgextra = nil; + XCTAssertEqualObjects([ThreeBaseCell cellIdentifierForRow:plain], @"ThreeFirstCell"); + + ThreeModel *multi = [ThreeModel new]; + multi.imgextra = @[@{}]; + XCTAssertEqualObjects([ThreeBaseCell cellIdentifierForRow:multi], @"ThreeSecondCell"); + + ThreeModel *big = [ThreeModel new]; + big.imgType = @1; + XCTAssertEqualObjects([ThreeBaseCell cellIdentifierForRow:big], @"ThreeThirdCell"); + + ThreeModel *head = [ThreeModel new]; + head.hasHead = @(YES); + XCTAssertEqualObjects([ThreeBaseCell cellIdentifierForRow:head], @"ThreeFourthCell"); +} + +@end diff --git a/SDAutoLayoutDemoUITests/DemoLayoutCatalog.swift b/SDAutoLayoutDemoUITests/DemoLayoutCatalog.swift new file mode 100644 index 0000000..20be461 --- /dev/null +++ b/SDAutoLayoutDemoUITests/DemoLayoutCatalog.swift @@ -0,0 +1,127 @@ +import XCTest + +struct DemoLayoutSpec { + let index: Int + let navigationTitle: String + let summary: String + let settleSeconds: TimeInterval + let check: (XCUIApplication, DemoLayoutContext) -> Void + + func makeContext() -> DemoLayoutContext { + DemoLayoutContext(index: index, navigationTitle: navigationTitle) + } +} + +enum DemoLayoutCatalog { + + static let demoCount = 15 + + static let all: [DemoLayoutSpec] = [ + DemoLayoutSpec( + index: 0, navigationTitle: "DemoVC0", summary: "约束动画联动-彩色块布局", + settleSeconds: 0.3 + ) { app, ctx in + LayoutAssertionHelper.assertColoredLayoutBoxes( + in: app, context: ctx, minCount: 4, indices: [0, 1, 2, 3, 4, 5] + ) + }, + + DemoLayoutSpec( + index: 1, navigationTitle: "DemoVC1", summary: "内容/宽高自适应 label 与 button", + settleSeconds: 0.3 + ) { app, ctx in + DemoVC1LayoutAssertions.assertMatchesLayoutCode(in: app, context: ctx) + }, + + DemoLayoutSpec( + index: 2, navigationTitle: "DemoVC2", summary: "自定义 Button + 流式子 view 排布", + settleSeconds: 0.3 + ) { app, ctx in + DemoScreenLayoutAssertions.assertDemo2(in: app, context: ctx) + }, + + DemoLayoutSpec( + index: 3, navigationTitle: "DemoVC3", summary: "简单 TableView cell 高度自适应", + settleSeconds: 0.5 + ) { app, ctx in + DemoScreenLayoutAssertions.assertDemo3(in: app, context: ctx) + }, + + DemoLayoutSpec( + index: 4, navigationTitle: "DemoVC4", summary: "多行 attributed label + 全宽按钮", + settleSeconds: 0.3 + ) { app, ctx in + DemoScreenLayoutAssertions.assertDemo4(in: app, context: ctx) + }, + + DemoLayoutSpec( + index: 5, navigationTitle: "DemoVC5", summary: "TableHeader 自适应 + 图文 cell", + settleSeconds: 0.8 + ) { app, ctx in + DemoScreenLayoutAssertions.assertDemo5(in: app, context: ctx) + }, + + DemoLayoutSpec( + index: 6, navigationTitle: "DemoVC6", summary: "ScrollView contentsize + 圆角块", + settleSeconds: 0.5 + ) { app, ctx in + DemoScreenLayoutAssertions.assertDemo6(in: app, context: ctx) + }, + + DemoLayoutSpec( + index: 7, navigationTitle: "DemoVC7", summary: "多 cell 类型-两步高度自适应", + settleSeconds: 0.8 + ) { app, ctx in + DemoScreenLayoutAssertions.assertDemo7(in: app, context: ctx) + }, + + DemoLayoutSpec( + index: 8, navigationTitle: "DemoVC8", summary: "升级版多 cell 一步高度自适应", + settleSeconds: 0.8 + ) { app, ctx in + DemoScreenLayoutAssertions.assertDemo8(in: app, context: ctx) + }, + + DemoLayoutSpec( + index: 9, navigationTitle: "DemoVC9", summary: "朋友圈时间线", + settleSeconds: 1.0 + ) { app, ctx in + DemoScreenLayoutAssertions.assertDemo9(in: app, context: ctx) + }, + + DemoLayoutSpec( + index: 10, navigationTitle: "DemoVC10", summary: "网易新闻列表", + settleSeconds: 1.0 + ) { app, ctx in + DemoScreenLayoutAssertions.assertDemo10(in: app, context: ctx) + }, + + DemoLayoutSpec( + index: 11, navigationTitle: "DemoVC11", summary: "聊天列表", + settleSeconds: 0.8 + ) { app, ctx in + DemoScreenLayoutAssertions.assertDemo11(in: app, context: ctx) + }, + + DemoLayoutSpec( + index: 12, navigationTitle: "DemoVC12", summary: "Scroll 流式布局", + settleSeconds: 0.5 + ) { app, ctx in + DemoScreenLayoutAssertions.assertDemo12(in: app, context: ctx) + }, + + DemoLayoutSpec( + index: 13, navigationTitle: "DemoVC13", summary: "Scroll 纵向内容自适应", + settleSeconds: 0.5 + ) { app, ctx in + DemoScreenLayoutAssertions.assertDemo13(in: app, context: ctx) + }, + + DemoLayoutSpec( + index: 14, navigationTitle: "DemoVC14", summary: "XIB cell 高度自适应", + settleSeconds: 0.8 + ) { app, ctx in + DemoScreenLayoutAssertions.assertDemo14(in: app, context: ctx) + } + ] +} diff --git a/SDAutoLayoutDemoUITests/DemoLayoutContext.swift b/SDAutoLayoutDemoUITests/DemoLayoutContext.swift new file mode 100644 index 0000000..a52a681 --- /dev/null +++ b/SDAutoLayoutDemoUITests/DemoLayoutContext.swift @@ -0,0 +1,10 @@ +import Foundation + +/// 当前正在验收的 Demo 页面,用于拼接到失败信息里。 +struct DemoLayoutContext { + let index: Int + let navigationTitle: String + + /// 例如:[Demo 4 DemoVC4] + var prefix: String { "[Demo \(index) \(navigationTitle)]" } +} diff --git a/SDAutoLayoutDemoUITests/DemoScreenLayoutAssertions.swift b/SDAutoLayoutDemoUITests/DemoScreenLayoutAssertions.swift new file mode 100644 index 0000000..f12b4e9 --- /dev/null +++ b/SDAutoLayoutDemoUITests/DemoScreenLayoutAssertions.swift @@ -0,0 +1,302 @@ +import XCTest + +/// Demo 2–14 与 `DemoVC*.m` 中 sd_layout 意图一一对应的 UI 验收(Demo 1 见 `DemoVC1LayoutAssertions`)。 +enum DemoScreenLayoutAssertions { + + private static let marginTolerance: CGFloat = 10 + private static let sizeTolerance: CGFloat = 8 + + // MARK: - Demo 2 自定义 Button + 流式子 view + + static func assertDemo2(in app: XCUIApplication, context: DemoLayoutContext) { + let window = LayoutAssertionHelper.controllerViewBounds(for: app) + let vcView = LayoutAssertionHelper.controllerContentFrame(for: app) + + let button = app.buttons["demo2.customButton"] + LayoutAssertionHelper.assertVisibleLayout( + button, in: vcView, context: context, elementName: "自定义 Button", minSize: 40 + ) + LayoutAssertionHelper.assertCenteredHorizontallyInControllerView( + button, in: app, tolerance: marginTolerance, context: context, elementName: "自定义 Button" + ) + LayoutAssertionHelper.assertTopInsetFromControllerView( + button, in: app, top: 10, tolerance: marginTolerance, context: context, elementName: "自定义 Button" + ) + LayoutAssertionHelper.assertApproxEqual( + button.frame.width, window.width * 0.5, tolerance: window.width * 0.08, context: context, + message: "自定义 Button 宽度应为屏幕一半 (widthRatioToView 0.5)" + ) + LayoutAssertionHelper.assertApproxEqual( + button.frame.height, 120, tolerance: sizeTolerance, context: context, + message: "自定义 Button 高度应为 120pt (heightIs(120))" + ) + + let green = app.otherElements["demo2.autoWidthContainer"] + let blue = app.otherElements["demo2.autoMarginContainer"] + LayoutAssertionHelper.assertVisibleLayout( + green, in: vcView, context: context, elementName: "固定间距流式容器(绿)", minSize: 30 + ) + LayoutAssertionHelper.assertVisibleLayout( + blue, in: vcView, context: context, elementName: "固定宽度流式容器(蓝)", minSize: 30 + ) + LayoutAssertionHelper.assertHorizontalInsetsFromControllerView( + green, in: app, left: 10, right: 10, tolerance: marginTolerance, + context: context, elementName: "绿色容器" + ) + LayoutAssertionHelper.assertApproxEqual( + green.frame.minY - button.frame.maxY, 10, tolerance: marginTolerance, context: context, + message: "绿色容器应在 Button 下方 10pt" + ) + LayoutAssertionHelper.assertHorizontalInsetsFromControllerView( + blue, in: app, left: 10, right: 10, tolerance: marginTolerance, + context: context, elementName: "蓝色容器" + ) + LayoutAssertionHelper.assertApproxEqual( + blue.frame.minY - green.frame.maxY, 10, tolerance: marginTolerance, context: context, + message: "蓝色容器应在绿色容器下方 10pt" + ) + XCTAssertGreaterThan( + blue.frame.maxY, green.frame.maxY + 40, + "\(context.prefix) 蓝色流式区域应有可见高度(setupAutoMarginFlowItems)" + ) + } + + // MARK: - Demo 3 简单 TableView + TestCell2 高度自适应 + + static func assertDemo3(in app: XCUIApplication, context: DemoLayoutContext) { + let table = LayoutAssertionHelper.demoTable(3, in: app) + LayoutAssertionHelper.assertTableAutoLayoutHealthy( + table, in: app, context: context, minCells: 1, minFirstCellHeight: 55 + ) + let cell = table.cells.element(boundBy: 0) + XCTAssertGreaterThan( + cell.frame.width, LayoutAssertionHelper.controllerViewBounds(for: app).width * 0.85, + "\(context.prefix) cell 应接近全宽(左右仅 contentView 边距)" + ) + } + + // MARK: - Demo 4 多行 attributed label + 全宽按钮 + + static func assertDemo4(in app: XCUIApplication, context: DemoLayoutContext) { + let vcView = LayoutAssertionHelper.controllerContentFrame(for: app) + + let label = app.staticTexts["demo4.attributedLabel"] + let refresh = app.buttons["demo4.refreshButton"] + LayoutAssertionHelper.assertVisibleLayout( + label, in: vcView, context: context, elementName: "富文本 Label", minSize: 16 + ) + LayoutAssertionHelper.assertHorizontalInsetsFromControllerView( + label, in: app, left: 10, right: 10, tolerance: marginTolerance, + context: context, elementName: "富文本 Label" + ) + LayoutAssertionHelper.assertTopInsetFromControllerView( + label, in: app, top: 0, tolerance: marginTolerance, context: context, + elementName: "富文本 Label" + ) + XCTAssertGreaterThan( + label.frame.height, 44, + "\(context.prefix) 富文本应多行折行 (autoHeightRatio(0)),高度=\(label.frame.height)pt" + ) + + LayoutAssertionHelper.assertVisibleLayout( + refresh, in: vcView, context: context, elementName: "刷新按钮", minSize: 20 + ) + LayoutAssertionHelper.assertLeftAlignedWithControllerView( + refresh, in: app, tolerance: sizeTolerance, context: context, elementName: "刷新按钮" + ) + LayoutAssertionHelper.assertRightAlignedWithControllerView( + refresh, in: app, tolerance: sizeTolerance, context: context, elementName: "刷新按钮" + ) + LayoutAssertionHelper.assertApproxEqual( + refresh.frame.minY - label.frame.maxY, 20, tolerance: marginTolerance, context: context, + message: "刷新按钮应在 Label 下方 20pt" + ) + LayoutAssertionHelper.assertApproxEqual( + refresh.frame.height, 30, tolerance: sizeTolerance, context: context, + message: "刷新按钮高度 30pt (heightIs(30))" + ) + } + + // MARK: - Demo 5 TableHeader 自适应 + 图文 cell + + static func assertDemo5(in app: XCUIApplication, context: DemoLayoutContext) { + let table = LayoutAssertionHelper.demoTable(5, in: app) + let dateLabel = app.staticTexts["demo5.header.dateLabel"] + XCTAssertTrue( + dateLabel.waitForExistence(timeout: 3), + "\(context.prefix) TableHeader 应包含日期 Label (demo5.header.dateLabel)" + ) + XCTAssertTrue( + dateLabel.label.contains("更新时间"), + "\(context.prefix) Header 日期文案异常:\(dateLabel.label)" + ) + LayoutAssertionHelper.assertTableAutoLayoutHealthy( + table, in: app, context: context, minCells: 1, minFirstCellHeight: 85 + ) + } + + // MARK: - Demo 6 ScrollView contentsize + 圆角块 + + static func assertDemo6(in app: XCUIApplication, context: DemoLayoutContext) { + let vcView = LayoutAssertionHelper.controllerContentFrame(for: app) + let scroll = LayoutAssertionHelper.demoScroll(in: app) + + let scrollFrame = scroll.frame + XCTAssertGreaterThan( + scrollFrame.width, vcView.width * 0.95, + "\(context.prefix) ScrollView 应铺满 (spaceToSuperView)" + ) + + let view0 = LayoutAssertionHelper.layoutBox(0, in: app) + let view1 = LayoutAssertionHelper.layoutBox(1, in: app) + let view3 = LayoutAssertionHelper.layoutBox(3, in: app) + LayoutAssertionHelper.assertVisibleLayout( + view0, in: vcView, context: context, + elementName: "view0", minSize: 40 + ) + LayoutAssertionHelper.assertHorizontalInsets( + view0, in: scrollFrame, left: 20, right: 20, tolerance: marginTolerance, + context: context, elementName: "view0(相对 ScrollView)" + ) + LayoutAssertionHelper.assertTopInset( + view0, in: scrollFrame, top: 20, tolerance: marginTolerance, + context: context, elementName: "view0(相对 ScrollView)" + ) + LayoutAssertionHelper.assertApproxEqual( + view0.frame.height, 150, tolerance: sizeTolerance, context: context, + message: "view0 高度 150pt" + ) + + LayoutAssertionHelper.assertApproxEqual( + view1.frame.width, 200, tolerance: sizeTolerance, context: context, + message: "view1 宽度 200pt (widthIs(200))" + ) + LayoutAssertionHelper.assertApproxEqual( + view1.frame.height, 200, tolerance: sizeTolerance, context: context, + message: "view1 高度 200pt" + ) + LayoutAssertionHelper.assertCenteredHorizontally( + view1, in: scrollFrame, tolerance: marginTolerance, context: context, elementName: "view1" + ) + LayoutAssertionHelper.assertApproxEqual( + view1.frame.minY - view0.frame.maxY, 20, tolerance: marginTolerance, context: context, + message: "view1 应在 view0 下方 20pt" + ) + + LayoutAssertionHelper.assertApproxEqual( + view3.frame.width, 250, tolerance: sizeTolerance, context: context, + message: "view3 宽度 250pt" + ) + LayoutAssertionHelper.assertApproxEqual( + view3.frame.width, view3.frame.height, tolerance: sizeTolerance, context: context, + message: "view3 应为正方形 (heightEqualToWidth)" + ) + LayoutAssertionHelper.assertApproxEqual( + view3.frame.minY - LayoutAssertionHelper.layoutBox(2, in: app).frame.maxY, + 20, tolerance: marginTolerance, context: context, + message: "view3 应在 view2 下方 20pt" + ) + + // 几何断言需在初始 contentOffset;assertScrollContentLayoutHealthy 会 swipeUp + LayoutAssertionHelper.assertScrollContentLayoutHealthy(scroll, in: app, context: context) + } + + // MARK: - Demo 7 / 8 多 cell 高度自适应 + + static func assertDemo7(in app: XCUIApplication, context: DemoLayoutContext) { + LayoutAssertionHelper.assertTableAutoLayoutHealthy( + LayoutAssertionHelper.demoTable(7, in: app), in: app, context: context, + minCells: 2, minFirstCellHeight: 60 + ) + } + + static func assertDemo8(in app: XCUIApplication, context: DemoLayoutContext) { + LayoutAssertionHelper.assertTableAutoLayoutHealthy( + LayoutAssertionHelper.demoTable(8, in: app), in: app, context: context, + minCells: 2, minFirstCellHeight: 60 + ) + } + + // MARK: - Demo 9 朋友圈 + + static func assertDemo9(in app: XCUIApplication, context: DemoLayoutContext) { + LayoutAssertionHelper.assertTableAutoLayoutHealthy( + LayoutAssertionHelper.demoTable(9, in: app), in: app, context: context, + minCells: 1, minFirstCellHeight: 100 + ) + } + + // MARK: - Demo 10 网易新闻 + + static func assertDemo10(in app: XCUIApplication, context: DemoLayoutContext) { + let table = app.tables["demoVC10TableView"] + let target = table.exists ? table : app.tables.element(boundBy: 0) + let vcView = LayoutAssertionHelper.controllerContentFrame(for: app) + LayoutAssertionHelper.assertVisibleLayout( + target, in: vcView, context: context, elementName: "新闻 TableView", minSize: 100 + ) + if target.cells.count > 0 { + LayoutAssertionHelper.assertTableAutoLayoutHealthy( + target, in: app, context: context, minCells: 1, minFirstCellHeight: 44 + ) + } + } + + // MARK: - Demo 11 聊天 + + static func assertDemo11(in app: XCUIApplication, context: DemoLayoutContext) { + LayoutAssertionHelper.assertTableAutoLayoutHealthy( + LayoutAssertionHelper.demoTable(11, in: app), in: app, context: context, + minCells: 1, minFirstCellHeight: 44 + ) + } + + // MARK: - Demo 12 / 13 Scroll 流式 / 纵向自适应 + + static func assertDemo12(in app: XCUIApplication, context: DemoLayoutContext) { + let scroll = LayoutAssertionHelper.demoScroll(in: app) + LayoutAssertionHelper.assertScrollContentLayoutHealthy(scroll, in: app, context: context) + + let flow = app.otherElements["demo12.flowContent"] + LayoutAssertionHelper.assertVisibleLayout( + flow, in: LayoutAssertionHelper.controllerContentFrame(for: app), context: context, + elementName: "流式内容区 demo12.flowContent", minSize: 80 + ) + XCTAssertGreaterThan( + flow.frame.height, 300, + "\(context.prefix) 流式内容区应被 35 个子项撑高(setupAutoWidthFlowItems),高度=\(flow.frame.height)pt" + ) + let beforeMaxY = flow.frame.maxY + scroll.swipeUp() + RunLoop.current.run(until: Date().addingTimeInterval(0.35)) + XCTAssertGreaterThan( + scroll.frame.height, 200, + "\(context.prefix) ScrollView 应可滚动展示长内容" + ) + _ = beforeMaxY + } + + static func assertDemo13(in app: XCUIApplication, context: DemoLayoutContext) { + let scroll = LayoutAssertionHelper.demoScroll(in: app) + LayoutAssertionHelper.assertScrollContentLayoutHealthy(scroll, in: app, context: context) + + let content = app.otherElements["demo13.scrollContent"] + LayoutAssertionHelper.assertVisibleLayout( + content, in: LayoutAssertionHelper.controllerContentFrame(for: app), context: context, + elementName: "纵向排版内容 demo13.scrollContent", minSize: 100 + ) + XCTAssertGreaterThan( + content.frame.height, 400, + "\(context.prefix) wrapperView 应由多块内容撑高(setupAutoHeightWithBottomView),高度=\(content.frame.height)pt" + ) + } + + // MARK: - Demo 14 XIB cell 高度自适应 + + static func assertDemo14(in app: XCUIApplication, context: DemoLayoutContext) { + LayoutAssertionHelper.assertTableAutoLayoutHealthy( + LayoutAssertionHelper.demoTable(14, in: app), in: app, context: context, + minCells: 2, minFirstCellHeight: 60 + ) + } +} diff --git a/SDAutoLayoutDemoUITests/DemoVC1LayoutAssertions.swift b/SDAutoLayoutDemoUITests/DemoVC1LayoutAssertions.swift new file mode 100644 index 0000000..dfef1b5 --- /dev/null +++ b/SDAutoLayoutDemoUITests/DemoVC1LayoutAssertions.swift @@ -0,0 +1,162 @@ +import XCTest + +/// DemoVC1 布局意图与 `DemoVC1.m` 中 sd_layout 链一一对应的 UI 验收。 +/// +/// 对应关系: +/// - setupAutoHeightView → view1 包紫色多行 label + 橙色条,高度随内容 +/// - setupAutoWidthLabel → 右下宽度自适应 label(max 180, h=20) +/// - setupAutoHeightLabel → 左下固定宽 100、高度自适应,与右 label 底对齐 +/// - setupAutoSizeButton → view1 下方 20pt 水平居中,按钮随文字变宽、高 25 +enum DemoVC1LayoutAssertions { + + private static let marginTolerance: CGFloat = 10 + private static let sizeTolerance: CGFloat = 6 + + static func assertMatchesLayoutCode(in app: XCUIApplication, context: DemoLayoutContext) { + let vcView = LayoutAssertionHelper.controllerContentFrame(for: app) + let window = LayoutAssertionHelper.controllerViewBounds(for: app) + + // MARK: demo1 — view1 内容自适应容器 + let view1 = LayoutAssertionHelper.layoutBox(1, in: app) + LayoutAssertionHelper.assertVisibleLayout( + view1, in: vcView, context: context, elementName: "灰色 view1 (demo.layout.view1)", minSize: 40 + ) + // .leftSpaceToView(self.view, 10) .rightSpaceToView(self.view, 10) .topSpaceToView(self.view, 80) + LayoutAssertionHelper.assertHorizontalInsetsFromControllerView( + view1, in: app, left: 10, right: 10, tolerance: marginTolerance, + context: context, elementName: "view1" + ) + LayoutAssertionHelper.assertTopInsetFromControllerView( + view1, in: app, top: 80, tolerance: marginTolerance, + context: context, elementName: "view1" + ) + + let purple = app.staticTexts["demo1.purpleLabel"] + let orange = app.otherElements["demo1.orangeBar"] + LayoutAssertionHelper.assertVisibleLayout( + purple, in: vcView, context: context, elementName: "紫色多行 Label", minSize: 16 + ) + LayoutAssertionHelper.assertVisibleLayout( + orange, in: vcView, context: context, elementName: "橙色条", minSize: 16 + ) + + // subview1: 距 view1 左/右/上 10;autoHeightRatio(0) → 多行 + LayoutAssertionHelper.assertHorizontalInsets( + purple, in: view1.frame, left: 10, right: 10, tolerance: marginTolerance, + context: context, elementName: "紫色 Label(相对 view1)" + ) + LayoutAssertionHelper.assertTopInset( + purple, in: view1.frame, top: 10, tolerance: marginTolerance, + context: context, elementName: "紫色 Label(相对 view1)" + ) + XCTAssertGreaterThan( + purple.frame.height, 44, + "\(context.prefix) 紫色 Label 应随文字多行增高(autoHeightRatio(0)),高度=\(purple.frame.height)pt" + ) + + // subview2: topSpaceToView(subview1, 10) widthRatioToView(subview1,1) heightIs(30) leftEqualToView(subview1) + LayoutAssertionHelper.assertApproxEqual( + orange.frame.minY - purple.frame.maxY, 10, tolerance: sizeTolerance, context: context, + message: "橙色条应在紫色 Label 下方 10pt" + ) + LayoutAssertionHelper.assertApproxEqual( + orange.frame.height, 30, tolerance: sizeTolerance, context: context, + message: "橙色条高度应为 30pt (heightIs(30))" + ) + LayoutAssertionHelper.assertApproxEqual( + orange.frame.width, purple.frame.width, tolerance: sizeTolerance, context: context, + message: "橙色条宽度应与紫色 Label 相同 (widthRatioToView 1)" + ) + LayoutAssertionHelper.assertApproxEqual( + orange.frame.minX, purple.frame.minX, tolerance: sizeTolerance, context: context, + message: "橙色条左缘应与紫色 Label 对齐 (leftEqualToView)" + ) + + // setupAutoHeightWithBottomView:subview2 bottomMargin:10 + LayoutAssertionHelper.assertApproxEqual( + view1.frame.maxY - orange.frame.maxY, 10, tolerance: sizeTolerance, context: context, + message: "view1 底边应在橙色条下方 10pt" + ) + XCTAssertGreaterThan( + view1.frame.height, 70, + "\(context.prefix) view1 总高度应被内容撑开(> 70pt),实际 \(view1.frame.height)pt" + ) + + // MARK: demo2 — 右下宽度自适应 label + let widthLabel = app.staticTexts["demo1.autoWidthLabel"] + LayoutAssertionHelper.assertVisibleLayout( + widthLabel, in: vcView, context: context, elementName: "宽度自适应 Label", minSize: 16 + ) + // .rightSpaceToView(self.view, 10) .bottomSpaceToView(self.view, 50) .heightIs(20) + LayoutAssertionHelper.assertApproxEqual( + window.maxX - widthLabel.frame.maxX, 10, tolerance: marginTolerance, context: context, + message: "宽度自适应 Label 距 self.view 右边应为 10pt" + ) + LayoutAssertionHelper.assertBottomInsetFromControllerView( + widthLabel, in: app, bottom: 50, tolerance: marginTolerance, + context: context, elementName: "宽度自适应 Label" + ) + LayoutAssertionHelper.assertApproxEqual( + widthLabel.frame.height, 20, tolerance: sizeTolerance, context: context, + message: "宽度自适应 Label 高度应为 20pt (heightIs(20))" + ) + XCTAssertLessThanOrEqual( + widthLabel.frame.width, 180 + sizeTolerance, + "\(context.prefix) 宽度自适应 Label 不应超过 maxWidth 180pt,实际 \(widthLabel.frame.width)pt" + ) + XCTAssertGreaterThan( + widthLabel.frame.width, 80, + "\(context.prefix) 宽度自适应 Label 应随文字展宽,实际 \(widthLabel.frame.width)pt" + ) + + // MARK: demo3 — 左下固定宽、高度自适应,底与右 label 对齐 + let heightLabel = app.staticTexts["demo1.autoHeightLabel"] + LayoutAssertionHelper.assertVisibleLayout( + heightLabel, in: vcView, context: context, elementName: "高度自适应 Label", minSize: 16 + ) + // .leftSpaceToView(self.view, 10) .widthIs(100) .bottomEqualToView(_autoWidthLabel) + LayoutAssertionHelper.assertApproxEqual( + heightLabel.frame.minX, window.minX + 10, tolerance: marginTolerance, context: context, + message: "高度自适应 Label 距 self.view 左边应为 10pt" + ) + LayoutAssertionHelper.assertApproxEqual( + heightLabel.frame.width, 100, tolerance: sizeTolerance, context: context, + message: "高度自适应 Label 宽度应为 100pt (widthIs(100))" + ) + LayoutAssertionHelper.assertBottomAligned( + heightLabel, widthLabel, tolerance: sizeTolerance, context: context, + message: "高度自适应 Label 应与宽度自适应 Label 底边对齐 (bottomEqualToView)" + ) + XCTAssertGreaterThan( + heightLabel.frame.height, 28, + "\(context.prefix) 高度自适应 Label 应随文字折行增高(autoHeightRatio(0)),高度=\(heightLabel.frame.height)pt" + ) + + // MARK: demo4 — view1 下方居中,按钮随文字 + let button = app.buttons["demo1.autoSizeButton"] + LayoutAssertionHelper.assertVisibleLayout( + button, in: vcView, context: context, elementName: "自适应 Button", minSize: 20 + ) + // .centerXEqualToView(self.view) .topSpaceToView(self.view1, 20) + LayoutAssertionHelper.assertCenteredHorizontallyInControllerView( + button, in: app, tolerance: marginTolerance, context: context, elementName: "自适应 Button" + ) + LayoutAssertionHelper.assertApproxEqual( + button.frame.minY - view1.frame.maxY, 20, tolerance: marginTolerance, context: context, + message: "Button 应在 view1 下方 20pt (topSpaceToView view1, 20)" + ) + // setupAutoSizeWithHorizontalPadding:10 buttonHeight:25 + LayoutAssertionHelper.assertApproxEqual( + button.frame.height, 25, tolerance: sizeTolerance, context: context, + message: "Button 高度应为 25pt (buttonHeight:25)" + ) + XCTAssertGreaterThan( + button.frame.width, 120, + "\(context.prefix) Button 宽度应随标题变宽(含左右 padding 10),实际 \(button.frame.width)pt" + ) + XCTAssertLessThan( + button.frame.width, window.width * 0.85, + "\(context.prefix) Button 宽度不应异常铺满全屏" + ) + } +} diff --git a/SDAutoLayoutDemoUITests/Info.plist b/SDAutoLayoutDemoUITests/Info.plist new file mode 100644 index 0000000..6c6c23c --- /dev/null +++ b/SDAutoLayoutDemoUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/SDAutoLayoutDemoUITests/LayoutAssertionHelper.swift b/SDAutoLayoutDemoUITests/LayoutAssertionHelper.swift new file mode 100644 index 0000000..fd536d1 --- /dev/null +++ b/SDAutoLayoutDemoUITests/LayoutAssertionHelper.swift @@ -0,0 +1,441 @@ +import XCTest + +/// 通过 XCUITest 的 frame 信息检查控件是否「布局正常」:有尺寸、落在屏幕可见区域内。 +enum LayoutAssertionHelper { + + /// 与 pushed VC 的 `self.view` 对齐(`topSpaceToView(self.view, …)` 等;非透明导航栏时顶边在导航栏下) + static func controllerContentFrame(for app: XCUIApplication) -> CGRect { + contentBounds(for: app) + } + + /// 主窗口 frame;仅用于必须相对屏幕的断言(如 Demo2 的 widthRatioToView 相对屏宽) + static func controllerViewBounds(for app: XCUIApplication) -> CGRect { + let window = app.windows.element(boundBy: 0) + return window.exists ? window.frame : app.frame + } + + static func contentBounds(for app: XCUIApplication) -> CGRect { + let window = app.windows.element(boundBy: 0) + guard window.exists else { return app.frame } + var bounds = window.frame + if let nav = app.navigationBars.allElementsBoundByIndex.first, nav.exists { + let top = nav.frame.maxY + if top > bounds.minY { + bounds.origin.y = top + bounds.size.height -= (top - window.frame.minY) + } + } + return bounds + } + + private static func frameDescription(_ frame: CGRect) -> String { + String( + format: "frame=(x:%.0f,y:%.0f,w:%.0f,h:%.0f)", + frame.origin.x, frame.origin.y, frame.width, frame.height + ) + } + + @discardableResult + static func assertVisibleLayout( + _ element: XCUIElement, + in contentBounds: CGRect, + context: DemoLayoutContext, + elementName: String, + minSize: CGFloat = 8, + file: StaticString = #filePath, + line: UInt = #line + ) -> Bool { + let p = context.prefix + XCTAssertTrue( + element.exists, + "\(p) \(elementName):元素不存在(accessibility 未找到)", + file: file, line: line + ) + let frame = element.frame + XCTAssertGreaterThan( + frame.width, minSize, + "\(p) \(elementName):宽度过小(要求 > \(minSize)pt),\(frameDescription(frame))", + file: file, line: line + ) + XCTAssertGreaterThan( + frame.height, minSize, + "\(p) \(elementName):高度过小(要求 > \(minSize)pt),\(frameDescription(frame))", + file: file, line: line + ) + let visible = frame.intersection(contentBounds) + XCTAssertGreaterThan( + visible.width, minSize, + "\(p) \(elementName):在内容区水平方向不可见,\(frameDescription(frame)),内容区 \(frameDescription(contentBounds))", + file: file, line: line + ) + XCTAssertGreaterThan( + visible.height, minSize, + "\(p) \(elementName):在内容区垂直方向不可见,\(frameDescription(frame)),内容区 \(frameDescription(contentBounds))", + file: file, line: line + ) + return true + } + + static func layoutBox(_ index: Int, in app: XCUIApplication) -> XCUIElement { + app.otherElements["demo.layout.view\(index)"] + } + + static func demoTable(_ index: Int, in app: XCUIApplication) -> XCUIElement { + let named = app.tables["demo.\(index).table"] + return named.exists ? named : app.tables.element(boundBy: 0) + } + + static func demoScroll(in app: XCUIApplication) -> XCUIElement { + let named = app.scrollViews["demo.scroll.main"] + return named.exists ? named : app.scrollViews.element(boundBy: 0) + } + + static func assertColoredLayoutBoxes( + in app: XCUIApplication, + context: DemoLayoutContext, + minCount: Int, + indices: [Int] = Array(0...8), + file: StaticString = #filePath, + line: UInt = #line + ) { + let bounds = contentBounds(for: app) + var visible = 0 + var missing: [Int] = [] + for index in indices { + let box = layoutBox(index, in: app) + guard box.exists else { + missing.append(index) + continue + } + let frame = box.frame + let visiblePart = frame.intersection(bounds) + if frame.width > 8, frame.height > 8, + visiblePart.width > 8, visiblePart.height > 8 { + visible += 1 + } + } + XCTAssertGreaterThanOrEqual( + visible, minCount, + """ + \(context.prefix) 彩色布局块数量不足:需要至少 \(minCount) 个可见 \ + (demo.layout.view0–8),实际 \(visible) 个;\ + 未找到的索引: \(missing.map(String.init).joined(separator: ",")) + """, + file: file, line: line + ) + } + + static func assertTableAutoLayoutHealthy( + _ table: XCUIElement, + in app: XCUIApplication, + context: DemoLayoutContext, + minCells: Int = 1, + minFirstCellHeight: CGFloat = 44, + file: StaticString = #filePath, + line: UInt = #line + ) { + let bounds = contentBounds(for: app) + assertVisibleLayout( + table, in: bounds, context: context, elementName: "主列表 TableView", minSize: 100, + file: file, line: line + ) + + let cells = table.cells + XCTAssertGreaterThanOrEqual( + cells.count, minCells, + "\(context.prefix) TableView cell 数量不足:需要 ≥ \(minCells),实际 \(cells.count)", + file: file, line: line + ) + + for index in 0.. \(minFirstCellHeight)pt,\(frameDescription(frame)) + """, + file: file, line: line + ) + } else { + XCTAssertGreaterThan( + frame.height, 20, + "\(context.prefix) 第 \(index) 个 cell 高度过小,\(frameDescription(frame))", + file: file, line: line + ) + } + } + } + + static func assertScrollContentLayoutHealthy( + _ scroll: XCUIElement, + in app: XCUIApplication, + context: DemoLayoutContext, + file: StaticString = #filePath, + line: UInt = #line + ) { + let bounds = contentBounds(for: app) + assertVisibleLayout( + scroll, in: bounds, context: context, elementName: "ScrollView (demo.scroll.main)", + minSize: 120, file: file, line: line + ) + + scroll.swipeUp() + RunLoop.current.run(until: Date().addingTimeInterval(0.4)) + + let hasContent = + app.staticTexts.count > 0 + || app.images.count > 0 + || app.buttons.count > 0 + || app.otherElements.count > 5 + XCTAssertTrue( + hasContent, + """ + \(context.prefix) ScrollView 上滑后未发现有效子内容\ + (texts=\(app.staticTexts.count) images=\(app.images.count) \ + buttons=\(app.buttons.count) others=\(app.otherElements.count)) + """, + file: file, line: line + ) + } + + // MARK: - 几何关系(与 SDAutoLayout 链式约束对应) + + static func assertApproxEqual( + _ actual: CGFloat, + _ expected: CGFloat, + tolerance: CGFloat, + context: DemoLayoutContext, + message: String, + file: StaticString = #filePath, + line: UInt = #line + ) { + XCTAssertLessThanOrEqual( + abs(actual - expected), tolerance, + "\(context.prefix) \(message):实际 \(actual),期望 \(expected)±\(tolerance)", + file: file, line: line + ) + } + + /// 元素左右边距相对容器(对应 leftSpaceToView / rightSpaceToView) + static func assertHorizontalInsets( + _ element: XCUIElement, + in container: CGRect, + left: CGFloat, + right: CGFloat, + tolerance: CGFloat = 8, + context: DemoLayoutContext, + elementName: String, + file: StaticString = #filePath, + line: UInt = #line + ) { + let frame = element.frame + assertApproxEqual( + frame.minX, container.minX + left, tolerance: tolerance, context: context, + message: "\(elementName) 左边距应为 \(left)pt", file: file, line: line + ) + assertApproxEqual( + container.maxX - frame.maxX, right, tolerance: tolerance, context: context, + message: "\(elementName) 右边距应为 \(right)pt", file: file, line: line + ) + } + + // MARK: - 相对 self.view(DemoVC 里最常见的父视图) + + /// 透明导航栏时 `self.view` 顶与窗口齐;`translucent = NO` 时在导航栏下。按元素实际 y 自动选参照。 + static func resolvedControllerFrame( + for app: XCUIApplication, + element: XCUIElement, + expectedTop: CGFloat + ) -> CGRect { + let window = controllerViewBounds(for: app) + let content = controllerContentFrame(for: app) + guard content.minY > window.minY + 1 else { return window } + let y = element.frame.minY + let windowError = abs(y - (window.minY + expectedTop)) + let contentError = abs(y - (content.minY + expectedTop)) + return windowError <= contentError ? window : content + } + + static func assertTopInsetFromControllerView( + _ element: XCUIElement, + in app: XCUIApplication, + top: CGFloat, + tolerance: CGFloat = 8, + context: DemoLayoutContext, + elementName: String, + file: StaticString = #filePath, + line: UInt = #line + ) { + let container = resolvedControllerFrame(for: app, element: element, expectedTop: top) + assertTopInset( + element, in: container, top: top, tolerance: tolerance, + context: context, elementName: elementName, file: file, line: line + ) + } + + static func assertHorizontalInsetsFromControllerView( + _ element: XCUIElement, + in app: XCUIApplication, + left: CGFloat, + right: CGFloat, + tolerance: CGFloat = 8, + context: DemoLayoutContext, + elementName: String, + file: StaticString = #filePath, + line: UInt = #line + ) { + let container = resolvedControllerFrame(for: app, element: element, expectedTop: 0) + assertHorizontalInsets( + element, in: container, left: left, right: right, + tolerance: tolerance, context: context, elementName: elementName, file: file, line: line + ) + } + + static func assertBottomInsetFromControllerView( + _ element: XCUIElement, + in app: XCUIApplication, + bottom: CGFloat, + tolerance: CGFloat = 8, + context: DemoLayoutContext, + elementName: String, + file: StaticString = #filePath, + line: UInt = #line + ) { + let window = controllerViewBounds(for: app) + let content = controllerContentFrame(for: app) + let container = + (content.minY > window.minY + 1 && abs(element.frame.maxY - content.maxY) < abs(element.frame.maxY - window.maxY)) + ? content : window + assertApproxEqual( + container.maxY - element.frame.maxY, bottom, tolerance: tolerance, context: context, + message: "\(elementName) 距 self.view 底边应为 \(bottom)pt", file: file, line: line + ) + } + + static func assertCenteredHorizontallyInControllerView( + _ element: XCUIElement, + in app: XCUIApplication, + tolerance: CGFloat = 8, + context: DemoLayoutContext, + elementName: String, + file: StaticString = #filePath, + line: UInt = #line + ) { + assertCenteredHorizontally( + element, in: resolvedControllerFrame(for: app, element: element, expectedTop: 0), + tolerance: tolerance, context: context, elementName: elementName, file: file, line: line + ) + } + + static func assertLeftAlignedWithControllerView( + _ element: XCUIElement, + in app: XCUIApplication, + tolerance: CGFloat = 8, + context: DemoLayoutContext, + elementName: String, + file: StaticString = #filePath, + line: UInt = #line + ) { + let vc = resolvedControllerFrame(for: app, element: element, expectedTop: 0) + assertApproxEqual( + element.frame.minX, vc.minX, tolerance: tolerance, context: context, + message: "\(elementName) 应左对齐 self.view (leftEqualToView)", file: file, line: line + ) + } + + static func assertRightAlignedWithControllerView( + _ element: XCUIElement, + in app: XCUIApplication, + tolerance: CGFloat = 8, + context: DemoLayoutContext, + elementName: String, + file: StaticString = #filePath, + line: UInt = #line + ) { + let vc = resolvedControllerFrame(for: app, element: element, expectedTop: 0) + assertApproxEqual( + element.frame.maxX, vc.maxX, tolerance: tolerance, context: context, + message: "\(elementName) 应右对齐 self.view (rightEqualToView)", file: file, line: line + ) + } + + /// 元素顶部相对容器(对应 topSpaceToView) + static func assertTopInset( + _ element: XCUIElement, + in container: CGRect, + top: CGFloat, + tolerance: CGFloat = 8, + context: DemoLayoutContext, + elementName: String, + file: StaticString = #filePath, + line: UInt = #line + ) { + assertApproxEqual( + element.frame.minY, container.minY + top, tolerance: tolerance, context: context, + message: "\(elementName) 上边距应为 \(top)pt", file: file, line: line + ) + } + + /// 两元素底边对齐(对应 bottomEqualToView) + static func assertBottomAligned( + _ a: XCUIElement, + _ b: XCUIElement, + tolerance: CGFloat = 4, + context: DemoLayoutContext, + message: String, + file: StaticString = #filePath, + line: UInt = #line + ) { + assertApproxEqual( + a.frame.maxY, b.frame.maxY, tolerance: tolerance, context: context, + message: message, file: file, line: line + ) + } + + /// 水平居中(对应 centerXEqualToView) + static func assertCenteredHorizontally( + _ element: XCUIElement, + in container: CGRect, + tolerance: CGFloat = 8, + context: DemoLayoutContext, + elementName: String, + file: StaticString = #filePath, + line: UInt = #line + ) { + let centerX = element.frame.midX + let expected = container.midX + assertApproxEqual( + centerX, expected, tolerance: tolerance, context: context, + message: "\(elementName) 应水平居中", file: file, line: line + ) + } + + static func countSubstantialElements(in app: XCUIApplication, bounds: CGRect) -> Int { + var count = 0 + let types: [XCUIElementQuery] = [ + app.staticTexts, app.buttons, app.images, app.otherElements + ] + for query in types { + for index in 0.. 12, part.height > 12 { count += 1 } + } + } + return count + } +} diff --git a/SDAutoLayoutDemoUITests/SDAutoLayoutDemoUITests.swift b/SDAutoLayoutDemoUITests/SDAutoLayoutDemoUITests.swift new file mode 100644 index 0000000..34e6372 --- /dev/null +++ b/SDAutoLayoutDemoUITests/SDAutoLayoutDemoUITests.swift @@ -0,0 +1,107 @@ +import XCTest + +final class SDAutoLayoutDemoUITests: XCTestCase { + + private var app: XCUIApplication! + + override func setUpWithError() throws { + continueAfterFailure = false + app = XCUIApplication() + app.launchArguments.append("-SDAutoLayoutUITest") + app.launch() + } + + /// 依次进入 Demo 0–14,按 `DemoLayoutCatalog` 规则检查自动布局。 + func testAllDemosAutoLayoutIsHealthy() { + for spec in DemoLayoutCatalog.all { + let ctx = spec.makeContext() + XCTContext.runActivity(named: "Demo \(spec.index): \(spec.summary)") { _ in + openDemo(row: spec.index, context: ctx) + if spec.settleSeconds > 0 { + RunLoop.current.run(until: Date().addingTimeInterval(spec.settleSeconds)) + } + spec.check(app, ctx) + navigateBackToDemoList(context: ctx) + } + } + } + + func testDemoListIsVisible() { + let list = app.tables["demoListTable"] + XCTAssertTrue( + list.waitForExistence(timeout: 5), + "首页 Demo 列表未出现(demoListTable)" + ) + XCTAssertTrue( + app.staticTexts["Demo -- 0"].exists, + "首页缺少 Demo -- 0 入口" + ) + XCTAssertTrue( + app.staticTexts["Demo -- 14"].exists, + "首页缺少 Demo -- 14 入口(可尝试上滑列表)" + ) + } + + func testOpenDemo1LayoutSnapshot() { + let spec = DemoLayoutCatalog.all[1] + let ctx = spec.makeContext() + XCTContext.runActivity(named: "Demo 1: \(spec.summary)") { _ in + openDemo(row: spec.index, context: ctx) + RunLoop.current.run(until: Date().addingTimeInterval(spec.settleSeconds)) + spec.check(app, ctx) + navigateBackToDemoList(context: ctx) + } + } + + // MARK: - Navigation + + private func openDemo(row: Int, context: DemoLayoutContext) { + let list = app.tables["demoListTable"] + XCTAssertTrue( + list.waitForExistence(timeout: 5), + "\(context.prefix) 无法返回首页:Demo 列表 (demoListTable) 未出现" + ) + + if !app.cells["demoCell.\(row)"].exists { + list.swipeUp() + } + + let cell = app.cells["demoCell.\(row)"] + if cell.waitForExistence(timeout: 5) { + cell.tap() + } else { + let title = app.staticTexts["Demo -- \(row)"] + XCTAssertTrue( + title.waitForExistence(timeout: 3), + "\(context.prefix) 进入失败:找不到 Demo -- \(row) 入口" + ) + title.tap() + } + + XCTAssertTrue( + app.navigationBars[context.navigationTitle].waitForExistence(timeout: 10), + """ + \(context.prefix) 进入失败:10s 内未出现导航栏「\(context.navigationTitle)」。\ + 可能 push 失败或页面标题不一致 + """ + ) + } + + private func navigateBackToDemoList(context: DemoLayoutContext) { + let navBar = app.navigationBars.element(boundBy: 0) + let backButton = navBar.buttons.element(boundBy: 0) + if backButton.waitForExistence(timeout: 3), backButton.isHittable { + backButton.tap() + } else if app.navigationBars.buttons["Demo"].exists { + app.navigationBars.buttons["Demo"].tap() + } else { + XCTFail("\(context.prefix) 返回失败:未找到返回按钮(Back 或「Demo」)") + return + } + + XCTAssertTrue( + app.tables["demoListTable"].waitForExistence(timeout: 8), + "\(context.prefix) 返回失败:8s 内未回到首页 Demo 列表 (demoListTable)" + ) + } +} diff --git a/SDAutoLayoutSwiftDemo/AppDelegate.swift b/SDAutoLayoutSwiftDemo/AppDelegate.swift new file mode 100644 index 0000000..32a8ed6 --- /dev/null +++ b/SDAutoLayoutSwiftDemo/AppDelegate.swift @@ -0,0 +1,46 @@ +// +// AppDelegate.swift +// SDAutoLayoutSwiftDemo +// +// Created by 李响 on 2018/9/29. +// Copyright © 2018 gsd. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..12247f0 --- /dev/null +++ b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "icon-20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "icon-20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "icon-29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "icon-29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "icon-40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "icon-40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "icon-60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "icon-60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "icon-20.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "icon-20@2x-1.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "icon-29.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "icon-29@2x-1.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "icon-40.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "icon-40@2x-1.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "icon-76.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "icon-76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "icon-83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "icon.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-20.png b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-20.png new file mode 100644 index 0000000..1f8fef9 Binary files /dev/null and b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-20.png differ diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-20@2x-1.png b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-20@2x-1.png new file mode 100644 index 0000000..ccbea68 Binary files /dev/null and b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-20@2x-1.png differ diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png new file mode 100644 index 0000000..ccbea68 Binary files /dev/null and b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png differ diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png new file mode 100644 index 0000000..54c8102 Binary files /dev/null and b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png differ diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-29.png b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-29.png new file mode 100644 index 0000000..8359a2d Binary files /dev/null and b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-29.png differ diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-29@2x-1.png b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-29@2x-1.png new file mode 100644 index 0000000..4db14ed Binary files /dev/null and b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-29@2x-1.png differ diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png new file mode 100644 index 0000000..4db14ed Binary files /dev/null and b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png differ diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png new file mode 100644 index 0000000..b87e74a Binary files /dev/null and b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png differ diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-40.png b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-40.png new file mode 100644 index 0000000..ccbea68 Binary files /dev/null and b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-40.png differ diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-40@2x-1.png b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-40@2x-1.png new file mode 100644 index 0000000..acc4732 Binary files /dev/null and b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-40@2x-1.png differ diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png new file mode 100644 index 0000000..acc4732 Binary files /dev/null and b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png differ diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png new file mode 100644 index 0000000..f158d6d Binary files /dev/null and b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png differ diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png new file mode 100644 index 0000000..f158d6d Binary files /dev/null and b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png differ diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png new file mode 100644 index 0000000..d5c8a37 Binary files /dev/null and b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png differ diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-76.png b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-76.png new file mode 100644 index 0000000..39871c5 Binary files /dev/null and b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-76.png differ diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png new file mode 100644 index 0000000..ac87791 Binary files /dev/null and b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png differ diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png new file mode 100644 index 0000000..55de275 Binary files /dev/null and b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png differ diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon.png b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon.png new file mode 100644 index 0000000..393611f Binary files /dev/null and b/SDAutoLayoutSwiftDemo/Assets.xcassets/AppIcon.appiconset/icon.png differ diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/Contents.json b/SDAutoLayoutSwiftDemo/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/SDAutoLayoutSwiftDemo/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/LaunchImage.launchimage/Contents.json b/SDAutoLayoutSwiftDemo/Assets.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000..a96f2a2 --- /dev/null +++ b/SDAutoLayoutSwiftDemo/Assets.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "extent" : "full-screen", + "idiom" : "iphone", + "subtype" : "retina4", + "filename" : "Launch.png", + "minimum-system-version" : "7.0", + "orientation" : "portrait", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SDAutoLayoutSwiftDemo/Assets.xcassets/LaunchImage.launchimage/Launch.png b/SDAutoLayoutSwiftDemo/Assets.xcassets/LaunchImage.launchimage/Launch.png new file mode 100644 index 0000000..c84361d Binary files /dev/null and b/SDAutoLayoutSwiftDemo/Assets.xcassets/LaunchImage.launchimage/Launch.png differ diff --git a/SDAutoLayoutSwiftDemo/Base.lproj/LaunchScreen.storyboard b/SDAutoLayoutSwiftDemo/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..bfa3612 --- /dev/null +++ b/SDAutoLayoutSwiftDemo/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SDAutoLayoutSwiftDemo/Base.lproj/Main.storyboard b/SDAutoLayoutSwiftDemo/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f1bcf38 --- /dev/null +++ b/SDAutoLayoutSwiftDemo/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SDAutoLayoutSwiftDemo/Bridge-Header.h b/SDAutoLayoutSwiftDemo/Bridge-Header.h new file mode 100644 index 0000000..9aaab32 --- /dev/null +++ b/SDAutoLayoutSwiftDemo/Bridge-Header.h @@ -0,0 +1,14 @@ +// +// Bridge.h +// SDAutoLayoutSwiftDemo +// +// Created by 李响 on 2018/9/29. +// Copyright © 2018 gsd. All rights reserved. +// + +#ifndef Bridge_h +#define Bridge_h + +#import "SDAutoLayout.h" + +#endif /* Bridge_h */ diff --git "a/SDAutoLayout \346\265\213\350\257\225 Demo/Info.plist" b/SDAutoLayoutSwiftDemo/Info.plist similarity index 91% rename from "SDAutoLayout \346\265\213\350\257\225 Demo/Info.plist" rename to SDAutoLayoutSwiftDemo/Info.plist index f140639..16be3b6 100644 --- "a/SDAutoLayout \346\265\213\350\257\225 Demo/Info.plist" +++ b/SDAutoLayoutSwiftDemo/Info.plist @@ -3,11 +3,11 @@ CFBundleDevelopmentRegion - en + $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - --.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -16,8 +16,6 @@ APPL CFBundleShortVersionString 1.0 - CFBundleSignature - ???? CFBundleVersion 1 LSRequiresIPhoneOS diff --git a/SDAutoLayoutSwiftDemo/ViewController.swift b/SDAutoLayoutSwiftDemo/ViewController.swift new file mode 100644 index 0000000..36c4668 --- /dev/null +++ b/SDAutoLayoutSwiftDemo/ViewController.swift @@ -0,0 +1,50 @@ +// +// ViewController.swift +// SDAutoLayoutSwiftDemo +// +// Created by 李响 on 2018/9/29. +// Copyright © 2018 gsd. All rights reserved. +// + +import UIKit + +class ViewController: UIViewController { + + private lazy var redView: UIView = { + $0.backgroundColor = .red + return $0 + } ( UIView() ) + + private lazy var blueView: UIView = { + $0.backgroundColor = .blue + return $0 + } ( UIView() ) + + override func viewDidLoad() { + super.viewDidLoad() + + setup() + setupLayout() + } + + private func setup() { + + view.addSubview(redView) + view.addSubview(blueView) + } + + private func setupLayout() { + + _ = redView.sd_layout() + .topSpaceToView(view, 80) + .leftSpaceToView(view, 10) + .rightSpaceToView(view, 10) + .heightIs(100) + + _ = blueView.sd_layout() + .topSpaceToView(redView, 10) + .leftSpaceToView(view, 10) + .rightSpaceToView(view, 10) + .heightIs(200) + } +} diff --git a/icon/iTunesArtwork b/icon/iTunesArtwork new file mode 100644 index 0000000..30c214c Binary files /dev/null and b/icon/iTunesArtwork differ diff --git a/icon/iTunesArtwork@2x b/icon/iTunesArtwork@2x new file mode 100644 index 0000000..a923bfc Binary files /dev/null and b/icon/iTunesArtwork@2x differ diff --git a/icon/icon-20.png b/icon/icon-20.png new file mode 100644 index 0000000..1f8fef9 Binary files /dev/null and b/icon/icon-20.png differ diff --git a/icon/icon-20@2x.png b/icon/icon-20@2x.png new file mode 100644 index 0000000..ccbea68 Binary files /dev/null and b/icon/icon-20@2x.png differ diff --git a/icon/icon-20@3x.png b/icon/icon-20@3x.png new file mode 100644 index 0000000..54c8102 Binary files /dev/null and b/icon/icon-20@3x.png differ diff --git a/icon/icon-29.png b/icon/icon-29.png new file mode 100644 index 0000000..8359a2d Binary files /dev/null and b/icon/icon-29.png differ diff --git a/icon/icon-29@2x.png b/icon/icon-29@2x.png new file mode 100644 index 0000000..4db14ed Binary files /dev/null and b/icon/icon-29@2x.png differ diff --git a/icon/icon-29@3x.png b/icon/icon-29@3x.png new file mode 100644 index 0000000..b87e74a Binary files /dev/null and b/icon/icon-29@3x.png differ diff --git a/icon/icon-40.png b/icon/icon-40.png new file mode 100644 index 0000000..ccbea68 Binary files /dev/null and b/icon/icon-40.png differ diff --git a/icon/icon-40@2x.png b/icon/icon-40@2x.png new file mode 100644 index 0000000..acc4732 Binary files /dev/null and b/icon/icon-40@2x.png differ diff --git a/icon/icon-40@3x.png b/icon/icon-40@3x.png new file mode 100644 index 0000000..f158d6d Binary files /dev/null and b/icon/icon-40@3x.png differ diff --git a/icon/icon-60@2x.png b/icon/icon-60@2x.png new file mode 100644 index 0000000..f158d6d Binary files /dev/null and b/icon/icon-60@2x.png differ diff --git a/icon/icon-60@3x.png b/icon/icon-60@3x.png new file mode 100644 index 0000000..d5c8a37 Binary files /dev/null and b/icon/icon-60@3x.png differ diff --git a/icon/icon-76.png b/icon/icon-76.png new file mode 100644 index 0000000..39871c5 Binary files /dev/null and b/icon/icon-76.png differ diff --git a/icon/icon-76@2x.png b/icon/icon-76@2x.png new file mode 100644 index 0000000..ac87791 Binary files /dev/null and b/icon/icon-76@2x.png differ diff --git a/icon/icon-83.5@2x.png b/icon/icon-83.5@2x.png new file mode 100644 index 0000000..55de275 Binary files /dev/null and b/icon/icon-83.5@2x.png differ diff --git a/icon/icon.png b/icon/icon.png new file mode 100644 index 0000000..393611f Binary files /dev/null and b/icon/icon.png differ diff --git a/scripts/run_all_tests.sh b/scripts/run_all_tests.sh new file mode 100755 index 0000000..a0a9cc2 --- /dev/null +++ b/scripts/run_all_tests.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# 在工程根目录执行:./scripts/run_all_tests.sh +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT" + +if [[ ! -f "SDAutoLayoutDemo.xcworkspace/contents.xcworkspacedata" ]]; then + echo "请先执行: pod install" + exit 1 +fi + +DESTINATION="${DESTINATION:-platform=iOS Simulator,name=iPhone 17,OS=26.5}" + +echo "==> xcodebuild test (unit + UI @ ${DESTINATION})" +xcodebuild test \ + -workspace SDAutoLayoutDemo.xcworkspace \ + -scheme SDAutoLayoutDemo \ + -destination "$DESTINATION" \ + CODE_SIGNING_ALLOWED=NO + +echo "全部测试通过。" diff --git a/scripts/run_ui_tests.sh b/scripts/run_ui_tests.sh new file mode 100755 index 0000000..697c334 --- /dev/null +++ b/scripts/run_ui_tests.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# 在工程根目录执行:./scripts/run_ui_tests.sh +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT" + +if [[ ! -f "SDAutoLayoutDemo.xcworkspace/contents.xcworkspacedata" ]]; then + echo "请先执行: pod install" + exit 1 +fi + +DESTINATION="${DESTINATION:-platform=iOS Simulator,name=iPhone 17,OS=26.5}" + +echo "==> xcodebuild test (SDAutoLayoutDemoUITests @ ${DESTINATION})" +xcodebuild test \ + -workspace SDAutoLayoutDemo.xcworkspace \ + -scheme SDAutoLayoutDemo \ + -destination "$DESTINATION" \ + -only-testing:SDAutoLayoutDemoUITests \ + CODE_SIGNING_ALLOWED=NO + +echo "UI 测试通过。" diff --git a/scripts/run_unit_tests.sh b/scripts/run_unit_tests.sh new file mode 100755 index 0000000..db40ae5 --- /dev/null +++ b/scripts/run_unit_tests.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# 在工程根目录执行:./scripts/run_unit_tests.sh +# 跑全部测试(单元 + UI):./scripts/run_all_tests.sh +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT" + +if [[ ! -f "SDAutoLayoutDemo.xcworkspace/contents.xcworkspacedata" ]]; then + echo "请先执行: pod install" + exit 1 +fi + +if [[ ! -f "Pods/Target Support Files/Pods-SDAutoLayoutDemoTests/Pods-SDAutoLayoutDemoTests.debug.xcconfig" ]]; then + echo "缺少 Pods-SDAutoLayoutDemoTests 配置,请执行: pod install" + exit 1 +fi + +DESTINATION="${DESTINATION:-platform=iOS Simulator,name=iPhone 17,OS=26.5}" + +echo "==> xcodebuild test (SDAutoLayoutDemoTests @ ${DESTINATION})" +xcodebuild test \ + -workspace SDAutoLayoutDemo.xcworkspace \ + -scheme SDAutoLayoutDemo \ + -destination "$DESTINATION" \ + -only-testing:SDAutoLayoutDemoTests \ + CODE_SIGNING_ALLOWED=NO + +echo "单元测试通过。"