首次提交

This commit is contained in:
启星
2025-09-22 18:48:29 +08:00
parent 28ae935e93
commit ae9be0b58e
8941 changed files with 999209 additions and 2 deletions

View File

@@ -0,0 +1,65 @@
//
// OpenUDID.h
// openudid
//
// initiated by Yann Lechelle (cofounder @Appsfire) on 8/28/11.
// Copyright 2011 OpenUDID.org
//
// Main branches
// iOS code: https://github.com/ylechelle/OpenUDID
//
/*
!!! IMPORTANT !!!
IF YOU ARE GOING TO INTEGRATE OpenUDID INSIDE A (STATIC) LIBRARY,
PLEASE MAKE SURE YOU REFACTOR THE OpenUDID CLASS WITH A PREFIX OF YOUR OWN,
E.G. ACME_OpenUDID. THIS WILL AVOID CONFUSION BY DEVELOPERS WHO ARE ALSO
USING OpenUDID IN THEIR OWN CODE.
!!! IMPORTANT !!!
*/
/*
http://en.wikipedia.org/wiki/Zlib_License
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#import <Foundation/Foundation.h>
//
// Usage:
// #include "OpenUDID.h"
// NSString* openUDID = [OpenUDID value];
//
#define kOpenUDIDErrorNone 0
#define kOpenUDIDErrorOptedOut 1
#define kOpenUDIDErrorCompromised 2
@interface OpenUDID : NSObject {
}
+ (NSString*) value;
+ (NSString*) valueWithError:(NSError**)error;
+ (void) setOptOut:(BOOL)optOutValue;
@end

427
QXLive/Third/OpenUDID/OpenUDID.m Executable file
View File

@@ -0,0 +1,427 @@
//
// OpenUDID.m
// openudid
//
// initiated by Yann Lechelle (cofounder @Appsfire) on 8/28/11.
// Copyright 2011, 2012 OpenUDID.org
//
// Initiators/root branches
// iOS code: https://github.com/ylechelle/OpenUDID
// Android code: https://github.com/vieux/OpenUDID
//
// Contributors:
// https://github.com/ylechelle/OpenUDID/contributors
//
/*
http://en.wikipedia.org/wiki/Zlib_License
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#if __has_feature(objc_arc)
//#error This file uses the classic non-ARC retain/release model; hints below...
// to selectively compile this file as non-ARC, do as follows:
// https://img.skitch.com/20120717-g3ag5h9a6ehkgpmpjiuen3qpwp.png
#endif
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#import "OpenUDID.h"
#import <CommonCrypto/CommonDigest.h> // Need to import for CC_MD5 access
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
#import <UIKit/UIPasteboard.h>
#import <UIKit/UIKit.h>
#else
#import <AppKit/NSPasteboard.h>
#endif
#define OpenUDIDLog(fmt, ...)
//#define OpenUDIDLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
//#define OpenUDIDLog(fmt, ...) NSLog((@"[Line %d] " fmt), __LINE__, ##__VA_ARGS__);
static NSString * kOpenUDIDSessionCache = nil;
static NSString * const kOpenUDIDDescription = @"OpenUDID_with_iOS6_Support";
static NSString * const kOpenUDIDKey = @"OpenUDID";
static NSString * const kOpenUDIDSlotKey = @"OpenUDID_slot";
static NSString * const kOpenUDIDAppUIDKey = @"OpenUDID_appUID";
static NSString * const kOpenUDIDTSKey = @"OpenUDID_createdTS";
static NSString * const kOpenUDIDOOTSKey = @"OpenUDID_optOutTS";
static NSString * const kOpenUDIDDomain = @"org.OpenUDID";
static NSString * const kOpenUDIDSlotPBPrefix = @"org.OpenUDID.slot.";
static int const kOpenUDIDRedundancySlots = 100;
@interface OpenUDID (Private)
+ (void) _setDict:(id)dict forPasteboard:(id)pboard;
+ (NSMutableDictionary*) _getDictFromPasteboard:(id)pboard;
+ (NSString*) _generateFreshOpenUDID;
@end
@implementation OpenUDID
// Archive a NSDictionary inside a pasteboard of a given type
// Convenience method to support iOS & Mac OS X
//
+ (void) _setDict:(id)dict forPasteboard:(id)pboard {
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
[pboard setData:[NSKeyedArchiver archivedDataWithRootObject:dict] forPasteboardType:kOpenUDIDDomain];
#else
[pboard setData:[NSKeyedArchiver archivedDataWithRootObject:dict] forType:kOpenUDIDDomain];
#endif
}
// Retrieve an NSDictionary from a pasteboard of a given type
// Convenience method to support iOS & Mac OS X
//
+ (NSMutableDictionary*) _getDictFromPasteboard:(id)pboard {
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
id item = [pboard dataForPasteboardType:kOpenUDIDDomain];
#else
id item = [pboard dataForType:kOpenUDIDDomain];
#endif
if (item) {
@try{
item = [NSKeyedUnarchiver unarchiveObjectWithData:item];
} @catch(NSException* e) {
OpenUDIDLog(@"Unable to unarchive item %@ on pasteboard!", [pboard name]);
item = nil;
}
}
// return an instance of a MutableDictionary
return [NSMutableDictionary dictionaryWithDictionary:(item == nil || [item isKindOfClass:[NSDictionary class]]) ? item : nil];
}
// Private method to create and return a new OpenUDID
// Theoretically, this function is called once ever per application when calling [OpenUDID value] for the first time.
// After that, the caching/pasteboard/redundancy mechanism inside [OpenUDID value] returns a persistent and cross application OpenUDID
//
+ (NSString*) _generateFreshOpenUDID {
NSString* _openUDID = nil;
// August 2011: One day, this may no longer be allowed in iOS. When that is, just comment this line out.
// March 25th 2012: this day has come, let's remove this "outlawed" call...
// August 2012: well, perhaps much ado about nothing; in any case WWDC2012 gave us something to work with; read below
#if TARGET_OS_IPHONE
// if([UIDevice instancesRespondToSelector:@selector(uniqueIdentifier)]){
// _openUDID = [[UIDevice currentDevice] uniqueIdentifier];
// }
#endif
//
// !!!!! IMPORTANT README !!!!!
//
// August 2012: iOS 6 introduces new APIs that help us deal with the now deprecated [UIDevice uniqueIdentifier]
// Since iOS 6 is still pre-release and under NDA, the following piece of code is meant to produce an error at
// compile time. Accredited developers integrating OpenUDID are expected to review the iOS 6 release notes and
// documentation, and replace the underscore ______ in the last part of the selector below with the right
// selector syntax as described here (make sure to use the right one! last word starts with the letter "A"):
// https://developer.apple.com/library/prerelease/ios/#documentation/UIKit/Reference/UIDevice_Class/Reference/UIDevice.html
//
// The semantic compiler warnings are still normal if you are compiling for iOS 5 only since Xcode will not
// know about the two instance methods used on that line; the code running on iOS will work well at run-time.
// Either way, it's time that you junped on the iOS 6 bandwagon and start testing your code on iOS 6 ;-)
//
// WHAT IS THE SIGNIFICANCE OF THIS CHANGE?
//
// Essentially, this means that OpenUDID will keep on behaving the same way as before for existing users or
// new users in iOS 5 and before. For new users on iOS 6 and after, the new official public APIs will take over.
// OpenUDID will therefore be obsoleted when iOS reaches significant adoption, anticipated around mid-2013.
/*
September 14; ok, new development. The alleged API has moved!
This part of the code will therefore be updated when iOS 6 is actually released.
Nevertheless, if you want to go ahead, the code should be pretty easy to
guess... it involves including a .h file from a nine-letter framework that ends
with the word "Support", and then assigning _openUDID with the only identifier method called on the sharedManager of that new class... don't forget to add
the framework to your project!
#if TARGET_OS_IPHONE
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"6.0")) {
_openUDID = [[[UIDevice currentDevice] identifierForA_______] UUIDString];
# error ^ read comments above, fix accordingly, and remove this #error line
}
#endif
*/
// Next we generate a UUID.
// UUIDs (Universally Unique Identifiers), also known as GUIDs (Globally Unique Identifiers) or IIDs
// (Interface Identifiers), are 128-bit values guaranteed to be unique. A UUID is made unique over
// both space and time by combining a value unique to the computer on which it was generatedusually the
// Ethernet hardware addressand a value representing the number of 100-nanosecond intervals since
// October 15, 1582 at 00:00:00.
// We then hash this UUID with md5 to get 32 bytes, and then add 4 extra random bytes
// Collision is possible of course, but unlikely and suitable for most industry needs (e.g. aggregate tracking)
//
if (_openUDID==nil) {
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
CFStringRef cfstring = CFUUIDCreateString(kCFAllocatorDefault, uuid);
const char *cStr = CFStringGetCStringPtr(cfstring,CFStringGetFastestEncoding(cfstring));
unsigned char result[16];
CC_MD5( cStr, strlen(cStr), result );
CFRelease(uuid);
CFRelease(cfstring);
_openUDID = [NSString stringWithFormat:
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%08x",
result[0], result[1], result[2], result[3],
result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11],
result[12], result[13], result[14], result[15],
(NSUInteger)(arc4random() % NSUIntegerMax)];
}
// Call to other developers in the Open Source community:
//
// feel free to suggest better or alternative "UDID" generation code above.
// NOTE that the goal is NOT to find a better hash method, but rather, find a decentralized (i.e. not web-based)
// 160 bits / 20 bytes random string generator with the fewest possible collisions.
//
return _openUDID;
}
// Main public method that returns the OpenUDID
// This method will generate and store the OpenUDID if it doesn't exist, typically the first time it is called
// It will return the null udid (forty zeros) if the user has somehow opted this app out (this is subject to 3rd party implementation)
// Otherwise, it will register the current app and return the OpenUDID
//
+ (NSString*) value {
return [OpenUDID valueWithError:nil];
}
+ (NSString*) valueWithError:(NSError **)error {
if (kOpenUDIDSessionCache!=nil) {
if (error!=nil)
*error = [NSError errorWithDomain:kOpenUDIDDomain
code:kOpenUDIDErrorNone
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"OpenUDID in cache from first call",@"description", nil]];
return kOpenUDIDSessionCache;
}
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// The AppUID will uniquely identify this app within the pastebins
//
NSString * appUID = [defaults objectForKey:kOpenUDIDAppUIDKey];
if(appUID == nil)
{
// generate a new uuid and store it in user defaults
CFUUIDRef uuid = CFUUIDCreate(NULL);
appUID = (NSString *) CFBridgingRelease(CFUUIDCreateString(NULL, uuid));
CFRelease(uuid);
}
NSString* openUDID = nil;
NSString* myRedundancySlotPBid = nil;
NSDate* optedOutDate = nil;
BOOL optedOut = NO;
BOOL saveLocalDictToDefaults = NO;
BOOL isCompromised = NO;
// Do we have a local copy of the OpenUDID dictionary?
// This local copy contains a copy of the openUDID, myRedundancySlotPBid (and unused in this block, the local bundleid, and the timestamp)
//
id localDict = [defaults objectForKey:kOpenUDIDKey];
if ([localDict isKindOfClass:[NSDictionary class]]) {
localDict = [NSMutableDictionary dictionaryWithDictionary:localDict]; // we might need to set/overwrite the redundancy slot
openUDID = [localDict objectForKey:kOpenUDIDKey];
myRedundancySlotPBid = [localDict objectForKey:kOpenUDIDSlotKey];
optedOutDate = [localDict objectForKey:kOpenUDIDOOTSKey];
optedOut = optedOutDate!=nil;
OpenUDIDLog(@"localDict = %@",localDict);
}
// Here we go through a sequence of slots, each of which being a UIPasteboard created by each participating app
// The idea behind this is to both multiple and redundant representations of OpenUDIDs, as well as serve as placeholder for potential opt-out
//
NSString* availableSlotPBid = nil;
NSMutableDictionary* frequencyDict = [NSMutableDictionary dictionaryWithCapacity:kOpenUDIDRedundancySlots];
for (int n=0; n<kOpenUDIDRedundancySlots; n++) {
NSString* slotPBid = [NSString stringWithFormat:@"%@%d",kOpenUDIDSlotPBPrefix,n];
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
UIPasteboard* slotPB = [UIPasteboard pasteboardWithName:slotPBid create:NO];
#else
NSPasteboard* slotPB = [NSPasteboard pasteboardWithName:slotPBid];
#endif
OpenUDIDLog(@"SlotPB name = %@",slotPBid);
if (slotPB==nil) {
// assign availableSlotPBid to be the first one available
if (availableSlotPBid==nil) availableSlotPBid = slotPBid;
} else {
NSDictionary* dict = [OpenUDID _getDictFromPasteboard:slotPB];
NSString* oudid = [dict objectForKey:kOpenUDIDKey];
OpenUDIDLog(@"SlotPB dict = %@",dict);
if (oudid==nil) {
// availableSlotPBid could inside a non null slot where no oudid can be found
if (availableSlotPBid==nil) availableSlotPBid = slotPBid;
} else {
// increment the frequency of this oudid key
int count = [[frequencyDict valueForKey:oudid] intValue];
[frequencyDict setObject:[NSNumber numberWithInt:++count] forKey:oudid];
}
// if we have a match with the app unique id,
// then let's look if the external UIPasteboard representation marks this app as OptedOut
NSString* gid = [dict objectForKey:kOpenUDIDAppUIDKey];
if (gid!=nil && [gid isEqualToString:appUID]) {
myRedundancySlotPBid = slotPBid;
// the local dictionary is prime on the opt-out subject, so ignore if already opted-out locally
if (optedOut) {
optedOutDate = [dict objectForKey:kOpenUDIDOOTSKey];
optedOut = optedOutDate!=nil;
}
}
}
}
// sort the Frequency dict with highest occurence count of the same OpenUDID (redundancy, failsafe)
// highest is last in the list
//
NSArray* arrayOfUDIDs = [frequencyDict keysSortedByValueUsingSelector:@selector(compare:)];
NSString* mostReliableOpenUDID = (arrayOfUDIDs!=nil && [arrayOfUDIDs count]>0)? [arrayOfUDIDs lastObject] : nil;
OpenUDIDLog(@"Freq Dict = %@\nMost reliable %@",frequencyDict,mostReliableOpenUDID);
// if openUDID was not retrieved from the local preferences, then let's try to get it from the frequency dictionary above
//
if (openUDID==nil) {
if (mostReliableOpenUDID==nil) {
// this is the case where this app instance is likely to be the first one to use OpenUDID on this device
// we create the OpenUDID, legacy or semi-random (i.e. most certainly unique)
//
openUDID = [OpenUDID _generateFreshOpenUDID];
} else {
// or we leverage the OpenUDID shared by other apps that have already gone through the process
//
openUDID = mostReliableOpenUDID;
}
// then we create a local representation
//
if (localDict==nil) {
localDict = [NSMutableDictionary dictionaryWithCapacity:4];
[localDict setObject:openUDID forKey:kOpenUDIDKey];
[localDict setObject:appUID forKey:kOpenUDIDAppUIDKey];
[localDict setObject:[NSDate date] forKey:kOpenUDIDTSKey];
if (optedOut) [localDict setObject:optedOutDate forKey:kOpenUDIDTSKey];
saveLocalDictToDefaults = YES;
}
}
else {
// Sanity/tampering check
//
if (mostReliableOpenUDID!=nil && ![mostReliableOpenUDID isEqualToString:openUDID])
isCompromised = YES;
}
// Here we store in the available PB slot, if applicable
//
OpenUDIDLog(@"Available Slot %@ Existing Slot %@",availableSlotPBid,myRedundancySlotPBid);
if (availableSlotPBid!=nil && (myRedundancySlotPBid==nil || [availableSlotPBid isEqualToString:myRedundancySlotPBid])) {
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
UIPasteboard* slotPB = [UIPasteboard pasteboardWithName:availableSlotPBid create:YES];
[slotPB setPersistent:YES];
#else
NSPasteboard* slotPB = [NSPasteboard pasteboardWithName:availableSlotPBid];
#endif
// save slotPBid to the defaults, and remember to save later
//
if (localDict) {
[localDict setObject:availableSlotPBid forKey:kOpenUDIDSlotKey];
saveLocalDictToDefaults = YES;
}
// Save the local dictionary to the corresponding UIPasteboard slot
//
if (openUDID && localDict)
[OpenUDID _setDict:localDict forPasteboard:slotPB];
}
// Save the dictionary locally if applicable
//
if (localDict && saveLocalDictToDefaults)
[defaults setObject:localDict forKey:kOpenUDIDKey];
// If the UIPasteboard external representation marks this app as opted-out, then to respect privacy, we return the ZERO OpenUDID, a sequence of 40 zeros...
// This is a *new* case that developers have to deal with. Unlikely, statistically low, but still.
// To circumvent this and maintain good tracking (conversion ratios, etc.), developers are invited to calculate how many of their users have opted-out from the full set of users.
// This ratio will let them extrapolate convertion ratios more accurately.
//
if (optedOut) {
if (error!=nil) *error = [NSError errorWithDomain:kOpenUDIDDomain
code:kOpenUDIDErrorOptedOut
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Application with unique id %@ is opted-out from OpenUDID as of %@",appUID,optedOutDate],@"description", nil]];
kOpenUDIDSessionCache = [NSString stringWithFormat:@"%040x",0];
return kOpenUDIDSessionCache;
}
// return the well earned openUDID!
//
if (error!=nil) {
if (isCompromised)
*error = [NSError errorWithDomain:kOpenUDIDDomain
code:kOpenUDIDErrorCompromised
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Found a discrepancy between stored OpenUDID (reliable) and redundant copies; one of the apps on the device is most likely corrupting the OpenUDID protocol",@"description", nil]];
else
*error = [NSError errorWithDomain:kOpenUDIDDomain
code:kOpenUDIDErrorNone
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"OpenUDID succesfully retrieved",@"description", nil]];
}
kOpenUDIDSessionCache = openUDID;
return kOpenUDIDSessionCache;
}
+ (void) setOptOut:(BOOL)optOutValue {
// init call
[OpenUDID value];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// load the dictionary from local cache or create one
id dict = [defaults objectForKey:kOpenUDIDKey];
if ([dict isKindOfClass:[NSDictionary class]]) {
dict = [NSMutableDictionary dictionaryWithDictionary:dict];
} else {
dict = [NSMutableDictionary dictionaryWithCapacity:2];
}
// set the opt-out date or remove key, according to parameter
if (optOutValue)
[dict setObject:[NSDate date] forKey:kOpenUDIDOOTSKey];
else
[dict removeObjectForKey:kOpenUDIDOOTSKey];
// store the dictionary locally
[defaults setObject:dict forKey:kOpenUDIDKey];
OpenUDIDLog(@"Local dict after opt-out = %@",dict);
// reset memory cache
kOpenUDIDSessionCache = nil;
}
@end

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 950 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -0,0 +1,24 @@
//
// ATAuthSDK.h
// ATAuthSDK
//
// Created by yangli on 2020/11/11.
// Copyright © 2020. All rights reserved.
//
#import <Foundation/Foundation.h>
//! Project version number for ATAuthSDK.
FOUNDATION_EXPORT double ATAuthSDKVersionNumber;
//! Project version string for ATAuthSDK.
FOUNDATION_EXPORT const unsigned char ATAuthSDKVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <ATAuthSDK/PublicHeader.h>
#import "TXCommonHandler.h"
#import "TXCommonUtils.h"
#import "PNSReturnCode.h"
#import "TXCustomModel.h"
#import "PNSReporter.h"

View File

@@ -0,0 +1,37 @@
//
// PNSReporter.h
// ATAuthSDK
//
// Created by 刘超的MacBook on 2020/5/21.
// Copyright © 2020. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, PNSLoggerLevel) {
PNSLoggerLevelVerbose = 1,
PNSLoggerLevelDebug,
PNSLoggerLevelInfo,
PNSLoggerLevelWarn,
PNSLoggerLevelError
};
@interface PNSReporter : NSObject
/**
* 控制台日志输出开关若开启会以PNS_LOGGER为开始标记对日志进行输出Release模式记得关闭
* @param enable 开关参数默认为NO
*/
- (void)setConsolePrintLoggerEnable:(BOOL)enable;
/**
* 设置埋点上传开关,但不会对通过 setupUploader: 接口实现的自定义上传方法起作用
* @param enable 开关设置BOOL值默认为YES
*/
- (void)setUploadEnable:(BOOL)enable DEPRECATED_MSG_ATTRIBUTE("日志不再上传");;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,88 @@
//
// PNSReturnCode.h
// ATAuthSDK
//
// Created by 刘超的MacBook on 2019/9/4.
// Copyright © 2019. All rights reserved.
//
#ifndef PNSReturnCode_h
#define PNSReturnCode_h
#import <Foundation/Foundation.h>
#pragma mark - 该返回码为阿里云号码认证SDK⾃身的返回码请注意600011及600012错误内均含有运营商返回码具体错误在碰到之后查阅 https://help.aliyun.com/document_detail/85351.html?spm=a2c4g.11186623.6.561.32a7360cxvWk6H
/// 接口成功
static NSString * const PNSCodeSuccess = @"600000";
/// 获取运营商配置信息失败
static NSString * const PNSCodeGetOperatorInfoFailed = @"600004";
/// 未检测到sim卡
static NSString * const PNSCodeNoSIMCard = @"600007";
/// 蜂窝网络未开启或不稳定
static NSString * const PNSCodeNoCellularNetwork = @"600008";
/// 无法判运营商
static NSString * const PNSCodeUnknownOperator = @"600009";
/// 未知异常
static NSString * const PNSCodeUnknownError = @"600010";
/// 获取token失败
static NSString * const PNSCodeGetTokenFailed = @"600011";
/// 预取号失败
static NSString * const PNSCodeGetMaskPhoneFailed = @"600012";
/// 运营商维护升级,该功能不可用
static NSString * const PNSCodeInterfaceDemoted = @"600013";
/// 运营商维护升级,该功能已达最大调用次数
static NSString * const PNSCodeInterfaceLimited = @"600014";
/// 接口超时
static NSString * const PNSCodeInterfaceTimeout = @"600015";
/// AppID、Appkey解析失败
static NSString * const PNSCodeDecodeAppInfoFailed = @"600017";
/// 该号码已被运营商管控,目前只有联通号码有该功能
static NSString * const PNSCodePhoneBlack = @"600018";
/// 运营商已切换
static NSString * const PNSCodeCarrierChanged = @"600021";
/// 终端环境检测失败(终端不支持认证 / 终端检测参数错误)
static NSString * const PNSCodeEnvCheckFail = @"600025";
/*************** 号码认证授权页相关返回码 START ***************/
/// 唤起授权页成功
static NSString * const PNSCodeLoginControllerPresentSuccess = @"600001";
/// 唤起授权页失败
static NSString * const PNSCodeLoginControllerPresentFailed = @"600002";
/// 授权页已加载时不允许调用加速或预取号接口
static NSString * const PNSCodeCallPreLoginInAuthPage = @"600026";
/// 点击返回,⽤户取消一键登录
static NSString * const PNSCodeLoginControllerClickCancel = @"700000";
/// 点击切换按钮,⽤户取消免密登录
static NSString * const PNSCodeLoginControllerClickChangeBtn = @"700001";
/// 点击登录按钮事件
static NSString * const PNSCodeLoginControllerClickLoginBtn = @"700002";
/// 点击CheckBox事件
static NSString * const PNSCodeLoginControllerClickCheckBoxBtn = @"700003";
/// 点击协议富文本文字
static NSString * const PNSCodeLoginControllerClickProtocol = @"700004";
/// 中断页面消失的时候也就是suspendDisMissVC设置为YES的时候点击左上角返回按钮时透出的状态码
static NSString * const PNSCodeLoginControllerSuspendDisMissVC = @"700010";
/// 授权页已销毁
static NSString * const PNSCodeLoginControllerDeallocVC = @"700020";
/*************** 号码认证授权页相关返回码 FINISH ***************/
/*************** 二次授权页返回code码 START ***************/
/// 点击一键登录拉起授权页二次弹窗
static NSString * const PNSCodeLoginClickPrivacyAlertView = @"700006";
/// 隐私协议二次弹窗关闭
static NSString * const PNSCodeLoginPrivacyAlertViewClose = @"700007";
/// 隐私协议二次弹窗点击确认并继续
static NSString * const PNSCodeLoginPrivacyAlertViewClickContinue = @"700008";
/// 点击隐私协议二次弹窗上的协议富文本文字
static NSString * const PNSCodeLoginPrivacyAlertViewPrivacyContentClick = @"700009";
/*************** 二次授权页返回code码 FINISH ***************/
#endif /* PNSReturnCode_h */

View File

@@ -0,0 +1,137 @@
//
// TXCommonHandler.h
// ATAuthSDK
//
// Created by yangli on 15/03/2018.
#import <Foundation/Foundation.h>
#import "TXCustomModel.h"
#import "PNSReporter.h"
typedef NS_ENUM(NSInteger, PNSAuthType) {
PNSAuthTypeVerifyToken = 1, //本机号码校验
PNSAuthTypeLoginToken = 2 //一键登录
};
@interface TXCommonHandler : NSObject
/**
* 获取该类的单例实例对象
* @return 单例实例对象
*/
+ (instancetype _Nonnull )sharedInstance;
/**
* 获取当前SDK版本号
* @return 字符串sdk版本号
*/
- (NSString *_Nonnull)getVersion;
/**
* SDK鉴权app生命周期内调用一次
* @param info app对应的秘钥
* @param complete 结果异步回调到主线程成功时resultDic=@{resultCode:600000, msg:...},其他情况时"resultCode"值请参考PNSReturnCode
* @note 重复调用时以最新info信息为准
*/
- (void)setAuthSDKInfo:(NSString * _Nonnull)info complete:(void(^_Nullable)(NSDictionary * _Nonnull resultDic))complete;
/**
* 检查当前环境是否支持一键登录或号码认证resultDic 返回 PNSCodeSuccess 说明当前环境支持
* @param authType 服务类型 PNSAuthTypeVerifyToken 本机号码校验流程PNSAuthTypeLoginToken 一键登录流程
* @param complete 结果异步回调到主线程成功时resultDic=@{resultCode:600000, msg:...},其他情况时"resultCode"值请参考PNSReturnCode只有成功回调才能保障后续接口调用
*/
- (void)checkEnvAvailableWithAuthType:(PNSAuthType)authType complete:(void (^_Nullable)(NSDictionary * _Nullable resultDic))complete;
/**
* 加速获取本机号码校验token防止调用 getVerifyTokenWithTimeout:complete: 获取token时间过长
* @param timeout 接口超时时间单位s默认为3.0s
* @param complete 结果异步回调到主线程成功时resultDic=@{resultCode:600000, token:..., msg:...},其他情况时"resultCode"值请参考PNSReturnCode
*/
- (void)accelerateVerifyWithTimeout:(NSTimeInterval)timeout complete:(void (^_Nullable)(NSDictionary * _Nonnull resultDic))complete;
/**
* 获取本机号码校验Token
* @param timeout 接口超时时间单位s默认为3.0s
* @param complete 结果异步回调到主线程成功时resultDic=@{resultCode:600000, token:..., msg:...},其他情况时"resultCode"值请参考PNSReturnCode
*/
- (void)getVerifyTokenWithTimeout:(NSTimeInterval)timeout complete:(void (^_Nullable)(NSDictionary * _Nonnull resultDic))complete;
/**
* 加速一键登录授权页弹起,防止调用 getLoginTokenWithTimeout:controller:model:complete: 等待弹起授权页时间过长
* @param timeout 接口超时时间单位s默认为3.0s
* @param complete 结果异步回调到主线程成功时resultDic=@{resultCode:600000, msg:...},其他情况时"resultCode"值请参考PNSReturnCode
*/
- (void)accelerateLoginPageWithTimeout:(NSTimeInterval)timeout complete:(void (^_Nullable)(NSDictionary * _Nonnull resultDic))complete;
/**
* 获取一键登录Token调用该接口首先会弹起授权页点击授权页的登录按钮获取Token
* @warning 注意的是,如果前面没有调用 accelerateLoginPageWithTimeout:complete: 接口,该接口内部会自动先帮我们调用,成功后才会弹起授权页,所以有一个明显的等待过程
* @param timeout 接口超时时间单位s默认为3.0s
* @param controller 唤起自定义授权页的容器,内部会对其进行验证,检查是否符合条件
* @param model 自定义授权页面选项可为nil采用默认的授权页面具体请参考TXCustomModel.h文件
* @param complete 结果异步回调到主线程,"resultDic"里面的"resultCode"值请参考PNSReturnCode如下
*
* 授权页控件点击事件700000点击授权页返回按钮、700001点击切换其他登录方式
* 700002点击登录按钮事件根据返回字典里面的 "isChecked"字段来区分check box是否被选中只有被选中的时候内部才会去获取Token、700003点击check box事件、700004点击协议富文本文字
接口回调其他事件600001授权页唤起成功、600002授权页唤起失败、600000成功获取Token、600011获取Token失败
* 600015获取Token超时、600013运营商维护升级该功能不可用、600014运营商维护升级该功能已达最大调用次数.....
*/
- (void)getLoginTokenWithTimeout:(NSTimeInterval)timeout controller:(UIViewController *_Nonnull)controller model:(TXCustomModel *_Nullable)model complete:(void (^_Nullable)(NSDictionary * _Nonnull resultDic))complete;
/**
* 此接口仅用于开发期间用于一键登录页面不同机型尺寸适配调试可支持模拟器非正式页面手机掩码为0不能正常登录请开发者注意下
* @param controller 唤起自定义授权页的容器,内部会对其进行验证,检查是否符合条件
* @param model 自定义授权页面选项可为nil采用默认的授权页面具体请参考TXCustomModel.h文件
* @param complete 结果异步回调到主线程,"resultDic"里面的"resultCode"值请参考PNSReturnCode
*/
- (void)debugLoginUIWithController:(UIViewController *_Nonnull)controller model:(TXCustomModel *_Nullable)model complete:(void (^_Nullable)(NSDictionary * _Nonnull resultDic))complete;
/**
* 授权页弹起后修改checkbox按钮选中状态当checkout按钮隐藏时设置不生效
*/
- (void)setCheckboxIsChecked:(BOOL)isChecked;
/**
* 查询授权页checkbox是否勾选YES勾选NO未勾选
*/
- (BOOL)queryCheckBoxIsChecked;
/**
* 授权页协议内容动画执行注意必须设置privacyAnimation属性才会执行动画
*/
- (void)privacyAnimationStart;
/**
* 授权页checkbox动画执行注意必须设置checkboxAnimation属性才会执行动画
*/
- (void)checkboxAnimationStart;
/**
* 手动隐藏一键登录获取登录Token之后的等待动画默认为自动隐藏当设置 TXCustomModel 实例 autoHideLoginLoading = NO 时, 可调用该方法手动隐藏
*/
- (void)hideLoginLoading;
/**
* 注销授权页,建议用此方法,对于移动卡授权页的消失会清空一些数据
* @param flag 是否添加动画
* @param complete 成功返回
*/
- (void)cancelLoginVCAnimated:(BOOL)flag complete:(void (^_Nullable)(void))complete;
/**
* 获取日志埋点相关控制对象
*/
- (PNSReporter * _Nonnull)getReporter;
/**
* 关闭二次授权弹窗页
*/
- (void)closePrivactAlertView;
/**
* 检查及准备调用环境resultDic返回PNSCodeSuccess才能调用下面的功能接口
* @param complete 结果异步回调到主线程成功时resultDic=@{resultCode:600000, msg:...},其他情况时"resultCode"值请参考PNSReturnCode只有成功回调才能保障后续接口调用
*/
- (void)checkEnvAvailableWithComplete:(void (^_Nullable)(NSDictionary * _Nullable resultDic))complete DEPRECATED_MSG_ATTRIBUTE("Please use checkEnvAvailableWithAuthType:complete: instead");
@end

View File

@@ -0,0 +1,81 @@
//
// TXCommonUtils.h
// authsdk
//
// Created by yangli on 12/03/2018.
#import <Foundation/Foundation.h>
@interface TXCommonUtils : NSObject
/**
判断当前设备蜂窝数据网络是否开启即3G/4G
@return 结果
*/
+ (BOOL)checkDeviceCellularDataEnable;
/**
判断当前上网卡运营商是否是中国联通
@return 结果
*/
+ (BOOL)isChinaUnicom;
/**
判断当前上网卡运营商是否是中国移动
@return 结果
*/
+ (BOOL)isChinaMobile;
/**
判断当前上网卡运营商是否是中国电信
@return 结果
*/
+ (BOOL)isChinaTelecom;
/**
获取当前上网卡运营商名称,比如中国移动、中国电信、中国联通
@return 结果
*/
+ (NSString *)getCurrentCarrierName;
/**
获取当前上网卡网络类型比如WiFi4G
@return 结果
*/
+ (NSString *)getNetworktype;
/**
判断当前设备是否有SIM卡
@return 结果
*/
+ (BOOL)simSupportedIsOK;
/**
判断wwan是否开着通过p0网卡判断无wifi或有wifi情况下都能检测到
@return 结果
*/
+ (BOOL)isWWANOpen;
/**
判断wwan是否开着仅无wifi情况下
@return 结果
*/
+ (BOOL)reachableViaWWAN;
/**
获取设备当前网络私网IP地址
@return 结果
*/
+ (NSString *)getMobilePrivateIPAddress:(BOOL)preferIPv4;
/**
获取当前设备的唯一标识ID
*/
+ (NSString *)getUniqueID;
/**
通过颜色设置生成图片,支持弧度设置,比如一键登录按钮背景图片
*/
+ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size isRoundedCorner:(BOOL )isRounded radius:(CGFloat)radius;
@end

View File

@@ -0,0 +1,441 @@
//
// TXCustomModel.h
// ATAuthSDK
//
// Created by yangli on 2019/4/4.
// Copyright © 2019. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, PNSPresentationDirection){
PNSPresentationDirectionBottom = 0,
PNSPresentationDirectionRight,
PNSPresentationDirectionTop,
PNSPresentationDirectionLeft,
};
/**
* 构建控件的frameview布局时会调用该block得到控件的frame
* @param screenSize 屏幕的size可以通过该size来判断是横屏还是竖屏
* @param superViewSize 该控件的super view的size可以通过该size辅助该控件重新布局
* @param frame 控件默认的位置
* @return 控件新设置的位置
*/
typedef CGRect(^PNSBuildFrameBlock)(CGSize screenSize, CGSize superViewSize, CGRect frame);
@interface TXCustomModel : NSObject
/**
* 说明可设置的Y轴距离waring: 以下所有关于Y轴的设置<=0都将不生效请注意
* 全屏模式默认是以375x667pt为基准其他屏幕尺寸可以根据(ratio = 屏幕高度/667)比率来适配,比如 Y*ratio
*/
#pragma mark- 全屏、弹窗模式设置
/**
* 授权页面中渲染并显示所有控件的view称content view不实现该block默认为全屏模式
* 实现弹窗的方案 x >= 0 || y >= 0 width <= 屏幕宽度 || height <= 屏幕高度
*/
@property (nonatomic, copy) PNSBuildFrameBlock contentViewFrameBlock;
#pragma mark- 竖屏、横屏模式设置
/** 屏幕是否支持旋转方向默认UIInterfaceOrientationMaskPortrait注意在刘海屏UIInterfaceOrientationMaskPortraitUpsideDown属性慎用 */
@property (nonatomic, assign) UIInterfaceOrientationMask supportedInterfaceOrientations;
#pragma mark- 仅弹窗模式属性
/** 底部蒙层背景颜色,默认黑色 */
@property (nonatomic, strong) UIColor *alertBlurViewColor;
/** 底部蒙层背景透明度默认0.5 */
@property (nonatomic, assign) CGFloat alertBlurViewAlpha;
/** contentView背景颜色默认白色 */
@property (nonatomic, strong) UIColor *alertContentViewColor;
/** contentView背景透明度默认1.0 ,即不透明*/
@property (nonatomic, assign) CGFloat alertContentViewAlpha;
/** contentView的四个圆角值顺序为左上左下右下右上需要填充4个值不足4个值则无效如果值<=0则为直角 */
@property (nonatomic, copy) NSArray<NSNumber *> *alertCornerRadiusArray;
/** 标题栏背景颜色 */
@property (nonatomic, strong) UIColor *alertTitleBarColor;
/** 标题栏是否隐藏默认NO */
@property (nonatomic, assign) BOOL alertBarIsHidden;
/** 标题栏标题,内容、字体、大小、颜色 */
@property (nonatomic, copy) NSAttributedString *alertTitle;
/** 标题栏右侧关闭按钮图片设置*/
@property (nonatomic, strong) UIImage *alertCloseImage;
/** 标题栏右侧关闭按钮是否显示默认NO*/
@property (nonatomic, assign) BOOL alertCloseItemIsHidden;
/** 构建标题栏的frameview布局或布局发生变化时调用不实现则按默认处理实现时仅有height生效 */
@property (nonatomic, copy) PNSBuildFrameBlock alertTitleBarFrameBlock;
/** 构建标题栏标题的frameview布局或布局发生变化时调用不实现则按默认处理 */
@property (nonatomic, copy) PNSBuildFrameBlock alertTitleFrameBlock;
/** 构建标题栏右侧关闭按钮的frameview布局或布局发生变化时调用不实现则按默认处理 */
@property (nonatomic, copy) PNSBuildFrameBlock alertCloseItemFrameBlock;
/** 弹窗位置是否根据键盘弹起关闭动态调整仅在键盘弹起后遮挡弹窗的情况生效调整后弹窗将居于键盘上方默认NO*/
@property (nonatomic, assign) BOOL alertFrameChangeWithKeyboard;
#pragma mark- 导航栏(只对全屏模式有效)
/**授权页显示中导航栏是否隐藏默认NO*/
@property (nonatomic, assign) BOOL navIsHidden;
/**授权页push到其他页面后导航栏是否隐藏默认NO*/
@property (nonatomic, assign) BOOL navIsHiddenAfterLoginVCDisappear;
/**是否需要中断返回,如果设置为YES则点击左上角返回按钮的时候默认页面不消失同时透出状态码700010需要自己调用TXCommonHandler cancelLoginVCAnimated方法隐藏页面默认为NO*/
@property (nonatomic, assign) BOOL suspendDisMissVC;
/** 导航栏主题色 */
@property (nonatomic, strong) UIColor *navColor;
/** 导航栏标题,内容、字体、大小、颜色 */
@property (nonatomic, copy) NSAttributedString *navTitle;
/** 导航栏返回图片 */
@property (nonatomic, strong) UIImage *navBackImage;
/** 是否隐藏授权页导航栏返回按钮,默认不隐藏 */
@property (nonatomic, assign) BOOL hideNavBackItem;
/** 导航栏右侧自定义控件可以在创建该VIEW的时候添加手势操作或者创建按钮或其他赋值给VIEW */
@property (nonatomic, strong) UIView *navMoreView;
/** 构建导航栏返回按钮的frameview布局或布局发生变化时调用不实现则按默认处理 */
@property (nonatomic, copy) PNSBuildFrameBlock navBackButtonFrameBlock;
/** 构建导航栏标题的frameview布局或布局发生变化时调用不实现则按默认处理 */
@property (nonatomic, copy) PNSBuildFrameBlock navTitleFrameBlock;
/** 构建导航栏右侧more view的frameview布局或布局发生变化时调用不实现则按默认处理边界 CGRectGetMinX(frame) >= (superViewSizeViewSize / 0.3) && CGRectGetWidth(frame) <= (superViewSize.width / 3.0) */
@property (nonatomic, copy) PNSBuildFrameBlock navMoreViewFrameBlock;
#pragma mark- 全屏、弹窗模式共同属性
#pragma mark- 授权页动画相关
/** 授权页弹出方向默认PNSPresentationDirectionBottom该属性只对自带动画起效不影响自定义动画 */
@property (nonatomic, assign) PNSPresentationDirection presentDirection;
/** 授权页显示和消失动画时间默认为0.25s<= 0 时关闭动画,该属性只对自带动画起效,不影响自定义动画 **/
@property (nonatomic, assign) CGFloat animationDuration;
/** 授权页显示动画(弹窗 & 全屏不设置或设置为nil默认使用自带动画SDK内部会主动更改动画的一些属性包括removedOnCompletion = NO、fillMode = kCAFillModeForwards 及 delegate **/
@property (nonatomic, strong, nullable) CAAnimation *entryAnimation;
/** 授权页消失动画(弹窗 & 全屏不设置或设置为nil默认使用自带动画SDK内部会主动更改动画的一些属性包括removedOnCompletion = NO、fillMode = kCAFillModeForwards 及 delegate **/
@property (nonatomic, strong, nullable) CAAnimation *exitAnimation;
/** 授权页显示时的背景动画仅弹窗不设置或设置为nil默认使用自带动画SDK内部会主动更改动画的一些属性包括removedOnCompletion = NO、fillMode = kCAFillModeForwards 及 delegate **/
@property (nonatomic, strong, nullable) CAAnimation *bgEntryAnimation;
/** 授权页消失时的背景动画仅弹窗不设置或设置为nil默认使用自带动画SDK内部会主动更改动画的一些属性包括removedOnCompletion = NO、fillMode = kCAFillModeForwards 及 delegate **/
@property (nonatomic, strong, nullable) CAAnimation *bgExitAnimation;
#pragma mark- 状态栏
/** 状态栏是否隐藏默认NO */
@property (nonatomic, assign) BOOL prefersStatusBarHidden;
/** 状态栏主题风格默认UIStatusBarStyleDefault */
@property (nonatomic, assign) UIStatusBarStyle preferredStatusBarStyle;
#pragma mark- 背景
/** 授权页背景色 */
@property (nonatomic, strong) UIColor *backgroundColor;
/** 授权页背景图片 */
@property (nonatomic, strong) UIImage *backgroundImage;
/** 授权页背景图片view的 content mode默认为 UIViewContentModeScaleAspectFill */
@property (nonatomic, assign) UIViewContentMode backgroundImageContentMode;
/** 点击授权页背景是否关闭授权页只有在弹窗模式下生效默认NO*/
@property (nonatomic, assign) BOOL tapAuthPageMaskClosePage;
#pragma mark- logo图片
/** logo图片设置 */
@property (nonatomic, strong) UIImage *logoImage;
/** logo是否隐藏默认NO */
@property (nonatomic, assign) BOOL logoIsHidden;
/** 构建logo的frameview布局或布局发生变化时调用不实现则按默认处理 */
@property (nonatomic, copy) PNSBuildFrameBlock logoFrameBlock;
/** logo的宽设置 */
@property (nonatomic, assign) CGFloat logoWidth DEPRECATED_MSG_ATTRIBUTE("Please use logoFrameBlock instead");
/** logo的高设置 */
@property (nonatomic, assign) CGFloat logoHeight DEPRECATED_MSG_ATTRIBUTE("Please use logoFrameBlock instead");
/** logo相对导航栏底部或标题栏底部的Y轴距离 */
@property (nonatomic, assign) CGFloat logoTopOffetY DEPRECATED_MSG_ATTRIBUTE("Please use logoFrameBlock instead");
#pragma mark- slogan
/** slogan文案内容、字体、大小、颜色 */
@property (nonatomic, copy) NSAttributedString *sloganText;
/** slogan是否隐藏默认NO */
@property (nonatomic, assign) BOOL sloganIsHidden;
/** 构建slogan的frameview布局或布局发生变化时调用不实现则按默认处理 */
@property (nonatomic, copy) PNSBuildFrameBlock sloganFrameBlock;
/** slogan相对导航栏底部或标题栏底部的Y轴距离 */
@property (nonatomic, assign) CGFloat sloganTopOffetY DEPRECATED_MSG_ATTRIBUTE("Please use sloganFrameBlock instead");
#pragma mark- 号码
/** 号码颜色设置 */
@property (nonatomic, strong) UIColor *numberColor;
/** 号码字体设置大小小于16则不生效 */
@property (nonatomic, strong) UIFont *numberFont;
/**
* 构建号码的frameview布局或布局发生变化时调用只有x、y生效不实现则按默认处理
* 注:设置不能超出父视图 content view
*/
@property (nonatomic, copy) PNSBuildFrameBlock numberFrameBlock;
/**
* 号码相对导航栏底部或标题栏底部的Y轴距离不设置则按默认处理
* 注:设置超出父视图 content view 时不生效
*/
@property (nonatomic, assign) CGFloat numberTopOffetY DEPRECATED_MSG_ATTRIBUTE("Please use numberFrameBlock instead");
/**
* 号码相对屏幕中线的X轴偏移距离不设置则按默认处理默认为0水平居中
* 注:设置不能超出父视图 content view
*/
@property (nonatomic, assign) CGFloat numberOffetX DEPRECATED_MSG_ATTRIBUTE("Please use numberFrameBlock instead");
#pragma mark- 登录
/** 登陆按钮文案,内容、字体、大小、颜色*/
@property (nonatomic, strong) NSAttributedString *loginBtnText;
/** 登录按钮背景图片组默认高度50.0pt@[激活状态的图片,失效状态的图片,高亮状态的图片] */
@property (nonatomic, strong) NSArray<UIImage *> *loginBtnBgImgs;
/**
* 是否自动隐藏点击登录按钮之后授权页上转圈的 loading, 默认为YES在获取登录Token成功后自动隐藏
* 如果设置为 NO需要自己手动调用 [[TXCommonHandler sharedInstance] hideLoginLoading] 隐藏
*/
@property (nonatomic, assign) BOOL autoHideLoginLoading;
/**
* 构建登录按钮的frameview布局或布局发生变化时调用不实现则按默认处理
* 注:不能超出父视图 content viewheight不能小于20width不能小于父视图宽度的一半
*/
@property (nonatomic, copy) PNSBuildFrameBlock loginBtnFrameBlock;
/**
* 登录按钮相对导航栏底部或标题栏底部的Y轴距离不设置则按默认处理
* 注:设置超出父视图 content view 时不生效
*/
@property (nonatomic, assign) CGFloat loginBtnTopOffetY DEPRECATED_MSG_ATTRIBUTE("Please use loginBtnFrameBlock instead");
/** 登录按钮高度小于20.0pt不生效,不设置则按默认处理 */
@property (nonatomic, assign) CGFloat loginBtnHeight DEPRECATED_MSG_ATTRIBUTE("Please use loginBtnFrameBlock instead");
/** 登录按钮相对content view的左右边距按钮宽度必须大于等于屏幕的一半不设置则按默认处理 */
@property (nonatomic, assign) CGFloat loginBtnLRPadding DEPRECATED_MSG_ATTRIBUTE("Please use loginBtnFrameBlock instead");
#pragma mark- 协议
/** checkBox图片组[uncheckedImg,checkedImg]*/
@property (nonatomic, copy) NSArray<UIImage *> *checkBoxImages;
/** checkBox图片距离控件边框的填充默认为 UIEdgeInsetsZero确保控件大小减去内填充大小为资源图片大小情况下图片才不会变形 **/
@property (nonatomic, assign) UIEdgeInsets checkBoxImageEdgeInsets;
/** checkBox是否勾选默认NO */
@property (nonatomic, assign) BOOL checkBoxIsChecked;
/** checkBox是否隐藏默认NO */
@property (nonatomic, assign) BOOL checkBoxIsHidden;
/** checkBox大小高宽一样必须大于0 */
@property (nonatomic, assign) CGFloat checkBoxWH;
/** checkBox是否和协议内容垂直居中默认NO即顶部对齐 */
@property (nonatomic, assign) BOOL checkBoxVerticalCenter;
/** 协议1[协议名称,协议Url],注:三个协议名称不能相同 */
@property (nonatomic, copy) NSArray<NSString *> *privacyOne;
/** 协议2[协议名称,协议Url],注:三个协议名称不能相同 */
@property (nonatomic, copy) NSArray<NSString *> *privacyTwo;
/** 协议3[协议名称,协议Url],注:三个协议名称不能相同 */
@property (nonatomic, copy) NSArray<NSString *> *privacyThree;
/** 协议名称之间连接字符串数组,默认 ["和","、","、"] ,即第一个为"和",其他为"、",按顺序读取,为空则取默认 */
@property (nonatomic, copy) NSArray<NSString *> *privacyConectTexts;
/** 协议内容颜色数组,[非点击文案颜色,点击文案颜色] */
@property (nonatomic, copy) NSArray<UIColor *> *privacyColors;
/** 运营商协议内容颜色 优先级最高如果privacyOperatorColors不设置则取privacyColors中的点击文案颜色privacyColors不设置则是默认色*/
@property (nonatomic, strong) UIColor *privacyOperatorColor;
/** 协议1内容颜色优先级最高如果privacyOneColors不设置则取privacyColors中的点击文案颜色privacyColors不设置则是默认色*/
@property (nonatomic, strong) UIColor *privacyOneColor;
/** 协议2内容颜色优先级最高如果privacyTwoColors不设置则取privacyColors中的点击文案颜色privacyColors不设置则是默认色*/
@property (nonatomic, strong) UIColor *privacyTwoColor;
/** 协议3内容颜色优先级最高如果privacyThreeColors不设置则取privacyColors中的点击文案颜色privacyColors不设置则是默认色*/
@property (nonatomic, strong) UIColor *privacyThreeColor;
/** 协议文案支持居中、居左、居右设置,默认居左 */
@property (nonatomic, assign) NSTextAlignment privacyAlignment;
/** 协议整体文案,前缀部分文案 */
@property (nonatomic, copy) NSString *privacyPreText;
/** 协议整体文案,后缀部分文案 */
@property (nonatomic, copy) NSString *privacySufText;
/** 运营商协议名称前缀文案,仅支持 <([《(【『 */
@property (nonatomic, copy) NSString *privacyOperatorPreText;
/** 运营商协议名称后缀文案,仅支持 >)]》)】』*/
@property (nonatomic, copy) NSString *privacyOperatorSufText;
/** 运营商协议指定显示顺序默认0即第1个协议显示最大值可为3即第4个协议显示*/
@property (nonatomic, assign) NSInteger privacyOperatorIndex;
/** 协议整体文案字体小于12.0不生效 */
@property (nonatomic, strong) UIFont *privacyFont;
/** 协议整体文案行间距默认0 */
@property (nonatomic, assign) CGFloat privacyLineSpaceDp;
/** 运营商协议文案字体仅对运营商协议本体文案和前后缀生效小于12.0不生效 */
@property (nonatomic, strong) UIFont *privacyOperatorFont;
/** 运营商协议文案下划线仅对运营商协议本体文案和前后缀生效YES展示下划线NO不展示下划线默认不展示 */
@property (nonatomic, assign) BOOL privacyOperatorUnderline;
/** checkBox是否扩大按钮可交互范围至"协议前缀部分文案(默认:我已阅读并同意)"区域默认NO */
@property (nonatomic, assign) BOOL expandAuthPageCheckedScope;
/**
* 构建协议整体包括checkBox的frameview布局或布局发生变化时调用不实现则按默认处理
* 如果设置的width小于checkBox的宽则不生效最小x、y为0最大width、height为父试图宽高
* 最终会根据设置进来的width对协议文本进行自适应得到的size是协议控件的最终大小
*/
@property (nonatomic, copy) PNSBuildFrameBlock privacyFrameBlock;
/**
* 未同意协议时点击登录按钮协议整体文案的动画效果不设置或设置为nil默认没有动画SDK内部会主动更改动画的一些属性包括removedOnCompletion = NO、fillMode = kCAFillModeRemoved 及 delegate
*/
@property (nonatomic, strong, nullable) CAAnimation *privacyAnimation;
/**
* 未同意协议时点击登录按钮checkbox的动画效果不设置或设置为nil默认没有动画SDK内部会主动更改动画的一些属性包括removedOnCompletion = NO、fillMode = kCAFillModeRemoved 及 delegate
*/
@property (nonatomic, strong, nullable) CAAnimation *checkboxAnimation;
/** 协议整体相对屏幕底部的Y轴距离与其他有区别不能小于0 */
@property (nonatomic, assign) CGFloat privacyBottomOffetY DEPRECATED_MSG_ATTRIBUTE("Please use privacyFrameBlock instead");
/** 协议整体包括checkBox相对content view的左右边距当协议整体宽度小于content view宽度-2*左右边距且居中模式则左右边距设置无效不能小于0 */
@property (nonatomic, assign) CGFloat privacyLRPadding DEPRECATED_MSG_ATTRIBUTE("Please use privacyFrameBlock instead");
#pragma mark- 切换到其他方式
/** changeBtn标题内容、字体、大小、颜色 */
@property (nonatomic, copy) NSAttributedString *changeBtnTitle;
/** changeBtn是否隐藏默认NO*/
@property (nonatomic, assign) BOOL changeBtnIsHidden;
/** 构建changeBtn的frameview布局或布局发生变化时调用不实现则按默认处理 */
@property (nonatomic, copy) PNSBuildFrameBlock changeBtnFrameBlock;
/** changeBtn相对导航栏底部或标题栏底部的Y轴距离 */
@property (nonatomic, assign) CGFloat changeBtnTopOffetY DEPRECATED_MSG_ATTRIBUTE("Please use changeBtnFrameBlock instead");
#pragma mark- 协议详情页
/** 协议详情页容器是否自定义默认NO若为YES则根据 PNSCodeLoginControllerClickProtocol 返回码获取协议点击详情信息 */
@property (nonatomic, assign) BOOL privacyVCIsCustomized;
/** 导航栏背景颜色设置 */
@property (nonatomic, strong) UIColor *privacyNavColor;
/** 导航栏标题字体、大小 */
@property (nonatomic, strong) UIFont *privacyNavTitleFont;
/** 导航栏标题颜色 */
@property (nonatomic, strong) UIColor *privacyNavTitleColor;
/** 导航栏返回图片 */
@property (nonatomic, strong) UIImage *privacyNavBackImage;
#pragma mark- 其他自定义控件添加及布局
/**
* 自定义控件添加,注意:自定义视图的创建初始化和添加到父视图,都需要在主线程!!
* @param superCustomView 父视图
*/
@property (nonatomic, copy) void(^customViewBlock)(UIView *superCustomView);
/**
* 每次授权页布局完成时会调用该block可以在该block实现里面可设置自定义添加控件的frame
* @param screenSize 屏幕的size
* @param contentViewFrame content view的frame
* @param navFrame 导航栏的frame仅全屏时有效
* @param titleBarFrame 标题栏的frame仅弹窗时有效
* @param logoFrame logo图片的frame
* @param sloganFrame slogan的frame
* @param numberFrame 号码栏的frame
* @param loginFrame 登录按钮的frame
* @param changeBtnFrame 切换到其他方式按钮的frame
* @param privacyFrame 协议整体包括checkBox的frame
*/
@property (nonatomic, copy) void(^customViewLayoutBlock)(CGSize screenSize, CGRect contentViewFrame, CGRect navFrame, CGRect titleBarFrame, CGRect logoFrame, CGRect sloganFrame, CGRect numberFrame, CGRect loginFrame, CGRect changeBtnFrame, CGRect privacyFrame);
#pragma mark - 二次隐私协议弹窗设置
/** 二次隐私协议弹窗是否需要显示, 默认NO */
@property (nonatomic, assign) BOOL privacyAlertIsNeedShow;
/** 二次隐私协议弹窗点击按钮是否需要执行登陆默认YES */
@property (nonatomic, assign) BOOL privacyAlertIsNeedAutoLogin;
/** 二次隐私协议弹窗显示自定义动画,默认从下往上位移动画 */
@property (nonatomic, strong, nullable) CAAnimation *privacyAlertEntryAnimation;
/** 二次隐私协议弹窗隐藏自定义动画,默认从上往下位移动画 */
@property (nonatomic, strong, nullable) CAAnimation *privacyAlertExitAnimation;
/** 二次隐私协议弹窗的四个圆角值顺序为左上左下右下右上需要填充4个值不足4个值则无效如果值<=0则为直角 默认0*/
@property (nonatomic, copy) NSArray<NSNumber *> *privacyAlertCornerRadiusArray;
/** 二次隐私协议弹窗背景颜色,默认为白色 */
@property (nonatomic, strong) UIColor *privacyAlertBackgroundColor;
/** 二次隐私协议弹窗透明度默认不透明1.0 设置范围0.3~1.0之间 */
@property (nonatomic, assign) CGFloat privacyAlertAlpha;
/** 二次隐私协议弹窗标题文字内容,默认"请阅读并同意以下条款" */
@property (nonatomic, copy) NSString *privacyAlertTitleContent;
/** 二次隐私协议弹窗标题文字字体最小12默认12 */
@property (nonatomic, strong) UIFont *privacyAlertTitleFont;
/** 二次隐私协议弹窗标题文字颜色,默认黑色 */
@property (nonatomic, strong) UIColor *privacyAlertTitleColor;
/** 二次隐私协议弹窗标题背景颜色,默认白色*/
@property (nonatomic, strong) UIColor *privacyAlertTitleBackgroundColor;
/** 二次隐私协议弹窗标题位置,默认居中*/
@property (nonatomic, assign) NSTextAlignment privacyAlertTitleAlignment;
/** 二次隐私协议弹窗协议内容文字字体最小12默认12 */
@property (nonatomic, strong) UIFont *privacyAlertContentFont;
/** 二次隐私协议弹窗协议内容行间距默认0 */
@property (nonatomic, assign) CGFloat privacyAlertLineSpaceDp;
/** 二次隐私协议弹窗协议内容背景颜色,默认白色 */
@property (nonatomic, strong) UIColor *privacyAlertContentBackgroundColor;
/** 二次隐私协议弹窗协议内容颜色数组,[非点击文案颜色,点击文案颜色],默认[0x999999,0x1890FF] */
@property (nonatomic, copy) NSArray<UIColor *> *privacyAlertContentColors;
/** 二次隐私协议弹窗运营商协议内容文字字体仅对运营商协议部分的文本生效最小12默认12 */
@property (nonatomic, strong) UIFont *privacyAlertContentOperatorFont;
/** 二次隐私协议弹窗运营商协议内容文字下划线仅对运营商协议部分的文本生效YES展示下划线NO不展示下划线默认不展示 */
@property (nonatomic, assign) BOOL privacyAlertContentUnderline;
/** 二次隐私协议弹窗协议运营商协议内容颜色优先级最高如果privacyAlertOperatorColors不设置则取privacyAlertContentColors中的点击文案颜色privacyAlertContentColors不设置则是默认色*/
@property (nonatomic, strong) UIColor *privacyAlertOperatorColor;
/** 二次隐私协议弹窗协议协议1内容颜色 优先级最高如果privacyAlertOneColors不设置则取privacyAlertContentColors中的点击文案颜色privacyAlertContentColors不设置则是默认色*/
@property (nonatomic, strong) UIColor *privacyAlertOneColor;
/** 二次隐私协议弹窗协议协议2内容颜色 优先级最高如果privacyAlertTwoColors不设置则取privacyAlertContentColors中的点击文案颜色privacyAlertContentColors不设置则是默认色*/
@property (nonatomic, strong) UIColor *privacyAlertTwoColor;
/** 二次隐私协议弹窗协议协议3内容颜色 优先级最高如果privacyAlertThreeColors不设置则取privacyAlertContentColors中的点击文案颜色privacyAlertContentColors不设置则是默认色*/
@property (nonatomic, strong) UIColor *privacyAlertThreeColor;
/** 二次隐私协议弹窗协议文案支持居中、居左、居右设置,默认居左 */
@property (nonatomic, assign) NSTextAlignment privacyAlertContentAlignment;
/** 二次隐私协议弹窗协议整体文案,前缀部分文案 ,如果不赋值默认使用privacyPreText*/
@property (nonatomic, copy) NSString *privacyAlertPreText;
/** 二次隐私协议弹窗协议整体文案,后缀部分文案 如果不赋值默认使用privacySufText*/
@property (nonatomic, copy) NSString *privacyAlertSufText;
/** 二次隐私协议弹窗按钮文字内容 默认“同意”*/
@property (nonatomic, copy) NSString *privacyAlertBtnContent;
/** 二次隐私协议弹窗登录按钮的圆角值,如果值<=0则为直角 默认0*/
@property (nonatomic, assign) CGFloat privacyAlertBtnCornerRadius;
/** 二次隐私协议弹窗按钮按钮背景图片 ,默认高度50.0pt@[激活状态的图片,高亮状态的图片] */
@property (nonatomic, copy) NSArray<UIImage *> *privacyAlertBtnBackgroundImages;
/** 二次隐私协议弹窗按钮文字颜色,默认黑色, @[激活状态的颜色,高亮状态的颜色] */
@property (nonatomic, copy) NSArray<UIColor *> *privacyAlertButtonTextColors;
/** 二次隐私协议弹窗按钮文字字体最小10默认18*/
@property (nonatomic, strong) UIFont *privacyAlertButtonFont;
/** 二次隐私协议弹窗关闭按钮是否显示,默认显示 */
@property (nonatomic, assign) BOOL privacyAlertCloseButtonIsNeedShow;
/** 二次隐私协议弹窗右侧关闭按钮图片设置默认内置的X图片*/
@property (nonatomic, strong) UIImage *privacyAlertCloseButtonImage;
/** 二次隐私协议弹窗背景蒙层是否显示 ,默认YES*/
@property (nonatomic, assign) BOOL privacyAlertMaskIsNeedShow;
/** 二次隐私协议弹窗点击背景蒙层是否关闭弹窗 ,默认YES*/
@property (nonatomic, assign) BOOL tapPrivacyAlertMaskCloseAlert;
/** 二次隐私协议弹窗蒙版背景颜色,默认黑色 */
@property (nonatomic, strong) UIColor *privacyAlertMaskColor;
/** 二次隐私协议弹窗蒙版透明度 设置范围0.3~1.0之间 默认0.5*/
@property (nonatomic, assign) CGFloat privacyAlertMaskAlpha;
/** 二次隐私协议弹窗蒙版显示动画,默认渐显动画 */
@property (nonatomic, strong) CAAnimation *privacyAlertMaskEntryAnimation;
/** 二次隐私协议弹窗蒙版消失动画,默认渐隐动画 */
@property (nonatomic, strong) CAAnimation *privacyAlertMaskExitAnimation;
/** 二次隐私协议弹窗尺寸设置,不能超出父视图 content viewheight不能小于50width不能小于0默认屏幕居中宽为屏幕的宽度减掉80高为200 */
@property (nonatomic, copy) PNSBuildFrameBlock privacyAlertFrameBlock;
/** 二次隐私协议弹窗标题尺寸默认x=0y=0width=弹窗宽度最小宽度为100height=根据文本计算的高度最小高度为15不能超出父视图 */
@property (nonatomic, copy) PNSBuildFrameBlock privacyAlertTitleFrameBlock;
/** 二次隐私协议弹窗内容尺寸默认为从标题顶部位置开始最终会根据设置进来的width对协议文本进行自适应得到的size是协议控件的最终大小。不能超出父视图 */
@property (nonatomic, copy) PNSBuildFrameBlock privacyAlertPrivacyContentFrameBlock;
/** 二次隐私协议弹窗确认按钮尺寸,默认为父视图的宽度一半,居中显示。高度默认50, */
@property (nonatomic, copy) PNSBuildFrameBlock privacyAlertButtonFrameBlock;
/** 二次隐私协议弹窗右侧关闭按钮尺寸默认宽高44居弹窗右侧15居弹窗顶部0*/
@property (nonatomic, copy) PNSBuildFrameBlock privacyAlertCloseFrameBlock;
/**
* 二次授权页弹窗自定义控件添加,注意:自定义视图的创建初始化和添加到父视图,都需要在主线程!!
* @param superPrivacyAlertCustomView 父视图
*/
@property (nonatomic, copy) void(^privacyAlertCustomViewBlock)(UIView *superPrivacyAlertCustomView);
/**
* 二次授权页弹窗布局完成时会调用该block可以在该block实现里面可设置自定义添加控件的frame
* @param privacyAlertFrame 二次授权页弹窗frame
* @param privacyAlertTitleFrame 二次授权页弹窗标题frame
* @param privacyAlertPrivacyContentFrame 二次授权页弹窗协议内容frame
* @param privacyAlertButtonFrame 二次授权页弹窗确认按钮frame
* @param privacyAlertCloseFrame 二次授权页弹窗右上角关闭按钮frame
*/
@property (nonatomic, copy) void(^privacyAlertCustomViewLayoutBlock)(CGRect privacyAlertFrame, CGRect privacyAlertTitleFrame, CGRect privacyAlertPrivacyContentFrame, CGRect privacyAlertButtonFrame, CGRect privacyAlertCloseFrame);
@end
NS_ASSUME_NONNULL_END

Binary file not shown.

View File

@@ -0,0 +1,6 @@
framework module ATAuthSDK {
umbrella header "ATAuthSDK.h"
export *
module * { export * }
}

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyCollectedDataTypes</key>
<array>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeOtherDataTypes</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<false/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
</array>
</dict>
</array>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>LibraryIdentifier</key>
<string>ios-arm64_i386_x86_64-simulator</string>
<key>LibraryPath</key>
<string>TencentOpenAPI.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
<string>i386</string>
<string>x86_64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict>
<dict>
<key>LibraryIdentifier</key>
<string>ios-arm64_armv7</string>
<key>LibraryPath</key>
<string>TencentOpenAPI.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
<string>armv7</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>

View File

@@ -0,0 +1,338 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>ios-arm64_armv7/TencentOpenAPI.framework/Headers/Modules/module.modulemap</key>
<data>
NyClSJF5AkbzsaKwZiYthWwFtEQ=
</data>
<key>ios-arm64_armv7/TencentOpenAPI.framework/Headers/QQApiInterface.h</key>
<data>
gGvGGJpjkKChFdTHG4JCYcZFU4Q=
</data>
<key>ios-arm64_armv7/TencentOpenAPI.framework/Headers/QQApiInterfaceObject.h</key>
<data>
bR/sIFOcBRXlm/PPqkV9E5cBGW0=
</data>
<key>ios-arm64_armv7/TencentOpenAPI.framework/Headers/SDKDef.h</key>
<data>
pNuALDKV3dBaHRTHj87Ha0FRn2M=
</data>
<key>ios-arm64_armv7/TencentOpenAPI.framework/Headers/TencentOAuth.h</key>
<data>
qY4CtCUAIMH/VEBpkqo8h+uRhy8=
</data>
<key>ios-arm64_armv7/TencentOpenAPI.framework/Headers/TencentOpenApiUmbrellaHeader.h</key>
<data>
qGEWrIy0/CmpJkMMCxSd9xQoDh0=
</data>
<key>ios-arm64_armv7/TencentOpenAPI.framework/PrivacyInfo.xcprivacy</key>
<data>
NQnJAwD5Cl2cdaDNq498CVy7QsU=
</data>
<key>ios-arm64_armv7/TencentOpenAPI.framework/TencentOpenAPI</key>
<data>
NN04LW43ikzIT3W64odX5+4dKfQ=
</data>
<key>ios-arm64_i386_x86_64-simulator/TencentOpenAPI.framework/Headers/Modules/module.modulemap</key>
<data>
NyClSJF5AkbzsaKwZiYthWwFtEQ=
</data>
<key>ios-arm64_i386_x86_64-simulator/TencentOpenAPI.framework/Headers/QQApiInterface.h</key>
<data>
gGvGGJpjkKChFdTHG4JCYcZFU4Q=
</data>
<key>ios-arm64_i386_x86_64-simulator/TencentOpenAPI.framework/Headers/QQApiInterfaceObject.h</key>
<data>
bR/sIFOcBRXlm/PPqkV9E5cBGW0=
</data>
<key>ios-arm64_i386_x86_64-simulator/TencentOpenAPI.framework/Headers/SDKDef.h</key>
<data>
pNuALDKV3dBaHRTHj87Ha0FRn2M=
</data>
<key>ios-arm64_i386_x86_64-simulator/TencentOpenAPI.framework/Headers/TencentOAuth.h</key>
<data>
qY4CtCUAIMH/VEBpkqo8h+uRhy8=
</data>
<key>ios-arm64_i386_x86_64-simulator/TencentOpenAPI.framework/Headers/TencentOpenApiUmbrellaHeader.h</key>
<data>
qGEWrIy0/CmpJkMMCxSd9xQoDh0=
</data>
<key>ios-arm64_i386_x86_64-simulator/TencentOpenAPI.framework/PrivacyInfo.xcprivacy</key>
<data>
NQnJAwD5Cl2cdaDNq498CVy7QsU=
</data>
<key>ios-arm64_i386_x86_64-simulator/TencentOpenAPI.framework/TencentOpenAPI</key>
<data>
YaSNr/4iXdMf0ABNzNjThh6Ovd4=
</data>
</dict>
<key>files2</key>
<dict>
<key>ios-arm64_armv7/TencentOpenAPI.framework/Headers/Modules/module.modulemap</key>
<dict>
<key>hash</key>
<data>
NyClSJF5AkbzsaKwZiYthWwFtEQ=
</data>
<key>hash2</key>
<data>
foQZVVDy5Y31H0ExXBx9x0xFKY8cAS8r03UUsRf4BiE=
</data>
</dict>
<key>ios-arm64_armv7/TencentOpenAPI.framework/Headers/QQApiInterface.h</key>
<dict>
<key>hash</key>
<data>
gGvGGJpjkKChFdTHG4JCYcZFU4Q=
</data>
<key>hash2</key>
<data>
MxO8YQDQsllAe5FE7OhaoXJ0m7es4H7C/BX/1bwH/aM=
</data>
</dict>
<key>ios-arm64_armv7/TencentOpenAPI.framework/Headers/QQApiInterfaceObject.h</key>
<dict>
<key>hash</key>
<data>
bR/sIFOcBRXlm/PPqkV9E5cBGW0=
</data>
<key>hash2</key>
<data>
3anKz8DaNOJRnDNgPEW2oyacBhq9160MiwLe8VQ5r9Q=
</data>
</dict>
<key>ios-arm64_armv7/TencentOpenAPI.framework/Headers/SDKDef.h</key>
<dict>
<key>hash</key>
<data>
pNuALDKV3dBaHRTHj87Ha0FRn2M=
</data>
<key>hash2</key>
<data>
GqM+994bCqOt7Y0BeiHKE53NauFkYQkGums/PGAPcyo=
</data>
</dict>
<key>ios-arm64_armv7/TencentOpenAPI.framework/Headers/TencentOAuth.h</key>
<dict>
<key>hash</key>
<data>
qY4CtCUAIMH/VEBpkqo8h+uRhy8=
</data>
<key>hash2</key>
<data>
xT3XtPEo90J7djfYWyf/8z0YS2IzECDHUGqfpQUYM9g=
</data>
</dict>
<key>ios-arm64_armv7/TencentOpenAPI.framework/Headers/TencentOpenApiUmbrellaHeader.h</key>
<dict>
<key>hash</key>
<data>
qGEWrIy0/CmpJkMMCxSd9xQoDh0=
</data>
<key>hash2</key>
<data>
/WKoVcUHfNNJlwWXqATrmrg8erC1ZXQ33PY6t7yp7o0=
</data>
</dict>
<key>ios-arm64_armv7/TencentOpenAPI.framework/PrivacyInfo.xcprivacy</key>
<dict>
<key>hash</key>
<data>
NQnJAwD5Cl2cdaDNq498CVy7QsU=
</data>
<key>hash2</key>
<data>
Q8KZWbmrIft5Xi6JXOchEUdN4mK7Ao545O0RGUHTTcI=
</data>
</dict>
<key>ios-arm64_armv7/TencentOpenAPI.framework/TencentOpenAPI</key>
<dict>
<key>hash</key>
<data>
NN04LW43ikzIT3W64odX5+4dKfQ=
</data>
<key>hash2</key>
<data>
3k0T5SqN9XWe6OMpeba+dHK6FCjzxXjquC9Fu6Kf5+w=
</data>
</dict>
<key>ios-arm64_i386_x86_64-simulator/TencentOpenAPI.framework/Headers/Modules/module.modulemap</key>
<dict>
<key>hash</key>
<data>
NyClSJF5AkbzsaKwZiYthWwFtEQ=
</data>
<key>hash2</key>
<data>
foQZVVDy5Y31H0ExXBx9x0xFKY8cAS8r03UUsRf4BiE=
</data>
</dict>
<key>ios-arm64_i386_x86_64-simulator/TencentOpenAPI.framework/Headers/QQApiInterface.h</key>
<dict>
<key>hash</key>
<data>
gGvGGJpjkKChFdTHG4JCYcZFU4Q=
</data>
<key>hash2</key>
<data>
MxO8YQDQsllAe5FE7OhaoXJ0m7es4H7C/BX/1bwH/aM=
</data>
</dict>
<key>ios-arm64_i386_x86_64-simulator/TencentOpenAPI.framework/Headers/QQApiInterfaceObject.h</key>
<dict>
<key>hash</key>
<data>
bR/sIFOcBRXlm/PPqkV9E5cBGW0=
</data>
<key>hash2</key>
<data>
3anKz8DaNOJRnDNgPEW2oyacBhq9160MiwLe8VQ5r9Q=
</data>
</dict>
<key>ios-arm64_i386_x86_64-simulator/TencentOpenAPI.framework/Headers/SDKDef.h</key>
<dict>
<key>hash</key>
<data>
pNuALDKV3dBaHRTHj87Ha0FRn2M=
</data>
<key>hash2</key>
<data>
GqM+994bCqOt7Y0BeiHKE53NauFkYQkGums/PGAPcyo=
</data>
</dict>
<key>ios-arm64_i386_x86_64-simulator/TencentOpenAPI.framework/Headers/TencentOAuth.h</key>
<dict>
<key>hash</key>
<data>
qY4CtCUAIMH/VEBpkqo8h+uRhy8=
</data>
<key>hash2</key>
<data>
xT3XtPEo90J7djfYWyf/8z0YS2IzECDHUGqfpQUYM9g=
</data>
</dict>
<key>ios-arm64_i386_x86_64-simulator/TencentOpenAPI.framework/Headers/TencentOpenApiUmbrellaHeader.h</key>
<dict>
<key>hash</key>
<data>
qGEWrIy0/CmpJkMMCxSd9xQoDh0=
</data>
<key>hash2</key>
<data>
/WKoVcUHfNNJlwWXqATrmrg8erC1ZXQ33PY6t7yp7o0=
</data>
</dict>
<key>ios-arm64_i386_x86_64-simulator/TencentOpenAPI.framework/PrivacyInfo.xcprivacy</key>
<dict>
<key>hash</key>
<data>
NQnJAwD5Cl2cdaDNq498CVy7QsU=
</data>
<key>hash2</key>
<data>
Q8KZWbmrIft5Xi6JXOchEUdN4mK7Ao545O0RGUHTTcI=
</data>
</dict>
<key>ios-arm64_i386_x86_64-simulator/TencentOpenAPI.framework/TencentOpenAPI</key>
<dict>
<key>hash</key>
<data>
YaSNr/4iXdMf0ABNzNjThh6Ovd4=
</data>
<key>hash2</key>
<data>
o2MeTXJecokZ0y01eqLpa7QLp9z3MN0L57D6PCAjiZI=
</data>
</dict>
</dict>
<key>rules</key>
<dict>
<key>^.*</key>
<true/>
<key>^.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^.*</key>
<true/>
<key>^.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,4 @@
module TencentOpenApi{
umbrella header "TencentOpenApiUmbrellaHeader.h"
export *
}

View File

@@ -0,0 +1,220 @@
///
/// \file QQApiInterface.h
/// \brief QQApi接口简化封装
///
/// Created by Tencent on 12-5-15.
/// Copyright (c) 2012年 Tencent. All rights reserved.
///
#import <Foundation/Foundation.h>
#import "QQApiInterfaceObject.h"
typedef void (^sendResultBlock)(NSDictionary *result);
// 发送消息回调是否发送成功
typedef void(^QQApiInterfaceSendMessageResultBlock)(QQApiSendResultCode sendResultCode, NSString *message);
/**
\brief 处理来至QQ的请求及响应的回调协议
*/
@protocol QQApiInterfaceDelegate <NSObject>
/**
处理来至QQ的请求
*/
- (void)onReq:(QQBaseReq *)req;
/**
处理来至QQ的响应
*/
- (void)onResp:(QQBaseResp *)resp;
/**
处理QQ在线状态的回调
*/
- (void)isOnlineResponse:(NSDictionary *)response;
@end
/**
\brief 对QQApi的简单封装类
*/
@interface QQApiInterface : NSObject
/**
处理由手Q唤起的普通跳转请求
\param url 待处理的url跳转请求
\param delegate 第三方应用用于处理来至QQ请求及响应的委托对象
\return 跳转请求处理结果YES表示成功处理NO表示不支持的请求协议或处理失败
*/
+ (BOOL)handleOpenURL:(NSURL *)url delegate:(id<QQApiInterfaceDelegate>)delegate;
/**
处理由手Q唤起的universallink跳转请求
\param universallink 待处理的universallink跳转请求
\param delegate 第三方应用用于处理来至QQ请求及响应的委托对象
\return 跳转请求处理结果YES表示成功处理NO表示不支持的请求协议或处理失败
*/
+ (BOOL)handleOpenUniversallink:(NSURL*)universallink delegate:(id<QQApiInterfaceDelegate>)delegate;
/**
向手Q发起分享请求
\param req 分享内容的请求
\return 请求发送结果码
*/
+ (QQApiSendResultCode)sendReq:(QQBaseReq *)req;
/**
向手Q QZone结合版发起分享请求
\note H5分享只支持单张网络图片的传递
\param req 分享内容的请求
\return 请求发送结果码
*/
+ (QQApiSendResultCode)SendReqToQZone:(QQBaseReq *)req;
/**
向手Q发起设置QQ头像
\param req 分享内容的请求
\return 请求发送结果码
*/
+ (QQApiSendResultCode)sendMessageToQQAvatarWithReq:(QQBaseReq*)req;
+ (QQApiSendResultCode)sendMessageToQQAuthWithReq:(QQBaseReq*)req;
/**
向手Q发起绑群请求
\param req 请求的内容
\param resultBlock 请求回调
*/
+ (void)sendThirdAppBindGroupReq:(QQBaseReq *)req resultBlock:(sendResultBlock)resultBlock;
/**
向手Q发起加群请求
\param req 请求的内容
\param resultBlock 请求回调
*/
+ (void)sendThirdAppJoinGroupReq:(QQBaseReq *)req resultBlock:(sendResultBlock)resultBlock;
/**
向手Q发起解绑群请求
\param req 请求的内容
\param resultBlock 请求回调
*/
+ (void)sendThirdAppUnBindGroupReq:(QQBaseReq *)req resultBlock:(sendResultBlock)resultBlock;
/**
向手Q发起创建QQ频道的请求
\param req 请求的内容
\param resultBlock 回调发送结果
\return void
*/
+ (void)sendMessageToCreateQQGroupProWithMessageRequest:(SendMessageToQQReq *)messageRequest sendResultBlock:(QQApiInterfaceSendMessageResultBlock)sendResultBlock;
/**
向手Q发起加入QQ频道的请求
\param req 请求的内容
\param resultBlock 回调发送结果
\return void
*/
+ (void)sendMessageToJoinQQGroupProWithMessageRequest:(SendMessageToQQReq *)messageRequest sendResultBlock:(QQApiInterfaceSendMessageResultBlock)sendResultBlock;
/**
向手Q发起查询QQ频道openID的请求
\param req 请求的内容
\param resultBlock 请求回调
*/
+ (void)sendQueryQQGroupProInfo:(QQBaseReq *)req resultBlock:(sendResultBlock)resultBlock;
/**
向手Q发起组图分享到表情收藏
\param req 分享内容的请求
\return 请求发送结果码
*/
+ (QQApiSendResultCode)sendMessageToFaceCollectionWithReq:(QQBaseReq*)req;
/**
检测是否已安装QQ
\return 如果QQ已安装则返回YES否则返回NO
\note SDK目前已经支持QQ、TIM授权登录及分享功能 会按照QQ>TIM的顺序进行调用。
只要用户安装了QQ、TIM中任意一个应用都可为第三方应用进行授权登录、分享功能。
第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。
*/
+ (BOOL)isQQInstalled;
/**
检测是否已安装TIM
\return 如果TIM已安装则返回YES否则返回NO
\note SDK目前已经支持QQ、TIM授权登录及分享功能 会按照QQ>TIM的顺序进行调用。
只要用户安装了QQ、TIM中任意一个应用都可为第三方应用进行授权登录、分享功能。
第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。
*/
+ (BOOL)isTIMInstalled;
/**
检测QQ是否支持API调用
\return 如果当前安装QQ版本支持API调用则返回YES否则返回NO
*/
+ (BOOL)isQQSupportApi;
/**
检测TIM是否支持API调用
\return 如果当前安装TIM版本支持API调用则返回YES否则返回NO
*/
+ (BOOL)isTIMSupportApi __attribute__((deprecated("已过期, 建议删除调用调用地方用YES替代。")));
/**
检测是否支持分享
\return 如果当前已安装QQ且QQ版本支持API调用 或者 当前已安装TIM且TIM版本支持API调用则返回YES否则返回NO
*/
+ (BOOL)isSupportShareToQQ;
/**
检测是否支持分享到QQ结合版QZone
\return 如果当前已安装QQ且QQ版本支持API调用则返回YES否则返回NO
*/
+ (BOOL)isSupportPushToQZone;
/**
获取QQ下载地址
如果App通过<code>QQApiInterface#isQQInstalled</code>和<code>QQApiInterface#isQQSupportApi</code>检测发现QQ没安装或当前版本QQ不支持API调用可引导用户通过打开此链接下载最新版QQ。
\return iPhoneQQ下载地址
*/
+ (NSString *)getQQInstallUrl;
/**
获取TIM下载地址
如果App通过<code>QQApiInterface#isTIMInstalled</code>检测发现TIM没安装或当前版本TIM不支持API调用可引导用户通过打开此链接下载最新版TIM。
\return iPhoneTIM下载地址
*/
+ (NSString *)getTIMInstallUrl;
#pragma mark - Log
/*! @brief 调用此函数可以导出QQSDK的Log到第三方中用于定位问题
注意1:SDK会强引用这个block,注意不要导致内存泄漏,注意不要导致内存泄漏
注意2:调用过一次startLog by block之后如果再调用一次任意方式的startLoad,会释放上一次logBlock不再回调上一个logBlock
*
* @param logBlock 打印log的回调block
*/
+ (void)startLogWithBlock:(QQApiLogBolock)logBlock;
///停止回调打印
+ (void)stopLog;
///设置打印日志到文件开关on/off如果不设置默认不打印到文件
+ (void)setSwitchPrintLogToFile:(BOOL)on;
///日志文件目录
+ (NSString *)getLogFilePath;
@end

View File

@@ -0,0 +1,754 @@
///
/// \file QQApiInterfaceObject.h
/// \brief QQApiInterface所依赖的请求及应答消息对象封装帮助类
///
/// Created by Tencent on 12-5-15.
/// Copyright (c) 2012年 Tencent. All rights reserved.
///
#ifndef QQApiInterface_QQAPIOBJECT_h
#define QQApiInterface_QQAPIOBJECT_h
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, QQApiSendResultCode) {
EQQAPISENDSUCESS = 0,
EQQAPIQQNOTINSTALLED = 1, // QQ未安装
EQQAPIQQNOTSUPPORTAPI = 2, // QQ api不支持
EQQAPIMESSAGETYPEINVALID = 3,
EQQAPIMESSAGECONTENTNULL = 4,
EQQAPIMESSAGECONTENTINVALID = 5,
EQQAPIAPPNOTREGISTED = 6,
EQQAPIAPPSHAREASYNC = 7,
EQQAPIQQNOTSUPPORTAPI_WITH_ERRORSHOW = 8, // QQ api不支持 && SDK显示error提示已废弃
EQQAPIMESSAGEARKCONTENTNULL = 9, // ark内容为空
EQQAPIMESSAGE_MINI_CONTENTNULL = 10, // 小程序参数为空
EQQAPISENDFAILD = -1, // 发送失败
EQQAPISHAREDESTUNKNOWN = -2, // 未指定分享到QQ或TIM
EQQAPITIMSENDFAILD = -3, // 发送失败
EQQAPITIMNOTINSTALLED = 11, // TIM未安装
EQQAPITIMNOTSUPPORTAPI = 12, // TIM api不支持
EQQAPI_INCOMING_PARAM_ERROR = 13, // 外部传参错误
EQQAPI_THIRD_APP_GROUP_ERROR_APP_NOT_AUTHORIZIED = 14, // APP未获得授权
EQQAPI_THIRD_APP_GROUP_ERROR_CGI_FAILED = 15, // CGI请求失败
EQQAPI_THIRD_APP_GROUP_ERROR_HAS_BINDED = 16, // 该组织已经绑定群聊
EQQAPI_THIRD_APP_GROUP_ERROR_NOT_BINDED = 17, // 该组织尚未绑定群聊
EQQAPI_THIRD_APP_GROUP_ERROR_HAS_UNBINDED = 18, // 该组织已经解绑群聊
EQQAPI_IMAGE_SIZE_OUT_OF_BOUNND = 19, // 图片大小超过限制图片不能超过5M缩略图不能超过1M
EQQAPI_TITLE_LENGTH_OUT_OF_BOUNND = 20, // 标题长度超过限制不能超过128
EQQAPI_TITLE_NIL_ERROR = 21, // 标题不能为空
EQQAPI_DESC_LENGTH_OUT_OF_BOUNND = 22, // 描述信息长度超过限制不能超过512
EQQAPI_URL_LENGTH_OUT_OF_BOUNND = 23, // URL参数长度超过限制不能超过1024
EQQAPI_URL_NIL_ERROR = 24, // URL参数不能为空
EQQAPIQZONENOTSUPPORTTEXT = 10000, // qzone分享不支持text类型分享
EQQAPIQZONENOTSUPPORTIMAGE = 10001, // qzone分享不支持image类型分享
EQQAPIVERSIONNEEDUPDATE = 10002, // 当前QQ版本太低需要更新至新版本才可以支持
ETIMAPIVERSIONNEEDUPDATE = 10004, // 当前TIM版本太低需要更新至新版本才可以支持
EAPPURLTYPESILLEGALITY = 20000, // (>=3.3.8)第三方APP的info.plist中UrlTypes字段存在QQ的UrlScheme
EQQAPI_ERROR_USER_NOT_AGREED_AUTHORIZATION = 30001, // 用户未同意隐私协议,用户同意隐私协议后,需要设置[TencentOAuth setIsUserAgreedAuthorization:YES];
};
#pragma mark - QQApiObject(分享对象类型)
// QQApiObject control flags
typedef NS_ENUM(NSUInteger,kQQAPICtrlFlag) {
kQQAPICtrlFlagQZoneShareOnStart = 0x01,
kQQAPICtrlFlagQZoneShareForbid = 0x02, //屏蔽好友选择器上的空间入口
kQQAPICtrlFlagQQShare = 0x04,
kQQAPICtrlFlagQQShareFavorites = 0x08, //收藏
kQQAPICtrlFlagQQShareDataline = 0x10, //数据线
kQQAPICtrlFlagQQShareEnableArk = 0x20, //支持ARK
kQQAPICtrlFlagQQShareEnableMiniProgram = 0x40, //支持小程序
};
// 分享到QQ或TIM
typedef NS_ENUM(NSUInteger, ShareDestType) {
ShareDestTypeQQ = 0,
ShareDestTypeTIM,
};
//小程序的类型
typedef NS_ENUM(NSUInteger, MiniProgramType) {
MiniProgramType_Develop = 0, // 开发版
MiniProgramType_Test = 1, // 测试版
MiniProgramType_Online = 3, // 正式版,默认
MiniProgramType_Preview = 4, // 预览版
};
/// 打印回调的block
typedef void(^QQApiLogBolock)(NSString *logStr);
// QQApiObject
/** \brief 所有在QQ及插件间发送的数据对象的根类。
*/
__attribute__((visibility("default"))) @interface QQApiObject : NSObject
@property (nonatomic, copy) NSString *title; ///< 标题最长128个字符
@property (nonatomic, copy) NSString *description; ///<简要描述最长512个字符
@property (nonatomic, copy) NSString *universalLink; ///(>=3.3.7)支持第三方传入在互联开放平台注册的universallink
@property (nonatomic, assign) uint64_t cflag;
//353新增两个字断给游戏侧使用对齐微信sdk
@property (nonatomic, copy) NSString *tagName;
@property (nonatomic, copy) NSString *messageExt;
/*
* 分享到QQ/TIM
* SDK根据是否安装对应客户端进行判断判断顺序QQ > TIM
* 默认分享到QQ如果QQ未安装检测TIM是否安装
*/
@property (nonatomic, assign) ShareDestType shareDestType;
/**
* 检查参数是否完整有效
* @return 检查参数结果
*/
- (QQApiSendResultCode)checkParamValid;
@end
// ArkObject
/** \brief 支持Ark的根类。
*/
__attribute__((visibility("default"))) @interface ArkObject : NSObject
@property (nonatomic, copy) NSString *arkData; ///< 显示Ark所需的数据json串长度暂不限制
@property (nonatomic, strong) QQApiObject* qqApiObject; ///<原有老版本的QQApiObject
- (id)initWithData:(NSString *)arkData qqApiObject:(QQApiObject*)qqApiObject;
+ (id)objectWithData:(NSString *)arkData qqApiObject:(QQApiObject*)qqApiObject;
@end
#pragma mark QQ小程序
//分享小程序消息 - QQ 8.0.8
__attribute__((visibility("default"))) @interface QQApiMiniProgramObject : NSObject
@property (nonatomic, strong) QQApiObject* qqApiObject; //原有老版本的QQApiObject
@property (nonatomic, copy) NSString *miniAppID; //必填小程序的AppId必须在QQ互联平台中将该小程序与分享的App绑定
@property (nonatomic, copy) NSString *miniPath; //必填,小程序的展示路径
@property (nonatomic, copy) NSString *webpageUrl; //必填,兼容低版本的网页链接
@property (nonatomic, assign) MiniProgramType miniprogramType; //非必填,小程序的类型,默认正式版(3),可选测试版(1)、预览版(4)
@end
//唤起小程序 - QQ 8.1.8
__attribute__((visibility("default"))) @interface QQApiLaunchMiniProgramObject : QQApiObject
@property (nonatomic, copy) NSString *miniAppID; //必填小程序的AppId必须在QQ互联平台中将该小程序与分享的App绑定
@property (nonatomic, copy) NSString *miniPath; //小程序的展示路径,不填展示默认小程序首页
@property (nonatomic, assign) MiniProgramType miniprogramType; //非必填,小程序的类型,默认正式版(3),可选测试版(1)、开发版(0)
@end
//小程序唤起第三方 - SDK 3.3.9
__attribute__((visibility("default"))) @interface QQApiMiniProgramLaunchObject : QQApiObject
@property (nonatomic, copy) NSString *appParameter; //小程序带来的数据,透传
+ (instancetype)newWithAppParameter:(NSString *)parameter;
@end
// QQApiResultObject
/** \brief 用于请求回应的数据类型。
<h3>可能错误码及描述如下:</h3>
<TABLE>
<TR><TD>error</TD><TD>errorDescription</TD><TD>注释</TD></TR>
<TR><TD>0</TD><TD>nil</TD><TD>成功</TD></TR>
<TR><TD>-1</TD><TD>param error</TD><TD>参数错误</TD></TR>
<TR><TD>-2</TD><TD>group code is invalid</TD><TD>该群不在自己的群列表里面</TD></TR>
<TR><TD>-3</TD><TD>upload photo failed</TD><TD>上传图片失败</TD></TR>
<TR><TD>-4</TD><TD>user give up the current operation</TD><TD>用户放弃当前操作</TD></TR>
<TR><TD>-5</TD><TD>client internal error</TD><TD>客户端内部处理错误</TD></TR>
</TABLE>
*/
__attribute__((visibility("default"))) @interface QQApiResultObject : QQApiObject
@property (nonatomic, copy) NSString *error; ///<错误
@property (nonatomic, copy) NSString *errorDescription; ///<错误描述
@property (nonatomic, copy) NSString *extendInfo; ///<扩展信息
@property (nonatomic, copy) NSDictionary *otherInfo; ///<其他扩展信息
@end
// QQApiTextObject
/** \brief 文本对象
*/
@interface QQApiTextObject : QQApiObject
@property (nonatomic, copy)NSString *text; ///<文本内容必填最长1536个字符
- (id)initWithText:(NSString *)text; ///<初始化方法
+ (id)objectWithText:(NSString *)text;///<工厂方法获取一个QQApiTextObject对象.
@end
// QQApiURLObject
typedef NS_ENUM(NSUInteger, QQApiURLTargetType) {
QQApiURLTargetTypeNotSpecified = 0x00,
QQApiURLTargetTypeAudio = 0x01,
QQApiURLTargetTypeVideo = 0x02,
QQApiURLTargetTypeNews = 0x03
};
/** @brief URL对象类型。
包括URL地址URL地址所指向的目标类型及预览图像。
*/
__attribute__((visibility("default"))) @interface QQApiURLObject : QQApiObject
/**
URL地址所指向的目标类型.
@note 参见QQApi.h 中的 QQApiURLTargetType 定义.
*/
@property (nonatomic)QQApiURLTargetType targetContentType;
@property (nonatomic, strong) NSURL *url; ///<URL地址,必填最长512个字符
@property (nonatomic, copy) NSData *previewImageData;///<预览图像数据最大1M字节
@property (nonatomic, strong) NSURL *previewImageURL; ///<预览图像URL **预览图像数据与预览图像URL可二选一
/**
初始化方法
*/
- (id)initWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageData:(NSData *)data targetContentType:(QQApiURLTargetType)targetContentType;
- (id)initWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageURL:(NSURL *)previewURL targetContentType:(QQApiURLTargetType)targetContentType;
/**
工厂方法,获取一个QQApiURLObject对象
*/
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageData:(NSData *)data targetContentType:(QQApiURLTargetType)targetContentType;
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageURL:(NSURL *)previewURL targetContentType:(QQApiURLTargetType)targetContentType;
@end
// QQApiExtendObject
/** @brief 扩展数据类型
*/
@interface QQApiExtendObject : QQApiObject
@property (nonatomic, copy) NSData *data;///<具体数据内容必填最大5M字节
@property (nonatomic, copy) NSData *previewImageData;///<预览图像最大1M字节
@property (nonatomic, copy) NSArray *imageDataArray;///图片数组(多图暂只支持分享到手机QQ收藏功能)
/**
初始化方法
@param data 数据内容
@param previewImageData 用于预览的图片
@param title 标题
@param description 此对象,分享的描述
*/
- (id)initWithData:(NSData *)data previewImageData:(NSData *)previewImageData title:(NSString *)title description:(NSString *)description;
/**
初始化方法
@param data 数据内容
@param title 标题
@param description 此对象,分享的描述
@param imageDataArray 发送的多张图片队列
*/
- (id)initWithData:(NSData *)data previewImageData:(NSData *)previewImageData title:(NSString *)title description:(NSString *)description imageDataArray:(NSArray *)imageDataArray;
/**
helper方法获取一个autorelease的<code>QQApiExtendObject</code>对象
@param data 数据内容
@param previewImageData 用于预览的图片
@param title 标题
@param description 此对象,分享的描述
@return
一个自动释放的<code>QQApiExtendObject</code>实例
*/
+ (id)objectWithData:(NSData *)data previewImageData:(NSData *)previewImageData title:(NSString *)title description:(NSString *)description;
/**
helper方法获取一个autorelease的<code>QQApiExtendObject</code>对象
@param data 数据内容
@param previewImageData 用于预览的图片
@param title 标题
@param description 此对象,分享的描述
@param imageDataArray 发送的多张图片队列
@return
一个自动释放的<code>QQApiExtendObject</code>实例
*/
+ (id)objectWithData:(NSData *)data previewImageData:(NSData *)previewImageData title:(NSString *)title description:(NSString *)description imageDataArray:(NSArray *)imageDataArray;
@end
// QQApiImageObject
/** @brief 图片对象
用于分享图片内容的对象,是一个指定为图片类型的<code>QQApiExtendObject</code>
*/
@interface QQApiImageObject : QQApiExtendObject
@end
// QQApiImageForQQAvatarObject
/** @brief 图片对象
用于设置QQ头像内容的对象是一个指定为图片类型的<code>QQApiExtendObject</code>
*/
@interface QQApiImageForQQAvatarObject : QQApiExtendObject
@end
/**
* @brief 视频对象
* 用于设置动态头像
* assetURL可传ALAsset的ALAssetPropertyAssetURL或者PHAsset的localIdentifier
从手Q返回的错误码
//第三方设置动态头像结果
@"ret=0"//设置成功
@"ret=-10&error_des=user cancel"//用户取消设置
@"ret=-11&error_des=pasteboard have no video data"//剪切板没有数据
@"ret=-12&error_des=export data failed"//从剪切板导出数据到本地失败
@"ret=-13&error_des=url param invalid"//sdk传递过来的数据有误
@"ret=-14&error_des=video param invalid"//视频的参数不符合要求检测第三方视频源方案1、分辨率跟480*480保持一致2、视频长度0.5s8s
@"ret=-15&error_des=app authorised failed"//应用鉴权失败
@"ret=-16&error_des=upload video failed"//设置头像,上传到后台失败
@"ret=-17&error_des=account diff"//账号不一致
*/
@interface QQApiVideoForQQAvatarObject : QQApiExtendObject
@property (nonatomic, copy) NSString *assetURL;
@end
//QQApiAuthObject 用于拉起手Q的授权详情页
@interface QQApiAuthObject : QQApiObject
@end
// QQApiImageArrayForFaceCollectionObject
/** @brief 图片数组对象
用于分享图片组到表情收藏,是一个指定为图片类型的<code>QQApiObject</code>
*/
@interface QQApiImageArrayForFaceCollectionObject : QQApiObject
@property (nonatomic, copy) NSArray *imageDataArray;///图片数组
/**
初始化方法
@param imageDataArray 图片数组
*/
- (id)initWithImageArrayData:(NSArray *)imageDataArray;
/**
helper方法获取一个autorelease的<code>QQApiObject</code>对象
@param imageDataArray 发送的多张图片队列
@return
一个自动释放的<code>QQApiObject</code>实例
*/
+ (id)objectWithimageDataArray:(NSArray *)imageDataArray;
@end
// QQApiImageArrayForQZoneObject
/** @brief 图片对象
用于分享图片到空间,走写说说路径,是一个指定为图片类型的,当图片数组为空时,默认走文本写说说<code>QQApiObject</code>
*/
@interface QQApiImageArrayForQZoneObject : QQApiObject
@property (nonatomic, copy) NSArray *imageDataArray;///图片数组
@property (nonatomic, copy) NSDictionary *extMap; // 扩展字段
/**
初始化方法
@param imageDataArray 图片数组
@param title 写说说的内容,可以为空
@param extMap 扩展字段
*/
- (id)initWithImageArrayData:(NSArray *)imageDataArray title:(NSString *)title extMap:(NSDictionary *)extMap;
/**
helper方法获取一个autorelease的<code>QQApiExtendObject</code>对象
@param title 写说说的内容,可以为空
@param imageDataArray 发送的多张图片队列
@param extMap 扩展字段
@return
一个自动释放的<code>QQApiExtendObject</code>实例
*/
+ (id)objectWithimageDataArray:(NSArray *)imageDataArray title:(NSString *)title extMap:(NSDictionary *)extMap;
@end
// QQApiVideoForQZoneObject
/** @brief 视频对象
用于分享视频到空间,走写说说路径<code>QQApiObject</code>,assetURL和videoData两个参数必须设置至少一个参数如果assetURL设置了忽略videoData参数
@param assetURL可传ALAsset的ALAssetPropertyAssetURL或者PHAsset的localIdentifier
@param extMap 扩展字段
@param videoData 视频数据大小不超过50M
*/
@interface QQApiVideoForQZoneObject : QQApiObject
@property (nonatomic, copy) NSString *assetURL;
@property (nonatomic, copy) NSDictionary *extMap; // 扩展字段
@property (nonatomic, copy) NSData *videoData;
- (id)initWithAssetURL:(NSString *)assetURL title:(NSString *)title extMap:(NSDictionary *)extMap;
+ (id)objectWithAssetURL:(NSString *)assetURL title:(NSString *)title extMap:(NSDictionary *)extMap;
- (id)initWithVideoData:(NSData *)videoData title:(NSString *)title extMap:(NSDictionary *)extMap;
+ (id)objectWithVideoData:(NSData *)videoData title:(NSString *)title extMap:(NSDictionary *)extMap;
@end
// QQApiWebImageObject
/** @brief 图片对象
用于分享网络图片内容的对象是一个指定网络图片url的: 该类型只在2.9.0的h5分享中才支持
原有的手q分享是不支持该类型的。
*/
@interface QQApiWebImageObject : QQApiObject
@property (nonatomic, strong) NSURL *previewImageURL; ///<预览图像URL
/**
初始化方法
@param previewImageURL 用于预览的图片
@param title 标题
@param description 此对象,分享的描述
*/
- (id)initWithPreviewImageURL:(NSURL *)previewImageURL title:(NSString *)title description:(NSString *)description;
/**
helper方法获取一个autorelease的<code>QQApiWebImageObject</code>对象
@param previewImageURL 用于预览的图片
@param title 标题
@param description 此对象,分享的描述
*/
+ (id)objectWithPreviewImageURL:(NSURL *)previewImageURL title:(NSString *)title description:(NSString *)description;
@end
//QQApiFileObject
/** @brief 本地文件对象(暂只支持分享到手机QQ数据线功能)
用于分享文件内容的对象,是一个指定为文件类型的<code>QQApiExtendObject</code>
*/
@interface QQApiFileObject : QQApiExtendObject {
NSString *_fileName;
}
@property (nonatomic, copy)NSString *fileName;
@end
// QQApiAudioObject
/** @brief 音频URL对象
用于分享目标内容为音频的URL的对象
*/
@interface QQApiAudioObject : QQApiURLObject
@property (nonatomic, strong) NSURL *flashURL; ///<音频URL地址最长512个字符
/**
获取一个autorelease的<code>QQApiAudioObject</code>
@param url 音频内容的目标URL
@param title 分享内容的标题
@param description 分享内容的描述
@param data 分享内容的预览图像
@note 如果url为空调用<code>QQApi#sendMessage:</code>时将返回FALSE
*/
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageData:(NSData *)data;
/**
获取一个autorelease的<code>QQApiAudioObject</code>
@param url 音频内容的目标URL
@param title 分享内容的标题
@param description 分享内容的描述
@param previewURL 分享内容的预览图像URL
@note 如果url为空调用<code>QQApi#sendMessage:</code>时将返回FALSE
*/
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageURL:(NSURL *)previewURL;
@end
// QQApiVideoObject
/** @brief 视频URL对象
用于分享目标内容为视频的URL的对象
QQApiVideoObject类型的分享目前在Android和PC QQ上接收消息时展现有待完善待手机QQ版本以后更新支持
目前如果要分享视频,推荐使用 QQApiNewsObject 类型
*/
@interface QQApiVideoObject : QQApiURLObject
@property (nonatomic, strong) NSURL *flashURL; ///<视频URL地址最长512个字符
/**
获取一个autorelease的<code>QQApiVideoObject</code>
@param url 视频内容的目标URL
@param title 分享内容的标题
@param description 分享内容的描述
@param data 分享内容的预览图像
@note 如果url为空调用<code>QQApi#sendMessage:</code>时将返回FALSE
*/
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageData:(NSData *)data;
/**
获取一个autorelease的<code>QQApiVideoObject</code>
@param url 视频内容的目标URL
@param title 分享内容的标题
@param description 分享内容的描述
@param previewURL 分享内容的预览图像URL
@note 如果url为空调用<code>QQApi#sendMessage:</code>时将返回FALSE
*/
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageURL:(NSURL *)previewURL;
@end
// QQApiNewsObject
/** @brief 新闻URL对象
用于分享目标内容为新闻的URL的对象
*/
@interface QQApiNewsObject : QQApiURLObject
/**
获取一个autorelease的<code>QQApiNewsObject</code>
@param url 视频内容的目标URL
@param title 分享内容的标题
@param description 分享内容的描述
@param data 分享内容的预览图像
@note 如果url为空调用<code>QQApi#sendMessage:</code>时将返回FALSE
*/
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageData:(NSData *)data;
/**
获取一个autorelease的<code>QQApiNewsObject</code>
@param url 视频内容的目标URL
@param title 分享内容的标题
@param description 分享内容的描述
@param previewURL 分享内容的预览图像URL
@note 如果url为空调用<code>QQApi#sendMessage:</code>时将返回FALSE
*/
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageURL:(NSURL *)previewURL;
@end
// QQApiCommonContentObject;
/** @brief 通用模板类型对象
用于分享一个固定显示模板的图文混排对象
@note 图片列表和文本列表不能同时为空
*/
@interface QQApiCommonContentObject : QQApiObject
/**
预定义的界面布局类型
*/
@property (nonatomic,assign) unsigned int layoutType;
@property (nonatomic, copy) NSData *previewImageData;///<预览图
@property (nonatomic, copy) NSArray *textArray;///<文本列表
@property (nonatomic, copy) NSArray *pictureDataArray;///<图片列表
+ (id)objectWithLayoutType:(int)layoutType textArray:(NSArray *)textArray pictureArray:(NSArray *)pictureArray previewImageData:(NSData *)data;
/**
将一个NSDictionary对象转化为QQApiCommomContentObject如果无法转换则返回空
*/
+ (id)objectWithDictionary:(NSDictionary *)dic;
- (NSDictionary *)toDictionary;
@end
// QQApiExtraServiceObject; 通用业务消息处理类旧版后续使用QQApiCommonServiceObject
/**
@brief OpenSDK扩展支持的服务通用接口后续会扩充能力
@param serviceID [必选] 扩展支持的服务类型ID参考官方文档说明
@param openID [必选] 授权登录后对该用户的唯一标识
@param toUin [可选] 对方的QQ号码
@param extraInfo [可选] 扩展字段
@note 该接口的使用须先登录
*/
@interface QQApiExtraServiceObject : QQApiObject
@property (nonatomic, copy) NSString *serviceID;
@property (nonatomic, copy) NSString *openID;
@property (nonatomic, copy) NSString *toUin;
@property (nonatomic, copy) NSDictionary *extraInfo;
- (id)initWithOpenID:(NSString *)openID serviceID:(NSString *)serviceID;
+ (id)objecWithOpenID:(NSString *)openID serviceID:(NSString *)serviceID;
@end
/**
* QQApiCommonServiceObject; 通用业务消息处理类可以适用所有的需要通过互联SDK发消息给手Q的业务去处理。
* 使用前需要申请serviceID每个业务功能有个对应的serviceID
*/
@interface QQApiCommonServiceObject : QQApiObject
// [必选] 授权登录后对该用户的唯一标识
@property (nonatomic, copy) NSString *openID;
// [必选] 扩展支持的服务类型ID参考官方文档说明
@property (nonatomic, copy) NSString *serviceID;
// [可选] 扩展字段,由调用方跟具体的业务方协定具体的字段
@property (nonatomic, copy) NSDictionary *extendInfo;
- (instancetype)initWithOpenID:(NSString *)openID
serviceID:(NSString *)serviceID
extendInfo:(NSDictionary *)extendInfo;
+ (instancetype)objecWithOpenID:(NSString *)openID
serviceID:(NSString *)serviceID
extendInfo:(NSDictionary *)extendInfo;
@end
#pragma mark - QQApi请求消息类型
/**
QQApi请求消息类型
*/
typedef NS_ENUM(NSUInteger, QQApiInterfaceReqType) {
EGETMESSAGEFROMQQREQTYPE = 0, /// < 手Q -> 第三方应用请求第三方应用向手Q发送消息
ESENDMESSAGETOQQREQTYPE = 1, /// < 第三方应用 -> 手Q第三方应用向手Q分享消息
ESHOWMESSAGEFROMQQREQTYPE = 2, /// < 手Q -> 第三方应用,请求第三方应用展现消息中的数据
ESENDMESSAGEARKTOQQREQTYPE = 3, /// < 第三方应用 -> 手Q第三方应用向手Q分享Ark消息
ESENDMESSAGE_MINI_TOQQREQTYPE = 4 /// < 第三方应用 -> 手Q第三方应用向手Q分享小程序消息
};
/**
QQApi应答消息类型
*/
typedef NS_ENUM(NSUInteger, QQApiInterfaceRespType) {
ESHOWMESSAGEFROMQQRESPTYPE = 0, /// < 第三方应用 -> 手Q第三方应用应答消息展现结果
EGETMESSAGEFROMQQRESPTYPE = 1, /// < 第三方应用 -> 手Q第三方应用回应发往手Q的消息
ESENDMESSAGETOQQRESPTYPE = 2 /// < 手Q -> 第三方应用手Q应答处理分享消息的结果
};
/**
QQApi请求消息基类
*/
@interface QQBaseReq : NSObject
/** 请求消息类型,参见\ref QQApiInterfaceReqType */
@property (nonatomic, assign) int type;
@end
/**
QQApi应答消息基类
*/
@interface QQBaseResp : NSObject
/** 请求处理结果 */
@property (nonatomic, copy) NSString *result;
/** 具体错误描述信息 */
@property (nonatomic, copy) NSString *errorDescription;
/** 应答消息类型,参见\ref QQApiInterfaceRespType */
@property (nonatomic, assign) int type;
/** 扩展信息 */
@property (nonatomic, copy) NSString *extendInfo;
@end
/**
GetMessageFromQQReq请求帮助类
*/
@interface GetMessageFromQQReq : QQBaseReq
/**
创建一个GetMessageFromQQReq请求实例
*/
+ (GetMessageFromQQReq *)req;
@end
@interface SendMessageToQQReq : QQBaseReq
/**
创建一个SendMessageToQQReq请求实例
\param message 具体分享消息实例
\return 新创建的SendMessageToQQReq请求实例
*/
+ (SendMessageToQQReq *)reqWithContent:(QQApiObject *)message;
/**
创建一个支持Ark的SendMessageToQQReq请求实例
\param message 具体分享消息实例
\return 新创建的SendMessageToQQReq请求实例
*/
+ (SendMessageToQQReq *)reqWithArkContent:(ArkObject *)message;
/**
* 创建一个支持小程序的消息请求实例
* @param miniMessage 小程序实例对象
* @return 消息请求实例
*/
+ (SendMessageToQQReq *)reqWithMiniContent:(QQApiMiniProgramObject *)miniMessage;
/** 具体分享消息 */
@property (nonatomic, strong) QQApiObject *apiObject;
/** 支持Ark的具体分享消息 */
@property (nonatomic, strong) ArkObject *arkObject;
/** 支持小程序的具体分享消息 */
@property (nonatomic, strong) QQApiMiniProgramObject *miniProgramObject;
@end
/**
SendMessageToQQResp应答帮助类
*/
@interface SendMessageToQQResp : QQBaseResp
/** 其他扩展信息 */
@property (nonatomic, copy) NSDictionary *otherInfo;
/**
创建一个SendMessageToQQResp应答实例
\param result 请求处理结果
\param errDesp 具体错误描述信息
\param extendInfo 扩展信息
\return 新创建的SendMessageToQQResp应答实例
*/
+ (SendMessageToQQResp *)respWithResult:(NSString *)result errorDescription:(NSString *)errDesp extendInfo:(NSString *)extendInfo;
+ (SendMessageToQQResp *) respWithResult:(NSString *)result errorDescription:(NSString *)errDesp extendInfo:(NSString *)extendInfo otherInfo:(NSDictionary *)otherInfo;
@end
/**
ShowMessageFromQQReq请求帮助类
*/
@interface ShowMessageFromQQReq : QQBaseReq
/**
创建一个ShowMessageFromQQReq请求实例
\param message 具体待展现消息实例
\return 新创建的ShowMessageFromQQReq请求实例
*/
+ (ShowMessageFromQQReq *)reqWithContent:(QQApiObject *)message;
/** 具体待展现消息 */
@property (nonatomic, strong) QQApiObject *message;
@end
#pragma mark --一键加群&建群&解绑群
// QQApiThirdAppBindGroupObject
/** \brief 第三方app绑定群
*/
@interface QQApiThirdAppBindGroupObject : QQApiObject
@property (nonatomic, copy) NSString *accessToken;
@property (nonatomic, copy) NSString *payToken;
@property (nonatomic, copy) NSString *pfkey;
@property (nonatomic, copy) NSString *unionID;
@property (nonatomic, copy) NSString *appDisplayName;
- (id)initWithAccessToken:(NSString *)accessToken payToken:(NSString *)payToken pfkey:(NSString *)pfkey unionID:(NSString *)unionID appDisplayName:(NSString *)appDisplayName; ///<初始化方法
+ (id)objectWithAccessToken:(NSString *)accessToken payToken:(NSString *)payToken pfkey:(NSString *)pfkey unionID:(NSString *)unionID appDisplayName:(NSString *)appDisplayName; ///<工厂方法获取一个QQApiThirdAppBindGroupObject对象.
@end
// QQApiThirdAppJoinGroupObject
/** \brief 第三方app加入群
*/
@interface QQApiThirdAppJoinGroupObject : QQApiObject
@property (nonatomic, copy) NSString *accessToken;
@property (nonatomic, copy) NSString *payToken;
@property (nonatomic, copy) NSString *pfkey;
@property (nonatomic, copy) NSString *unionID;
- (id)initWithAccessToken:(NSString *)accessToken payToken:(NSString *)payToken pfkey:(NSString *)pfkey unionID:(NSString *)unionID; ///<初始化方法
+ (id)objectWithAccessToken:(NSString *)accessToken payToken:(NSString *)payToken pfkey:(NSString *)pfkey unionID:(NSString *)unionID; ///<工厂方法获取一个QQApiThirdAppJoinGroupObject对象.
@end
// QQApiThirdAppUnBindGroupObject
/** \brief 第三方app解绑群
*/
@interface QQApiThirdAppUnBindGroupObject : QQApiObject
@property (nonatomic, copy) NSString *accessToken;
@property (nonatomic, copy) NSString *openId;
@property (nonatomic, copy) NSString *payToken;
@property (nonatomic, copy) NSString *pfkey;
@property (nonatomic, copy) NSString *unionID;
- (id)initWithAccessToken:(NSString *)accessToken payToken:(NSString *)payToken pfkey:(NSString *)pfkey unionID:(NSString *)unionID openId:(NSString *)openId appId:(NSString *)appId; ///<初始化方法
+ (id)objectWithAccessToken:(NSString *)accessToken payToken:(NSString *)payToken pfkey:(NSString *)pfkey unionID:(NSString *)unionID openId:(NSString *)openId appId:(NSString *)appId; ///<工厂方法获取一个QQApiThirdAppBindGroupObject对象.
@end
#endif

View File

@@ -0,0 +1,420 @@
///
/// \file sdkdef.h
/// \brief SDK中相关常量定义
///
/// Created by Tencent on 12-12-25.
/// Copyright (c) 2012年 Tencent. All rights reserved.
///
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
/**
* \brief 设置sdk的log等级
*/
typedef enum {
TCOLogLevel_Disabled = -1, // 关闭所有log
TCOLogLevel_Error = 0,
TCOLogLevel_Warning,
TCOLogLevel_Info,
TCOLogLevel_Debug,
} TCOLogLevel;
/**
* \breif 授权/分享 方式
*/
typedef enum TencentAuthShareType {
AuthShareType_QQ,
AuthShareType_TIM,
}TencentAuthShareType;
/**
* \brief APIResponse.retCode可能的枚举常量
*/
typedef enum
{
URLREQUEST_SUCCEED = 0, /**< 网络请求成功发送至服务器,并且服务器返回数据格式正确
* \note 这里包括所请求业务操作失败的情况,例如没有授权等原因导致
*/
URLREQUEST_FAILED = 1, /**< 网络异常,或服务器返回的数据格式不正确导致无法解析 */
} REPONSE_RESULT;
/**
* \brief 增量授权失败原因
*
* \note 增量授权失败不影响原token的有效性原token已失效的情况除外
*/
typedef enum
{
kUpdateFailUnknown = 1, ///< 未知原因
kUpdateFailUserCancel, ///< 用户取消
kUpdateFailNetwork, ///< 网络问题
} UpdateFailType;
/**
* \brief 封装服务器返回的结果
*
* APIResponse用于封装所有请求的返回结果包括错误码、错误信息、原始返回数据以及返回数据的json格式字典
*/
@interface APIResponse : NSObject<NSSecureCoding> {
int _detailRetCode;
int _retCode;
int _seq;
NSString *_errorMsg;
NSDictionary *_jsonResponse;
NSString *_message;
id _userData;
}
/**
* 新增的详细错误码\n
* detailRetCode主要用于区分不同的错误情况参见\ref OpenSDKError
*/
@property (nonatomic, assign) int detailRetCode;
/**
* 网络请求是否成功送达服务器,以及服务器返回的数据格式是否正确\n
* retCode具体取值可参考\ref REPONSE_RESULT
*/
@property (nonatomic, assign) int retCode;
/**
* 网络请求对应的递增序列号,方便内部管理
*/
@property (nonatomic, assign) int seq;
/**
* 错误提示语
*/
@property (nonatomic, copy) NSString *errorMsg;
/**
* 服务器返回数据的json格式字典\n
* 字典内具体参数的命名和含义请参考\ref api_spec
*/
@property (nonatomic, copy) NSDictionary *jsonResponse;
/**
* 服务器返回的原始数据字符串
*/
@property (nonatomic, copy) NSString *message;
/**
* 用户保留数据
*/
@property (nonatomic, strong) id userData;
@end
/**
* 用户自定义的保留字段
*/
FOUNDATION_EXTERN NSString * const PARAM_USER_DATA;
/**
* \name 应用邀请参数字段定义
*/
///@{
/** 应用邀请展示图片url的key */
FOUNDATION_EXTERN NSString * const PARAM_APP_ICON;
/** 应用邀请描述文本的key */
FOUNDATION_EXTERN NSString * const PARAM_APP_DESC;
/** 应用邀请好友列表的key */
FOUNDATION_EXTERN NSString * const PARAM_APP_INVITED_OPENIDS;
///@}
/**
* \name sendStory新分享参数字段定义
*/
///@{
/** 预填入接受人列表的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_RECEIVER;
/** 分享feeds标题的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_TITLE;
/** 分享feeds评论内容的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_COMMENT;
/** 分享feeds摘要的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_SUMMARY;
/** 分享feeds展示图片url的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_IMAGE;
/** 分享feeds跳转链接url的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_URL;
/** 分享feeds点击操作默认行为的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_ACT;
///@}
/**
* \name 设置头像参数字段定义
*/
///@{
/** 头像图片数据的key */
FOUNDATION_EXTERN NSString * const PARAM_SETUSERHEAD_PIC;
/** 头像图片文件名的key */
FOUNDATION_EXTERN NSString * const PARAM_SETUSERHEAD_FILENAME;
///@}
/**
* \name 服务器返回数据的参数字段定义
*/
///@{
/** 服务器返回码的key */
FOUNDATION_EXTERN NSString * const PARAM_RETCODE;
/** 服务器返回错误信息的key */
FOUNDATION_EXTERN NSString * const PARAM_MESSAGE;
/** 服务器返回额外数据的key */
FOUNDATION_EXTERN NSString * const PARAM_DATA;
///@}
/**
* \name 错误信息相关常量定义
*/
///@{
/** 详细错误信息字典中额外信息的key */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorKeyExtraInfo;
/** 详细错误信息字典中返回码的key */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorKeyRetCode;
/** 详细错误信息字典中错误语句的key */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorKeyMsg;
/** 不支持的接口 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUnsupportedAPI;
/** 操作成功 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgSuccess;
/** 未知错误 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUnknown;
/** 用户取消 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUserCancel;
/** 请重新登录 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgReLogin;
/** 应用没有操作权限 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgOperationDeny;
/** 网络异常或没有网络 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgNetwork;
/** URL格式或协议错误 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgURL;
/** 解析数据出错 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgDataParse;
/** 传入参数有误 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgParam;
/** 连接超时 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgTimeout;
/** 安全问题 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgSecurity;
/** 文件读写错误 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgIO;
/** 服务器端错误 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgServer;
/** 页面错误 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgWebPage;
/** 设置头像图片过大 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUserHeadPicLarge;
/** 用户未同意授权隐私协议 */
FOUNDATION_EXPORT NSString * const TCOpenSDKErrorMsgUserNotAgreedAuthorization;
///@}
/**
* \brief SDK新增详细错误常量
*/
typedef enum
{
kOpenSDKInvalid = -1, ///< 无效的错误码
kOpenSDKErrorUnsupportedAPI = -2, ///< 不支持的接口
/**
* \name CommonErrorCode
* 公共错误码
*/
///@{
kOpenSDKErrorSuccess = 0, ///< 成功
kOpenSDKErrorUnknown, ///< 未知错误
kOpenSDKErrorUserCancel, ///< 用户取消
kOpenSDKErrorReLogin, ///< token无效或用户未授权相应权限需要重新登录
kOpenSDKErrorOperationDeny, ///< 第三方应用没有该api操作的权限
///@}
/**
* \name NetworkRelatedErrorCode
* 网络相关错误码
*/
///@{
kOpenSDKErrorNetwork, ///< 网络错误,网络不通或连接不到服务器
kOpenSDKErrorURL, ///< URL格式或协议错误
kOpenSDKErrorDataParse, ///< 数据解析错误,服务器返回的数据解析出错
kOpenSDKErrorParam, ///< 传入参数错误
kOpenSDKErrorConnTimeout, ///< http连接超时
kOpenSDKErrorSecurity, ///< 安全问题
kOpenSDKErrorIO, ///< 下载和文件IO错误
kOpenSDKErrorServer, ///< 服务器端错误
///@}
/**
* \name WebViewRelatedError
* webview特有错误
*/
///@{
kOpenSDKErrorWebPage, ///< 页面错误
///@}
/**
* \name SetUserHeadRelatedErrorCode
* 设置头像自定义错误码段
*/
///@{
kOpenSDKErrorUserHeadPicLarge = 0x010000, ///< 图片过大 设置头像自定义错误码
///@}
} OpenSDKError;
/**
* \name SDK版本(v1.3)支持的授权列表常量
*/
///@{
/** 发表一条说说到QQ空间(<b>需要申请权限</b>) */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_ADD_TOPIC;
/** 创建一个QQ空间相册(<b>需要申请权限</b>) */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_ADD_ALBUM;
/** 上传一张照片到QQ空间相册(<b>需要申请权限</b>) */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_UPLOAD_PIC;
/** 获取用户QQ空间相册列表(<b>需要申请权限</b>) */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_LIST_ALBUM;
/** 验证是否认证空间粉丝 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_CHECK_PAGE_FANS;
/** 获取登录用户自己的详细信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_INFO;
/** 获取其他用户的详细信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_OTHER_INFO;
/** 获取会员用户基本信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_VIP_INFO;
/** 获取会员用户详细信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_VIP_RICH_INFO;
/** 获取用户信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_USER_INFO;
/** 移动端获取用户信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_SIMPLE_USER_INFO;
/** 移动端获取用户信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_ALL;
///@}
/**
* \name CGI接口相关参数类型定义
*/
/** 必填的字符串类型参数 */
typedef NSString *TCRequiredStr;
/** 必填的UIImage类型参数 */
typedef UIImage *TCRequiredImage;
/** 必填的整型参数 */
typedef NSInteger TCRequiredInt;
/** 必填的数字类型 */
typedef NSNumber *TCRequiredNumber;
/** 必填的NSData参数 */
typedef NSData *TCRequiredData;
/** 可选的字符串类型参数 */
typedef NSString *TCOptionalStr;
/** 可选的UIImage类型参数 */
typedef UIImage *TCOptionalImage;
/** 可选的整型参数 */
typedef NSInteger TCOptionalInt;
/** 可选的数字类型 */
typedef NSNumber *TCOptionalNumber;
/** 可选的不定类型参数 */
typedef id TCRequiredId;
///@}
/**
* \brief CGI请求的参数字典封装辅助基类
*
* 将相应属性的值以key-value的形式保存到参数字典中
*/
@interface TCAPIRequest : NSMutableDictionary
/** CGI请求的URL地址 */
@property (nonatomic, readonly) NSURL *apiURL;
/** CGI请求方式"GET""POST" */
@property (nonatomic, readonly) NSString *method;
/**
* API参数中的保留字段可以塞入任意字典支持的类型再调用完成后会带回给调用方
*/
@property (nonatomic, strong) TCRequiredId paramUserData;
/**
* APIResponse,API的返回结果
*/
@property (nonatomic, readonly) APIResponse *response;
/** 取消相应的CGI请求任务 */
- (void)cancel;
@end
@protocol TCAPIRequestDelegate <NSObject>
@optional
- (void)cgiRequest:(TCAPIRequest *)request didResponse:(APIResponse *)response;
@end

View File

@@ -0,0 +1,555 @@
///
/// \file TencentOAuth.h
/// \brief QQ互联开放平台授权登录及相关开放接口实现类
///
/// Created by Tencent on 12-12-21.
/// Copyright (c) 2012年 Tencent. All rights reserved.
///
#import <UIKit/UIKit.h>
#import "SDKDef.h"
@protocol TencentSessionDelegate;
@protocol TencentLoginDelegate;
@protocol TencentApiInterfaceDelegate;
@protocol TencentWebViewDelegate;
@class TencentApiReq;
@class TencentApiResp;
typedef NS_ENUM(NSUInteger, TencentAuthorizeState) {
kTencentNotAuthorizeState,
kTencentSSOAuthorizeState,
kTencentWebviewAuthorzieState,
};
typedef NS_ENUM(NSUInteger, TencentAuthMode) {
kAuthModeClientSideToken,
kAuthModeServerSideCode,
};
#pragma mark - TencentOAuth(授权登录及相关开放接口调用)
/**
* \brief TencentOpenAPI授权登录及相关开放接口调用
*
* TencentOAuth实现授权登录逻辑以及相关开放接口的请求调用
*/
@interface TencentOAuth : NSObject {
NSMutableDictionary *_apiRequests;
}
/** Access Token凭证用于后续访问各开放接口 */
@property(nonatomic, copy) NSString *accessToken;
/** Access Token的失效期 */
@property(nonatomic, strong) NSDate *expirationDate;
/** 已实现的开放接口的回调委托对象 */
@property(nonatomic, weak) id<TencentSessionDelegate> sessionDelegate;
/** 第三方应用在开发过程中设置的URLSchema用于浏览器登录后后跳到第三方应用 */
@property(nonatomic, copy) NSString *localAppId;
/** 用户授权登录后对该用户的唯一标识 */
@property(nonatomic, copy) NSString *openId;
/** 用户登录成功过后的跳转页面地址 */
@property(nonatomic, copy) NSString *redirectURI;
/** 第三方应用在互联开放平台申请的appID */
@property(nonatomic, copy) NSString *appId;
/** 第三方应用在互联开放平台注册的UniversalLink */
@property(nonatomic, copy) NSString *universalLink;
/** 主要是互娱的游戏设置uin */
@property(nonatomic, copy) NSString *uin;
/** 主要是互娱的游戏设置鉴定票据 */
@property(nonatomic, copy) NSString *skey;
/** 登陆透传的数据 */
@property(nonatomic, copy) NSDictionary *passData;
/** 授权方式(Client Side Token或者Server Side Code) */
@property(nonatomic, assign) TencentAuthMode authMode;
/** union id */
@property(nonatomic, copy) NSString *unionid;
/** 第三方在授权登录/分享 时选择 QQ还是TIM 。在授权前一定要指定其中一个类型*/
@property(nonatomic, assign) TencentAuthShareType authShareType;
/** SDK打开web登录页支持自动填充账号 */
@property (nonatomic, copy) NSString *defaultUin;
/**
* 获取上次登录得到的token
*
**/
- (NSString *)getCachedToken;
/**
* 获取上次登录得到的openid
*
**/
- (NSString *)getCachedOpenID;
/**
* 获取上次登录的token过期日期
*
**/
- (NSDate *)getCachedExpirationDate;
/**
* 上次登录的token是否过期(本地判断)
**/
- (BOOL)isCachedTokenValid;
/**
* 删除上次登录登录的token信息
*
**/
- (BOOL)deleteCachedToken;
/**
* 删除openid
*
**/
- (void)deleteOpenId;
/**
* 用来获得当前sdk的版本号
* \return 返回sdk版本号
**/
+ (NSString *)sdkVersion;
/**
* 用来获得当前sdk的小版本号
* \return 返回sdk小版本号
**/
+ (NSString *)sdkSubVersion;
/**
* 用来获得当前sdk的是否精简版
* @return 返回YES表示精简版
**/
+ (BOOL)isLiteSDK;
/**
* 主要是用来帮助判断是否有登陆被发起,但是还没有过返回结果
* \return
* kTencentNotAuthorizeState:无授权
* kTencentSSOAuthorizeState:有人发起了sso授权但无返回
* kTencentWebviewAuthorzieState:有人发起了webview授权还未返回
**/
+ (TencentAuthorizeState *)authorizeState;
/**
* 获取TencentOAuth单例
*/
+ (instancetype)sharedInstance;
/**
* 设置SDK参数
*
* @param appId 不可为nil第三方应用在互联开放平台申请的唯一标识
* @param enableUniveralLink 默认为NO第三方应用是否将sdk和手机QQ的交互方式切换为UniversalLink方式启用后则在iOS9及以上的系统都会生效UniversalLink方式否则默认仅在iOS13及以上的系统生效UniversalLink方式。
* @param universalLink 可以为nil第三方应用在互联开放平台注册的UniversalLink和bundleID一一对应当为nil时互联平台会按规则生成UniversalLink详见官网说明
* @param delegate 不可为nil第三方应用用于接收请求返回结果的委托对象
*
* @note
* 使用说明】
* 1、支持sdk与手Q的交互切换为UniversalLink模式主要目的"是为了避免手Q的UrlScheme被其他应用抢注后导致sdk接口功能受到影响"。
* 2 、由于手Q版本在 >=8.1.3 后才适配了UniversalLink所以一旦开启了enabled开关“务必做到”及时知会用户升级手Q版本。
*
*/
- (void)setupAppId:(NSString *)appId
enableUniveralLink:(BOOL)enableUniveralLink
universalLink:(NSString *)universalLink
delegate:(id<TencentSessionDelegate>)delegate;
/**
* 初始化TencentOAuth对象
* 注意3.5.17版本开始,内部单例实现,多次调用返回同一实例
*
* @param appId 不可为nil第三方应用在互联开放平台申请的唯一标识
* @param delegate 不可为nil第三方应用用于接收请求返回结果的委托对象
* @return 初始化后的授权登录对象
*
* 推荐使用初始化方法并且适配UniversalLink
* - (instancetype)initWithAppId:(NSString *)appId
* enableUniveralLink:(BOOL)enabled
* universalLink:(NSString *)universalLink
* delegate:(id<TencentSessionDelegate>)delegate;
*/
- (instancetype)initWithAppId:(NSString *)appId
andDelegate:(id<TencentSessionDelegate>)delegate __attribute__((deprecated("此接口即将下线请使用setupAppId:enableUniveralLink:universalLink:delegate")));
/**
* 初始化TencentOAuth对象>=3.3.7
* 注意3.5.17版本开始,内部单例实现,多次调用返回同一实例
*
* @param appId 不可为nil第三方应用在互联开放平台申请的唯一标识
* @param universalLink 可以为nil第三方应用在互联开放平台注册的UniversalLink和bundleID一一对应当为nil时互联平台会按规则生成universallink详见官网说明
* @param delegate 不可为nil第三方应用用于接收请求返回结果的委托对象
* @return 初始化后的授权登录对象
*
* 【使用说明】
* 1、支持BundleId与UniversalLink的一一对应主要目的"是为了解决应用的iPhone版本和iPad HD版本共用同一个AppId导致同时安装情况下的跳转问题"。
* 2、由于手Q版本在 >=8.1.8 后才支持了这种对应方式所以一旦使用“务必做到”及时知会用户升级手Q版本。
*
* 推荐使用初始化方法并且适配UniversalLink
* - (instancetype)initWithAppId:(NSString *)appId
* enableUniveralLink:(BOOL)enabled
* universalLink:(NSString *)universalLink
* delegate:(id<TencentSessionDelegate>)delegate;
*
*/
- (instancetype)initWithAppId:(NSString *)appId
andUniversalLink:(NSString *)universalLink
andDelegate:(id<TencentSessionDelegate>)delegate __attribute__((deprecated("此接口即将下线请使用setupAppId:enableUniveralLink:universalLink:delegate")));
/**
* 初始化TencentOAuth对象>=3.3.8
* 注意3.5.17版本开始,内部单例实现,多次调用返回同一实例
*
* @param appId 不可为nil第三方应用在互联开放平台申请的唯一标识
* @param enabled 默认为NO第三方应用是否将sdk和手机QQ的交互方式切换为UniversalLink方式启用后则在iOS9及以上的系统都会生效UniversalLink方式否则默认仅在iOS13及以上的系统生效UniversalLink方式。
* @param universalLink 可以为nil第三方应用在互联开放平台注册的UniversalLink和bundleID一一对应当为nil时互联平台会按规则生成UniversalLink详见官网说明
* @param delegate 不可为nil第三方应用用于接收请求返回结果的委托对象
* @return 初始化后的授权登录对象
*
* @note
* 使用说明】
* 1、支持sdk与手Q的交互切换为UniversalLink模式主要目的"是为了避免手Q的UrlScheme被其他应用抢注后导致sdk接口功能受到影响"。
* 2 、由于手Q版本在 >=8.1.3 后才适配了UniversalLink所以一旦开启了enabled开关“务必做到”及时知会用户升级手Q版本。
*
*/
- (instancetype)initWithAppId:(NSString *)appId
enableUniveralLink:(BOOL)enabled
universalLink:(NSString *)universalLink
delegate:(id<TencentSessionDelegate>)delegate __attribute__((deprecated("此接口即将下线请使用setupAppId:enableUniveralLink:universalLink:delegate")));
/**
* 设置用户是否已经授权同意授权隐私协议在主体应用中用户同意授权隐私协议后再初始化互联SDK默认未同意授权
* 注意如未同意授权隐私协议则互联SDK的所有功能都无法使用包括初始化
* 从3.5.7版本开始支持该方法
*
* @param isAgreedAuthorization 是否已经授权isAgreedAuthorization=YES, 表示已经同意授权isAgreedAuthorization=NO表示未同意授权互联SDK的所有功能都无法使用
*/
+ (void)setIsUserAgreedAuthorization:(BOOL)isUserAgreedAuthorization;
/**
* 获取当前用户是否已经同意授权隐私协议
* 从3.5.7版本开始支持该方法
*/
+ (BOOL)isUserAgreedAuthorization;
/**
* 判断用户手机上是否安装手机QQ
* @return YES:安装 NO:没安装
*
* @note SDK目前已经支持QQ、TIM授权登录及分享功能 会按照QQ>TIM的顺序进行调用。
* 只要用户安装了QQ、TIM中任意一个应用都可为第三方应用进行授权登录、分享功能。
* 第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。
*/
+ (BOOL)iphoneQQInstalled;
/**
* 判断用户手机上是否安装手机TIM
* @return YES:安装 NO:没安装
*
* @note SDK目前已经支持QQ、TIM授权登录及分享功能 会按照QQ>TIM的顺序进行调用。
* 只要用户安装了QQ、TIM中任意一个应用都可为第三方应用进行授权登录、分享功能。
* 第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。
*/
+ (BOOL)iphoneTIMInstalled;
/**
* 发起授权登录如果安装了QQ APP则拉起QQ发起授权登录如果为安装QQ APP则在第三方应用进入H5页面输入账密授权登录。
*
* @param permissions 授权信息列
* @return 发起调用是否成功
*/
- (BOOL)authorize:(NSArray *)permissions;
/**
* 发起授权登录, 在第三方应用进入H5页面显示二维码通过扫码完成授权登录
*
* @param permissions 授权信息列
* @return 发起调用结果
*/
- (BOOL)authorizeWithQRlogin:(NSArray *)permissions;
/**
* 增量授权,因用户没有授予相应接口调用的权限,需要用户确认是否授权
* @param permissions 需增量授权的信息列表
* @return 发起增量授权调用是否成功
*/
- (BOOL)incrAuthWithPermissions:(NSArray *)permissions;
/**
* 重新授权因token废除或失效导致接口调用失败需用户重新授权
* @param permissions 授权信息列表,同登录授权
* @return 重新授权调用是否成功
*/
- (BOOL)reauthorizeWithPermissions:(NSArray *)permissions;
/**
* 获取UnindID,可以根据UnindID的比较来确定OpenID是否属于同一个用户
* 通过代理方法:- (void)didGetUnionID;返回请求结果。
*
* @return NO未登录信息不足YES条件满足发送请求成功请等待回调
*/
- (BOOL)RequestUnionId;
/**
* (静态方法)处理应用拉起协议
* @param url 处理被其他应用呼起时的逻辑
* @return 处理结果YES表示成功NO表示失败
*/
+ (BOOL)HandleOpenURL:(NSURL *)url;
/**
* (静态方法)sdk是否可以处理应用拉起协议
* @param url 处理被其他应用呼起时的逻辑
* @return 处理结果YES表示可以 NO表示不行
*/
+ (BOOL)CanHandleOpenURL:(NSURL *)url;
/**
* (静态方法)处理应用的UniversalLink拉起协议
* @param url 处理被其他应用呼起时的逻辑
* @return 处理结果YES表示成功NO表示失败
*/
+ (BOOL)HandleUniversalLink:(NSURL *)url;
/**
* (静态方法)sdk是否可以处理应用的Universallink拉起协议
* @param url 处理被其他应用呼起时的逻辑(应用的Universallink链接须满足官网注册时的格式要求)
* @return 处理结果YES表示可以 NO表示不行
* 注在调用其他Universallink相关处理接口之前均需进行此项判断
*/
+ (BOOL)CanHandleUniversalLink:(NSURL *)url;
/**
* (静态方法)获取TencentOAuth调用的上一次错误信息
*/
+ (NSString *)getLastErrorMsg;
/**
* 以Server Side Code模式授权登录时通过此接口获取返回的code值;
* 以Client Side Token模式授权登录时忽略此接口。
*/
- (NSString *)getServerSideCode;
/**
* 退出登录(退出登录后TecentOAuth失效需要重新初始化)
* @param delegate 第三方应用用于接收请求返回结果的委托对象
*/
- (void)logout:(id<TencentSessionDelegate>)delegate;
/**
* 判断登录态是否有效
* @return 处理结果YES表示有效NO表示无效请用户重新登录授权
*/
- (BOOL)isSessionValid;
/**
* 获取用户个人信息
* @return 处理结果YES表示API调用成功NO表示API调用失败登录态失败重新登录
*/
- (BOOL)getUserInfo;
/**
* 退出指定API调用
* @param userData 用户调用某条API的时候传入的保留参数
* @return 处理结果YES表示成功 NO表示失败
*/
- (BOOL)cancel:(id)userData;
/**
* CGI类任务创建接口
* @param apiURL CGI请求的URL地址
* @param method CGI请求方式"GET""POST"
* @param params CGI请求参数字典
* @param callback CGI请求结果的回调接口对象
* @return CGI请求任务实例用于取消任务返回nil代表任务创建失败
*/
- (TCAPIRequest *)cgiRequestWithURL:(NSURL *)apiURL method:(NSString *)method params:(NSDictionary *)params callback:(id<TCAPIRequestDelegate>)callback;
/**
* TencentOpenApi发送任务统一接口
* @param request 请求发送的任务
* @param callback 任务发送后的回调地址
*/
- (BOOL)sendAPIRequest:(TCAPIRequest *)request callback:(id<TCAPIRequestDelegate>)callback;
/**
* 获得用户的openId,仅有内存缓存
* @return 返回openId
*/
- (NSString *)getUserOpenID;
/**
* 获取appSignToken
* @return 返回appSignToken
*/
+ (NSString *)getAppSignToken;
/**
* 设置appSignToken跨进程的应用可以通过该方法手动设置appSignToken
*/
+ (void)setupAppSignToken:(NSString *)appSignToken;
@end
#pragma mark - TencentLoginDelegate(授权登录回调协议)
/**
* \brief TencentLoginDelegate iOS Open SDK 1.3 API回调协议
*
* 第三方应用实现登录的回调协议
*/
@protocol TencentLoginDelegate <NSObject>
@required
/**
* 登录成功后的回调
*/
- (void)tencentDidLogin;
/**
* 登录失败后的回调
* \param cancelled 代表用户是否主动退出登录
*/
- (void)tencentDidNotLogin:(BOOL)cancelled;
/**
* 登录时网络有问题的回调
*/
- (void)tencentDidNotNetWork;
@optional
/**
* 登录时权限信息的获得
*/
- (NSArray *)getAuthorizedPermissions:(NSArray *)permissions withExtraParams:(NSDictionary *)extraParams __attribute__((deprecated("该接口已过期, 建议删除调用")));
/**
* unionID获得
*/
- (void)didGetUnionID;
/**
* 强制网页登录,包括账号密码登录和二维码登录
* return YES时就算本地有手Q也会打开web界面
*/
- (BOOL)forceWebLogin;
/* 获得appSignToken回调 */
- (void)tencentDidGetAppSignToken:(NSString *)appSignToken;
@end
#pragma mark - TencentSessionDelegate(开放接口回调协议)
/**
* \brief TencentSessionDelegate iOS Open SDK 1.3 API回调协议
*
* 第三方应用需要实现每条需要调用的API的回调协议
*/
@protocol TencentSessionDelegate <NSObject, TencentLoginDelegate, TencentWebViewDelegate>
@optional
/**
* 退出登录的回调
*/
- (void)tencentDidLogout;
/**
* 因用户未授予相应权限而需要执行增量授权。在用户调用某个api接口时如果服务器返回操作未被授权则触发该回调协议接口由第三方决定是否跳转到增量授权页面让用户重新授权。
* @param tencentOAuth 登录授权对象。
* @param permissions 需增量授权的权限列表。
* @return 是否仍然回调返回原始的api请求结果。
* @note 不实现该协议接口则默认为不开启增量授权流程。若需要增量授权请调用\ref TencentOAuth#incrAuthWithPermissions: \n注意增量授权时用户可能会修改登录的帐号
*/
- (BOOL)tencentNeedPerformIncrAuth:(TencentOAuth *)tencentOAuth withPermissions:(NSArray *)permissions;
/**
* [该逻辑未实现]因token失效而需要执行重新登录授权。在用户调用某个api接口时如果服务器返回token失效则触发该回调协议接口由第三方决定是否跳转到登录授权页面让用户重新授权。
* @param tencentOAuth 登录授权对象。
* @return 是否仍然回调返回原始的api请求结果。
* @note 不实现该协议接口则默认为不开启重新登录授权流程。若需要重新登录授权请调用\ref TencentOAuth#reauthorizeWithPermissions: \n注意重新登录授权时用户可能会修改登录的帐号
*/
- (BOOL)tencentNeedPerformReAuth:(TencentOAuth *)tencentOAuth;
/**
* 用户通过增量授权流程重新授权登录token及有效期限等信息已被更新。
* @param tencentOAuth token及有效期限等信息更新后的授权实例对象
* @note 第三方应用需更新已保存的token及有效期限等信息。
*/
- (void)tencentDidUpdate:(TencentOAuth *)tencentOAuth;
/**
* 用户增量授权过程中因取消或网络问题导致授权失败
* @param reason 授权失败原因具体失败原因参见sdkdef.h文件中\ref UpdateFailType
*/
- (void)tencentFailedUpdate:(UpdateFailType)reason;
/**
* 获取用户个人信息回调
* @param response API返回结果具体定义参见sdkdef.h文件中\ref APIResponse
* @remarks 正确返回示例: \snippet example/getUserInfoResponse.exp success
* 错误返回示例: \snippet example/getUserInfoResponse.exp fail
*/
- (void)getUserInfoResponse:(APIResponse*) response;
/**
* 社交API统一回调接口
* @param response API返回结果具体定义参见sdkdef.h文件中\ref APIResponse
* @param message 响应的消息目前支持SendStory,AppInvitationAppChallengeAppGiftRequest
*/
- (void)responseDidReceived:(APIResponse*)response forMessage:(NSString *)message;
/**
* post请求的上传进度
* @param tencentOAuth 返回回调的tencentOAuth对象
* @param bytesWritten 本次回调上传的数据字节数
* @param totalBytesWritten 总共已经上传的字节数
* @param totalBytesExpectedToWrite 总共需要上传的字节数
* @param userData 用户自定义数据
*/
- (void)tencentOAuth:(TencentOAuth *)tencentOAuth didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite userData:(id)userData;
/**
* 通知第三方界面需要被关闭
* @param tencentOAuth 返回回调的tencentOAuth对象
* @param viewController 需要关闭的viewController
*/
- (void)tencentOAuth:(TencentOAuth *)tencentOAuth doCloseViewController:(UIViewController *)viewController;
@end
#pragma mark - TencentWebViewDelegate(H5登录webview旋转方向回调)
/**
* @brief TencentWebViewDelegate: H5登录webview旋转方向回调协议
*
* 第三方应用可以根据自己APP的旋转方向限制通过此协议设置
*/
@protocol TencentWebViewDelegate <NSObject>
@optional
- (BOOL) tencentWebViewShouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation;
- (NSUInteger) tencentWebViewSupportedInterfaceOrientationsWithWebkit;
- (BOOL) tencentWebViewShouldAutorotateWithWebkit;
@end

View File

@@ -0,0 +1,24 @@
//
// TencentOpenApiUmbrellaHeader.h
// TencentOpenApi_IOS
//
// Created by jyukeizhang(张储祺) on 2020/7/27.
// Copyright © 2020 Tencent. All rights reserved.
//
#ifndef TencentOpenApiUmbrellaHeader_h
#define TencentOpenApiUmbrellaHeader_h
#import <Foundation/Foundation.h>
FOUNDATION_EXPORT double StaticLibraryModuleVersionNumber;
FOUNDATION_EXPORT const unsigned char StaticLibraryModuleVersionString[];
#import "QQApiInterface.h"
#import "QQApiInterfaceObject.h"
#import "SDKDef.h"
#import "TencentOAuth.h"
#endif /* TencentOpenApiUmbrellaHeader_h */

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyCollectedDataTypes</key>
<array>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeUserID</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<false/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>用户同意授权后仅用于UserID授权第三方应用登录。</string>
</array>
</dict>
</array>
<key>NSPrivacyTrackingDomains</key>
<array/>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>用于存储第三方授权登录结果数据,便于应用能快速访问授权登录结果数据</string>
</array>
</dict>
</array>
<key>NSPrivacyTracking</key>
<false/>
</dict>
</plist>

View File

@@ -0,0 +1,4 @@
module TencentOpenApi{
umbrella header "TencentOpenApiUmbrellaHeader.h"
export *
}

View File

@@ -0,0 +1,220 @@
///
/// \file QQApiInterface.h
/// \brief QQApi接口简化封装
///
/// Created by Tencent on 12-5-15.
/// Copyright (c) 2012年 Tencent. All rights reserved.
///
#import <Foundation/Foundation.h>
#import "QQApiInterfaceObject.h"
typedef void (^sendResultBlock)(NSDictionary *result);
// 发送消息回调是否发送成功
typedef void(^QQApiInterfaceSendMessageResultBlock)(QQApiSendResultCode sendResultCode, NSString *message);
/**
\brief 处理来至QQ的请求及响应的回调协议
*/
@protocol QQApiInterfaceDelegate <NSObject>
/**
处理来至QQ的请求
*/
- (void)onReq:(QQBaseReq *)req;
/**
处理来至QQ的响应
*/
- (void)onResp:(QQBaseResp *)resp;
/**
处理QQ在线状态的回调
*/
- (void)isOnlineResponse:(NSDictionary *)response;
@end
/**
\brief 对QQApi的简单封装类
*/
@interface QQApiInterface : NSObject
/**
处理由手Q唤起的普通跳转请求
\param url 待处理的url跳转请求
\param delegate 第三方应用用于处理来至QQ请求及响应的委托对象
\return 跳转请求处理结果YES表示成功处理NO表示不支持的请求协议或处理失败
*/
+ (BOOL)handleOpenURL:(NSURL *)url delegate:(id<QQApiInterfaceDelegate>)delegate;
/**
处理由手Q唤起的universallink跳转请求
\param universallink 待处理的universallink跳转请求
\param delegate 第三方应用用于处理来至QQ请求及响应的委托对象
\return 跳转请求处理结果YES表示成功处理NO表示不支持的请求协议或处理失败
*/
+ (BOOL)handleOpenUniversallink:(NSURL*)universallink delegate:(id<QQApiInterfaceDelegate>)delegate;
/**
向手Q发起分享请求
\param req 分享内容的请求
\return 请求发送结果码
*/
+ (QQApiSendResultCode)sendReq:(QQBaseReq *)req;
/**
向手Q QZone结合版发起分享请求
\note H5分享只支持单张网络图片的传递
\param req 分享内容的请求
\return 请求发送结果码
*/
+ (QQApiSendResultCode)SendReqToQZone:(QQBaseReq *)req;
/**
向手Q发起设置QQ头像
\param req 分享内容的请求
\return 请求发送结果码
*/
+ (QQApiSendResultCode)sendMessageToQQAvatarWithReq:(QQBaseReq*)req;
+ (QQApiSendResultCode)sendMessageToQQAuthWithReq:(QQBaseReq*)req;
/**
向手Q发起绑群请求
\param req 请求的内容
\param resultBlock 请求回调
*/
+ (void)sendThirdAppBindGroupReq:(QQBaseReq *)req resultBlock:(sendResultBlock)resultBlock;
/**
向手Q发起加群请求
\param req 请求的内容
\param resultBlock 请求回调
*/
+ (void)sendThirdAppJoinGroupReq:(QQBaseReq *)req resultBlock:(sendResultBlock)resultBlock;
/**
向手Q发起解绑群请求
\param req 请求的内容
\param resultBlock 请求回调
*/
+ (void)sendThirdAppUnBindGroupReq:(QQBaseReq *)req resultBlock:(sendResultBlock)resultBlock;
/**
向手Q发起创建QQ频道的请求
\param req 请求的内容
\param resultBlock 回调发送结果
\return void
*/
+ (void)sendMessageToCreateQQGroupProWithMessageRequest:(SendMessageToQQReq *)messageRequest sendResultBlock:(QQApiInterfaceSendMessageResultBlock)sendResultBlock;
/**
向手Q发起加入QQ频道的请求
\param req 请求的内容
\param resultBlock 回调发送结果
\return void
*/
+ (void)sendMessageToJoinQQGroupProWithMessageRequest:(SendMessageToQQReq *)messageRequest sendResultBlock:(QQApiInterfaceSendMessageResultBlock)sendResultBlock;
/**
向手Q发起查询QQ频道openID的请求
\param req 请求的内容
\param resultBlock 请求回调
*/
+ (void)sendQueryQQGroupProInfo:(QQBaseReq *)req resultBlock:(sendResultBlock)resultBlock;
/**
向手Q发起组图分享到表情收藏
\param req 分享内容的请求
\return 请求发送结果码
*/
+ (QQApiSendResultCode)sendMessageToFaceCollectionWithReq:(QQBaseReq*)req;
/**
检测是否已安装QQ
\return 如果QQ已安装则返回YES否则返回NO
\note SDK目前已经支持QQ、TIM授权登录及分享功能 会按照QQ>TIM的顺序进行调用。
只要用户安装了QQ、TIM中任意一个应用都可为第三方应用进行授权登录、分享功能。
第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。
*/
+ (BOOL)isQQInstalled;
/**
检测是否已安装TIM
\return 如果TIM已安装则返回YES否则返回NO
\note SDK目前已经支持QQ、TIM授权登录及分享功能 会按照QQ>TIM的顺序进行调用。
只要用户安装了QQ、TIM中任意一个应用都可为第三方应用进行授权登录、分享功能。
第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。
*/
+ (BOOL)isTIMInstalled;
/**
检测QQ是否支持API调用
\return 如果当前安装QQ版本支持API调用则返回YES否则返回NO
*/
+ (BOOL)isQQSupportApi;
/**
检测TIM是否支持API调用
\return 如果当前安装TIM版本支持API调用则返回YES否则返回NO
*/
+ (BOOL)isTIMSupportApi __attribute__((deprecated("已过期, 建议删除调用调用地方用YES替代。")));
/**
检测是否支持分享
\return 如果当前已安装QQ且QQ版本支持API调用 或者 当前已安装TIM且TIM版本支持API调用则返回YES否则返回NO
*/
+ (BOOL)isSupportShareToQQ;
/**
检测是否支持分享到QQ结合版QZone
\return 如果当前已安装QQ且QQ版本支持API调用则返回YES否则返回NO
*/
+ (BOOL)isSupportPushToQZone;
/**
获取QQ下载地址
如果App通过<code>QQApiInterface#isQQInstalled</code>和<code>QQApiInterface#isQQSupportApi</code>检测发现QQ没安装或当前版本QQ不支持API调用可引导用户通过打开此链接下载最新版QQ。
\return iPhoneQQ下载地址
*/
+ (NSString *)getQQInstallUrl;
/**
获取TIM下载地址
如果App通过<code>QQApiInterface#isTIMInstalled</code>检测发现TIM没安装或当前版本TIM不支持API调用可引导用户通过打开此链接下载最新版TIM。
\return iPhoneTIM下载地址
*/
+ (NSString *)getTIMInstallUrl;
#pragma mark - Log
/*! @brief 调用此函数可以导出QQSDK的Log到第三方中用于定位问题
注意1:SDK会强引用这个block,注意不要导致内存泄漏,注意不要导致内存泄漏
注意2:调用过一次startLog by block之后如果再调用一次任意方式的startLoad,会释放上一次logBlock不再回调上一个logBlock
*
* @param logBlock 打印log的回调block
*/
+ (void)startLogWithBlock:(QQApiLogBolock)logBlock;
///停止回调打印
+ (void)stopLog;
///设置打印日志到文件开关on/off如果不设置默认不打印到文件
+ (void)setSwitchPrintLogToFile:(BOOL)on;
///日志文件目录
+ (NSString *)getLogFilePath;
@end

View File

@@ -0,0 +1,754 @@
///
/// \file QQApiInterfaceObject.h
/// \brief QQApiInterface所依赖的请求及应答消息对象封装帮助类
///
/// Created by Tencent on 12-5-15.
/// Copyright (c) 2012年 Tencent. All rights reserved.
///
#ifndef QQApiInterface_QQAPIOBJECT_h
#define QQApiInterface_QQAPIOBJECT_h
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, QQApiSendResultCode) {
EQQAPISENDSUCESS = 0,
EQQAPIQQNOTINSTALLED = 1, // QQ未安装
EQQAPIQQNOTSUPPORTAPI = 2, // QQ api不支持
EQQAPIMESSAGETYPEINVALID = 3,
EQQAPIMESSAGECONTENTNULL = 4,
EQQAPIMESSAGECONTENTINVALID = 5,
EQQAPIAPPNOTREGISTED = 6,
EQQAPIAPPSHAREASYNC = 7,
EQQAPIQQNOTSUPPORTAPI_WITH_ERRORSHOW = 8, // QQ api不支持 && SDK显示error提示已废弃
EQQAPIMESSAGEARKCONTENTNULL = 9, // ark内容为空
EQQAPIMESSAGE_MINI_CONTENTNULL = 10, // 小程序参数为空
EQQAPISENDFAILD = -1, // 发送失败
EQQAPISHAREDESTUNKNOWN = -2, // 未指定分享到QQ或TIM
EQQAPITIMSENDFAILD = -3, // 发送失败
EQQAPITIMNOTINSTALLED = 11, // TIM未安装
EQQAPITIMNOTSUPPORTAPI = 12, // TIM api不支持
EQQAPI_INCOMING_PARAM_ERROR = 13, // 外部传参错误
EQQAPI_THIRD_APP_GROUP_ERROR_APP_NOT_AUTHORIZIED = 14, // APP未获得授权
EQQAPI_THIRD_APP_GROUP_ERROR_CGI_FAILED = 15, // CGI请求失败
EQQAPI_THIRD_APP_GROUP_ERROR_HAS_BINDED = 16, // 该组织已经绑定群聊
EQQAPI_THIRD_APP_GROUP_ERROR_NOT_BINDED = 17, // 该组织尚未绑定群聊
EQQAPI_THIRD_APP_GROUP_ERROR_HAS_UNBINDED = 18, // 该组织已经解绑群聊
EQQAPI_IMAGE_SIZE_OUT_OF_BOUNND = 19, // 图片大小超过限制图片不能超过5M缩略图不能超过1M
EQQAPI_TITLE_LENGTH_OUT_OF_BOUNND = 20, // 标题长度超过限制不能超过128
EQQAPI_TITLE_NIL_ERROR = 21, // 标题不能为空
EQQAPI_DESC_LENGTH_OUT_OF_BOUNND = 22, // 描述信息长度超过限制不能超过512
EQQAPI_URL_LENGTH_OUT_OF_BOUNND = 23, // URL参数长度超过限制不能超过1024
EQQAPI_URL_NIL_ERROR = 24, // URL参数不能为空
EQQAPIQZONENOTSUPPORTTEXT = 10000, // qzone分享不支持text类型分享
EQQAPIQZONENOTSUPPORTIMAGE = 10001, // qzone分享不支持image类型分享
EQQAPIVERSIONNEEDUPDATE = 10002, // 当前QQ版本太低需要更新至新版本才可以支持
ETIMAPIVERSIONNEEDUPDATE = 10004, // 当前TIM版本太低需要更新至新版本才可以支持
EAPPURLTYPESILLEGALITY = 20000, // (>=3.3.8)第三方APP的info.plist中UrlTypes字段存在QQ的UrlScheme
EQQAPI_ERROR_USER_NOT_AGREED_AUTHORIZATION = 30001, // 用户未同意隐私协议,用户同意隐私协议后,需要设置[TencentOAuth setIsUserAgreedAuthorization:YES];
};
#pragma mark - QQApiObject(分享对象类型)
// QQApiObject control flags
typedef NS_ENUM(NSUInteger,kQQAPICtrlFlag) {
kQQAPICtrlFlagQZoneShareOnStart = 0x01,
kQQAPICtrlFlagQZoneShareForbid = 0x02, //屏蔽好友选择器上的空间入口
kQQAPICtrlFlagQQShare = 0x04,
kQQAPICtrlFlagQQShareFavorites = 0x08, //收藏
kQQAPICtrlFlagQQShareDataline = 0x10, //数据线
kQQAPICtrlFlagQQShareEnableArk = 0x20, //支持ARK
kQQAPICtrlFlagQQShareEnableMiniProgram = 0x40, //支持小程序
};
// 分享到QQ或TIM
typedef NS_ENUM(NSUInteger, ShareDestType) {
ShareDestTypeQQ = 0,
ShareDestTypeTIM,
};
//小程序的类型
typedef NS_ENUM(NSUInteger, MiniProgramType) {
MiniProgramType_Develop = 0, // 开发版
MiniProgramType_Test = 1, // 测试版
MiniProgramType_Online = 3, // 正式版,默认
MiniProgramType_Preview = 4, // 预览版
};
/// 打印回调的block
typedef void(^QQApiLogBolock)(NSString *logStr);
// QQApiObject
/** \brief 所有在QQ及插件间发送的数据对象的根类。
*/
__attribute__((visibility("default"))) @interface QQApiObject : NSObject
@property (nonatomic, copy) NSString *title; ///< 标题最长128个字符
@property (nonatomic, copy) NSString *description; ///<简要描述最长512个字符
@property (nonatomic, copy) NSString *universalLink; ///(>=3.3.7)支持第三方传入在互联开放平台注册的universallink
@property (nonatomic, assign) uint64_t cflag;
//353新增两个字断给游戏侧使用对齐微信sdk
@property (nonatomic, copy) NSString *tagName;
@property (nonatomic, copy) NSString *messageExt;
/*
* 分享到QQ/TIM
* SDK根据是否安装对应客户端进行判断判断顺序QQ > TIM
* 默认分享到QQ如果QQ未安装检测TIM是否安装
*/
@property (nonatomic, assign) ShareDestType shareDestType;
/**
* 检查参数是否完整有效
* @return 检查参数结果
*/
- (QQApiSendResultCode)checkParamValid;
@end
// ArkObject
/** \brief 支持Ark的根类。
*/
__attribute__((visibility("default"))) @interface ArkObject : NSObject
@property (nonatomic, copy) NSString *arkData; ///< 显示Ark所需的数据json串长度暂不限制
@property (nonatomic, strong) QQApiObject* qqApiObject; ///<原有老版本的QQApiObject
- (id)initWithData:(NSString *)arkData qqApiObject:(QQApiObject*)qqApiObject;
+ (id)objectWithData:(NSString *)arkData qqApiObject:(QQApiObject*)qqApiObject;
@end
#pragma mark QQ小程序
//分享小程序消息 - QQ 8.0.8
__attribute__((visibility("default"))) @interface QQApiMiniProgramObject : NSObject
@property (nonatomic, strong) QQApiObject* qqApiObject; //原有老版本的QQApiObject
@property (nonatomic, copy) NSString *miniAppID; //必填小程序的AppId必须在QQ互联平台中将该小程序与分享的App绑定
@property (nonatomic, copy) NSString *miniPath; //必填,小程序的展示路径
@property (nonatomic, copy) NSString *webpageUrl; //必填,兼容低版本的网页链接
@property (nonatomic, assign) MiniProgramType miniprogramType; //非必填,小程序的类型,默认正式版(3),可选测试版(1)、预览版(4)
@end
//唤起小程序 - QQ 8.1.8
__attribute__((visibility("default"))) @interface QQApiLaunchMiniProgramObject : QQApiObject
@property (nonatomic, copy) NSString *miniAppID; //必填小程序的AppId必须在QQ互联平台中将该小程序与分享的App绑定
@property (nonatomic, copy) NSString *miniPath; //小程序的展示路径,不填展示默认小程序首页
@property (nonatomic, assign) MiniProgramType miniprogramType; //非必填,小程序的类型,默认正式版(3),可选测试版(1)、开发版(0)
@end
//小程序唤起第三方 - SDK 3.3.9
__attribute__((visibility("default"))) @interface QQApiMiniProgramLaunchObject : QQApiObject
@property (nonatomic, copy) NSString *appParameter; //小程序带来的数据,透传
+ (instancetype)newWithAppParameter:(NSString *)parameter;
@end
// QQApiResultObject
/** \brief 用于请求回应的数据类型。
<h3>可能错误码及描述如下:</h3>
<TABLE>
<TR><TD>error</TD><TD>errorDescription</TD><TD>注释</TD></TR>
<TR><TD>0</TD><TD>nil</TD><TD>成功</TD></TR>
<TR><TD>-1</TD><TD>param error</TD><TD>参数错误</TD></TR>
<TR><TD>-2</TD><TD>group code is invalid</TD><TD>该群不在自己的群列表里面</TD></TR>
<TR><TD>-3</TD><TD>upload photo failed</TD><TD>上传图片失败</TD></TR>
<TR><TD>-4</TD><TD>user give up the current operation</TD><TD>用户放弃当前操作</TD></TR>
<TR><TD>-5</TD><TD>client internal error</TD><TD>客户端内部处理错误</TD></TR>
</TABLE>
*/
__attribute__((visibility("default"))) @interface QQApiResultObject : QQApiObject
@property (nonatomic, copy) NSString *error; ///<错误
@property (nonatomic, copy) NSString *errorDescription; ///<错误描述
@property (nonatomic, copy) NSString *extendInfo; ///<扩展信息
@property (nonatomic, copy) NSDictionary *otherInfo; ///<其他扩展信息
@end
// QQApiTextObject
/** \brief 文本对象
*/
@interface QQApiTextObject : QQApiObject
@property (nonatomic, copy)NSString *text; ///<文本内容必填最长1536个字符
- (id)initWithText:(NSString *)text; ///<初始化方法
+ (id)objectWithText:(NSString *)text;///<工厂方法获取一个QQApiTextObject对象.
@end
// QQApiURLObject
typedef NS_ENUM(NSUInteger, QQApiURLTargetType) {
QQApiURLTargetTypeNotSpecified = 0x00,
QQApiURLTargetTypeAudio = 0x01,
QQApiURLTargetTypeVideo = 0x02,
QQApiURLTargetTypeNews = 0x03
};
/** @brief URL对象类型。
包括URL地址URL地址所指向的目标类型及预览图像。
*/
__attribute__((visibility("default"))) @interface QQApiURLObject : QQApiObject
/**
URL地址所指向的目标类型.
@note 参见QQApi.h 中的 QQApiURLTargetType 定义.
*/
@property (nonatomic)QQApiURLTargetType targetContentType;
@property (nonatomic, strong) NSURL *url; ///<URL地址,必填最长512个字符
@property (nonatomic, copy) NSData *previewImageData;///<预览图像数据最大1M字节
@property (nonatomic, strong) NSURL *previewImageURL; ///<预览图像URL **预览图像数据与预览图像URL可二选一
/**
初始化方法
*/
- (id)initWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageData:(NSData *)data targetContentType:(QQApiURLTargetType)targetContentType;
- (id)initWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageURL:(NSURL *)previewURL targetContentType:(QQApiURLTargetType)targetContentType;
/**
工厂方法,获取一个QQApiURLObject对象
*/
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageData:(NSData *)data targetContentType:(QQApiURLTargetType)targetContentType;
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageURL:(NSURL *)previewURL targetContentType:(QQApiURLTargetType)targetContentType;
@end
// QQApiExtendObject
/** @brief 扩展数据类型
*/
@interface QQApiExtendObject : QQApiObject
@property (nonatomic, copy) NSData *data;///<具体数据内容必填最大5M字节
@property (nonatomic, copy) NSData *previewImageData;///<预览图像最大1M字节
@property (nonatomic, copy) NSArray *imageDataArray;///图片数组(多图暂只支持分享到手机QQ收藏功能)
/**
初始化方法
@param data 数据内容
@param previewImageData 用于预览的图片
@param title 标题
@param description 此对象,分享的描述
*/
- (id)initWithData:(NSData *)data previewImageData:(NSData *)previewImageData title:(NSString *)title description:(NSString *)description;
/**
初始化方法
@param data 数据内容
@param title 标题
@param description 此对象,分享的描述
@param imageDataArray 发送的多张图片队列
*/
- (id)initWithData:(NSData *)data previewImageData:(NSData *)previewImageData title:(NSString *)title description:(NSString *)description imageDataArray:(NSArray *)imageDataArray;
/**
helper方法获取一个autorelease的<code>QQApiExtendObject</code>对象
@param data 数据内容
@param previewImageData 用于预览的图片
@param title 标题
@param description 此对象,分享的描述
@return
一个自动释放的<code>QQApiExtendObject</code>实例
*/
+ (id)objectWithData:(NSData *)data previewImageData:(NSData *)previewImageData title:(NSString *)title description:(NSString *)description;
/**
helper方法获取一个autorelease的<code>QQApiExtendObject</code>对象
@param data 数据内容
@param previewImageData 用于预览的图片
@param title 标题
@param description 此对象,分享的描述
@param imageDataArray 发送的多张图片队列
@return
一个自动释放的<code>QQApiExtendObject</code>实例
*/
+ (id)objectWithData:(NSData *)data previewImageData:(NSData *)previewImageData title:(NSString *)title description:(NSString *)description imageDataArray:(NSArray *)imageDataArray;
@end
// QQApiImageObject
/** @brief 图片对象
用于分享图片内容的对象,是一个指定为图片类型的<code>QQApiExtendObject</code>
*/
@interface QQApiImageObject : QQApiExtendObject
@end
// QQApiImageForQQAvatarObject
/** @brief 图片对象
用于设置QQ头像内容的对象是一个指定为图片类型的<code>QQApiExtendObject</code>
*/
@interface QQApiImageForQQAvatarObject : QQApiExtendObject
@end
/**
* @brief 视频对象
* 用于设置动态头像
* assetURL可传ALAsset的ALAssetPropertyAssetURL或者PHAsset的localIdentifier
从手Q返回的错误码
//第三方设置动态头像结果
@"ret=0"//设置成功
@"ret=-10&error_des=user cancel"//用户取消设置
@"ret=-11&error_des=pasteboard have no video data"//剪切板没有数据
@"ret=-12&error_des=export data failed"//从剪切板导出数据到本地失败
@"ret=-13&error_des=url param invalid"//sdk传递过来的数据有误
@"ret=-14&error_des=video param invalid"//视频的参数不符合要求检测第三方视频源方案1、分辨率跟480*480保持一致2、视频长度0.5s8s
@"ret=-15&error_des=app authorised failed"//应用鉴权失败
@"ret=-16&error_des=upload video failed"//设置头像,上传到后台失败
@"ret=-17&error_des=account diff"//账号不一致
*/
@interface QQApiVideoForQQAvatarObject : QQApiExtendObject
@property (nonatomic, copy) NSString *assetURL;
@end
//QQApiAuthObject 用于拉起手Q的授权详情页
@interface QQApiAuthObject : QQApiObject
@end
// QQApiImageArrayForFaceCollectionObject
/** @brief 图片数组对象
用于分享图片组到表情收藏,是一个指定为图片类型的<code>QQApiObject</code>
*/
@interface QQApiImageArrayForFaceCollectionObject : QQApiObject
@property (nonatomic, copy) NSArray *imageDataArray;///图片数组
/**
初始化方法
@param imageDataArray 图片数组
*/
- (id)initWithImageArrayData:(NSArray *)imageDataArray;
/**
helper方法获取一个autorelease的<code>QQApiObject</code>对象
@param imageDataArray 发送的多张图片队列
@return
一个自动释放的<code>QQApiObject</code>实例
*/
+ (id)objectWithimageDataArray:(NSArray *)imageDataArray;
@end
// QQApiImageArrayForQZoneObject
/** @brief 图片对象
用于分享图片到空间,走写说说路径,是一个指定为图片类型的,当图片数组为空时,默认走文本写说说<code>QQApiObject</code>
*/
@interface QQApiImageArrayForQZoneObject : QQApiObject
@property (nonatomic, copy) NSArray *imageDataArray;///图片数组
@property (nonatomic, copy) NSDictionary *extMap; // 扩展字段
/**
初始化方法
@param imageDataArray 图片数组
@param title 写说说的内容,可以为空
@param extMap 扩展字段
*/
- (id)initWithImageArrayData:(NSArray *)imageDataArray title:(NSString *)title extMap:(NSDictionary *)extMap;
/**
helper方法获取一个autorelease的<code>QQApiExtendObject</code>对象
@param title 写说说的内容,可以为空
@param imageDataArray 发送的多张图片队列
@param extMap 扩展字段
@return
一个自动释放的<code>QQApiExtendObject</code>实例
*/
+ (id)objectWithimageDataArray:(NSArray *)imageDataArray title:(NSString *)title extMap:(NSDictionary *)extMap;
@end
// QQApiVideoForQZoneObject
/** @brief 视频对象
用于分享视频到空间,走写说说路径<code>QQApiObject</code>,assetURL和videoData两个参数必须设置至少一个参数如果assetURL设置了忽略videoData参数
@param assetURL可传ALAsset的ALAssetPropertyAssetURL或者PHAsset的localIdentifier
@param extMap 扩展字段
@param videoData 视频数据大小不超过50M
*/
@interface QQApiVideoForQZoneObject : QQApiObject
@property (nonatomic, copy) NSString *assetURL;
@property (nonatomic, copy) NSDictionary *extMap; // 扩展字段
@property (nonatomic, copy) NSData *videoData;
- (id)initWithAssetURL:(NSString *)assetURL title:(NSString *)title extMap:(NSDictionary *)extMap;
+ (id)objectWithAssetURL:(NSString *)assetURL title:(NSString *)title extMap:(NSDictionary *)extMap;
- (id)initWithVideoData:(NSData *)videoData title:(NSString *)title extMap:(NSDictionary *)extMap;
+ (id)objectWithVideoData:(NSData *)videoData title:(NSString *)title extMap:(NSDictionary *)extMap;
@end
// QQApiWebImageObject
/** @brief 图片对象
用于分享网络图片内容的对象是一个指定网络图片url的: 该类型只在2.9.0的h5分享中才支持
原有的手q分享是不支持该类型的。
*/
@interface QQApiWebImageObject : QQApiObject
@property (nonatomic, strong) NSURL *previewImageURL; ///<预览图像URL
/**
初始化方法
@param previewImageURL 用于预览的图片
@param title 标题
@param description 此对象,分享的描述
*/
- (id)initWithPreviewImageURL:(NSURL *)previewImageURL title:(NSString *)title description:(NSString *)description;
/**
helper方法获取一个autorelease的<code>QQApiWebImageObject</code>对象
@param previewImageURL 用于预览的图片
@param title 标题
@param description 此对象,分享的描述
*/
+ (id)objectWithPreviewImageURL:(NSURL *)previewImageURL title:(NSString *)title description:(NSString *)description;
@end
//QQApiFileObject
/** @brief 本地文件对象(暂只支持分享到手机QQ数据线功能)
用于分享文件内容的对象,是一个指定为文件类型的<code>QQApiExtendObject</code>
*/
@interface QQApiFileObject : QQApiExtendObject {
NSString *_fileName;
}
@property (nonatomic, copy)NSString *fileName;
@end
// QQApiAudioObject
/** @brief 音频URL对象
用于分享目标内容为音频的URL的对象
*/
@interface QQApiAudioObject : QQApiURLObject
@property (nonatomic, strong) NSURL *flashURL; ///<音频URL地址最长512个字符
/**
获取一个autorelease的<code>QQApiAudioObject</code>
@param url 音频内容的目标URL
@param title 分享内容的标题
@param description 分享内容的描述
@param data 分享内容的预览图像
@note 如果url为空调用<code>QQApi#sendMessage:</code>时将返回FALSE
*/
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageData:(NSData *)data;
/**
获取一个autorelease的<code>QQApiAudioObject</code>
@param url 音频内容的目标URL
@param title 分享内容的标题
@param description 分享内容的描述
@param previewURL 分享内容的预览图像URL
@note 如果url为空调用<code>QQApi#sendMessage:</code>时将返回FALSE
*/
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageURL:(NSURL *)previewURL;
@end
// QQApiVideoObject
/** @brief 视频URL对象
用于分享目标内容为视频的URL的对象
QQApiVideoObject类型的分享目前在Android和PC QQ上接收消息时展现有待完善待手机QQ版本以后更新支持
目前如果要分享视频,推荐使用 QQApiNewsObject 类型
*/
@interface QQApiVideoObject : QQApiURLObject
@property (nonatomic, strong) NSURL *flashURL; ///<视频URL地址最长512个字符
/**
获取一个autorelease的<code>QQApiVideoObject</code>
@param url 视频内容的目标URL
@param title 分享内容的标题
@param description 分享内容的描述
@param data 分享内容的预览图像
@note 如果url为空调用<code>QQApi#sendMessage:</code>时将返回FALSE
*/
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageData:(NSData *)data;
/**
获取一个autorelease的<code>QQApiVideoObject</code>
@param url 视频内容的目标URL
@param title 分享内容的标题
@param description 分享内容的描述
@param previewURL 分享内容的预览图像URL
@note 如果url为空调用<code>QQApi#sendMessage:</code>时将返回FALSE
*/
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageURL:(NSURL *)previewURL;
@end
// QQApiNewsObject
/** @brief 新闻URL对象
用于分享目标内容为新闻的URL的对象
*/
@interface QQApiNewsObject : QQApiURLObject
/**
获取一个autorelease的<code>QQApiNewsObject</code>
@param url 视频内容的目标URL
@param title 分享内容的标题
@param description 分享内容的描述
@param data 分享内容的预览图像
@note 如果url为空调用<code>QQApi#sendMessage:</code>时将返回FALSE
*/
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageData:(NSData *)data;
/**
获取一个autorelease的<code>QQApiNewsObject</code>
@param url 视频内容的目标URL
@param title 分享内容的标题
@param description 分享内容的描述
@param previewURL 分享内容的预览图像URL
@note 如果url为空调用<code>QQApi#sendMessage:</code>时将返回FALSE
*/
+ (id)objectWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description previewImageURL:(NSURL *)previewURL;
@end
// QQApiCommonContentObject;
/** @brief 通用模板类型对象
用于分享一个固定显示模板的图文混排对象
@note 图片列表和文本列表不能同时为空
*/
@interface QQApiCommonContentObject : QQApiObject
/**
预定义的界面布局类型
*/
@property (nonatomic,assign) unsigned int layoutType;
@property (nonatomic, copy) NSData *previewImageData;///<预览图
@property (nonatomic, copy) NSArray *textArray;///<文本列表
@property (nonatomic, copy) NSArray *pictureDataArray;///<图片列表
+ (id)objectWithLayoutType:(int)layoutType textArray:(NSArray *)textArray pictureArray:(NSArray *)pictureArray previewImageData:(NSData *)data;
/**
将一个NSDictionary对象转化为QQApiCommomContentObject如果无法转换则返回空
*/
+ (id)objectWithDictionary:(NSDictionary *)dic;
- (NSDictionary *)toDictionary;
@end
// QQApiExtraServiceObject; 通用业务消息处理类旧版后续使用QQApiCommonServiceObject
/**
@brief OpenSDK扩展支持的服务通用接口后续会扩充能力
@param serviceID [必选] 扩展支持的服务类型ID参考官方文档说明
@param openID [必选] 授权登录后对该用户的唯一标识
@param toUin [可选] 对方的QQ号码
@param extraInfo [可选] 扩展字段
@note 该接口的使用须先登录
*/
@interface QQApiExtraServiceObject : QQApiObject
@property (nonatomic, copy) NSString *serviceID;
@property (nonatomic, copy) NSString *openID;
@property (nonatomic, copy) NSString *toUin;
@property (nonatomic, copy) NSDictionary *extraInfo;
- (id)initWithOpenID:(NSString *)openID serviceID:(NSString *)serviceID;
+ (id)objecWithOpenID:(NSString *)openID serviceID:(NSString *)serviceID;
@end
/**
* QQApiCommonServiceObject; 通用业务消息处理类可以适用所有的需要通过互联SDK发消息给手Q的业务去处理。
* 使用前需要申请serviceID每个业务功能有个对应的serviceID
*/
@interface QQApiCommonServiceObject : QQApiObject
// [必选] 授权登录后对该用户的唯一标识
@property (nonatomic, copy) NSString *openID;
// [必选] 扩展支持的服务类型ID参考官方文档说明
@property (nonatomic, copy) NSString *serviceID;
// [可选] 扩展字段,由调用方跟具体的业务方协定具体的字段
@property (nonatomic, copy) NSDictionary *extendInfo;
- (instancetype)initWithOpenID:(NSString *)openID
serviceID:(NSString *)serviceID
extendInfo:(NSDictionary *)extendInfo;
+ (instancetype)objecWithOpenID:(NSString *)openID
serviceID:(NSString *)serviceID
extendInfo:(NSDictionary *)extendInfo;
@end
#pragma mark - QQApi请求消息类型
/**
QQApi请求消息类型
*/
typedef NS_ENUM(NSUInteger, QQApiInterfaceReqType) {
EGETMESSAGEFROMQQREQTYPE = 0, /// < 手Q -> 第三方应用请求第三方应用向手Q发送消息
ESENDMESSAGETOQQREQTYPE = 1, /// < 第三方应用 -> 手Q第三方应用向手Q分享消息
ESHOWMESSAGEFROMQQREQTYPE = 2, /// < 手Q -> 第三方应用,请求第三方应用展现消息中的数据
ESENDMESSAGEARKTOQQREQTYPE = 3, /// < 第三方应用 -> 手Q第三方应用向手Q分享Ark消息
ESENDMESSAGE_MINI_TOQQREQTYPE = 4 /// < 第三方应用 -> 手Q第三方应用向手Q分享小程序消息
};
/**
QQApi应答消息类型
*/
typedef NS_ENUM(NSUInteger, QQApiInterfaceRespType) {
ESHOWMESSAGEFROMQQRESPTYPE = 0, /// < 第三方应用 -> 手Q第三方应用应答消息展现结果
EGETMESSAGEFROMQQRESPTYPE = 1, /// < 第三方应用 -> 手Q第三方应用回应发往手Q的消息
ESENDMESSAGETOQQRESPTYPE = 2 /// < 手Q -> 第三方应用手Q应答处理分享消息的结果
};
/**
QQApi请求消息基类
*/
@interface QQBaseReq : NSObject
/** 请求消息类型,参见\ref QQApiInterfaceReqType */
@property (nonatomic, assign) int type;
@end
/**
QQApi应答消息基类
*/
@interface QQBaseResp : NSObject
/** 请求处理结果 */
@property (nonatomic, copy) NSString *result;
/** 具体错误描述信息 */
@property (nonatomic, copy) NSString *errorDescription;
/** 应答消息类型,参见\ref QQApiInterfaceRespType */
@property (nonatomic, assign) int type;
/** 扩展信息 */
@property (nonatomic, copy) NSString *extendInfo;
@end
/**
GetMessageFromQQReq请求帮助类
*/
@interface GetMessageFromQQReq : QQBaseReq
/**
创建一个GetMessageFromQQReq请求实例
*/
+ (GetMessageFromQQReq *)req;
@end
@interface SendMessageToQQReq : QQBaseReq
/**
创建一个SendMessageToQQReq请求实例
\param message 具体分享消息实例
\return 新创建的SendMessageToQQReq请求实例
*/
+ (SendMessageToQQReq *)reqWithContent:(QQApiObject *)message;
/**
创建一个支持Ark的SendMessageToQQReq请求实例
\param message 具体分享消息实例
\return 新创建的SendMessageToQQReq请求实例
*/
+ (SendMessageToQQReq *)reqWithArkContent:(ArkObject *)message;
/**
* 创建一个支持小程序的消息请求实例
* @param miniMessage 小程序实例对象
* @return 消息请求实例
*/
+ (SendMessageToQQReq *)reqWithMiniContent:(QQApiMiniProgramObject *)miniMessage;
/** 具体分享消息 */
@property (nonatomic, strong) QQApiObject *apiObject;
/** 支持Ark的具体分享消息 */
@property (nonatomic, strong) ArkObject *arkObject;
/** 支持小程序的具体分享消息 */
@property (nonatomic, strong) QQApiMiniProgramObject *miniProgramObject;
@end
/**
SendMessageToQQResp应答帮助类
*/
@interface SendMessageToQQResp : QQBaseResp
/** 其他扩展信息 */
@property (nonatomic, copy) NSDictionary *otherInfo;
/**
创建一个SendMessageToQQResp应答实例
\param result 请求处理结果
\param errDesp 具体错误描述信息
\param extendInfo 扩展信息
\return 新创建的SendMessageToQQResp应答实例
*/
+ (SendMessageToQQResp *)respWithResult:(NSString *)result errorDescription:(NSString *)errDesp extendInfo:(NSString *)extendInfo;
+ (SendMessageToQQResp *) respWithResult:(NSString *)result errorDescription:(NSString *)errDesp extendInfo:(NSString *)extendInfo otherInfo:(NSDictionary *)otherInfo;
@end
/**
ShowMessageFromQQReq请求帮助类
*/
@interface ShowMessageFromQQReq : QQBaseReq
/**
创建一个ShowMessageFromQQReq请求实例
\param message 具体待展现消息实例
\return 新创建的ShowMessageFromQQReq请求实例
*/
+ (ShowMessageFromQQReq *)reqWithContent:(QQApiObject *)message;
/** 具体待展现消息 */
@property (nonatomic, strong) QQApiObject *message;
@end
#pragma mark --一键加群&建群&解绑群
// QQApiThirdAppBindGroupObject
/** \brief 第三方app绑定群
*/
@interface QQApiThirdAppBindGroupObject : QQApiObject
@property (nonatomic, copy) NSString *accessToken;
@property (nonatomic, copy) NSString *payToken;
@property (nonatomic, copy) NSString *pfkey;
@property (nonatomic, copy) NSString *unionID;
@property (nonatomic, copy) NSString *appDisplayName;
- (id)initWithAccessToken:(NSString *)accessToken payToken:(NSString *)payToken pfkey:(NSString *)pfkey unionID:(NSString *)unionID appDisplayName:(NSString *)appDisplayName; ///<初始化方法
+ (id)objectWithAccessToken:(NSString *)accessToken payToken:(NSString *)payToken pfkey:(NSString *)pfkey unionID:(NSString *)unionID appDisplayName:(NSString *)appDisplayName; ///<工厂方法获取一个QQApiThirdAppBindGroupObject对象.
@end
// QQApiThirdAppJoinGroupObject
/** \brief 第三方app加入群
*/
@interface QQApiThirdAppJoinGroupObject : QQApiObject
@property (nonatomic, copy) NSString *accessToken;
@property (nonatomic, copy) NSString *payToken;
@property (nonatomic, copy) NSString *pfkey;
@property (nonatomic, copy) NSString *unionID;
- (id)initWithAccessToken:(NSString *)accessToken payToken:(NSString *)payToken pfkey:(NSString *)pfkey unionID:(NSString *)unionID; ///<初始化方法
+ (id)objectWithAccessToken:(NSString *)accessToken payToken:(NSString *)payToken pfkey:(NSString *)pfkey unionID:(NSString *)unionID; ///<工厂方法获取一个QQApiThirdAppJoinGroupObject对象.
@end
// QQApiThirdAppUnBindGroupObject
/** \brief 第三方app解绑群
*/
@interface QQApiThirdAppUnBindGroupObject : QQApiObject
@property (nonatomic, copy) NSString *accessToken;
@property (nonatomic, copy) NSString *openId;
@property (nonatomic, copy) NSString *payToken;
@property (nonatomic, copy) NSString *pfkey;
@property (nonatomic, copy) NSString *unionID;
- (id)initWithAccessToken:(NSString *)accessToken payToken:(NSString *)payToken pfkey:(NSString *)pfkey unionID:(NSString *)unionID openId:(NSString *)openId appId:(NSString *)appId; ///<初始化方法
+ (id)objectWithAccessToken:(NSString *)accessToken payToken:(NSString *)payToken pfkey:(NSString *)pfkey unionID:(NSString *)unionID openId:(NSString *)openId appId:(NSString *)appId; ///<工厂方法获取一个QQApiThirdAppBindGroupObject对象.
@end
#endif

View File

@@ -0,0 +1,420 @@
///
/// \file sdkdef.h
/// \brief SDK中相关常量定义
///
/// Created by Tencent on 12-12-25.
/// Copyright (c) 2012年 Tencent. All rights reserved.
///
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
/**
* \brief 设置sdk的log等级
*/
typedef enum {
TCOLogLevel_Disabled = -1, // 关闭所有log
TCOLogLevel_Error = 0,
TCOLogLevel_Warning,
TCOLogLevel_Info,
TCOLogLevel_Debug,
} TCOLogLevel;
/**
* \breif 授权/分享 方式
*/
typedef enum TencentAuthShareType {
AuthShareType_QQ,
AuthShareType_TIM,
}TencentAuthShareType;
/**
* \brief APIResponse.retCode可能的枚举常量
*/
typedef enum
{
URLREQUEST_SUCCEED = 0, /**< 网络请求成功发送至服务器,并且服务器返回数据格式正确
* \note 这里包括所请求业务操作失败的情况,例如没有授权等原因导致
*/
URLREQUEST_FAILED = 1, /**< 网络异常,或服务器返回的数据格式不正确导致无法解析 */
} REPONSE_RESULT;
/**
* \brief 增量授权失败原因
*
* \note 增量授权失败不影响原token的有效性原token已失效的情况除外
*/
typedef enum
{
kUpdateFailUnknown = 1, ///< 未知原因
kUpdateFailUserCancel, ///< 用户取消
kUpdateFailNetwork, ///< 网络问题
} UpdateFailType;
/**
* \brief 封装服务器返回的结果
*
* APIResponse用于封装所有请求的返回结果包括错误码、错误信息、原始返回数据以及返回数据的json格式字典
*/
@interface APIResponse : NSObject<NSSecureCoding> {
int _detailRetCode;
int _retCode;
int _seq;
NSString *_errorMsg;
NSDictionary *_jsonResponse;
NSString *_message;
id _userData;
}
/**
* 新增的详细错误码\n
* detailRetCode主要用于区分不同的错误情况参见\ref OpenSDKError
*/
@property (nonatomic, assign) int detailRetCode;
/**
* 网络请求是否成功送达服务器,以及服务器返回的数据格式是否正确\n
* retCode具体取值可参考\ref REPONSE_RESULT
*/
@property (nonatomic, assign) int retCode;
/**
* 网络请求对应的递增序列号,方便内部管理
*/
@property (nonatomic, assign) int seq;
/**
* 错误提示语
*/
@property (nonatomic, copy) NSString *errorMsg;
/**
* 服务器返回数据的json格式字典\n
* 字典内具体参数的命名和含义请参考\ref api_spec
*/
@property (nonatomic, copy) NSDictionary *jsonResponse;
/**
* 服务器返回的原始数据字符串
*/
@property (nonatomic, copy) NSString *message;
/**
* 用户保留数据
*/
@property (nonatomic, strong) id userData;
@end
/**
* 用户自定义的保留字段
*/
FOUNDATION_EXTERN NSString * const PARAM_USER_DATA;
/**
* \name 应用邀请参数字段定义
*/
///@{
/** 应用邀请展示图片url的key */
FOUNDATION_EXTERN NSString * const PARAM_APP_ICON;
/** 应用邀请描述文本的key */
FOUNDATION_EXTERN NSString * const PARAM_APP_DESC;
/** 应用邀请好友列表的key */
FOUNDATION_EXTERN NSString * const PARAM_APP_INVITED_OPENIDS;
///@}
/**
* \name sendStory新分享参数字段定义
*/
///@{
/** 预填入接受人列表的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_RECEIVER;
/** 分享feeds标题的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_TITLE;
/** 分享feeds评论内容的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_COMMENT;
/** 分享feeds摘要的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_SUMMARY;
/** 分享feeds展示图片url的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_IMAGE;
/** 分享feeds跳转链接url的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_URL;
/** 分享feeds点击操作默认行为的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_ACT;
///@}
/**
* \name 设置头像参数字段定义
*/
///@{
/** 头像图片数据的key */
FOUNDATION_EXTERN NSString * const PARAM_SETUSERHEAD_PIC;
/** 头像图片文件名的key */
FOUNDATION_EXTERN NSString * const PARAM_SETUSERHEAD_FILENAME;
///@}
/**
* \name 服务器返回数据的参数字段定义
*/
///@{
/** 服务器返回码的key */
FOUNDATION_EXTERN NSString * const PARAM_RETCODE;
/** 服务器返回错误信息的key */
FOUNDATION_EXTERN NSString * const PARAM_MESSAGE;
/** 服务器返回额外数据的key */
FOUNDATION_EXTERN NSString * const PARAM_DATA;
///@}
/**
* \name 错误信息相关常量定义
*/
///@{
/** 详细错误信息字典中额外信息的key */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorKeyExtraInfo;
/** 详细错误信息字典中返回码的key */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorKeyRetCode;
/** 详细错误信息字典中错误语句的key */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorKeyMsg;
/** 不支持的接口 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUnsupportedAPI;
/** 操作成功 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgSuccess;
/** 未知错误 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUnknown;
/** 用户取消 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUserCancel;
/** 请重新登录 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgReLogin;
/** 应用没有操作权限 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgOperationDeny;
/** 网络异常或没有网络 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgNetwork;
/** URL格式或协议错误 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgURL;
/** 解析数据出错 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgDataParse;
/** 传入参数有误 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgParam;
/** 连接超时 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgTimeout;
/** 安全问题 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgSecurity;
/** 文件读写错误 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgIO;
/** 服务器端错误 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgServer;
/** 页面错误 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgWebPage;
/** 设置头像图片过大 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUserHeadPicLarge;
/** 用户未同意授权隐私协议 */
FOUNDATION_EXPORT NSString * const TCOpenSDKErrorMsgUserNotAgreedAuthorization;
///@}
/**
* \brief SDK新增详细错误常量
*/
typedef enum
{
kOpenSDKInvalid = -1, ///< 无效的错误码
kOpenSDKErrorUnsupportedAPI = -2, ///< 不支持的接口
/**
* \name CommonErrorCode
* 公共错误码
*/
///@{
kOpenSDKErrorSuccess = 0, ///< 成功
kOpenSDKErrorUnknown, ///< 未知错误
kOpenSDKErrorUserCancel, ///< 用户取消
kOpenSDKErrorReLogin, ///< token无效或用户未授权相应权限需要重新登录
kOpenSDKErrorOperationDeny, ///< 第三方应用没有该api操作的权限
///@}
/**
* \name NetworkRelatedErrorCode
* 网络相关错误码
*/
///@{
kOpenSDKErrorNetwork, ///< 网络错误,网络不通或连接不到服务器
kOpenSDKErrorURL, ///< URL格式或协议错误
kOpenSDKErrorDataParse, ///< 数据解析错误,服务器返回的数据解析出错
kOpenSDKErrorParam, ///< 传入参数错误
kOpenSDKErrorConnTimeout, ///< http连接超时
kOpenSDKErrorSecurity, ///< 安全问题
kOpenSDKErrorIO, ///< 下载和文件IO错误
kOpenSDKErrorServer, ///< 服务器端错误
///@}
/**
* \name WebViewRelatedError
* webview特有错误
*/
///@{
kOpenSDKErrorWebPage, ///< 页面错误
///@}
/**
* \name SetUserHeadRelatedErrorCode
* 设置头像自定义错误码段
*/
///@{
kOpenSDKErrorUserHeadPicLarge = 0x010000, ///< 图片过大 设置头像自定义错误码
///@}
} OpenSDKError;
/**
* \name SDK版本(v1.3)支持的授权列表常量
*/
///@{
/** 发表一条说说到QQ空间(<b>需要申请权限</b>) */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_ADD_TOPIC;
/** 创建一个QQ空间相册(<b>需要申请权限</b>) */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_ADD_ALBUM;
/** 上传一张照片到QQ空间相册(<b>需要申请权限</b>) */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_UPLOAD_PIC;
/** 获取用户QQ空间相册列表(<b>需要申请权限</b>) */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_LIST_ALBUM;
/** 验证是否认证空间粉丝 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_CHECK_PAGE_FANS;
/** 获取登录用户自己的详细信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_INFO;
/** 获取其他用户的详细信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_OTHER_INFO;
/** 获取会员用户基本信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_VIP_INFO;
/** 获取会员用户详细信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_VIP_RICH_INFO;
/** 获取用户信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_USER_INFO;
/** 移动端获取用户信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_SIMPLE_USER_INFO;
/** 移动端获取用户信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_ALL;
///@}
/**
* \name CGI接口相关参数类型定义
*/
/** 必填的字符串类型参数 */
typedef NSString *TCRequiredStr;
/** 必填的UIImage类型参数 */
typedef UIImage *TCRequiredImage;
/** 必填的整型参数 */
typedef NSInteger TCRequiredInt;
/** 必填的数字类型 */
typedef NSNumber *TCRequiredNumber;
/** 必填的NSData参数 */
typedef NSData *TCRequiredData;
/** 可选的字符串类型参数 */
typedef NSString *TCOptionalStr;
/** 可选的UIImage类型参数 */
typedef UIImage *TCOptionalImage;
/** 可选的整型参数 */
typedef NSInteger TCOptionalInt;
/** 可选的数字类型 */
typedef NSNumber *TCOptionalNumber;
/** 可选的不定类型参数 */
typedef id TCRequiredId;
///@}
/**
* \brief CGI请求的参数字典封装辅助基类
*
* 将相应属性的值以key-value的形式保存到参数字典中
*/
@interface TCAPIRequest : NSMutableDictionary
/** CGI请求的URL地址 */
@property (nonatomic, readonly) NSURL *apiURL;
/** CGI请求方式"GET""POST" */
@property (nonatomic, readonly) NSString *method;
/**
* API参数中的保留字段可以塞入任意字典支持的类型再调用完成后会带回给调用方
*/
@property (nonatomic, strong) TCRequiredId paramUserData;
/**
* APIResponse,API的返回结果
*/
@property (nonatomic, readonly) APIResponse *response;
/** 取消相应的CGI请求任务 */
- (void)cancel;
@end
@protocol TCAPIRequestDelegate <NSObject>
@optional
- (void)cgiRequest:(TCAPIRequest *)request didResponse:(APIResponse *)response;
@end

View File

@@ -0,0 +1,555 @@
///
/// \file TencentOAuth.h
/// \brief QQ互联开放平台授权登录及相关开放接口实现类
///
/// Created by Tencent on 12-12-21.
/// Copyright (c) 2012年 Tencent. All rights reserved.
///
#import <UIKit/UIKit.h>
#import "SDKDef.h"
@protocol TencentSessionDelegate;
@protocol TencentLoginDelegate;
@protocol TencentApiInterfaceDelegate;
@protocol TencentWebViewDelegate;
@class TencentApiReq;
@class TencentApiResp;
typedef NS_ENUM(NSUInteger, TencentAuthorizeState) {
kTencentNotAuthorizeState,
kTencentSSOAuthorizeState,
kTencentWebviewAuthorzieState,
};
typedef NS_ENUM(NSUInteger, TencentAuthMode) {
kAuthModeClientSideToken,
kAuthModeServerSideCode,
};
#pragma mark - TencentOAuth(授权登录及相关开放接口调用)
/**
* \brief TencentOpenAPI授权登录及相关开放接口调用
*
* TencentOAuth实现授权登录逻辑以及相关开放接口的请求调用
*/
@interface TencentOAuth : NSObject {
NSMutableDictionary *_apiRequests;
}
/** Access Token凭证用于后续访问各开放接口 */
@property(nonatomic, copy) NSString *accessToken;
/** Access Token的失效期 */
@property(nonatomic, strong) NSDate *expirationDate;
/** 已实现的开放接口的回调委托对象 */
@property(nonatomic, weak) id<TencentSessionDelegate> sessionDelegate;
/** 第三方应用在开发过程中设置的URLSchema用于浏览器登录后后跳到第三方应用 */
@property(nonatomic, copy) NSString *localAppId;
/** 用户授权登录后对该用户的唯一标识 */
@property(nonatomic, copy) NSString *openId;
/** 用户登录成功过后的跳转页面地址 */
@property(nonatomic, copy) NSString *redirectURI;
/** 第三方应用在互联开放平台申请的appID */
@property(nonatomic, copy) NSString *appId;
/** 第三方应用在互联开放平台注册的UniversalLink */
@property(nonatomic, copy) NSString *universalLink;
/** 主要是互娱的游戏设置uin */
@property(nonatomic, copy) NSString *uin;
/** 主要是互娱的游戏设置鉴定票据 */
@property(nonatomic, copy) NSString *skey;
/** 登陆透传的数据 */
@property(nonatomic, copy) NSDictionary *passData;
/** 授权方式(Client Side Token或者Server Side Code) */
@property(nonatomic, assign) TencentAuthMode authMode;
/** union id */
@property(nonatomic, copy) NSString *unionid;
/** 第三方在授权登录/分享 时选择 QQ还是TIM 。在授权前一定要指定其中一个类型*/
@property(nonatomic, assign) TencentAuthShareType authShareType;
/** SDK打开web登录页支持自动填充账号 */
@property (nonatomic, copy) NSString *defaultUin;
/**
* 获取上次登录得到的token
*
**/
- (NSString *)getCachedToken;
/**
* 获取上次登录得到的openid
*
**/
- (NSString *)getCachedOpenID;
/**
* 获取上次登录的token过期日期
*
**/
- (NSDate *)getCachedExpirationDate;
/**
* 上次登录的token是否过期(本地判断)
**/
- (BOOL)isCachedTokenValid;
/**
* 删除上次登录登录的token信息
*
**/
- (BOOL)deleteCachedToken;
/**
* 删除openid
*
**/
- (void)deleteOpenId;
/**
* 用来获得当前sdk的版本号
* \return 返回sdk版本号
**/
+ (NSString *)sdkVersion;
/**
* 用来获得当前sdk的小版本号
* \return 返回sdk小版本号
**/
+ (NSString *)sdkSubVersion;
/**
* 用来获得当前sdk的是否精简版
* @return 返回YES表示精简版
**/
+ (BOOL)isLiteSDK;
/**
* 主要是用来帮助判断是否有登陆被发起,但是还没有过返回结果
* \return
* kTencentNotAuthorizeState:无授权
* kTencentSSOAuthorizeState:有人发起了sso授权但无返回
* kTencentWebviewAuthorzieState:有人发起了webview授权还未返回
**/
+ (TencentAuthorizeState *)authorizeState;
/**
* 获取TencentOAuth单例
*/
+ (instancetype)sharedInstance;
/**
* 设置SDK参数
*
* @param appId 不可为nil第三方应用在互联开放平台申请的唯一标识
* @param enableUniveralLink 默认为NO第三方应用是否将sdk和手机QQ的交互方式切换为UniversalLink方式启用后则在iOS9及以上的系统都会生效UniversalLink方式否则默认仅在iOS13及以上的系统生效UniversalLink方式。
* @param universalLink 可以为nil第三方应用在互联开放平台注册的UniversalLink和bundleID一一对应当为nil时互联平台会按规则生成UniversalLink详见官网说明
* @param delegate 不可为nil第三方应用用于接收请求返回结果的委托对象
*
* @note
* 使用说明】
* 1、支持sdk与手Q的交互切换为UniversalLink模式主要目的"是为了避免手Q的UrlScheme被其他应用抢注后导致sdk接口功能受到影响"。
* 2 、由于手Q版本在 >=8.1.3 后才适配了UniversalLink所以一旦开启了enabled开关“务必做到”及时知会用户升级手Q版本。
*
*/
- (void)setupAppId:(NSString *)appId
enableUniveralLink:(BOOL)enableUniveralLink
universalLink:(NSString *)universalLink
delegate:(id<TencentSessionDelegate>)delegate;
/**
* 初始化TencentOAuth对象
* 注意3.5.17版本开始,内部单例实现,多次调用返回同一实例
*
* @param appId 不可为nil第三方应用在互联开放平台申请的唯一标识
* @param delegate 不可为nil第三方应用用于接收请求返回结果的委托对象
* @return 初始化后的授权登录对象
*
* 推荐使用初始化方法并且适配UniversalLink
* - (instancetype)initWithAppId:(NSString *)appId
* enableUniveralLink:(BOOL)enabled
* universalLink:(NSString *)universalLink
* delegate:(id<TencentSessionDelegate>)delegate;
*/
- (instancetype)initWithAppId:(NSString *)appId
andDelegate:(id<TencentSessionDelegate>)delegate __attribute__((deprecated("此接口即将下线请使用setupAppId:enableUniveralLink:universalLink:delegate")));
/**
* 初始化TencentOAuth对象>=3.3.7
* 注意3.5.17版本开始,内部单例实现,多次调用返回同一实例
*
* @param appId 不可为nil第三方应用在互联开放平台申请的唯一标识
* @param universalLink 可以为nil第三方应用在互联开放平台注册的UniversalLink和bundleID一一对应当为nil时互联平台会按规则生成universallink详见官网说明
* @param delegate 不可为nil第三方应用用于接收请求返回结果的委托对象
* @return 初始化后的授权登录对象
*
* 【使用说明】
* 1、支持BundleId与UniversalLink的一一对应主要目的"是为了解决应用的iPhone版本和iPad HD版本共用同一个AppId导致同时安装情况下的跳转问题"。
* 2、由于手Q版本在 >=8.1.8 后才支持了这种对应方式所以一旦使用“务必做到”及时知会用户升级手Q版本。
*
* 推荐使用初始化方法并且适配UniversalLink
* - (instancetype)initWithAppId:(NSString *)appId
* enableUniveralLink:(BOOL)enabled
* universalLink:(NSString *)universalLink
* delegate:(id<TencentSessionDelegate>)delegate;
*
*/
- (instancetype)initWithAppId:(NSString *)appId
andUniversalLink:(NSString *)universalLink
andDelegate:(id<TencentSessionDelegate>)delegate __attribute__((deprecated("此接口即将下线请使用setupAppId:enableUniveralLink:universalLink:delegate")));
/**
* 初始化TencentOAuth对象>=3.3.8
* 注意3.5.17版本开始,内部单例实现,多次调用返回同一实例
*
* @param appId 不可为nil第三方应用在互联开放平台申请的唯一标识
* @param enabled 默认为NO第三方应用是否将sdk和手机QQ的交互方式切换为UniversalLink方式启用后则在iOS9及以上的系统都会生效UniversalLink方式否则默认仅在iOS13及以上的系统生效UniversalLink方式。
* @param universalLink 可以为nil第三方应用在互联开放平台注册的UniversalLink和bundleID一一对应当为nil时互联平台会按规则生成UniversalLink详见官网说明
* @param delegate 不可为nil第三方应用用于接收请求返回结果的委托对象
* @return 初始化后的授权登录对象
*
* @note
* 使用说明】
* 1、支持sdk与手Q的交互切换为UniversalLink模式主要目的"是为了避免手Q的UrlScheme被其他应用抢注后导致sdk接口功能受到影响"。
* 2 、由于手Q版本在 >=8.1.3 后才适配了UniversalLink所以一旦开启了enabled开关“务必做到”及时知会用户升级手Q版本。
*
*/
- (instancetype)initWithAppId:(NSString *)appId
enableUniveralLink:(BOOL)enabled
universalLink:(NSString *)universalLink
delegate:(id<TencentSessionDelegate>)delegate __attribute__((deprecated("此接口即将下线请使用setupAppId:enableUniveralLink:universalLink:delegate")));
/**
* 设置用户是否已经授权同意授权隐私协议在主体应用中用户同意授权隐私协议后再初始化互联SDK默认未同意授权
* 注意如未同意授权隐私协议则互联SDK的所有功能都无法使用包括初始化
* 从3.5.7版本开始支持该方法
*
* @param isAgreedAuthorization 是否已经授权isAgreedAuthorization=YES, 表示已经同意授权isAgreedAuthorization=NO表示未同意授权互联SDK的所有功能都无法使用
*/
+ (void)setIsUserAgreedAuthorization:(BOOL)isUserAgreedAuthorization;
/**
* 获取当前用户是否已经同意授权隐私协议
* 从3.5.7版本开始支持该方法
*/
+ (BOOL)isUserAgreedAuthorization;
/**
* 判断用户手机上是否安装手机QQ
* @return YES:安装 NO:没安装
*
* @note SDK目前已经支持QQ、TIM授权登录及分享功能 会按照QQ>TIM的顺序进行调用。
* 只要用户安装了QQ、TIM中任意一个应用都可为第三方应用进行授权登录、分享功能。
* 第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。
*/
+ (BOOL)iphoneQQInstalled;
/**
* 判断用户手机上是否安装手机TIM
* @return YES:安装 NO:没安装
*
* @note SDK目前已经支持QQ、TIM授权登录及分享功能 会按照QQ>TIM的顺序进行调用。
* 只要用户安装了QQ、TIM中任意一个应用都可为第三方应用进行授权登录、分享功能。
* 第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。
*/
+ (BOOL)iphoneTIMInstalled;
/**
* 发起授权登录如果安装了QQ APP则拉起QQ发起授权登录如果为安装QQ APP则在第三方应用进入H5页面输入账密授权登录。
*
* @param permissions 授权信息列
* @return 发起调用是否成功
*/
- (BOOL)authorize:(NSArray *)permissions;
/**
* 发起授权登录, 在第三方应用进入H5页面显示二维码通过扫码完成授权登录
*
* @param permissions 授权信息列
* @return 发起调用结果
*/
- (BOOL)authorizeWithQRlogin:(NSArray *)permissions;
/**
* 增量授权,因用户没有授予相应接口调用的权限,需要用户确认是否授权
* @param permissions 需增量授权的信息列表
* @return 发起增量授权调用是否成功
*/
- (BOOL)incrAuthWithPermissions:(NSArray *)permissions;
/**
* 重新授权因token废除或失效导致接口调用失败需用户重新授权
* @param permissions 授权信息列表,同登录授权
* @return 重新授权调用是否成功
*/
- (BOOL)reauthorizeWithPermissions:(NSArray *)permissions;
/**
* 获取UnindID,可以根据UnindID的比较来确定OpenID是否属于同一个用户
* 通过代理方法:- (void)didGetUnionID;返回请求结果。
*
* @return NO未登录信息不足YES条件满足发送请求成功请等待回调
*/
- (BOOL)RequestUnionId;
/**
* (静态方法)处理应用拉起协议
* @param url 处理被其他应用呼起时的逻辑
* @return 处理结果YES表示成功NO表示失败
*/
+ (BOOL)HandleOpenURL:(NSURL *)url;
/**
* (静态方法)sdk是否可以处理应用拉起协议
* @param url 处理被其他应用呼起时的逻辑
* @return 处理结果YES表示可以 NO表示不行
*/
+ (BOOL)CanHandleOpenURL:(NSURL *)url;
/**
* (静态方法)处理应用的UniversalLink拉起协议
* @param url 处理被其他应用呼起时的逻辑
* @return 处理结果YES表示成功NO表示失败
*/
+ (BOOL)HandleUniversalLink:(NSURL *)url;
/**
* (静态方法)sdk是否可以处理应用的Universallink拉起协议
* @param url 处理被其他应用呼起时的逻辑(应用的Universallink链接须满足官网注册时的格式要求)
* @return 处理结果YES表示可以 NO表示不行
* 注在调用其他Universallink相关处理接口之前均需进行此项判断
*/
+ (BOOL)CanHandleUniversalLink:(NSURL *)url;
/**
* (静态方法)获取TencentOAuth调用的上一次错误信息
*/
+ (NSString *)getLastErrorMsg;
/**
* 以Server Side Code模式授权登录时通过此接口获取返回的code值;
* 以Client Side Token模式授权登录时忽略此接口。
*/
- (NSString *)getServerSideCode;
/**
* 退出登录(退出登录后TecentOAuth失效需要重新初始化)
* @param delegate 第三方应用用于接收请求返回结果的委托对象
*/
- (void)logout:(id<TencentSessionDelegate>)delegate;
/**
* 判断登录态是否有效
* @return 处理结果YES表示有效NO表示无效请用户重新登录授权
*/
- (BOOL)isSessionValid;
/**
* 获取用户个人信息
* @return 处理结果YES表示API调用成功NO表示API调用失败登录态失败重新登录
*/
- (BOOL)getUserInfo;
/**
* 退出指定API调用
* @param userData 用户调用某条API的时候传入的保留参数
* @return 处理结果YES表示成功 NO表示失败
*/
- (BOOL)cancel:(id)userData;
/**
* CGI类任务创建接口
* @param apiURL CGI请求的URL地址
* @param method CGI请求方式"GET""POST"
* @param params CGI请求参数字典
* @param callback CGI请求结果的回调接口对象
* @return CGI请求任务实例用于取消任务返回nil代表任务创建失败
*/
- (TCAPIRequest *)cgiRequestWithURL:(NSURL *)apiURL method:(NSString *)method params:(NSDictionary *)params callback:(id<TCAPIRequestDelegate>)callback;
/**
* TencentOpenApi发送任务统一接口
* @param request 请求发送的任务
* @param callback 任务发送后的回调地址
*/
- (BOOL)sendAPIRequest:(TCAPIRequest *)request callback:(id<TCAPIRequestDelegate>)callback;
/**
* 获得用户的openId,仅有内存缓存
* @return 返回openId
*/
- (NSString *)getUserOpenID;
/**
* 获取appSignToken
* @return 返回appSignToken
*/
+ (NSString *)getAppSignToken;
/**
* 设置appSignToken跨进程的应用可以通过该方法手动设置appSignToken
*/
+ (void)setupAppSignToken:(NSString *)appSignToken;
@end
#pragma mark - TencentLoginDelegate(授权登录回调协议)
/**
* \brief TencentLoginDelegate iOS Open SDK 1.3 API回调协议
*
* 第三方应用实现登录的回调协议
*/
@protocol TencentLoginDelegate <NSObject>
@required
/**
* 登录成功后的回调
*/
- (void)tencentDidLogin;
/**
* 登录失败后的回调
* \param cancelled 代表用户是否主动退出登录
*/
- (void)tencentDidNotLogin:(BOOL)cancelled;
/**
* 登录时网络有问题的回调
*/
- (void)tencentDidNotNetWork;
@optional
/**
* 登录时权限信息的获得
*/
- (NSArray *)getAuthorizedPermissions:(NSArray *)permissions withExtraParams:(NSDictionary *)extraParams __attribute__((deprecated("该接口已过期, 建议删除调用")));
/**
* unionID获得
*/
- (void)didGetUnionID;
/**
* 强制网页登录,包括账号密码登录和二维码登录
* return YES时就算本地有手Q也会打开web界面
*/
- (BOOL)forceWebLogin;
/* 获得appSignToken回调 */
- (void)tencentDidGetAppSignToken:(NSString *)appSignToken;
@end
#pragma mark - TencentSessionDelegate(开放接口回调协议)
/**
* \brief TencentSessionDelegate iOS Open SDK 1.3 API回调协议
*
* 第三方应用需要实现每条需要调用的API的回调协议
*/
@protocol TencentSessionDelegate <NSObject, TencentLoginDelegate, TencentWebViewDelegate>
@optional
/**
* 退出登录的回调
*/
- (void)tencentDidLogout;
/**
* 因用户未授予相应权限而需要执行增量授权。在用户调用某个api接口时如果服务器返回操作未被授权则触发该回调协议接口由第三方决定是否跳转到增量授权页面让用户重新授权。
* @param tencentOAuth 登录授权对象。
* @param permissions 需增量授权的权限列表。
* @return 是否仍然回调返回原始的api请求结果。
* @note 不实现该协议接口则默认为不开启增量授权流程。若需要增量授权请调用\ref TencentOAuth#incrAuthWithPermissions: \n注意增量授权时用户可能会修改登录的帐号
*/
- (BOOL)tencentNeedPerformIncrAuth:(TencentOAuth *)tencentOAuth withPermissions:(NSArray *)permissions;
/**
* [该逻辑未实现]因token失效而需要执行重新登录授权。在用户调用某个api接口时如果服务器返回token失效则触发该回调协议接口由第三方决定是否跳转到登录授权页面让用户重新授权。
* @param tencentOAuth 登录授权对象。
* @return 是否仍然回调返回原始的api请求结果。
* @note 不实现该协议接口则默认为不开启重新登录授权流程。若需要重新登录授权请调用\ref TencentOAuth#reauthorizeWithPermissions: \n注意重新登录授权时用户可能会修改登录的帐号
*/
- (BOOL)tencentNeedPerformReAuth:(TencentOAuth *)tencentOAuth;
/**
* 用户通过增量授权流程重新授权登录token及有效期限等信息已被更新。
* @param tencentOAuth token及有效期限等信息更新后的授权实例对象
* @note 第三方应用需更新已保存的token及有效期限等信息。
*/
- (void)tencentDidUpdate:(TencentOAuth *)tencentOAuth;
/**
* 用户增量授权过程中因取消或网络问题导致授权失败
* @param reason 授权失败原因具体失败原因参见sdkdef.h文件中\ref UpdateFailType
*/
- (void)tencentFailedUpdate:(UpdateFailType)reason;
/**
* 获取用户个人信息回调
* @param response API返回结果具体定义参见sdkdef.h文件中\ref APIResponse
* @remarks 正确返回示例: \snippet example/getUserInfoResponse.exp success
* 错误返回示例: \snippet example/getUserInfoResponse.exp fail
*/
- (void)getUserInfoResponse:(APIResponse*) response;
/**
* 社交API统一回调接口
* @param response API返回结果具体定义参见sdkdef.h文件中\ref APIResponse
* @param message 响应的消息目前支持SendStory,AppInvitationAppChallengeAppGiftRequest
*/
- (void)responseDidReceived:(APIResponse*)response forMessage:(NSString *)message;
/**
* post请求的上传进度
* @param tencentOAuth 返回回调的tencentOAuth对象
* @param bytesWritten 本次回调上传的数据字节数
* @param totalBytesWritten 总共已经上传的字节数
* @param totalBytesExpectedToWrite 总共需要上传的字节数
* @param userData 用户自定义数据
*/
- (void)tencentOAuth:(TencentOAuth *)tencentOAuth didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite userData:(id)userData;
/**
* 通知第三方界面需要被关闭
* @param tencentOAuth 返回回调的tencentOAuth对象
* @param viewController 需要关闭的viewController
*/
- (void)tencentOAuth:(TencentOAuth *)tencentOAuth doCloseViewController:(UIViewController *)viewController;
@end
#pragma mark - TencentWebViewDelegate(H5登录webview旋转方向回调)
/**
* @brief TencentWebViewDelegate: H5登录webview旋转方向回调协议
*
* 第三方应用可以根据自己APP的旋转方向限制通过此协议设置
*/
@protocol TencentWebViewDelegate <NSObject>
@optional
- (BOOL) tencentWebViewShouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation;
- (NSUInteger) tencentWebViewSupportedInterfaceOrientationsWithWebkit;
- (BOOL) tencentWebViewShouldAutorotateWithWebkit;
@end

View File

@@ -0,0 +1,24 @@
//
// TencentOpenApiUmbrellaHeader.h
// TencentOpenApi_IOS
//
// Created by jyukeizhang(张储祺) on 2020/7/27.
// Copyright © 2020 Tencent. All rights reserved.
//
#ifndef TencentOpenApiUmbrellaHeader_h
#define TencentOpenApiUmbrellaHeader_h
#import <Foundation/Foundation.h>
FOUNDATION_EXPORT double StaticLibraryModuleVersionNumber;
FOUNDATION_EXPORT const unsigned char StaticLibraryModuleVersionString[];
#import "QQApiInterface.h"
#import "QQApiInterfaceObject.h"
#import "SDKDef.h"
#import "TencentOAuth.h"
#endif /* TencentOpenApiUmbrellaHeader_h */

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyCollectedDataTypes</key>
<array>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeUserID</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<false/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>用户同意授权后仅用于UserID授权第三方应用登录。</string>
</array>
</dict>
</array>
<key>NSPrivacyTrackingDomains</key>
<array/>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>用于存储第三方授权登录结果数据,便于应用能快速访问授权登录结果数据</string>
</array>
</dict>
</array>
<key>NSPrivacyTracking</key>
<false/>
</dict>
</plist>

View File

@@ -0,0 +1,48 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/// 日志级别
extern NSString * const ACM_LOGGER_LEVEL_VERBOSE;
extern NSString * const ACM_LOGGER_LEVEL_DEBUG;
extern NSString * const ACM_LOGGER_LEVEL_INFO;
extern NSString * const ACM_LOGGER_LEVEL_WARN;
extern NSString * const ACM_LOGGER_LEVEL_ERROR;
extern NSString * const ACM_LOGGER_LEVEL_REALTIME;
@interface ACMLogger : NSObject
- (instancetype)init NS_UNAVAILABLE;
/// 日志是否入库,默认不入库
@property (nonatomic, assign) BOOL enterDatabase;;
/// 日志是否允许上传,默认不上传
@property (nonatomic, assign) BOOL isAllowUpload;
/**
* 日志入库
* @param obj 日志的具体内容
* @param level 日志等级
*/
- (BOOL)logger:(id)obj level:(NSString *)level ;
/**
* 上传日志
* @param startDate 日志开始时间如果传nil则查询不加该条件
* @param endDate 日志结束时间如果传nil则查询不加该条件
* @param levels 日志等级数组里面包含对应的日志等级字符串如果传nil则查询不加该条件
*/
- (void)uploadLoggersWithLevels:(NSArray <NSString *>* _Nullable)levels
startDate:(NSDate * _Nullable)startDate
endDate:(NSDate * _Nullable)endDate;
/**
* 上传失败的日志,一般放在重启应用后
*/
- (void)uploadFailedRecords;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,73 @@
#import <Foundation/Foundation.h>
#import "ACMLogger.h"
#import "ACMMonitor.h"
#import "ACMProtocol.h"
NS_ASSUME_NONNULL_BEGIN
@interface ACMManager : NSObject
/// 日志操作对象
@property (nonatomic, strong, readonly) ACMLogger *logger;
/// 埋点操作对象
@property (nonatomic, strong, readonly) ACMMonitor *monitor;
- (instancetype)init NS_UNAVAILABLE;
/**
* 初始化
* @param databaseName 数据库名,不指定则默认为 “ACMDatabase”
* @param monitorTableName 埋点表名,必须要指定,用来区分不同产品数据
* @param loggerTablename 日志表名,必须要指定,用来区分不同产品数据
* @param limitKeyPrefix 限流信息存储到本地key的前缀用来区分不同产品的限流缓存
*/
- (instancetype)initWithDatabaseName:(NSString * _Nullable)databaseName
monitorTableName:(NSString *)monitorTableName
loggerTableName:(NSString *)loggerTablename
limitKeyPrefix:(NSString *)limitKeyPrefix;
/**
* 初始化
* @param databaseName 数据库名,不指定则默认为 “ACMDatabase”
* @param monitorTableName 埋点表名,必须要指定,用来区分不同产品数据
* @param loggerTablename 日志表名,必须要指定,用来区分不同产品数据
* @param limitKeyPrefix 限流信息存储到本地key的前缀用来区分不同产品的限流缓存
* @param uploadCount 每次上传条数
* @param retryRightNow 上传失败是否立马重试,默认立马重试
* @param uploadOnce 是否只执行一轮上传默认NO
*/
- (instancetype)initWithDatabaseName:(NSString * _Nullable)databaseName
monitorTableName:(NSString *)monitorTableName
loggerTableName:(NSString *)loggerTablename
limitKeyPrefix:(NSString *)limitKeyPrefix
uploadCount:(NSInteger)uploadCount
retryRightNow:(BOOL)retryRightNow
uploadOnce:(BOOL)uploadOnce;
/**
* 获取组件当前版本号
*/
- (NSString *)getVersion;
/**
* 设置日志埋点上传代理对象
* 注:这里是强引用
* @param uploadDelegate 代理对象,需要实现 ACMProtocol 协议
*/
- (void)setUploadDelegate:(id<ACMProtocol> _Nullable)uploadDelegate;
/**
* 更新限流相关
* @param isLimit 是否限流
* @param limitTimeHour 限流区间大小
* @param limitCount 区间内限流次数
*/
- (void)updateLimitConfig:(BOOL)isLimit
limitTimeHour:(NSInteger)limitTimeHour
limitCount:(NSInteger)limitCount;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,55 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, ACM_DELETE_TYPE) {
ACM_DELETE_TYPE_ALL,
ACM_DELETE_TYPE_FAILED,
ACM_DELETE_TYPE_UNUPLOAD
};
@interface ACMMonitor : NSObject
- (instancetype)init NS_UNAVAILABLE;
/// 埋点是否入库,默认入库
@property (nonatomic, assign) BOOL enterDatabase;;
/// 埋点是否允许上传,默认上传
@property (nonatomic, assign) BOOL isAllowUpload;
/**
* 非实时埋点入库
* @param obj 埋点具体内容
*/
- (BOOL)monitor:(id)obj;
/**
* 实时埋点入库
* @param obj 埋点的具体内容
*/
- (BOOL)monitorRealtime:(id)obj;
/**
* 手动触发埋点上传,只上传未上传过的埋点
*/
- (void)uploadMonitorByManual;
/**
* 上传失败的埋点,一般放在重启应用后
*/
- (void)uploadFailedRecords;
/**
* 删除埋点
* @param type 删除类型
* @param block 结果的异步回调
*/
- (void)deleteRecordsByType:(ACM_DELETE_TYPE)type block:(void (^)(BOOL))block;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,17 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@protocol ACMProtocol <NSObject>
@required
/// 埋点抛出,可在抛出里面进行上传
- (BOOL)uploadMonitors:(NSArray<NSDictionary *> *)monitors;
/// 日志抛出,可在抛出里面进行上传
- (BOOL)uploadLoggers:(NSArray<NSDictionary *> *)loggers;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,3 @@
#import "ACMManager.h"
#import "ACMProtocol.h"

Binary file not shown.

View File

@@ -0,0 +1,6 @@
framework module YTXMonitor {
umbrella header "YTXMonitor.h"
export *
module * { export * }
}

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyCollectedDataTypes</key>
<array>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeOtherDataTypes</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<false/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
</array>
</dict>
</array>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
</array>
</dict>
</plist>

Binary file not shown.

View File

@@ -0,0 +1,100 @@
//
// YTXNetUtils.h
// YTXOperators
//
// Created by yangli on 2020/11/9.
// Copyright © 2020 com.alicom. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface YTXNetUtils : NSObject
/**
判断当前设备蜂窝数据网络是否开启即3G/4G
@return 结果
*/
- (BOOL)checkDeviceCellularDataEnable;
/**
判断当前上网卡运营商是否是中国联通
@return 结果
*/
- (BOOL)isChinaUnicom;
/**
判断当前上网卡运营商是否是中国移动
@return 结果
*/
- (BOOL)isChinaMobile;
/**
判断当前上网卡运营商是否是中国电信
@return 结果
*/
- (BOOL)isChinaTelecom;
/**
获取当前上网卡运营商名称,比如中国移动、中国电信、中国联通
@return 结果
*/
- (NSString *)getCurrentCarrierName;
/**
获取当前上网卡运营商编码比如46000、46001、46003
@return 结果
*/
- (NSString *)getCurrentCarrierCode API_DEPRECATED("废弃,完成不可用,返回空字符串", ios(4.0, 16.0));
/**
获取当前上网卡网络类型比如WiFi4G
@return 结果
*/
- (NSString *)getNetworktype;
/**
判断当前设备是否有SIM卡
@return 结果
*/
- (BOOL)simSupportedIsOK;
/**
判断wwan是否开着通过p0网卡判断无wifi或有wifi情况下都能检测到
@return 结果
*/
- (BOOL)isWWANOpen;
/**
判断WiFi是否开着
@return 结果
*/
- (BOOL)isWiFiOpen;
/**
判断wwan是否开着仅无wifi情况下
@return 结果
*/
- (BOOL)reachableViaWWAN;
/**
获取设备当前网络私网IP地址
@return 结果
*/
- (NSString *)getMobilePrivateIPAddress:(BOOL)preferIPv4;
/**
获取双卡设备下,非上网卡信息
@return 结果
*/
- (NSString *)getOptionalCarrierInfo API_DEPRECATED("废弃,完成不可用,返回空字符串", ios(4.0, 16.0));;
/**
获取当前蜂网络Ip地址
*/
- (NSString *)getCellularIp;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,20 @@
//
// YTXOperators.h
// YTXOperators
//
// Created by yangli on 2020/11/9.
// Copyright © 2020 com.alicom. All rights reserved.
//
#import <Foundation/Foundation.h>
//! Project version number for YTXOperators.
FOUNDATION_EXPORT double YTXOperatorsVersionNumber;
//! Project version string for YTXOperators.
FOUNDATION_EXPORT const unsigned char YTXOperatorsVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <YTXOperators/PublicHeader.h>
#import "YTXVendorService.h"
#import "YTXNetUtils.h"

View File

@@ -0,0 +1,85 @@
//
// YTXVendorService.h
// ATAuthSDK
//
// Created by 刘超的MacBook on 2020/1/15.
// Copyright © 2020. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface YTXRequest : NSObject
/// 接口调用超时时间目前内部限制最小超时时间为5s小于5s则按5s设置
@property (nonatomic, assign) NSTimeInterval timeout;
/// 是否是蜂窝网络
@property (nonatomic, assign) BOOL isReachableViaWWAN;
@end
@interface YTXVendorConfig : NSObject
/// 当前供应商标识中移互联cm_zyhl联通小沃cu_xw联通在线cu_zx,电信世纪龙ct_sjl
@property (nonatomic, copy) NSString *vendorKey;
/// 供应商二级标识
@property (nonatomic, copy) NSString *vendorSubKey;
/// 供应商 access id
@property (nonatomic, copy) NSString *vendorAccessId;
/// 供应商 access secret
@property (nonatomic, copy) NSString *vendorAccessSecret;
@end
@interface YTXVendorService : NSObject
/**
* 获取SDK版本号
*/
+ (NSString *)getVersion;
/**
* 获取供应商SDK版本号
*/
+ (NSDictionary *)getVendorsVersion;
/**
* 初始化或更新各个供应商的接口调用对象,根据各个供应商的配置信息
* @param vendorConfigs 各个供应商配置信息
*/
- (void)updateVendorHandlers:(NSArray<YTXVendorConfig *> *)vendorConfigs;
/**
* 获取本机号码校验Token
* @param request 请求参数结构体
* @param vendorConfig 当前供应商配置信息
* @param complete 结果回调
*/
- (void)getVerifyTokenWithRequest:(YTXRequest *)request
vendorConfig:(YTXVendorConfig *)vendorConfig
complete:(void(^)(NSDictionary *response))complete;
/**
* 获取手机掩码
* @param request 请求参数结构体
* @param vendorConfig 当前供应商配置信息
* @param complete 结果回调
*/
- (void)getMaskNumberWithRequest:(YTXRequest *)request
vendorConfig:(YTXVendorConfig *)vendorConfig
complete:(void(^)(NSDictionary *response))complete;
/**
* 电信/联通获取一键登录Token
* @param request 请求参数结构体
* @param vendorConfig 当前供应商配置信息
* @param complete 结果回调
* @abstract 移动的获取登录Token不走这个回调走弹起授权页的回调
*/
- (void)getLoginTokenWithRequest:(YTXRequest *)request
vendorConfig:(YTXVendorConfig *)vendorConfig
complete:(void(^)(NSDictionary *response))complete;
- (void)deleteCacheWithVendorConfigs:(NSArray<YTXVendorConfig *> *)vendorConfigs;
@end
NS_ASSUME_NONNULL_END

Binary file not shown.

View File

@@ -0,0 +1,6 @@
framework module YTXOperators {
umbrella header "YTXOperators.h"
export *
module * { export * }
}

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypes</key>
<array>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeOtherDataTypes</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<false/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
</array>
</dict>
</array>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
</array>
</dict>
</plist>

Binary file not shown.

View File

@@ -0,0 +1,25 @@
//
// ZLBaseEventModel.h
// ZLCollectionView
//
// Created by hqtech on 2020/4/18.
// Copyright © 2020 zhaoliang chen. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ZLBaseEventModel : NSObject
@property(nonatomic,copy)NSString* _Nullable eventName;
@property(nonatomic,strong)id _Nullable parameter;
- (instancetype)initWithEventName:(NSString* _Nullable)eventName;
- (instancetype)initWithEventName:(NSString* _Nullable)eventName parameter:(id _Nullable)parameter;
+ (instancetype)createWithEventName:(NSString* _Nullable)eventName;
+ (instancetype)createWithEventName:(NSString* _Nullable)eventName parameter:(id _Nullable)parameter;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,35 @@
//
// ZLBaseEventModel.m
// ZLCollectionView
//
// Created by hqtech on 2020/4/18.
// Copyright © 2020 zhaoliang chen. All rights reserved.
//
#import "ZLBaseEventModel.h"
@implementation ZLBaseEventModel
- (instancetype)initWithEventName:(NSString* _Nullable)eventName {
return [self initWithEventName:eventName parameter:nil];
}
- (instancetype)initWithEventName:(NSString* _Nullable)eventName parameter:(id _Nullable)parameter {
if (self == [super init]) {
self.eventName = eventName;
self.parameter = parameter;
}
return self;
}
+ (instancetype)createWithEventName:(NSString* _Nullable)eventName {
ZLBaseEventModel* eventModel = [[ZLBaseEventModel alloc]initWithEventName:eventName parameter:nil];
return eventModel;
}
+ (instancetype)createWithEventName:(NSString* _Nullable)eventName parameter:(id _Nullable)parameter {
ZLBaseEventModel* eventModel = [[ZLBaseEventModel alloc]initWithEventName:eventName parameter:parameter];
return eventModel;
}
@end

View File

@@ -0,0 +1,25 @@
//
// ZLCellFakeView.h
// ZLCollectionView
//
// Created by zhaoliang chen on 2018/7/25.
// Copyright © 2018年 zhaoliang chen. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ZLCellFakeView : UIView
@property (nonatomic, weak)UICollectionViewCell *cell;
@property (nonatomic, strong)UIImageView *cellFakeImageView;
@property (nonatomic, strong)UIImageView *cellFakeHightedView;
@property (nonatomic, strong)NSIndexPath *indexPath;
@property (nonatomic, assign)CGPoint originalCenter;
@property (nonatomic, assign)CGRect cellFrame;
- (instancetype)initWithCell:(UICollectionViewCell *)cell;
- (void)changeBoundsIfNeeded:(CGRect)bounds;
- (void)pushFowardView;
- (void)pushBackView:(void(^)(void))completion;
@end

View File

@@ -0,0 +1,100 @@
//
// ZLCellFakeView.m
// ZLCollectionView
//
// Created by zhaoliang chen on 2018/7/25.
// Copyright © 2018 zhaoliang chen. All rights reserved.
//
#import "ZLCellFakeView.h"
@implementation ZLCellFakeView
- (instancetype)initWithCell:(UICollectionViewCell *)cell{
self = [super initWithFrame:cell.frame];
if (self) {
self.cell = cell;
self.layer.shadowColor = [UIColor blackColor].CGColor;
self.layer.shadowOffset = CGSizeMake(0, 0);
self.layer.shadowOpacity = 0;
self.layer.shadowRadius = 5.0;
self.layer.shouldRasterize = false;
self.layer.masksToBounds = YES;
self.clipsToBounds = YES;
self.cellFakeImageView = [[UIImageView alloc]initWithFrame:self.bounds];
self.cellFakeImageView.contentMode = UIViewContentModeScaleAspectFill;
self.cellFakeImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.cellFakeHightedView = [[UIImageView alloc]initWithFrame:self.bounds];
self.cellFakeHightedView.contentMode = UIViewContentModeScaleAspectFill;
self.cellFakeHightedView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
cell.highlighted = YES;
self.cellFakeHightedView.image = [self getCellImage];
cell.highlighted = NO;
self.cellFakeImageView.image = [self getCellImage];
[self addSubview:self.cellFakeImageView];
[self addSubview:self.cellFakeHightedView];
}
return self;
}
- (void)changeBoundsIfNeeded:(CGRect)bounds{
if (CGRectEqualToRect(self.bounds, bounds)) {
return;
}
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionBeginFromCurrentState animations:^{
self.bounds = bounds;
} completion:nil];
}
- (void)pushFowardView{
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionBeginFromCurrentState animations:^{
self.center = self.originalCenter;
self.transform = CGAffineTransformMakeScale(1.1, 1.1);
self.cellFakeHightedView.alpha = 0;
CABasicAnimation *shadowAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"];
shadowAnimation.fromValue = @(0);
shadowAnimation.toValue = @(0.7);
shadowAnimation.removedOnCompletion = NO;
shadowAnimation.fillMode = kCAFillModeForwards;
[self.layer addAnimation:shadowAnimation forKey:@"applyShadow"];
} completion:^(BOOL finished) {
[self.cellFakeHightedView removeFromSuperview];
}];
}
- (void)pushBackView:(void(^)(void))completion{
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionBeginFromCurrentState animations:^{
//self.transform = CGAffineTransformIdentity;
//self.frame = self.cellFrame;
CABasicAnimation *shadowAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"];
shadowAnimation.fromValue = @(0.7);
shadowAnimation.toValue = @(0);
shadowAnimation.removedOnCompletion = NO;
shadowAnimation.fillMode = kCAFillModeForwards;
[self.layer addAnimation:shadowAnimation forKey:@"removeShadow"];
} completion:^(BOOL finished) {
if (completion) {
completion();
}
}];
}
- (UIImage *)getCellImage{
UIGraphicsBeginImageContextWithOptions(_cell.bounds.size, NO, [UIScreen mainScreen].scale * 2);
[self.cell.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
@end

View File

@@ -0,0 +1,17 @@
//
// ZLCollectionBackView.h
// ZLCollectionView
//
// Created by zhaoliang chen on 2020/4/17.
// Copyright © 2020 zhaoliang chen. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface ZLCollectionBaseDecorationView : UICollectionReusableView
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,46 @@
//
// ZLCollectionBackView.m
// ZLCollectionView
//
// Created by zhaoliang chen on 2020/4/17.
// Copyright © 2020 zhaoliang chen. All rights reserved.
//
#import "ZLCollectionBaseDecorationView.h"
#import "ZLCollectionViewBackgroundViewLayoutAttributes.h"
#import <objc/runtime.h>
@implementation ZLCollectionBaseDecorationView
- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes {
//
ZLCollectionViewBackgroundViewLayoutAttributes *myLayoutAttributes = (ZLCollectionViewBackgroundViewLayoutAttributes*)layoutAttributes;
unsigned int methodCount = 0;
Method *methods = class_copyMethodList([self class], &methodCount);
if ([myLayoutAttributes isKindOfClass:[ZLCollectionViewBackgroundViewLayoutAttributes class]] && myLayoutAttributes.eventName != nil && myLayoutAttributes.eventName.length > 0) {
for(int i = 0; i < methodCount; i++) {
Method method = methods[i];
SEL sel = method_getName(method);
const char *name = sel_getName(sel);
NSString* methodName = [NSString stringWithUTF8String:name];
if ([methodName isEqualToString:myLayoutAttributes.eventName]) {
//object_setClass(self, newClass);
SEL selector = NSSelectorFromString(myLayoutAttributes.eventName);
IMP imp = [self methodForSelector:selector];
if ([self respondsToSelector:selector]) {
if (myLayoutAttributes.parameter) {
void (*func) (id, SEL, id) = (void *)imp;
func(self,selector,myLayoutAttributes.parameter);
} else {
void (*func) (id, SEL) = (void *)imp;
func(self,selector);
}
}
break;
};
}
}
free(methods);
}
@end

View File

@@ -0,0 +1,13 @@
//
// ZLCollectionReusableView.h
// ZLCollectionView
//
// Created by zhaoliang chen on 2018/7/9.
// Copyright © 2018年 zhaoliang chen. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ZLCollectionReusableView : UICollectionReusableView
@end

View File

@@ -0,0 +1,57 @@
//
// ZLCollectionReusableView.m
// ZLCollectionView
//
// Created by zhaoliang chen on 2018/7/9.
// Copyright © 2018 zhaoliang chen. All rights reserved.
//
#import "ZLCollectionReusableView.h"
#import "ZLCollectionViewLayoutAttributes.h"
@interface ZLCollectionReusableView ()
@property(nonatomic,strong)UIImageView* ivBackground;
@end
@implementation ZLCollectionReusableView
- (instancetype)initWithFrame:(CGRect)frame {
if (self == [super initWithFrame:frame]) {
self.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:self.ivBackground];
self.ivBackground.translatesAutoresizingMaskIntoConstraints = NO;
[self addConstraints:@[
[NSLayoutConstraint constraintWithItem:self.ivBackground attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0.0],
[NSLayoutConstraint constraintWithItem:self.ivBackground attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1.0 constant: 0.0],
[NSLayoutConstraint constraintWithItem:self.ivBackground attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant: 0.0],
[NSLayoutConstraint constraintWithItem:self.ivBackground attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0 constant: 0.0]
]];
}
return self;
}
- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes {
[super applyLayoutAttributes:layoutAttributes];
//
ZLCollectionViewLayoutAttributes *ecLayoutAttributes = (ZLCollectionViewLayoutAttributes*)layoutAttributes;
if (ecLayoutAttributes.color) {
self.backgroundColor = ecLayoutAttributes.color;
}
if (ecLayoutAttributes.image) {
self.ivBackground.image = ecLayoutAttributes.image;
}
}
- (UIImageView*)ivBackground {
if (!_ivBackground) {
_ivBackground = [[UIImageView alloc]init];
_ivBackground.contentMode = UIViewContentModeScaleAspectFill;
_ivBackground.backgroundColor = [UIColor clearColor];
}
return _ivBackground;
}
@end

View File

@@ -0,0 +1,27 @@
//
// ZLCollectionViewBackViewLayoutAttributes.h
// ZLCollectionView
//
// Created by zhaoliang chen on 2020/4/17.
// Copyright © 2020 zhaoliang chen. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "ZLBaseEventModel.h"
NS_ASSUME_NONNULL_BEGIN
@interface ZLCollectionViewBackgroundViewLayoutAttributes : UICollectionViewLayoutAttributes
//此属性只是header会单独设置其他均直接返回其frame属性
@property(nonatomic,assign,readonly)CGRect headerFrame;
@property(nonatomic,assign,readonly)CGRect footerFrame;
@property(nonatomic,copy)NSString* eventName;
@property(nonatomic,copy)id parameter;
- (void)callMethod:(ZLBaseEventModel*)eventModel;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,45 @@
//
// ZLCollectionViewBackViewLayoutAttributes.m
// ZLCollectionView
//
// Created by zhaoliang chen on 2020/4/17.
// Copyright © 2020 zhaoliang chen. All rights reserved.
//
#import "ZLCollectionViewBackgroundViewLayoutAttributes.h"
@implementation ZLCollectionViewBackgroundViewLayoutAttributes
@synthesize headerFrame = _headerFrame;
@synthesize footerFrame = _footerFrame;
+ (instancetype)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind withIndexPath:(NSIndexPath *)indexPath orginalFrmae:(CGRect)orginalFrame{
ZLCollectionViewBackgroundViewLayoutAttributes *layoutAttributes = [super layoutAttributesForDecorationViewOfKind:decorationViewKind withIndexPath:indexPath];
[layoutAttributes setValue:[NSValue valueWithCGRect:orginalFrame] forKey:@"orginalFrame"];
layoutAttributes.frame = orginalFrame;
return layoutAttributes;
}
-(CGRect)orginalFrame {
if ([self.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
return _headerFrame;
} else if ([self.representedElementKind isEqualToString:UICollectionElementKindSectionFooter]) {
return _footerFrame;
} else {
return self.frame;
}
}
- (void)callMethod:(ZLBaseEventModel*)eventModel {
NSAssert([eventModel isKindOfClass:[ZLBaseEventModel class]], @"callMethod必须传入ZLBaseEventModel类型参数");
if (eventModel == nil) {
return;
}
if (eventModel.eventName != nil) {
self.eventName = eventModel.eventName;
}
if (eventModel.parameter) {
self.parameter = eventModel.parameter;
}
}
@end

View File

@@ -0,0 +1,136 @@
//
// ZLCollectionViewBaseFlowLayout.h
// ZLCollectionView
//
// Created by zhaoliang chen on 2019/1/25.
// Copyright © 2019 zhaoliang chen. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "ZLBaseEventModel.h"
/**
版本1.4.9
*/
NS_ASSUME_NONNULL_BEGIN
typedef enum {
LabelHorizontalLayout = 1, //标签横向
LabelVerticalLayout = 2, //标签纵向
LabelLayout = LabelHorizontalLayout, //标签页布局。 一堆label标签的集合
ClosedLayout = 3,
ColumnLayout = ClosedLayout, //列布局 指定列数按列数来等分一整行itemSize的width可以任意写在布局中会自动帮你计算。可用于瀑布流普通UITableViewCell
PercentLayout = 4, //百分比布局 需实现percentOfRow的代理根据设定值来计算每个itemSize的宽度
FillLayout = 5, //填充式布局 将一堆大小不一的view见缝插针的填充到一个平面内规则为先判断从左到右是否有间隙填充再从上到下判断。
AbsoluteLayout = 6, //绝对定位布局 需实现rectOfItem的代理指定每个item的frame
} ZLLayoutType;
typedef enum {
minHeight = 1, // 按最小高度
Sequence = 2, // 按顺序
} ZLColumnSortType;
@class ZLCollectionViewBaseFlowLayout;
@protocol ZLCollectionViewBaseFlowLayoutDelegate <NSObject, UICollectionViewDelegateFlowLayout>
@optional
//指定是什么布局如没有指定则为FillLayout(填充式布局)
- (ZLLayoutType)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout *)collectionViewLayout typeOfLayout:(NSInteger)section;
/******** 设置每个section的背景色 ***********/
//设置每个section的背景色
- (UIColor*)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout *)collectionViewLayout backColorForSection:(NSInteger)section;
//设置每个section的背景图
- (UIImage*)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout *)collectionViewLayout backImageForSection:(NSInteger)section;
//自定义每个section的背景view需要继承UICollectionReusableView(如要调用方法传递参数需要继承ZLCollectionBaseDecorationView),返回类名
- (NSString*)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout *)collectionViewLayout registerBackView:(NSInteger)section;
//向每个section自定义背景view传递自定义方法 eventName:方法名(注意带参数的方法名必须末尾加:,parameter:参数
- (ZLBaseEventModel*)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout *)collectionViewLayout backgroundViewMethodForSection:(NSInteger)section;
//背景是否延伸覆盖到headerView默认为NO
- (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout *)collectionViewLayout attachToTop:(NSInteger)section;
//背景是否延伸覆盖到footerView默认为NO
- (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout *)collectionViewLayout attachToBottom:(NSInteger)section;
/******** 提取出UICollectionViewLayoutAttributes的一些属性 ***********/
//设置每个item的zIndex不指定默认为0
- (NSInteger)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout *)collectionViewLayout zIndexOfItem:(NSIndexPath*)indexPath;
//设置每个item的CATransform3D不指定默认为CATransform3DIdentity
- (CATransform3D)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout *)collectionViewLayout transformOfItem:(NSIndexPath*)indexPath;
//设置每个item的alpha不指定默认为1
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout *)collectionViewLayout alphaOfItem:(NSIndexPath*)indexPath;
/******** ClosedLayout列布局需要的代理 ***********/
//在ClosedLayout列布局中指定一行有几列不指定默认为1列
- (NSInteger)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout*)collectionViewLayout columnCountOfSection:(NSInteger)section;
//在ClosedLayout列布局中指定哪列哪行可以是单行布局不指定以上个方法为准
- (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout*)collectionViewLayout singleColumnCountOfIndexPath:(NSIndexPath*)indexPath;
/******** PercentLayout百分比布局需要的代理 ***********/
//在PercentLayout百分比布局中指定每个item占该行的几分之几如3.0/4注意为大于0小于等于1的数字。不指定默认为1
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout*)collectionViewLayout percentOfRow:(NSIndexPath*)indexPath;
/******** AbsoluteLayout绝对定位布局需要的代理 ***********/
//在AbsoluteLayout绝对定位布局中指定每个item的frame不指定默认为CGRectZero
- (CGRect)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout*)collectionViewLayout rectOfItem:(NSIndexPath*)indexPath;
/******** 拖动cell的相关代理 ***************************/
//- (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout*)collectionViewLayout shouldMoveCell:(NSIndexPath*)indexPath;
- (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout*)collectionViewLayout didMoveCell:(NSIndexPath*)atIndexPath toIndexPath:(NSIndexPath*)toIndexPath;
@end
/***
此类是基类,不要调用。
纵向布局请调用 #import "ZLCollectionViewVerticalLayout.h"
横向布局请调用 #import "ZLCollectionViewHorzontalLayout.h"
***/
@interface ZLCollectionViewBaseFlowLayout : UICollectionViewFlowLayout
@property (nonatomic,weak) id<ZLCollectionViewBaseFlowLayoutDelegate> delegate;
@property (nonatomic,assign) BOOL isFloor; // 宽度是否向下取整默认YES用于填充布局未来加入百分比布局
@property (nonatomic,assign) BOOL canDrag; //是否允许拖动cell默认是NO
@property (nonatomic,assign) BOOL header_suspension; //头部是否悬浮默认是NO
@property (nonatomic,assign) ZLLayoutType layoutType; //指定layout的类型也可以在代理里设置
@property (nonatomic,assign) NSInteger columnCount; //指定列数
@property (nonatomic,assign) ZLColumnSortType columnSortType; // 瀑布流列排序的方式
@property (nonatomic,assign) CGFloat fixTop; //header偏移量
@property (nonatomic,assign) CGFloat xBeyond; //x轴允许超出的偏移量仅填充布局默认3px
//每个section的每一列的高度
@property (nonatomic, strong) NSMutableArray *collectionHeightsArray;
//存放每一个cell的属性
@property (nonatomic, strong) NSMutableArray *attributesArray;
//存放header属性, 外部不要干预
@property (nonatomic, strong, readonly) NSMutableArray *headerAttributesArray;
//是否需要重新计算所有布局
//内部控制,一般情况外部无需干预(内部会在外部调用reloadData,insertSections,insertItems,deleteItems...等方法调用时将此属性自动置为YES)
@property (nonatomic, assign, readonly) BOOL isNeedReCalculateAllLayout;
//提供一个方法来设置isNeedReCalculateAllLayout (之所以提供是因为特殊情况下外部可能需要强制重新计算布局)
//比如需要强制刷新布局时可以先调用此函数设置为YES, 一般情况外部无需干预
- (void)forceSetIsNeedReCalculateAllLayout:(BOOL)isNeedReCalculateAllLayout;
// 注册所有的背景view(传入类名)
- (void)registerDecorationView:(NSArray<NSString*>*)classNames;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,527 @@
//
// ZLCollectionViewBaseFlowLayout.m
// ZLCollectionView
//
// Created by zhaoliang chen on 2019/1/25.
// Copyright © 2019 zhaoliang chen. All rights reserved.
//
#import "ZLCollectionViewBaseFlowLayout.h"
#import "ZLCollectionViewLayoutAttributes.h"
#import "ZLCellFakeView.h"
typedef NS_ENUM(NSUInteger, LewScrollDirction) {
LewScrollDirctionStay,
LewScrollDirctionToTop,
LewScrollDirctionToEnd,
};
@interface ZLCollectionViewBaseFlowLayout ()
<UIGestureRecognizerDelegate>
//
@property (nonatomic, strong) ZLCellFakeView *cellFakeView;
@property (nonatomic, strong) UILongPressGestureRecognizer *longPress;
@property (nonatomic, strong) UIPanGestureRecognizer *panGesture;
@property (nonatomic, assign) CGPoint fakeCellCenter;
@property (nonatomic, assign) CGPoint panTranslation;
@property (nonatomic) LewScrollDirction continuousScrollDirection;
@property (nonatomic, strong) CADisplayLink *displayLink;
@end
@implementation ZLCollectionViewBaseFlowLayout {
BOOL _isNeedReCalculateAllLayout;
}
- (instancetype)init {
if (self == [super init]) {
self.isFloor = YES;
self.canDrag = NO;
self.header_suspension = NO;
self.layoutType = FillLayout;
self.columnCount = 1;
self.columnSortType = minHeight;
self.fixTop = 0;
self.xBeyond = 3;
_isNeedReCalculateAllLayout = YES;
_headerAttributesArray = @[].mutableCopy;
[self addObserver:self forKeyPath:@"collectionView" options:NSKeyValueObservingOptionNew context:nil];
}
return self;
}
#pragma mark -
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return self.header_suspension;
}
//+ (Class)layoutAttributesClass {
// return [ZLCollectionViewLayoutAttributes class];
//}
- (void)invalidateLayoutWithContext:(UICollectionViewLayoutInvalidationContext *)context {
//relaodData
//
//,,
_isNeedReCalculateAllLayout = context.invalidateEverything || context.invalidateDataSourceCounts;
[super invalidateLayoutWithContext:context];
}
// view()
- (void)registerDecorationView:(NSArray<NSString*>*)classNames {
for (NSString* className in classNames) {
if (className.length > 0) {
[self registerClass:NSClassFromString(className) forDecorationViewOfKind:className];
}
}
}
- (void)dealloc {
[self removeObserver:self forKeyPath:@"collectionView"];
}
#pragma mark - cellview
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
if (!self.attributesArray || self.collectionView.numberOfSections == 0) {
return [super layoutAttributesForElementsInRect:rect];
} else {
if (self.header_suspension) {
//headerAttributesArray
for (UICollectionViewLayoutAttributes *attriture in self.headerAttributesArray) {
if (![attriture.representedElementKind isEqualToString:UICollectionElementKindSectionHeader])
continue;
NSInteger section = attriture.indexPath.section;
CGRect frame = attriture.frame;
BOOL isNeedChangeFrame = NO;
if (section == 0) {
if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
CGFloat offsetY = self.collectionView.contentOffset.y + self.fixTop;
if (offsetY > 0 && offsetY < [self.collectionHeightsArray[0] floatValue]) {
frame.origin.y = offsetY;
attriture.zIndex = 1000+section;
attriture.frame = frame;
isNeedChangeFrame = YES;
}
} else {
CGFloat offsetX = self.collectionView.contentOffset.y + self.fixTop;
if (offsetX > 0 && offsetX < [self.collectionHeightsArray[0] floatValue]) {
frame.origin.x = offsetX;
attriture.zIndex = 1000+section;
attriture.frame = frame;
isNeedChangeFrame = YES;
}
}
} else {
if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
CGFloat offsetY = self.collectionView.contentOffset.y + self.fixTop;
if (offsetY > [self.collectionHeightsArray[section-1] floatValue] &&
offsetY < [self.collectionHeightsArray[section] floatValue]) {
frame.origin.y = offsetY;
attriture.zIndex = 1000+section;
attriture.frame = frame;
isNeedChangeFrame = YES;
}
} else {
CGFloat offsetX = self.collectionView.contentOffset.y + self.fixTop;
if (offsetX > [self.collectionHeightsArray[section-1] floatValue] &&
offsetX < [self.collectionHeightsArray[section] floatValue]) {
frame.origin.x = offsetX;
attriture.zIndex = 1000+section;
attriture.frame = frame;
isNeedChangeFrame = YES;
}
}
}
if (!isNeedChangeFrame) {
/*
headerAttframe
header,headerAtt
header
*/
if ([attriture isKindOfClass:[ZLCollectionViewLayoutAttributes class]]) {
attriture.frame = ((ZLCollectionViewLayoutAttributes*)attriture).orginalFrame;
}
}
}
}
return self.attributesArray;
}
}
// 1.4.8layoutAttributesForItemAtIndexPath
//- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
// UICollectionViewLayoutAttributes *layoutAttributes = (UICollectionViewLayoutAttributes*)self.attributesArray[indexPath.item];
// if(!layoutAttributes) {
// layoutAttributes = [super layoutAttributesForItemAtIndexPath:indexPath];
// }
// return layoutAttributes;
//}
/**
*/
#pragma mark
- (void)setCanDrag:(BOOL)canDrag {
_canDrag = canDrag;
if (canDrag) {
if (self.longPress == nil && self.panGesture == nil) {
[self setUpGestureRecognizers];
}
} else {
[self.collectionView removeGestureRecognizer:self.longPress];
self.longPress.delegate = nil;
self.longPress = nil;
[self.collectionView removeGestureRecognizer:self.panGesture];
self.panGesture.delegate = nil;
self.panGesture = nil;
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
if ([keyPath isEqualToString:@"collectionView"]) {
if (self.canDrag) {
[self setUpGestureRecognizers];
}
}else{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)setUpGestureRecognizers{
if (self.collectionView == nil) {
return;
}
self.longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(handleLongPress:)];
self.panGesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(handlePanGesture:)];
self.longPress.delegate = self;
self.panGesture.delegate = self;
self.panGesture.maximumNumberOfTouches = 1;
NSArray *gestures = [self.collectionView gestureRecognizers];
__weak typeof(ZLCollectionViewBaseFlowLayout*) weakSelf = self;
[gestures enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[UILongPressGestureRecognizer class]]) {
[(UILongPressGestureRecognizer *)obj requireGestureRecognizerToFail:weakSelf.longPress];
}
}];
[self.collectionView addGestureRecognizer:self.longPress];
[self.collectionView addGestureRecognizer:self.panGesture];
}
#pragma mark - gesture
- (void)handleLongPress:(UILongPressGestureRecognizer *)longPress {
CGPoint location = [longPress locationInView:self.collectionView];
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:location];
// __weak typeof(ZLCollectionViewBaseFlowLayout*) weakSelf = self;
// if ([weakSelf.delegate respondsToSelector:@selector(collectionView:layout:shouldMoveCell:)]) {
// if ([weakSelf.delegate collectionView:weakSelf.collectionView layout:weakSelf shouldMoveCell:indexPath] == NO) {
// return;
// }
// }
if (_cellFakeView != nil) {
indexPath = self.cellFakeView.indexPath;
}
if (indexPath == nil) {
return;
}
switch (longPress.state) {
case UIGestureRecognizerStateBegan:{
// will begin drag item
self.collectionView.scrollsToTop = NO;
UICollectionViewCell *currentCell = [self.collectionView cellForItemAtIndexPath:indexPath];
self.cellFakeView = [[ZLCellFakeView alloc]initWithCell:currentCell];
self.cellFakeView.indexPath = indexPath;
self.cellFakeView.originalCenter = currentCell.center;
self.cellFakeView.cellFrame = [self layoutAttributesForItemAtIndexPath:indexPath].frame;
[self.collectionView addSubview:self.cellFakeView];
self.fakeCellCenter = self.cellFakeView.center;
[self invalidateLayout];
[self.cellFakeView pushFowardView];
}
break;
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateEnded:
[self cancelDrag:indexPath];
default:
break;
}
}
// pan gesture
- (void)handlePanGesture:(UIPanGestureRecognizer *)pan {
_panTranslation = [pan translationInView:self.collectionView];
if (_cellFakeView != nil) {
switch (pan.state) {
case UIGestureRecognizerStateChanged:{
CGPoint center = _cellFakeView.center;
center.x = self.fakeCellCenter.x + self.panTranslation.x;
center.y = self.fakeCellCenter.y + self.panTranslation.y;
self.cellFakeView.center = center;
[self beginScrollIfNeeded];
[self moveItemIfNeeded];
}
break;
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateEnded:
[self invalidateDisplayLink];
default:
break;
}
}
}
// gesture recognize delegate
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
// allow move item
CGPoint location = [gestureRecognizer locationInView:self.collectionView];
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:location];
if (!indexPath) {
return NO;
}
if ([gestureRecognizer isEqual:self.longPress]){
return (self.collectionView.panGestureRecognizer.state == UIGestureRecognizerStatePossible || self.collectionView.panGestureRecognizer.state == UIGestureRecognizerStateFailed);
} else if ([gestureRecognizer isEqual:self.panGesture]){
return (self.longPress.state != UIGestureRecognizerStatePossible && self.longPress.state != UIGestureRecognizerStateFailed);
}
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
if ([self.panGesture isEqual:gestureRecognizer]) {
return [self.longPress isEqual:otherGestureRecognizer];
} else if ([self.collectionView.panGestureRecognizer isEqual:gestureRecognizer]) {
return (self.longPress.state != UIGestureRecognizerStatePossible && self.longPress.state != UIGestureRecognizerStateFailed);
}
return YES;
}
- (void)cancelDrag:(NSIndexPath *)toIndexPath {
if (self.cellFakeView == nil) {
return;
}
self.collectionView.scrollsToTop = YES;
self.fakeCellCenter = CGPointZero;
[self invalidateDisplayLink];
__weak typeof(ZLCollectionViewBaseFlowLayout*) weakSelf = self;
[self.cellFakeView pushBackView:^{
[weakSelf.cellFakeView removeFromSuperview];
weakSelf.cellFakeView = nil;
[weakSelf invalidateLayout];
}];
}
- (void)beginScrollIfNeeded{
if (self.cellFakeView == nil) {
return;
}
CGFloat offset = self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.collectionView.contentOffset.y : self.collectionView.contentOffset.x;
CGFloat trigerInsetTop = self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.collectionView.contentInset.top: self.collectionView.contentInset.left;
CGFloat trigerInsetEnd = self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.collectionView.contentInset.bottom : self.collectionView.contentInset.right;
CGFloat paddingTop = 0;
CGFloat paddingEnd = 0;
CGFloat length = self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.collectionView.frame.size.height : self.collectionView.frame.size.width;
CGFloat fakeCellTopEdge = self.scrollDirection == UICollectionViewScrollDirectionVertical ? CGRectGetMinY(self.cellFakeView.frame) : CGRectGetMinX(self.cellFakeView.frame);
CGFloat fakeCellEndEdge = self.scrollDirection == UICollectionViewScrollDirectionVertical ? CGRectGetMaxY(self.cellFakeView.frame) : CGRectGetMaxX(self.cellFakeView.frame);
if(fakeCellTopEdge <= offset + paddingTop + trigerInsetTop){
self.continuousScrollDirection = LewScrollDirctionToTop;
[self setUpDisplayLink];
}else if(fakeCellEndEdge >= offset + length - paddingEnd - trigerInsetEnd) {
self.continuousScrollDirection = LewScrollDirctionToEnd;
[self setUpDisplayLink];
}else {
[self invalidateDisplayLink];
}
}
// move item
- (void)moveItemIfNeeded {
NSIndexPath *atIndexPath = nil;
NSIndexPath *toIndexPath = nil;
__weak typeof(ZLCollectionViewBaseFlowLayout*) weakSelf = self;
if (self.cellFakeView) {
atIndexPath = _cellFakeView.indexPath;
toIndexPath = [self.collectionView indexPathForItemAtPoint:_cellFakeView.center];
}
if (atIndexPath.section != toIndexPath.section) {
return;
}
// if ([weakSelf.delegate respondsToSelector:@selector(collectionView:layout:shouldMoveCell:)]) {
// if ([weakSelf.delegate collectionView:weakSelf.collectionView layout:weakSelf shouldMoveCell:toIndexPath] == NO) {
// return;
// }
// }
if (atIndexPath == nil || toIndexPath == nil) {
return;
}
if ([atIndexPath isEqual:toIndexPath]) {
return;
}
UICollectionViewLayoutAttributes *attribute = nil;//[self layoutAttributesForItemAtIndexPath:toIndexPath];
for (ZLCollectionViewLayoutAttributes* attr in weakSelf.attributesArray) {
if (attr.indexPath.section == toIndexPath.section && attr.indexPath.item == toIndexPath.item &&
attr.representedElementKind != UICollectionElementKindSectionHeader &&
attr.representedElementKind != UICollectionElementKindSectionFooter) {
attribute = attr;
break;
}
}
//NSLog(@"拖动从%@到%@",atIndexPath,toIndexPath);
if (attribute != nil) {
[self.collectionView performBatchUpdates:^{
weakSelf.cellFakeView.indexPath = toIndexPath;
weakSelf.cellFakeView.cellFrame = attribute.frame;
[weakSelf.cellFakeView changeBoundsIfNeeded:attribute.bounds];
[weakSelf.collectionView moveItemAtIndexPath:atIndexPath toIndexPath:toIndexPath];
if ([weakSelf.delegate respondsToSelector:@selector(collectionView:layout:didMoveCell:toIndexPath:)]) {
[weakSelf.delegate collectionView:weakSelf.collectionView layout:weakSelf didMoveCell:atIndexPath toIndexPath:toIndexPath];
}
} completion:nil];
}
}
- (void)setUpDisplayLink{
if (_displayLink) {
return;
}
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(continuousScroll)];
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)invalidateDisplayLink{
_continuousScrollDirection = LewScrollDirctionStay;
[_displayLink invalidate];
_displayLink = nil;
}
- (void)continuousScroll{
if (_cellFakeView == nil) {
return;
}
CGFloat percentage = [self calcTrigerPercentage];
CGFloat scrollRate = [self scrollValueWithSpeed:10 andPercentage:percentage];
CGFloat offset = 0;
CGFloat insetTop = 0;
CGFloat insetEnd = 0;
CGFloat length = self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.collectionView.frame.size.height : self.collectionView.frame.size.width;
CGFloat contentLength = self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.collectionView.contentSize.height : self.collectionView.contentSize.width;
if (contentLength + insetTop + insetEnd <= length) {
return;
}
if (offset + scrollRate <= -insetTop) {
scrollRate = -insetTop - offset;
} else if (offset + scrollRate >= contentLength + insetEnd - length) {
scrollRate = contentLength + insetEnd - length - offset;
}
__weak typeof(ZLCollectionViewBaseFlowLayout*) weakSelf = self;
[self.collectionView performBatchUpdates:^{
if (weakSelf.scrollDirection == UICollectionViewScrollDirectionVertical) {
CGPoint point = weakSelf.fakeCellCenter;
point.y += scrollRate;
weakSelf.fakeCellCenter = point;
CGPoint center = weakSelf.cellFakeView.center;
center.y = weakSelf.fakeCellCenter.y + weakSelf.panTranslation.y;
weakSelf.cellFakeView.center = center;
CGPoint contentOffset = weakSelf.collectionView.contentOffset;
contentOffset.y += scrollRate;
weakSelf.collectionView.contentOffset = contentOffset;
} else {
CGPoint point = weakSelf.fakeCellCenter;
point.x += scrollRate;
weakSelf.fakeCellCenter = point;
//_fakeCellCenter.x += scrollRate;
CGPoint center = weakSelf.cellFakeView.center;
center.x = weakSelf.fakeCellCenter.x + weakSelf.panTranslation.x;
weakSelf.cellFakeView.center = center;
CGPoint contentOffset = weakSelf.collectionView.contentOffset;
contentOffset.x += scrollRate;
weakSelf.collectionView.contentOffset = contentOffset;
}
} completion:nil];
[self moveItemIfNeeded];
}
- (CGFloat)calcTrigerPercentage{
if (_cellFakeView == nil) {
return 0;
}
CGFloat offset = 0;
CGFloat offsetEnd = 0 + self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.collectionView.frame.size.height : self.collectionView.frame.size.width;
CGFloat insetTop = 0;
CGFloat trigerInsetTop = 0;
CGFloat trigerInsetEnd = 0;
CGFloat paddingTop = 0;
CGFloat paddingEnd = 0;
CGFloat percentage = 0.0;
if (self.continuousScrollDirection == LewScrollDirctionToTop) {
if (self.cellFakeView) {
percentage = 1.0 - (((self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.cellFakeView.frame.origin.y : self.cellFakeView.frame.origin.x) - (offset + paddingTop)) / trigerInsetTop);
}
} else if (self.continuousScrollDirection == LewScrollDirctionToEnd){
if (self.cellFakeView) {
percentage = 1.0 - (((insetTop + offsetEnd - paddingEnd) - ((self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.cellFakeView.frame.origin.y : self.cellFakeView.frame.origin.x) + (self.scrollDirection == UICollectionViewScrollDirectionVertical ? self.cellFakeView.frame.size.height : self.cellFakeView.frame.size.width) + insetTop)) / trigerInsetEnd);
}
}
percentage = fmin(1.0f, percentage);
percentage = fmax(0.0f, percentage);
return percentage;
}
#pragma mark - getter
- (CGFloat)scrollValueWithSpeed:(CGFloat)speed andPercentage:(CGFloat)percentage{
CGFloat value = 0.0f;
switch (_continuousScrollDirection) {
case LewScrollDirctionStay: {
return 0.0f;
break;
}
case LewScrollDirctionToTop: {
value = -speed;
break;
}
case LewScrollDirctionToEnd: {
value = speed;
break;
}
default: {
return 0.0f;
}
}
CGFloat proofedPercentage = fmax(fmin(1.0f, percentage), 0.0f);
return value * proofedPercentage;
}
- (void)forceSetIsNeedReCalculateAllLayout:(BOOL)isNeedReCalculateAllLayout
{
_isNeedReCalculateAllLayout = isNeedReCalculateAllLayout;
}
@end

View File

@@ -0,0 +1,17 @@
//
// ZLCollectionViewHorzontalLayout.h
// ZLCollectionView
//
// Created by zhaoliang chen on 2019/1/25.
// Copyright © 2019 zhaoliang chen. All rights reserved.
//
#import "ZLCollectionViewBaseFlowLayout.h"
NS_ASSUME_NONNULL_BEGIN
@interface ZLCollectionViewHorzontalLayout : ZLCollectionViewBaseFlowLayout
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,362 @@
//
// ZLCollectionViewHorzontalLayout.m
// ZLCollectionView
//
// Created by zhaoliang chen on 2019/1/25.
// Copyright © 2019 zhaoliang chen. All rights reserved.
//
#import "ZLCollectionViewHorzontalLayout.h"
#import "ZLCollectionReusableView.h"
#import "ZLCollectionViewLayoutAttributes.h"
#import "ZLCollectionViewBackgroundViewLayoutAttributes.h"
@interface ZLCollectionViewHorzontalLayout()
@end
@implementation ZLCollectionViewHorzontalLayout
#pragma mark -
- (instancetype)init {
self = [super init];
if (self) {
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
}
return self;
}
- (void)prepareLayout {
[super prepareLayout];
//怀...
if (!self.isNeedReCalculateAllLayout) {
return;
}
CGFloat totalHeight = self.collectionView.frame.size.height;
CGFloat x = 0;
CGFloat y = 0;
CGFloat headerW = 0;
CGFloat footerW = 0;
UIEdgeInsets edgeInsets = UIEdgeInsetsZero;
CGFloat minimumLineSpacing = 0;
CGFloat minimumInteritemSpacing = 0;
NSUInteger sectionCount = [self.collectionView numberOfSections];
self.attributesArray = [NSMutableArray new];
self.collectionHeightsArray = [NSMutableArray arrayWithCapacity:sectionCount];
for (int index= 0; index<sectionCount; index++) {
NSUInteger itemCount = [self.collectionView numberOfItemsInSection:index];
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]) {
headerW = [self.delegate collectionView:self.collectionView layout:self referenceSizeForHeaderInSection:index].width;
} else {
headerW = self.headerReferenceSize.width;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]) {
footerW = [self.delegate collectionView:self.collectionView layout:self referenceSizeForFooterInSection:index].width;
} else {
footerW = self.footerReferenceSize.width;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)]) {
edgeInsets = [self.delegate collectionView:self.collectionView layout:self insetForSectionAtIndex:index];
} else {
edgeInsets = self.sectionInset;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:minimumLineSpacingForSectionAtIndex:)]) {
minimumLineSpacing = [self.delegate collectionView:self.collectionView layout:self minimumLineSpacingForSectionAtIndex:index];
} else {
minimumLineSpacing = self.minimumLineSpacing;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:minimumInteritemSpacingForSectionAtIndex:)]) {
minimumInteritemSpacing = [self.delegate collectionView:self.collectionView layout:self minimumInteritemSpacingForSectionAtIndex:index];
} else {
minimumInteritemSpacing = self.minimumInteritemSpacing;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:registerBackView:)]) {
NSString* className = [self.delegate collectionView:self.collectionView layout:self registerBackView:index];
if (className != nil && className.length > 0) {
NSAssert([[NSClassFromString(className) alloc]init]!=nil, @"代理collectionView:layout:registerBackView:里面必须返回有效的类名!");
[self registerClass:NSClassFromString(className) forDecorationViewOfKind:className];
} else {
[self registerClass:[ZLCollectionReusableView class] forDecorationViewOfKind:@"ZLCollectionReusableView"];
}
}
else {
[self registerClass:[ZLCollectionReusableView class] forDecorationViewOfKind:@"ZLCollectionReusableView"];
}
x = [self maxHeightWithSection:index];
y = edgeInsets.top;
//
if (headerW > 0) {
NSIndexPath *headerIndexPath = [NSIndexPath indexPathForItem:0 inSection:index];
ZLCollectionViewLayoutAttributes* headerAttr = [ZLCollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:headerIndexPath];
headerAttr.frame = CGRectMake(x, 0, headerW, self.collectionView.frame.size.height);
[headerAttr setValue:[NSValue valueWithCGRect:headerAttr.frame] forKey:@"orginalFrame"];
[self.attributesArray addObject:headerAttr];
[self.headerAttributesArray addObject:headerAttr];
}
x += headerW ;
CGFloat itemStartX = x;
CGFloat lastX = x;
if (itemCount > 0) {
x += edgeInsets.left;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:typeOfLayout:)]) {
self.layoutType = [self.delegate collectionView:self.collectionView layout:self typeOfLayout:index];
}
NSAssert((self.layoutType==LabelVerticalLayout||self.layoutType==ColumnLayout||self.layoutType==AbsoluteLayout), @"横向布局暂时只支持LabelVerticalLayout,ColumnLayout,AbsoluteLayout!");
//NSInteger columnCount = 1;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:columnCountOfSection:)]) {
self.columnCount = [self.delegate collectionView:self.collectionView layout:self columnCountOfSection:index];
}
//
CGFloat *columnWidths = (CGFloat *) malloc(self.columnCount * sizeof(CGFloat));
CGFloat itemHeight = 0.0;
if (self.layoutType == ClosedLayout) {
for (int i=0; i<self.columnCount; i++) {
columnWidths[i] = x;
}
itemHeight = (totalHeight - edgeInsets.top - edgeInsets.bottom - minimumInteritemSpacing * (self.columnCount - 1)) / self.columnCount;
}
NSInteger lastColumnIndex = 0;
NSMutableArray* arrayOfAbsolute = [NSMutableArray new]; //
for (int i=0; i<itemCount; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:index];
CGSize itemSize = CGSizeZero;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:sizeForItemAtIndexPath:)]) {
itemSize = [self.delegate collectionView:self.collectionView layout:self sizeForItemAtIndexPath:indexPath];
} else {
itemSize = self.itemSize;
}
ZLCollectionViewLayoutAttributes *attributes = [ZLCollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
NSInteger preRow = self.attributesArray.count - 1;
switch (self.layoutType) {
#pragma mark
case LabelVerticalLayout: {
//cell
if(preRow >= 0){
if(i > 0) {
ZLCollectionViewLayoutAttributes *preAttr = self.attributesArray[preRow];
y = preAttr.frame.origin.y + preAttr.frame.size.height + minimumInteritemSpacing;
if (y + itemSize.height > totalHeight - edgeInsets.bottom) {
y = edgeInsets.top;
x += itemSize.width + minimumLineSpacing;
}
}
}
attributes.frame = CGRectMake(x, y, itemSize.width, itemSize.height);
}
break;
case LabelHorizontalLayout: {
}
break;
#pragma mark |
case ClosedLayout: {
CGFloat max = CGFLOAT_MAX;
NSInteger column = 0;
if (self.columnSortType == Sequence) {
column = lastColumnIndex;
} else {
for (int i = 0; i < self.columnCount; i++) {
if (columnWidths[i] < max) {
max = columnWidths[i];
column = i;
}
}
}
CGFloat itemX = columnWidths[column];
CGFloat itemY = edgeInsets.top + (itemHeight+minimumInteritemSpacing)*column;
attributes.frame = CGRectMake(itemX, itemY, itemSize.width, itemHeight);
columnWidths[column] += (itemSize.width + minimumLineSpacing);
lastColumnIndex++;
if (lastColumnIndex >= self.columnCount) {
lastColumnIndex = 0;
}
}
break;
case AbsoluteLayout: {
CGRect itemFrame = CGRectZero;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:rectOfItem:)]) {
itemFrame = [self.delegate collectionView:self.collectionView layout:self rectOfItem:indexPath];
}
CGFloat absolute_x = x+itemFrame.origin.x;
CGFloat absolute_y = edgeInsets.top+itemFrame.origin.y;
CGFloat absolute_h = itemFrame.size.height;
if ((absolute_y+absolute_h>self.collectionView.frame.size.height-edgeInsets.bottom)&&(absolute_y<self.collectionView.frame.size.height-edgeInsets.top)) {
absolute_h -= (absolute_y+absolute_h-(self.collectionView.frame.size.height-edgeInsets.bottom));
}
CGFloat absolute_w = itemFrame.size.width;
attributes.frame = CGRectMake(absolute_x, absolute_y, absolute_w, absolute_h);
[arrayOfAbsolute addObject:attributes];
}
break;
default: {
}
break;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:transformOfItem:)]) {
attributes.transform3D = [self.delegate collectionView:self.collectionView layout:self transformOfItem:indexPath];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:zIndexOfItem:)]) {
attributes.zIndex = [self.delegate collectionView:self.collectionView layout:self zIndexOfItem:indexPath];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:alphaOfItem:)]) {
attributes.alpha = [self.delegate collectionView:self.collectionView layout:self alphaOfItem:indexPath];
}
attributes.indexPath = indexPath;
if (self.layoutType != PercentLayout) {
//if (![self.attributesArray containsObject:attributes]) {
[self.attributesArray addObject:attributes];
//}
}
if (self.layoutType == ClosedLayout) {
CGFloat max = 0;
for (int i = 0; i < self.columnCount; i++) {
if (columnWidths[i] > max) {
max = columnWidths[i];
}
}
lastX = max;
} else if (self.layoutType == AbsoluteLayout) {
if (i==itemCount-1) {
for (ZLCollectionViewLayoutAttributes* attr in arrayOfAbsolute) {
if (lastX < attr.frame.origin.x+attr.frame.size.width) {
lastX = attr.frame.origin.x+attr.frame.size.width;
}
}
}
} else {
lastX = attributes.frame.origin.x + attributes.frame.size.width;
}
}
free(columnWidths);
}
if (self.layoutType == ClosedLayout) {
if (itemCount > 0) {
lastX -= minimumLineSpacing;
}
}
if (itemCount > 0) {
lastX += edgeInsets.right;
}
//
if (footerW > 0) {
NSIndexPath *footerIndexPath = [NSIndexPath indexPathForItem:0 inSection:index];
ZLCollectionViewLayoutAttributes *footerAttr = [ZLCollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter withIndexPath:footerIndexPath];
footerAttr.frame = CGRectMake(lastX, 0, footerW, self.collectionView.frame.size.height);
[self.attributesArray addObject:footerAttr];
lastX += footerW;
}
#pragma mark
CGFloat backWidth = lastX-itemStartX+([self isAttachToTop:index]?headerW:0)-([self isAttachToBottom:index]?0:footerW);
if (backWidth < 0) {
backWidth = 0;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:registerBackView:)]) {
NSString* className = [self.delegate collectionView:self.collectionView layout:self registerBackView:index];
if (className != nil && className.length > 0) {
ZLCollectionViewBackgroundViewLayoutAttributes *attr = [ZLCollectionViewBackgroundViewLayoutAttributes layoutAttributesForDecorationViewOfKind:className withIndexPath:[NSIndexPath indexPathForRow:0 inSection:index]];
attr.frame = CGRectMake([self isAttachToTop:index]?itemStartX-headerW:itemStartX, 0, backWidth, self.collectionView.frame.size.height);
attr.zIndex = -1000;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backgroundViewMethodForSection:)]) {
if ([self.delegate collectionView:self.collectionView layout:self backgroundViewMethodForSection:index] != nil) {
[attr callMethod:[self.delegate collectionView:self.collectionView layout:self backgroundViewMethodForSection:index]];
}
}
[self.attributesArray addObject:attr];
} else {
ZLCollectionViewLayoutAttributes *attr = [ZLCollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:@"ZLCollectionReusableView" withIndexPath:[NSIndexPath indexPathForRow:0 inSection:index]];
attr.frame = CGRectMake([self isAttachToTop:index]?itemStartX-headerW:itemStartX, 0, backWidth, self.collectionView.frame.size.height);
attr.color = self.collectionView.backgroundColor;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backColorForSection:)]) {
attr.color = [self.delegate collectionView:self.collectionView layout:self backColorForSection:index];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backImageForSection:)]) {
attr.image = [self.delegate collectionView:self.collectionView layout:self backImageForSection:index];
}
attr.zIndex = -1000;
[self.attributesArray addObject:attr];
}
} else {
ZLCollectionViewLayoutAttributes *attr = [ZLCollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:@"ZLCollectionReusableView" withIndexPath:[NSIndexPath indexPathForRow:0 inSection:index]];
attr.frame = CGRectMake([self isAttachToTop:index]?itemStartX-headerW:itemStartX, 0, backWidth, self.collectionView.frame.size.height);
attr.color = self.collectionView.backgroundColor;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backColorForSection:)]) {
attr.color = [self.delegate collectionView:self.collectionView layout:self backColorForSection:index];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backImageForSection:)]) {
attr.image = [self.delegate collectionView:self.collectionView layout:self backImageForSection:index];
}
attr.zIndex = -1000;
[self.attributesArray addObject:attr];
}
self.collectionHeightsArray[index] = [NSNumber numberWithFloat:lastX];
}
[self forceSetIsNeedReCalculateAllLayout:NO];
// for (int i=0; i<self.attributesArray.count; i++) {
// ZLCollectionViewLayoutAttributes* attr = self.attributesArray[i];
// NSLog(@"第%d个-----%@",i,NSStringFromCGRect(attr.frame));
// }
}
#pragma mark - CollectionView
- (CGSize)collectionViewContentSize
{
if (self.collectionHeightsArray.count <= 0) {
return CGSizeMake(self.collectionView.frame.size.width, self.collectionView.frame.size.height);
}
CGFloat footerW = 0.0f;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]) {
footerW = [self.delegate collectionView:self.collectionView layout:self referenceSizeForFooterInSection:self.collectionHeightsArray.count-1].width;
} else {
footerW = self.footerReferenceSize.width;
}
UIEdgeInsets edgeInsets = UIEdgeInsetsZero;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)]) {
edgeInsets = [self.delegate collectionView:self.collectionView layout:self insetForSectionAtIndex:self.collectionHeightsArray.count-1];
} else {
edgeInsets = self.sectionInset;
}
return CGSizeMake([self.collectionHeightsArray[self.collectionHeightsArray.count-1] floatValue], self.collectionView.frame.size.height);
}
/**
X
@param section
@return Y
*/
- (CGFloat)maxHeightWithSection:(NSInteger)section {
if (section>0) {
return [self.collectionHeightsArray[section-1] floatValue];
} else {
return 0;
}
}
- (BOOL)isAttachToTop:(NSInteger)section {
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:attachToTop:)]) {
return [self.delegate collectionView:self.collectionView layout:self attachToTop:section];
}
return NO;
}
- (BOOL)isAttachToBottom:(NSInteger)section {
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:attachToBottom:)]) {
return [self.delegate collectionView:self.collectionView layout:self attachToBottom:section];
}
return NO;
}
@end

View File

@@ -0,0 +1,21 @@
//
// ZLCollectionViewLayoutAttributes.h
// ZLCollectionView
//
// Created by zhaoliang chen on 2018/7/9.
// Copyright © 2018年 zhaoliang chen. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ZLCollectionViewLayoutAttributes : UICollectionViewLayoutAttributes
@property(nonatomic,copy)UIColor* color;
@property(nonatomic,copy)UIImage* image;
//此属性只是header会单独设置其他均直接返回其frame属性
@property(nonatomic,assign,readonly)CGRect orginalFrame;
@end

View File

@@ -0,0 +1,30 @@
//
// ZLCollectionViewLayoutAttributes.m
// ZLCollectionView
//
// Created by zhaoliang chen on 2018/7/9.
// Copyright © 2018 zhaoliang chen. All rights reserved.
//
#import "ZLCollectionViewLayoutAttributes.h"
#import "ZLCollectionReusableView.h"
@implementation ZLCollectionViewLayoutAttributes
@synthesize orginalFrame = _orginalFrame;
+ (instancetype)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind withIndexPath:(NSIndexPath *)indexPath orginalFrmae:(CGRect)orginalFrame{
ZLCollectionViewLayoutAttributes *layoutAttributes = [super layoutAttributesForDecorationViewOfKind:decorationViewKind withIndexPath:indexPath];
[layoutAttributes setValue:[NSValue valueWithCGRect:orginalFrame] forKey:@"orginalFrame"];
layoutAttributes.frame = orginalFrame;
return layoutAttributes;
}
-(CGRect)orginalFrame {
if ([self.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
return _orginalFrame;
} else {
return self.frame;
}
}
@end

View File

@@ -0,0 +1,17 @@
//
// ZLCollectionViewVerticalLayout.h
// ZLCollectionView
//
// Created by zhaoliang chen on 2019/1/25.
// Copyright © 2019 zhaoliang chen. All rights reserved.
//
#import "ZLCollectionViewBaseFlowLayout.h"
NS_ASSUME_NONNULL_BEGIN
@interface ZLCollectionViewVerticalLayout : ZLCollectionViewBaseFlowLayout
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,750 @@
//
// ZLCollectionViewVerticalLayout.m
// ZLCollectionView
//
// Created by zhaoliang chen on 2019/1/25.
// Copyright © 2019 zhaoliang chen. All rights reserved.
//
#import "ZLCollectionViewVerticalLayout.h"
#import "ZLCollectionReusableView.h"
#import "ZLCollectionViewLayoutAttributes.h"
#import "ZLCollectionViewBackgroundViewLayoutAttributes.h"
@interface ZLCollectionViewVerticalLayout ()
@end
@implementation ZLCollectionViewVerticalLayout
#pragma mark -
- (instancetype)init {
self = [super init];
if (self) {
self.scrollDirection = UICollectionViewScrollDirectionVertical;
}
return self;
}
- (void)prepareLayout {
[super prepareLayout];
//怀...
if (!self.isNeedReCalculateAllLayout) {
return;
}
CGFloat totalWidth = self.collectionView.frame.size.width;
CGFloat x = 0;
CGFloat y = 0;
CGFloat headerH = 0;
CGFloat footerH = 0;
UIEdgeInsets edgeInsets = UIEdgeInsetsZero;
CGFloat minimumLineSpacing = 0;
CGFloat minimumInteritemSpacing = 0;
NSUInteger sectionCount = [self.collectionView numberOfSections];
self.attributesArray = [NSMutableArray new];
[self.headerAttributesArray removeAllObjects];
self.collectionHeightsArray = [NSMutableArray arrayWithCapacity:sectionCount];
for (int index= 0; index<sectionCount; index++) {
NSUInteger itemCount = [self.collectionView numberOfItemsInSection:index];
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]) {
headerH = [self.delegate collectionView:self.collectionView layout:self referenceSizeForHeaderInSection:index].height;
} else {
headerH = self.headerReferenceSize.height;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]) {
footerH = [self.delegate collectionView:self.collectionView layout:self referenceSizeForFooterInSection:index].height;
} else {
footerH = self.footerReferenceSize.height;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)]) {
edgeInsets = [self.delegate collectionView:self.collectionView layout:self insetForSectionAtIndex:index];
} else {
edgeInsets = self.sectionInset;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:minimumLineSpacingForSectionAtIndex:)]) {
minimumLineSpacing = [self.delegate collectionView:self.collectionView layout:self minimumLineSpacingForSectionAtIndex:index];
} else {
minimumLineSpacing = self.minimumLineSpacing;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:minimumInteritemSpacingForSectionAtIndex:)]) {
minimumInteritemSpacing = [self.delegate collectionView:self.collectionView layout:self minimumInteritemSpacingForSectionAtIndex:index];
} else {
minimumInteritemSpacing = self.minimumInteritemSpacing;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:registerBackView:)]) {
NSString* className = [self.delegate collectionView:self.collectionView layout:self registerBackView:index];
if (className != nil && className.length > 0) {
NSAssert([[NSClassFromString(className) alloc]init]!=nil, @"代理collectionView:layout:registerBackView:里面必须返回有效的类名!");
[self registerClass:NSClassFromString(className) forDecorationViewOfKind:className];
} else {
[self registerClass:[ZLCollectionReusableView class] forDecorationViewOfKind:@"ZLCollectionReusableView"];
}
} else {
[self registerClass:[ZLCollectionReusableView class] forDecorationViewOfKind:@"ZLCollectionReusableView"];
}
x = edgeInsets.left;
y = [self maxHeightWithSection:index];
//
if (headerH > 0) {
NSIndexPath *headerIndexPath = [NSIndexPath indexPathForItem:0 inSection:index];
ZLCollectionViewLayoutAttributes* headerAttr = [ZLCollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:headerIndexPath];
headerAttr.frame = CGRectMake(0, y, self.collectionView.frame.size.width, headerH);
[headerAttr setValue:[NSValue valueWithCGRect:headerAttr.frame] forKey:@"orginalFrame"];
[self.attributesArray addObject:headerAttr];
[self.headerAttributesArray addObject:headerAttr];
}
y += headerH ;
CGFloat itemStartY = y;
CGFloat lastY = y;
if (itemCount > 0) {
y += edgeInsets.top;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:typeOfLayout:)]) {
self.layoutType = [self.delegate collectionView:self.collectionView layout:self typeOfLayout:index];
}
//NSInteger columnCount = 1;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:columnCountOfSection:)]) {
self.columnCount = [self.delegate collectionView:self.collectionView layout:self columnCountOfSection:index];
}
//
CGFloat *columnHeight = (CGFloat *) malloc(self.columnCount * sizeof(CGFloat));
CGFloat itemWidth = 0.0;
if (self.layoutType == ClosedLayout) {
for (int i=0; i<self.columnCount; i++) {
columnHeight[i] = y;
}
itemWidth = (totalWidth - edgeInsets.left - edgeInsets.right - minimumInteritemSpacing * (self.columnCount - 1)) / self.columnCount;
}
CGFloat maxYOfPercent = -1;
CGFloat maxYOfFill = y;
NSMutableArray* arrayOfPercent = [NSMutableArray new]; //
NSMutableArray* arrayOfFill = [NSMutableArray new]; //
NSMutableArray* arrayOfAbsolute = [NSMutableArray new]; //
NSMutableArray *arrayXOfFill = [NSMutableArray new]; //
[arrayXOfFill addObject:self.isFloor?@(floor(edgeInsets.left)):@(edgeInsets.left)];
NSMutableArray *arrayYOfFill = [NSMutableArray new]; //
[arrayYOfFill addObject:self.isFloor?@(floor(maxYOfFill)):@(maxYOfFill)];
NSInteger lastColumnIndex = 0;
for (int i=0; i<itemCount; i++) {
BOOL singleColumnCount = NO;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:singleColumnCountOfIndexPath:)]) {
singleColumnCount = [self.delegate collectionView:self.collectionView
layout:self
singleColumnCountOfIndexPath:[NSIndexPath indexPathForItem:i inSection:index]];
}
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:index];
CGSize itemSize = CGSizeZero;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:sizeForItemAtIndexPath:)]) {
itemSize = [self.delegate collectionView:self.collectionView layout:self sizeForItemAtIndexPath:indexPath];
} else {
itemSize = self.itemSize;
}
ZLCollectionViewLayoutAttributes *attributes = [ZLCollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
NSInteger preRow = self.attributesArray.count - 1;
switch (self.layoutType) {
#pragma mark
case LabelLayout: {
//cell
if(preRow >= 0){
if(i > 0) {
ZLCollectionViewLayoutAttributes *preAttr = self.attributesArray[preRow];
x = preAttr.frame.origin.x + preAttr.frame.size.width + minimumInteritemSpacing;
if (x + itemSize.width > totalWidth - edgeInsets.right) {
x = edgeInsets.left;
y += itemSize.height + minimumLineSpacing;
}
}
}
if (itemSize.width > (totalWidth-edgeInsets.left-edgeInsets.right)) {
itemSize.width = (totalWidth-edgeInsets.left-edgeInsets.right);
}
attributes.frame = CGRectMake(x, y, itemSize.width, itemSize.height);
}
break;
#pragma mark
case ClosedLayout: {
if (singleColumnCount) {
CGFloat max = 0;
for (int i = 0; i < self.columnCount; i++) {
if (columnHeight[i] > max) {
max = columnHeight[i];
}
}
CGFloat itemX = 0;
CGFloat itemY = max;
attributes.frame = CGRectMake(edgeInsets.left + itemX, itemY, totalWidth-edgeInsets.left-edgeInsets.right, itemSize.height);
for (int i = 0; i < self.columnCount; i++) {
columnHeight[i] = max + itemSize.height + minimumLineSpacing;
}
lastColumnIndex = 0;
} else {
CGFloat max = CGFLOAT_MAX;
NSInteger column = 0;
if (self.columnSortType == Sequence) {
column = lastColumnIndex;
} else {
for (int i = 0; i < self.columnCount; i++) {
if (columnHeight[i] < max) {
max = columnHeight[i];
column = i;
}
}
}
CGFloat itemX = edgeInsets.left + (itemWidth+minimumInteritemSpacing)*column;
CGFloat itemY = columnHeight[column];
attributes.frame = CGRectMake(itemX, itemY, itemWidth, itemSize.height);
columnHeight[column] += (itemSize.height + minimumLineSpacing);
lastColumnIndex++;
if (lastColumnIndex >= self.columnCount) {
lastColumnIndex = 0;
}
}
}
break;
#pragma mark
case PercentLayout: {
CGFloat percent = 0.0f;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:percentOfRow:)]) {
percent = [self.delegate collectionView:self.collectionView layout:self percentOfRow:indexPath];
} else {
percent = 1;
}
if (percent > 1 || percent <= 0) {
percent = 1;
}
if (arrayOfPercent.count > 0) {
CGFloat totalPercent = 0;
for (NSDictionary* dic in arrayOfPercent) {
totalPercent += [dic[@"percent"] floatValue];
}
if ((totalPercent+percent) >= 1.0) {
if ((totalPercent+percent) < 1.1) {
//1.1
//
attributes.indexPath = indexPath;
attributes.frame = CGRectMake(0, 0, (itemSize.width>self.collectionView.frame.size.width-edgeInsets.left-edgeInsets.right)?self.collectionView.frame.size.width-edgeInsets.left-edgeInsets.right:itemSize.width, itemSize.height);
//
[arrayOfPercent addObject:[NSMutableDictionary dictionaryWithDictionary:@{@"item":attributes,@"percent":[NSNumber numberWithFloat:percent],@"indexPath":indexPath}]];
if ((totalPercent+percent) > 1) {
NSMutableDictionary* lastDic = [NSMutableDictionary dictionaryWithDictionary:arrayOfPercent.lastObject];
CGFloat lastPercent = 1.0;
for (NSInteger i=0; i<arrayOfPercent.count-1; i++) {
NSMutableDictionary* dic = arrayOfPercent[i];
lastPercent -= [dic[@"percent"] floatValue];
}
lastDic[@"percent"] = [NSNumber numberWithFloat:lastPercent];
[arrayOfPercent replaceObjectAtIndex:arrayOfPercent.count-1 withObject:lastDic];
}
CGFloat realWidth = totalWidth - edgeInsets.left - edgeInsets.right - (arrayOfPercent.count-1)*minimumInteritemSpacing;
for (NSInteger i=0; i<arrayOfPercent.count; i++) {
NSDictionary* dic = arrayOfPercent[i];
ZLCollectionViewLayoutAttributes *newAttributes = dic[@"item"];
CGFloat itemX = 0.0f;
if (i==0) {
itemX = edgeInsets.left;
} else {
ZLCollectionViewLayoutAttributes *preAttr = arrayOfPercent[i-1][@"item"];
itemX = preAttr.frame.origin.x + preAttr.frame.size.width + minimumInteritemSpacing;
}
newAttributes.frame = CGRectMake(itemX, (maxYOfPercent==-1)?y:maxYOfPercent+minimumLineSpacing, realWidth*[dic[@"percent"] floatValue], newAttributes.frame.size.height);
newAttributes.indexPath = dic[@"indexPath"];
//if (![self.attributesArray containsObject:newAttributes]) {
[self.attributesArray addObject:newAttributes];
//}
}
for (NSInteger i=0; i<arrayOfPercent.count; i++) {
NSDictionary* dic = arrayOfPercent[i];
ZLCollectionViewLayoutAttributes *item = dic[@"item"];
if ((item.frame.origin.y + item.frame.size.height) > maxYOfPercent) {
maxYOfPercent = (item.frame.origin.y + item.frame.size.height);
}
}
[arrayOfPercent removeAllObjects];
}
else {
//
if (arrayOfPercent.count > 0) {
for (int i=0; i<arrayOfPercent.count; i++) {
NSDictionary* dic = arrayOfPercent[i];
ZLCollectionViewLayoutAttributes* attr = dic[@"item"];
if ((attr.frame.origin.y+attr.frame.size.height) > maxYOfPercent ) {
maxYOfPercent = attr.frame.origin.y+attr.frame.size.height;
}
}
}
attributes.indexPath = indexPath;
attributes.frame = CGRectMake(edgeInsets.left, maxYOfPercent, (itemSize.width>self.collectionView.frame.size.width-edgeInsets.left-edgeInsets.right)?self.collectionView.frame.size.width-edgeInsets.left-edgeInsets.right:itemSize.width, itemSize.height);
for (int i=0; i<arrayOfPercent.count; i++) {
NSDictionary* dic = arrayOfPercent[i];
ZLCollectionViewLayoutAttributes* attr = dic[@"item"];
attr.indexPath = dic[@"indexPath"];
[self.attributesArray addObject:attr];
}
[arrayOfPercent removeAllObjects];
//
[arrayOfPercent addObject:[NSMutableDictionary dictionaryWithDictionary:@{@"item":attributes,@"percent":[NSNumber numberWithFloat:percent],@"indexPath":indexPath}]];
//item1item
if (i==itemCount-1) {
CGFloat realWidth = totalWidth - edgeInsets.left - edgeInsets.right - (arrayOfPercent.count-1)*minimumInteritemSpacing;
for (NSInteger i=0; i<arrayOfPercent.count; i++) {
NSDictionary* dic = arrayOfPercent[i];
ZLCollectionViewLayoutAttributes *newAttributes = dic[@"item"];
CGFloat itemX = 0.0f;
if (i==0) {
itemX = edgeInsets.left;
} else {
ZLCollectionViewLayoutAttributes *preAttr = arrayOfPercent[i-1][@"item"];
itemX = preAttr.frame.origin.x + preAttr.frame.size.width + minimumInteritemSpacing;
}
newAttributes.frame = CGRectMake(itemX, (maxYOfPercent==-1)?y:maxYOfPercent+minimumLineSpacing, realWidth*[dic[@"percent"] floatValue], newAttributes.frame.size.height);
newAttributes.indexPath = dic[@"indexPath"];
[self.attributesArray addObject:newAttributes];
}
for (NSInteger i=0; i<arrayOfPercent.count; i++) {
NSDictionary* dic = arrayOfPercent[i];
ZLCollectionViewLayoutAttributes *item = dic[@"item"];
if ((item.frame.origin.y + item.frame.size.height) > maxYOfPercent) {
maxYOfPercent = (item.frame.origin.y + item.frame.size.height);
}
}
[arrayOfPercent removeAllObjects];
}
}
}
else {
//
attributes.indexPath = indexPath;
NSDictionary* lastDicForPercent = arrayOfPercent[arrayOfPercent.count-1];
ZLCollectionViewLayoutAttributes *lastAttributesForPercent = lastDicForPercent[@"item"];
attributes.frame = CGRectMake(lastAttributesForPercent.frame.origin.x+lastAttributesForPercent.frame.size.width+minimumInteritemSpacing, lastAttributesForPercent.frame.origin.y, itemSize.width, itemSize.height);
//
[arrayOfPercent addObject:[NSMutableDictionary dictionaryWithDictionary:@{@"item":attributes,@"percent":[NSNumber numberWithFloat:percent],@"indexPath":indexPath}]];
//
if (i==itemCount-1) {
NSInteger space = arrayOfPercent.count-1;
if (arrayOfPercent.count > 0) {
NSDictionary* dic = arrayOfPercent[0];
BOOL equal = YES;
for (NSDictionary* d in arrayOfPercent) {
if ([dic[@"percent"] floatValue] != [d[@"percent"] floatValue]) {
equal = NO;
break;
}
}
if (equal == YES) {
space = (1/([dic[@"percent"] floatValue]))-1;
}
}
CGFloat realWidth = totalWidth - edgeInsets.left - edgeInsets.right - space*minimumInteritemSpacing;
for (NSInteger i=0; i<arrayOfPercent.count; i++) {
NSDictionary* dic = arrayOfPercent[i];
ZLCollectionViewLayoutAttributes *newAttributes = dic[@"item"];
CGFloat itemX = 0.0f;
if (i==0) {
itemX = edgeInsets.left;
} else {
ZLCollectionViewLayoutAttributes *preAttr = arrayOfPercent[i-1][@"item"];
itemX = preAttr.frame.origin.x + preAttr.frame.size.width + minimumInteritemSpacing;
}
newAttributes.frame = CGRectMake(itemX, (maxYOfPercent==-1)?y:maxYOfPercent+minimumLineSpacing, realWidth*[dic[@"percent"] floatValue], newAttributes.frame.size.height);
newAttributes.indexPath = dic[@"indexPath"];
//if (![self.attributesArray containsObject:newAttributes]) {
[self.attributesArray addObject:newAttributes];
//}
}
for (NSInteger i=0; i<arrayOfPercent.count; i++) {
NSDictionary* dic = arrayOfPercent[i];
ZLCollectionViewLayoutAttributes *item = dic[@"item"];
if ((item.frame.origin.y + item.frame.size.height) > maxYOfPercent) {
maxYOfPercent = (item.frame.origin.y + item.frame.size.height);
}
}
[arrayOfPercent removeAllObjects];
} else {
}
}
}
else {
//
attributes.indexPath = indexPath;
attributes.frame = CGRectMake(edgeInsets.left, (maxYOfPercent==-1)?y:maxYOfPercent+minimumLineSpacing, (itemSize.width>self.collectionView.frame.size.width-edgeInsets.left-edgeInsets.right)?self.collectionView.frame.size.width-edgeInsets.left-edgeInsets.right:itemSize.width, itemSize.height);
//
[arrayOfPercent addObject:[NSMutableDictionary dictionaryWithDictionary:@{@"item":attributes,@"percent":[NSNumber numberWithFloat:percent],@"indexPath":indexPath}]];
//
if (i==itemCount-1) {
NSInteger space = arrayOfPercent.count-1;
if (arrayOfPercent.count > 0) {
NSDictionary* dic = arrayOfPercent[0];
BOOL equal = YES;
for (NSDictionary* d in arrayOfPercent) {
if ([dic[@"percent"] floatValue] != [d[@"percent"] floatValue]) {
equal = NO;
break;
}
}
if (equal == YES) {
space = (1/([dic[@"percent"] floatValue]))-1;
}
}
CGFloat realWidth = totalWidth - edgeInsets.left - edgeInsets.right - space*minimumInteritemSpacing;
for (NSInteger i=0; i<arrayOfPercent.count; i++) {
NSDictionary* dic = arrayOfPercent[i];
ZLCollectionViewLayoutAttributes *newAttributes = dic[@"item"];
CGFloat itemX = 0.0f;
if (i==0) {
itemX = edgeInsets.left;
} else {
ZLCollectionViewLayoutAttributes *preAttr = arrayOfPercent[i-1][@"item"];
itemX = preAttr.frame.origin.x + preAttr.frame.size.width + minimumInteritemSpacing;
}
newAttributes.frame = CGRectMake(itemX, (maxYOfPercent==-1)?y:maxYOfPercent+minimumLineSpacing, realWidth*[dic[@"percent"] floatValue], newAttributes.frame.size.height);
newAttributes.indexPath = dic[@"indexPath"];
[self.attributesArray addObject:newAttributes];
}
for (NSInteger i=0; i<arrayOfPercent.count; i++) {
NSDictionary* dic = arrayOfPercent[i];
ZLCollectionViewLayoutAttributes *item = dic[@"item"];
if ((item.frame.origin.y + item.frame.size.height) > maxYOfPercent) {
maxYOfPercent = (item.frame.origin.y + item.frame.size.height);
}
}
[arrayOfPercent removeAllObjects];
} else {
}
}
}
break;
#pragma mark
case FillLayout: {
BOOL qualified = YES;
if (arrayOfFill.count == 0) {
attributes.frame = CGRectMake(self.isFloor?floor(edgeInsets.left):edgeInsets.left, self.isFloor?floor(maxYOfFill):maxYOfFill, self.isFloor?floor(itemSize.width):itemSize.width, self.isFloor?floor(itemSize.height):itemSize.height);
[arrayOfFill addObject:attributes];
} else {
BOOL leftQualified = NO;
BOOL topQualified = NO;
for (NSNumber* yFill in arrayYOfFill) {
for (NSNumber* xFill in arrayXOfFill) {
qualified = YES;
CGFloat attrX = self.isFloor?(floor([xFill floatValue])==floor(edgeInsets.left)?floor([xFill floatValue]):(floor([xFill floatValue])+minimumInteritemSpacing)):([xFill floatValue]==edgeInsets.left?[xFill floatValue]:([xFill floatValue]+minimumInteritemSpacing));
CGFloat attrY = (fabs([yFill floatValue] - maxYOfFill) < 0.0001) ? [yFill floatValue] : [yFill floatValue] + minimumLineSpacing;
if (self.isFloor) {
attrY = floor([yFill floatValue])==floor(maxYOfFill)?floor([yFill floatValue]):floor([yFill floatValue])+floor(minimumLineSpacing);
}
attributes.frame = CGRectMake(attrX, attrY, self.isFloor?floor(itemSize.width):itemSize.width, self.isFloor?floor(itemSize.height):itemSize.height);
if (self.isFloor) {
if (floor(attributes.frame.origin.x)+floor(attributes.frame.size.width) > floor(totalWidth)-floor(edgeInsets.right)) {
qualified = NO;
break;
}
} else {
if (attributes.frame.origin.x+attributes.frame.size.width > totalWidth-edgeInsets.right+self.xBeyond) {
qualified = NO;
break;
}
}
for (ZLCollectionViewLayoutAttributes* attr in arrayOfFill) {
if (CGRectIntersectsRect(attributes.frame, attr.frame)) {
qualified = NO;
break;
}
}
if (qualified == NO) {
continue;
} else {
// cell
CGPoint leftPt = CGPointMake(attributes.frame.origin.x - floor(minimumInteritemSpacing), attributes.frame.origin.y);
CGRect leftRect = CGRectZero;
for (ZLCollectionViewLayoutAttributes* attr in arrayOfFill) {
if (CGRectContainsPoint(attr.frame, leftPt)) {
leftRect = attr.frame;
break;
}
}
if (CGRectEqualToRect(leftRect, CGRectZero)) {
leftQualified = YES;
} else {
if (attributes.frame.origin.x - (leftRect.origin.x + leftRect.size.width) >= floor(minimumInteritemSpacing)) {
leftQualified = YES;
} else if (floor(leftRect.origin.x) + floor(leftRect.size.width) <= leftPt.x) {
leftQualified = YES;
} else {
CGRect rc = attributes.frame;
rc.origin.x = leftRect.origin.x + leftRect.size.width + floor(minimumInteritemSpacing);
attributes.frame = rc;
for (ZLCollectionViewLayoutAttributes* attr in arrayOfFill) {
if (CGRectIntersectsRect(attributes.frame, attr.frame)) {
qualified = NO;
break;
}
}
}
}
// cell
CGPoint topPt = CGPointMake(attributes.frame.origin.x, attributes.frame.origin.y - floor(minimumLineSpacing));
CGRect topRect = CGRectZero;
for (ZLCollectionViewLayoutAttributes* attr in arrayOfFill) {
if (CGRectContainsPoint(attr.frame, topPt)) {
topRect = attr.frame;
break;
}
}
if (CGRectEqualToRect(topRect, CGRectZero)) {
topQualified = YES;
} else {
if (attributes.frame.origin.y - (topRect.origin.y + topRect.size.height) >= floor(minimumLineSpacing)) {
topQualified = YES;
} else if (floor(topRect.origin.y) + floor(topRect.size.height) <= topPt.y) {
topQualified = YES;
} else {
CGRect rc = attributes.frame;
rc.origin.y = topRect.origin.y + topRect.size.height + floor(minimumLineSpacing);
attributes.frame = rc;
for (ZLCollectionViewLayoutAttributes* attr in arrayOfFill) {
if (CGRectIntersectsRect(attributes.frame, attr.frame)) {
qualified = NO;
break;
}
}
}
}
if (leftQualified == YES && topQualified == YES) {
qualified = YES;
break;
}
}
}
if (qualified == YES) {
break;
}
}
if (qualified == YES) {
//NSLog(@"第%d个,合格的矩形区域=%@",i,NSStringFromCGRect(attributes.frame));
[arrayOfFill addObject:attributes];
}
}
if (qualified == YES) {
if (![arrayXOfFill containsObject:self.isFloor?@(floor(attributes.frame.origin.x)):@(attributes.frame.origin.x)]) {
[arrayXOfFill addObject:self.isFloor?@(floor(attributes.frame.origin.x)):@(attributes.frame.origin.x)];
}
if (![arrayXOfFill containsObject:self.isFloor?@(floor(attributes.frame.origin.x+attributes.frame.size.width)):@(attributes.frame.origin.x+attributes.frame.size.width)]) {
[arrayXOfFill addObject:self.isFloor?@(floor(attributes.frame.origin.x+attributes.frame.size.width)):@(attributes.frame.origin.x+attributes.frame.size.width)];
}
if (![arrayYOfFill containsObject:self.isFloor?@(floor(attributes.frame.origin.y)):@(attributes.frame.origin.y)]) {
[arrayYOfFill addObject:self.isFloor?@(floor(attributes.frame.origin.y)):@(attributes.frame.origin.y)];
}
if (![arrayYOfFill containsObject:self.isFloor?@(floor(attributes.frame.origin.y+attributes.frame.size.height)):@(attributes.frame.origin.y+attributes.frame.size.height)]) {
[arrayYOfFill addObject:self.isFloor?@(floor(attributes.frame.origin.y+attributes.frame.size.height)):@(attributes.frame.origin.y+attributes.frame.size.height)];
}
[arrayXOfFill sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
return [obj1 floatValue] > [obj2 floatValue];
}];
[arrayYOfFill sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
return [obj1 floatValue] > [obj2 floatValue];
}];
}
}
break;
#pragma mark
case AbsoluteLayout: {
CGRect itemFrame = CGRectZero;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:rectOfItem:)]) {
itemFrame = [self.delegate collectionView:self.collectionView layout:self rectOfItem:indexPath];
}
CGFloat absolute_x = edgeInsets.left+itemFrame.origin.x;
CGFloat absolute_y = y+itemFrame.origin.y;
CGFloat absolute_w = itemFrame.size.width;
if ((absolute_x+absolute_w>self.collectionView.frame.size.width-edgeInsets.right)&&(absolute_x<self.collectionView.frame.size.width-edgeInsets.right)) {
absolute_w -= (absolute_x+absolute_w-(self.collectionView.frame.size.width-edgeInsets.right));
}
CGFloat absolute_h = itemFrame.size.height;
attributes.frame = CGRectMake(absolute_x, absolute_y, absolute_w, absolute_h);
[arrayOfAbsolute addObject:attributes];
}
break;
default: {
//NSLog(@"%@",NSStringFromCGRect(attributes.frame));
}
break;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:transformOfItem:)]) {
attributes.transform3D = [self.delegate collectionView:self.collectionView layout:self transformOfItem:indexPath];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:zIndexOfItem:)]) {
attributes.zIndex = [self.delegate collectionView:self.collectionView layout:self zIndexOfItem:indexPath];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:alphaOfItem:)]) {
attributes.alpha = [self.delegate collectionView:self.collectionView layout:self alphaOfItem:indexPath];
}
attributes.indexPath = indexPath;
if (self.layoutType != PercentLayout) {
//if (![self.attributesArray containsObject:attributes]) {
[self.attributesArray addObject:attributes];
//}
}
if (self.layoutType == ClosedLayout) {
CGFloat max = 0;
for (int i = 0; i < self.columnCount; i++) {
if (columnHeight[i] > max) {
max = columnHeight[i];
}
}
lastY = max;
} else if (self.layoutType == PercentLayout) {
lastY = maxYOfPercent;
} else if (self.layoutType == FillLayout) {
if (i==itemCount-1) {
for (ZLCollectionViewLayoutAttributes* attr in arrayOfFill) {
if (maxYOfFill < attr.frame.origin.y+attr.frame.size.height) {
maxYOfFill = attr.frame.origin.y+attr.frame.size.height;
}
}
}
lastY = maxYOfFill;
} else if (self.layoutType == AbsoluteLayout) {
if (i==itemCount-1) {
for (ZLCollectionViewLayoutAttributes* attr in arrayOfAbsolute) {
if (lastY < attr.frame.origin.y+attr.frame.size.height) {
lastY = attr.frame.origin.y+attr.frame.size.height;
}
}
}
} else {
lastY = attributes.frame.origin.y + attributes.frame.size.height;
}
}
free(columnHeight);
}
if (self.layoutType == ClosedLayout) {
if (itemCount > 0) {
lastY -= minimumLineSpacing;
}
}
if (itemCount > 0) {
lastY += edgeInsets.bottom;
}
//
if (footerH > 0) {
NSIndexPath *footerIndexPath = [NSIndexPath indexPathForItem:0 inSection:index];
ZLCollectionViewLayoutAttributes *footerAttr = [ZLCollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter withIndexPath:footerIndexPath];
footerAttr.frame = CGRectMake(0, lastY, self.collectionView.frame.size.width, footerH);
[self.attributesArray addObject:footerAttr];
lastY += footerH;
}
#pragma mark
CGFloat backHeight = lastY-itemStartY+([self isAttachToTop:index]?headerH:0)-([self isAttachToBottom:index]?0:footerH);
if (backHeight < 0) {
backHeight = 0;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:registerBackView:)]) {
NSString* className = [self.delegate collectionView:self.collectionView layout:self registerBackView:index];
if (className != nil && className.length > 0) {
ZLCollectionViewBackgroundViewLayoutAttributes *attr = [ZLCollectionViewBackgroundViewLayoutAttributes layoutAttributesForDecorationViewOfKind:className withIndexPath:[NSIndexPath indexPathForRow:0 inSection:index]];
attr.frame = CGRectMake(0, [self isAttachToTop:index]?itemStartY-headerH:itemStartY, self.collectionView.frame.size.width, backHeight);
attr.zIndex = -1000;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backgroundViewMethodForSection:)]) {
if ([self.delegate collectionView:self.collectionView layout:self backgroundViewMethodForSection:index] != nil) {
[attr callMethod:[self.delegate collectionView:self.collectionView layout:self backgroundViewMethodForSection:index]];
}
}
[self.attributesArray addObject:attr];
} else {
ZLCollectionViewLayoutAttributes *attr = [ZLCollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:@"ZLCollectionReusableView" withIndexPath:[NSIndexPath indexPathForRow:0 inSection:index]];
attr.frame = CGRectMake(0, [self isAttachToTop:index]?itemStartY-headerH:itemStartY, self.collectionView.frame.size.width, backHeight);
attr.color = self.collectionView.backgroundColor;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backColorForSection:)]) {
attr.color = [self.delegate collectionView:self.collectionView layout:self backColorForSection:index];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backImageForSection:)]) {
attr.image = [self.delegate collectionView:self.collectionView layout:self backImageForSection:index];
}
attr.zIndex = -1000;
[self.attributesArray addObject:attr];
}
} else {
ZLCollectionViewLayoutAttributes *attr = [ZLCollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:@"ZLCollectionReusableView" withIndexPath:[NSIndexPath indexPathForRow:0 inSection:index]];
attr.frame = CGRectMake(0, [self isAttachToTop:index]?itemStartY-headerH:itemStartY, self.collectionView.frame.size.width, backHeight);
attr.color = self.collectionView.backgroundColor;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backColorForSection:)]) {
attr.color = [self.delegate collectionView:self.collectionView layout:self backColorForSection:index];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:backImageForSection:)]) {
attr.image = [self.delegate collectionView:self.collectionView layout:self backImageForSection:index];
}
attr.zIndex = -1000;
[self.attributesArray addObject:attr];
}
self.collectionHeightsArray[index] = [NSNumber numberWithFloat:lastY];
}
// for (ZLCollectionViewLayoutAttributes* attr in self.attributesArray) {
// NSLog(@"类型=%@,尺寸=%@",attr.representedElementKind, NSStringFromCGRect(attr.frame));
// }
[self forceSetIsNeedReCalculateAllLayout:NO];
}
#pragma mark - CollectionView
- (CGSize)collectionViewContentSize
{
if (self.collectionHeightsArray.count <= 0) {
return CGSizeMake(self.collectionView.frame.size.width, self.collectionView.frame.size.height);
}
CGFloat footerH = 0.0f;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]) {
footerH = [self.delegate collectionView:self.collectionView layout:self referenceSizeForFooterInSection:self.collectionHeightsArray.count-1].height;
} else {
footerH = self.footerReferenceSize.height;
}
UIEdgeInsets edgeInsets = UIEdgeInsetsZero;
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)]) {
edgeInsets = [self.delegate collectionView:self.collectionView layout:self insetForSectionAtIndex:self.collectionHeightsArray.count-1];
} else {
edgeInsets = self.sectionInset;
}
return CGSizeMake(self.collectionView.frame.size.width, [self.collectionHeightsArray[self.collectionHeightsArray.count-1] floatValue]);// + edgeInsets.bottom + footerH);
}
/**
Y
@param section
@return Y
*/
- (CGFloat)maxHeightWithSection:(NSInteger)section {
if (section>0) {
return [self.collectionHeightsArray[section-1] floatValue];
} else {
return 0;
}
}
- (BOOL)isAttachToTop:(NSInteger)section {
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:attachToTop:)]) {
return [self.delegate collectionView:self.collectionView layout:self attachToTop:section];
}
return NO;
}
- (BOOL)isAttachToBottom:(NSInteger)section {
if (self.delegate && [self.delegate respondsToSelector:@selector(collectionView:layout:attachToBottom:)]) {
return [self.delegate collectionView:self.collectionView layout:self attachToBottom:section];
}
return NO;
}
@end