提交
65
QXLive/Third/OpenUDID/OpenUDID.h
Executable 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
@@ -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 generated—usually the
|
||||
// Ethernet hardware address—and 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
|
||||
BIN
QXLive/Third/SDK/ATAuthSDK.framework/ATAuthSDK
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 240 B |
|
After Width: | Height: | Size: 309 B |
BIN
QXLive/Third/SDK/ATAuthSDK.framework/ATAuthSDK.bundle/icon_nav_back_gray@2x.png
Executable file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
QXLive/Third/SDK/ATAuthSDK.framework/ATAuthSDK.bundle/icon_nav_back_gray@3x.png
Executable file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
QXLive/Third/SDK/ATAuthSDK.framework/ATAuthSDK.bundle/icon_nav_back_light@2x.png
Executable file
|
After Width: | Height: | Size: 573 B |
BIN
QXLive/Third/SDK/ATAuthSDK.framework/ATAuthSDK.bundle/icon_nav_back_light@3x.png
Executable file
|
After Width: | Height: | Size: 950 B |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
24
QXLive/Third/SDK/ATAuthSDK.framework/Headers/ATAuthSDK.h
Normal 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"
|
||||
|
||||
37
QXLive/Third/SDK/ATAuthSDK.framework/Headers/PNSReporter.h
Normal 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
|
||||
88
QXLive/Third/SDK/ATAuthSDK.framework/Headers/PNSReturnCode.h
Normal 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 */
|
||||
137
QXLive/Third/SDK/ATAuthSDK.framework/Headers/TXCommonHandler.h
Normal 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
|
||||
81
QXLive/Third/SDK/ATAuthSDK.framework/Headers/TXCommonUtils.h
Normal 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;
|
||||
|
||||
/**
|
||||
获取当前上网卡网络类型,比如WiFi,4G
|
||||
@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
|
||||
441
QXLive/Third/SDK/ATAuthSDK.framework/Headers/TXCustomModel.h
Normal 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,
|
||||
};
|
||||
|
||||
/**
|
||||
* 构建控件的frame,view布局时会调用该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;
|
||||
|
||||
/** 构建标题栏的frame,view布局或布局发生变化时调用,不实现则按默认处理,实现时仅有height生效 */
|
||||
@property (nonatomic, copy) PNSBuildFrameBlock alertTitleBarFrameBlock;
|
||||
/** 构建标题栏标题的frame,view布局或布局发生变化时调用,不实现则按默认处理 */
|
||||
@property (nonatomic, copy) PNSBuildFrameBlock alertTitleFrameBlock;
|
||||
/** 构建标题栏右侧关闭按钮的frame,view布局或布局发生变化时调用,不实现则按默认处理 */
|
||||
@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;
|
||||
|
||||
/** 构建导航栏返回按钮的frame,view布局或布局发生变化时调用,不实现则按默认处理 */
|
||||
@property (nonatomic, copy) PNSBuildFrameBlock navBackButtonFrameBlock;
|
||||
/** 构建导航栏标题的frame,view布局或布局发生变化时调用,不实现则按默认处理 */
|
||||
@property (nonatomic, copy) PNSBuildFrameBlock navTitleFrameBlock;
|
||||
/** 构建导航栏右侧more view的frame,view布局或布局发生变化时调用,不实现则按默认处理,边界 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的frame,view布局或布局发生变化时调用,不实现则按默认处理 */
|
||||
@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的frame,view布局或布局发生变化时调用,不实现则按默认处理 */
|
||||
@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;
|
||||
|
||||
/**
|
||||
* 构建号码的frame,view布局或布局发生变化时调用,只有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;
|
||||
/**
|
||||
* 构建登录按钮的frame,view布局或布局发生变化时调用,不实现则按默认处理
|
||||
* 注:不能超出父视图 content view,height不能小于20,width不能小于父视图宽度的一半
|
||||
*/
|
||||
@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)的frame,view布局或布局发生变化时调用,不实现则按默认处理
|
||||
* 如果设置的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的frame,view布局或布局发生变化时调用,不实现则按默认处理 */
|
||||
@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 view,height不能小于50,width不能小于0,默认屏幕居中,宽为屏幕的宽度减掉80,高为200 */
|
||||
@property (nonatomic, copy) PNSBuildFrameBlock privacyAlertFrameBlock;
|
||||
/** 二次隐私协议弹窗标题尺寸,默认x=0,y=0,width=弹窗宽度,最小宽度为100,height=根据文本计算的高度,最小高度为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
|
||||
|
||||
BIN
QXLive/Third/SDK/ATAuthSDK.framework/Info.plist
Normal file
@@ -0,0 +1,6 @@
|
||||
framework module ATAuthSDK {
|
||||
umbrella header "ATAuthSDK.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
||||
42
QXLive/Third/SDK/ATAuthSDK.framework/PrivacyInfo.xcprivacy
Normal 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>
|
||||
42
QXLive/Third/SDK/TencentOpenAPI.xcframework/Info.plist
Normal 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>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,4 @@
|
||||
module TencentOpenApi{
|
||||
umbrella header "TencentOpenApiUmbrellaHeader.h"
|
||||
export *
|
||||
}
|
||||
@@ -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
|
||||
@@ -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.5s~8s)
|
||||
@"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
|
||||
@@ -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
|
||||
|
||||
@@ -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’,‘AppInvitation’,‘AppChallenge’,‘AppGiftRequest’
|
||||
*/
|
||||
- (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
|
||||
@@ -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 */
|
||||
|
||||
@@ -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>
|
||||
@@ -0,0 +1,4 @@
|
||||
module TencentOpenApi{
|
||||
umbrella header "TencentOpenApiUmbrellaHeader.h"
|
||||
export *
|
||||
}
|
||||
@@ -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
|
||||
@@ -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.5s~8s)
|
||||
@"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
|
||||
@@ -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
|
||||
|
||||
@@ -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’,‘AppInvitation’,‘AppChallenge’,‘AppGiftRequest’
|
||||
*/
|
||||
- (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
|
||||
@@ -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 */
|
||||
|
||||
@@ -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>
|
||||
48
QXLive/Third/SDK/YTXMonitor.framework/Headers/ACMLogger.h
Normal 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
|
||||
73
QXLive/Third/SDK/YTXMonitor.framework/Headers/ACMManager.h
Normal 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
|
||||
55
QXLive/Third/SDK/YTXMonitor.framework/Headers/ACMMonitor.h
Normal 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
|
||||
17
QXLive/Third/SDK/YTXMonitor.framework/Headers/ACMProtocol.h
Normal 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
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
#import "ACMManager.h"
|
||||
#import "ACMProtocol.h"
|
||||
BIN
QXLive/Third/SDK/YTXMonitor.framework/Info.plist
Normal file
@@ -0,0 +1,6 @@
|
||||
framework module YTXMonitor {
|
||||
umbrella header "YTXMonitor.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
||||
34
QXLive/Third/SDK/YTXMonitor.framework/PrivacyInfo.xcprivacy
Normal 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>
|
||||
BIN
QXLive/Third/SDK/YTXMonitor.framework/YTXMonitor
Normal file
100
QXLive/Third/SDK/YTXOperators.framework/Headers/YTXNetUtils.h
Normal 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));
|
||||
|
||||
/**
|
||||
获取当前上网卡网络类型,比如WiFi,4G
|
||||
@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
|
||||
@@ -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"
|
||||
@@ -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
|
||||
BIN
QXLive/Third/SDK/YTXOperators.framework/Info.plist
Normal file
@@ -0,0 +1,6 @@
|
||||
framework module YTXOperators {
|
||||
umbrella header "YTXOperators.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
||||
@@ -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>
|
||||
BIN
QXLive/Third/SDK/YTXOperators.framework/YTXOperators
Normal file
25
QXLive/Third/ZLCollectionViewFlowLayout/ZLBaseEventModel.h
Normal 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
|
||||
35
QXLive/Third/ZLCollectionViewFlowLayout/ZLBaseEventModel.m
Normal 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
|
||||
25
QXLive/Third/ZLCollectionViewFlowLayout/ZLCellFakeView.h
Normal 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
|
||||
100
QXLive/Third/ZLCollectionViewFlowLayout/ZLCellFakeView.m
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
136
QXLive/Third/ZLCollectionViewFlowLayout/ZLCollectionViewBaseFlowLayout.h
Executable 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
|
||||
527
QXLive/Third/ZLCollectionViewFlowLayout/ZLCollectionViewBaseFlowLayout.m
Executable 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 - 所有cell和view的布局属性
|
||||
- (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) {
|
||||
/*
|
||||
这里需要注意,在悬浮的情况下改变了headerAtt的frame
|
||||
在滑出header又滑回来时,headerAtt已经被修改过,需要改回原始值
|
||||
否则header无法正确归位
|
||||
*/
|
||||
if ([attriture isKindOfClass:[ZLCollectionViewLayoutAttributes class]]) {
|
||||
attriture.frame = ((ZLCollectionViewLayoutAttributes*)attriture).orginalFrame;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return self.attributesArray;
|
||||
}
|
||||
}
|
||||
|
||||
// 1.4.8暂时去掉layoutAttributesForItemAtIndexPath方法
|
||||
//- (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
|
||||
@@ -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
|
||||
362
QXLive/Third/ZLCollectionViewFlowLayout/ZLCollectionViewHorzontalLayout.m
Executable 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
|
||||
21
QXLive/Third/ZLCollectionViewFlowLayout/ZLCollectionViewLayoutAttributes.h
Executable 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
|
||||
30
QXLive/Third/ZLCollectionViewFlowLayout/ZLCollectionViewLayoutAttributes.m
Executable 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
|
||||
@@ -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
|
||||
750
QXLive/Third/ZLCollectionViewFlowLayout/ZLCollectionViewVerticalLayout.m
Executable 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}]];
|
||||
//如果该行item总比例还是小于1,但是item已经是最后一个
|
||||
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
|
||||