├── .gitignore ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── TJActivityViewController.h └── TJActivityViewController.m └── TJActivityViewController.podspec /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | .swiftpm 31 | 32 | # CocoaPods 33 | # 34 | # We recommend against adding the Pods directory to your .gitignore. However 35 | # you should judge for yourself, the pros and cons are mentioned at: 36 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 37 | # 38 | # Pods/ 39 | 40 | # Carthage 41 | # 42 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 43 | # Carthage/Checkouts 44 | 45 | Carthage/Build 46 | 47 | # fastlane 48 | # 49 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 50 | # screenshots whenever they are needed. 51 | # For more information about the recommended setup visit: 52 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 53 | 54 | fastlane/report.xml 55 | fastlane/Preview.html 56 | fastlane/screenshots/**/*.png 57 | fastlane/test_output 58 | 59 | # Code Injection 60 | # 61 | # After new code Injection tools there's a generated folder /iOSInjectionProject 62 | # https://github.com/johnno1962/injectionforxcode 63 | 64 | iOSInjectionProject/ 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, Tim Johnsen 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.7 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "TJActivityViewController", 7 | platforms: [.iOS(.v13), .macCatalyst(.v13)], 8 | products: [ 9 | .library( 10 | name: "TJActivityViewController", 11 | targets: ["TJActivityViewController"] 12 | ) 13 | ], 14 | targets: [ 15 | .target( 16 | name: "TJActivityViewController", 17 | path: ".", 18 | publicHeadersPath: "." 19 | ) 20 | ] 21 | ) 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TJActivityViewController 2 | 3 | `TJActivityViewController` is a handy subclass of [`UIActivityViewController`](https://developer.apple.com/documentation/uikit/uiactivityviewcontroller?language=objc) that allows you to override actions easily without implementing [`UIActivityItemSource`](https://developer.apple.com/documentation/uikit/uiactivityitemsource?language=objc). 4 | 5 | ## Usage 6 | 7 | You can override a particular activity or one matching a regex with a block using the following methods: 8 | 9 | ```objc 10 | UIImage *imageToShare = /* ... */; 11 | TJActivityViewController *viewController = [[TJActivityViewController alloc] initWithActivityItems:@[imageToShare] applicationActivities:nil]; 12 | 13 | // Override Twitter sharing with a block. 14 | [viewController overrideActivityType:UIActivityTypePostToTwitter withBlock:^{ 15 | // Launch a custom Twitter share action. 16 | }]; 17 | 18 | // Override actions matching a regex. 19 | [viewController overrideActivityTypeMatchingRegex:@"com\\.foo\\.bar\\..*" withBlock:^{ 20 | // Custom sharing actions. 21 | }]; 22 | ``` 23 | 24 | You can also override the item that's passed to a particular activity with a block using the following method: 25 | 26 | ```objc 27 | // TJActivityViewControllerSnapchatActivityType and a few others are provided for convenience 28 | [viewController overrideItemForActivityType:TJActivityViewControllerSnapchatActivityType // Snapchat's share extension 29 | withBlock:^id { 30 | return /* a 9:16 image cropped just for Snapchat. */; 31 | }]; 32 | ``` 33 | 34 | In iOS 13 and above, you can set the [link preview](https://developer.apple.com/videos/play/wwdc2019/262/?t=301) on an instance of `TJActivityViewController` using the `linkMetadata` property. 35 | 36 | ```objc 37 | LPLinkMetadata *linkMetadata = [LPLinkMetadata new]; 38 | linkMetadata.title = @"My Cool Link"; 39 | linkMetadata.imageProvider = /* an image provider for your link preview */; 40 | activityViewController.linkMetadata = linkMetadata; 41 | ``` 42 | 43 | ## Why 44 | 45 | While `UIActivityItemSource` is a powerful API for sharing through `UIActivityViewController`, it's a bit cumbersome to use and has limitations when it comes to overriding actions. Many products now have custom sharing SDKs that are more powerful than their built-in share extensions, but developers who use `UIActivityViewController` are forced into using the less powerful share extensions. 46 | 47 | Some developers have taken to building their own bespoke sharing menus to work around this limitation of `UIActivityViewController`, but that leads to fragmented and incomplete sharing experiences across products. `TJActivityViewController` gives you the best of both worlds: you get to use the standard iOS share menu, and can still customize sharing options with your own special touches. 48 | 49 | For a more detailed blog post on the subject, see [here](https://medium.com/p/f24410308699). 50 | 51 | ## About 52 | 53 | I originally wrote `TJActivityViewController` for [Close-up](https://closeup.wtf) and have since also used it in [Burst](http://theburstapp.com). If you decide to use `TJActivityViewController` [let me know](https://twitter.com/timonus)! 54 | -------------------------------------------------------------------------------- /Sources/TJActivityViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TJActivityViewController.h 3 | // 4 | // Created by Tim Johnsen on 1/1/15. 5 | // Copyright (c) 2015 Tim Johnsen. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | #define INCLUDE_RECIPIENTS 0 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | typedef NSString *TJActivityTypeRegex; 15 | 16 | extern TJActivityTypeRegex const TJActivityViewControllerFacebookRegexString; 17 | extern TJActivityTypeRegex const TJActivityViewControllerFacebookMessengerRegexString; 18 | extern TJActivityTypeRegex const TJActivityViewControllerInstagramRegexString; 19 | extern UIActivityType const TJActivityViewControllerSnapchatActivityType; 20 | extern TJActivityTypeRegex const TJActivityTypeSaveToCameraRollRegexString; // Compatible with Mac Catalyst 21 | extern UIActivityType const TJActivityViewControllerTikTokActivityType; 22 | extern UIActivityType const TJActivityViewControllerThreadsActivityType; 23 | extern UIActivityType const TJActivityViewControllerRetroActivityType; 24 | extern UIActivityType const TJActivityViewControllerBlueskyActivityType; 25 | 26 | typedef void (^TJActivityViewControllerOverrideBlock)(UIActivityType activityType, void (^completionHandler)(UIActivityType __nullable activityType, BOOL success)); 27 | 28 | @interface TJActivityViewController : UIActivityViewController 29 | 30 | /** 31 | Overrides a particular activity type with a block. 32 | @param activityType The activity type to override. 33 | @param block The block to execute in place of the given activity. 34 | */ 35 | - (void)overrideActivityType:(UIActivityType)activityType withBlock:(TJActivityViewControllerOverrideBlock)block; 36 | #if INCLUDE_RECIPIENTS 37 | - (void)overrideActivityType:(UIActivityType)activityType includeSpecificShareRecipients:(const BOOL)includeSpecificShareRecipients withBlock:(TJActivityViewControllerOverrideBlock)block; 38 | #endif 39 | 40 | /** 41 | Overrides activity types matching a regex with a block. 42 | @param regexString A regex that the tapped @c activityType is matched with. 43 | @param block The block to execute in place of the given activity. 44 | */ 45 | - (void)overrideActivityTypeMatchingRegex:(TJActivityTypeRegex)regexString withBlock:(TJActivityViewControllerOverrideBlock)block; 46 | #if INCLUDE_RECIPIENTS 47 | - (void)overrideActivityTypeMatchingRegex:(TJActivityTypeRegex)regexString includeSpecificShareRecipients:(const BOOL)includeSpecificShareRecipients withBlock:(TJActivityViewControllerOverrideBlock)block; 48 | #endif 49 | 50 | /** 51 | Overrides the item used for a particular activity. 52 | @param activityType The activity type to override. 53 | @param block A block that returns the overriden item to use for the activity. 54 | */ 55 | - (void)overrideItemForActivityType:(UIActivityType)activityType withBlock:(id (^)(void))block; 56 | 57 | #if defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 58 | 59 | /** 60 | Overrides the @c LPLinkMetadata that the activity view controller presents. 61 | */ 62 | @property (nonatomic) LPLinkMetadata *linkMetadata API_AVAILABLE(ios(13.0)); 63 | 64 | #endif 65 | 66 | /// TJActivityViewController only supports @c completionWithItemsHandler, so this is explicitly marked as unavailable. 67 | @property (nullable, nonatomic, copy) UIActivityViewControllerCompletionHandler completionHandler NS_UNAVAILABLE; 68 | 69 | #if INCLUDE_RECIPIENTS 70 | @property (nonatomic, readonly) BOOL activityIncludesRecipient; 71 | #endif 72 | 73 | @end 74 | 75 | NS_ASSUME_NONNULL_END 76 | -------------------------------------------------------------------------------- /Sources/TJActivityViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TJActivityViewController.m 3 | // 4 | // Created by Tim Johnsen on 1/1/15. 5 | // Copyright (c) 2015 Tim Johnsen. All rights reserved. 6 | // 7 | 8 | #import "TJActivityViewController.h" 9 | 10 | #import 11 | 12 | TJActivityTypeRegex const TJActivityViewControllerFacebookRegexString = @"(com\\.facebook\\.Facebook.*\\.ShareExtension|com\\.apple\\.UIKit\\.activity\\.PostToFacebook)"; 13 | TJActivityTypeRegex const TJActivityViewControllerFacebookMessengerRegexString = @"com\\.facebook\\.(Messenger|Orca).*\\.ShareExtension"; 14 | TJActivityTypeRegex const TJActivityViewControllerInstagramRegexString = @"com\\.(facebook|burbn)\\.(?i)instagram.*\\.shareextension(.*)?"; 15 | UIActivityType const TJActivityViewControllerSnapchatActivityType = @"com.toyopagroup.picaboo.share"; 16 | TJActivityTypeRegex const TJActivityTypeSaveToCameraRollRegexString = @"com\\.apple\\.(UIKit\\.activity\\.SaveToCameraRoll|share\\.System\\.add-to-iphoto)"; 17 | UIActivityType const TJActivityViewControllerTikTokActivityType = @"com.zhiliaoapp.musically.ShareExtension"; 18 | UIActivityType const TJActivityViewControllerThreadsActivityType = @"burbn.barcelona.ShareExtension"; 19 | UIActivityType const TJActivityViewControllerRetroActivityType = @"io.lonepalm.Retro.ShareExtension"; 20 | UIActivityType const TJActivityViewControllerBlueskyActivityType = @"xyz.blueskyweb.app.Share-with-Bluesky"; 21 | 22 | @interface TJActivityItemProxy : NSObject 23 | 24 | - (instancetype)init NS_UNAVAILABLE; 25 | - (instancetype)initWithPlaceholderItem:(id)placeholderItem NS_DESIGNATED_INITIALIZER; 26 | - (instancetype)initWithItemSource:(id)itemSource NS_DESIGNATED_INITIALIZER; 27 | 28 | @end 29 | 30 | #if defined(__has_attribute) && __has_attribute(objc_direct_members) 31 | __attribute__((objc_direct_members)) 32 | #endif 33 | @interface TJActivityViewController () 34 | 35 | @property (nonatomic) NSMutableDictionary *overrideBlocksForMatchBlocks; 36 | @property (nonatomic) NSMutableDictionary *itemBlocksForOverriddenActivityTypes; 37 | 38 | #if INCLUDE_RECIPIENTS 39 | @property (nonatomic, readwrite) BOOL activityIncludesRecipient; 40 | #endif 41 | @property (nonatomic) BOOL hasHandledActivities; 42 | 43 | @property (nonatomic) os_unfair_lock *lock; 44 | 45 | @property (nonatomic) BOOL threadsafeIsPresented; 46 | 47 | @end 48 | 49 | @implementation TJActivityViewController 50 | 51 | @dynamic completionHandler; 52 | 53 | - (instancetype)initWithActivityItems:(NSArray *)activityItems applicationActivities:(NSArray *)applicationActivities 54 | { 55 | NSMutableArray *const activityItemProxies = [NSMutableArray arrayWithCapacity:activityItems.count]; 56 | for (const id activityItem in activityItems) { 57 | TJActivityItemProxy *proxy = nil; 58 | // Cheaper than -conformsToProtocol: per https://twitter.com/invalidname/status/1333528812177514497 59 | if ([activityItem respondsToSelector:@selector(activityViewControllerPlaceholderItem:)] && [activityItem respondsToSelector:@selector(activityViewController:itemForActivityType:)]) { 60 | proxy = [[TJActivityItemProxy alloc] initWithItemSource:(id)activityItem]; 61 | } else { 62 | proxy = [[TJActivityItemProxy alloc] initWithPlaceholderItem:activityItem]; 63 | } 64 | [activityItemProxies addObject:proxy]; 65 | } 66 | 67 | if (self = [super initWithActivityItems:activityItemProxies applicationActivities:applicationActivities]) { 68 | self.overrideBlocksForMatchBlocks = [NSMutableDictionary new]; 69 | self.itemBlocksForOverriddenActivityTypes = [NSMutableDictionary new]; 70 | self.lock = malloc(sizeof(os_unfair_lock_t)); 71 | *self.lock = OS_UNFAIR_LOCK_INIT; 72 | 73 | #if INCLUDE_RECIPIENTS 74 | // Determining if there are recipients is an exercise left to the reader. 75 | #endif 76 | } 77 | 78 | return self; 79 | } 80 | 81 | - (void)dealloc 82 | { 83 | free(self.lock); 84 | } 85 | 86 | - (void)dismissViewControllerAnimated:(BOOL)flag completion:(dispatch_block_t)completion 87 | { 88 | // Reset this in case the view controller is reused multiple times. 89 | if (!self.presentedViewController) { 90 | self.hasHandledActivities = NO; 91 | } 92 | 93 | [super dismissViewControllerAnimated:flag completion:completion]; 94 | } 95 | 96 | - (void)viewDidAppear:(BOOL)animated 97 | { 98 | [super viewDidAppear:animated]; 99 | self.threadsafeIsPresented = YES; 100 | } 101 | 102 | - (void)viewDidDisappear:(BOOL)animated 103 | { 104 | [super viewDidDisappear:animated]; 105 | self.threadsafeIsPresented = NO; 106 | } 107 | 108 | - (void)overrideActivityType:(UIActivityType)activityType withBlock:(TJActivityViewControllerOverrideBlock)block 109 | #if INCLUDE_RECIPIENTS 110 | { 111 | [self overrideActivityType:activityType includeSpecificShareRecipients:NO withBlock:block]; 112 | } 113 | 114 | - (void)overrideActivityType:(UIActivityType)activityType includeSpecificShareRecipients:(const BOOL)includeSpecificShareRecipients withBlock:(TJActivityViewControllerOverrideBlock)block 115 | #endif 116 | { 117 | NSParameterAssert(activityType); 118 | NSParameterAssert(block); 119 | 120 | [self.overrideBlocksForMatchBlocks setObject:block 121 | forKey:^BOOL (UIActivityType matchActivityType, BOOL activityIncludesRecipient) { 122 | return [matchActivityType isEqualToString:activityType] 123 | #if INCLUDE_RECIPIENTS 124 | && (!activityIncludesRecipient || includeSpecificShareRecipients) 125 | #endif 126 | ; 127 | }]; 128 | } 129 | 130 | - (void)overrideActivityTypeMatchingRegex:(TJActivityTypeRegex)regexString withBlock:(TJActivityViewControllerOverrideBlock)block 131 | #if INCLUDE_RECIPIENTS 132 | { 133 | [self overrideActivityTypeMatchingRegex:regexString includeSpecificShareRecipients:NO withBlock:block]; 134 | } 135 | 136 | - (void)overrideActivityTypeMatchingRegex:(TJActivityTypeRegex)regexString includeSpecificShareRecipients:(const BOOL)includeSpecificShareRecipients withBlock:(TJActivityViewControllerOverrideBlock)block 137 | #endif 138 | { 139 | NSParameterAssert(regexString); 140 | NSParameterAssert(block); 141 | 142 | [self.overrideBlocksForMatchBlocks setObject:block 143 | forKey:^BOOL (UIActivityType matchActivityType, BOOL activityIncludesRecipient) { 144 | return matchActivityType.length > 0 && [matchActivityType rangeOfString:regexString options:NSRegularExpressionSearch].location != NSNotFound 145 | #if INCLUDE_RECIPIENTS 146 | && (!activityIncludesRecipient || includeSpecificShareRecipients) 147 | #endif 148 | ; 149 | }]; 150 | } 151 | 152 | - (void)overrideItemForActivityType:(UIActivityType)activityType withBlock:(id (^)(void))block 153 | { 154 | NSParameterAssert(activityType); 155 | NSParameterAssert(block); 156 | 157 | [self.itemBlocksForOverriddenActivityTypes setObject:block forKey:activityType]; 158 | } 159 | 160 | @end 161 | 162 | #if defined(__has_attribute) && __has_attribute(objc_direct_members) 163 | __attribute__((objc_direct_members)) 164 | #endif 165 | @implementation TJActivityItemProxy { 166 | // Mutually exclusive, one must be populated. 167 | id _placeholderItem; 168 | id _itemSource; 169 | } 170 | 171 | - (instancetype)initWithPlaceholderItem:(id)placeholderItem 172 | { 173 | NSParameterAssert(placeholderItem); 174 | 175 | if (self = [super init]) { 176 | _placeholderItem = placeholderItem; 177 | } 178 | 179 | return self; 180 | } 181 | 182 | - (instancetype)initWithItemSource:(id)itemSource 183 | { 184 | NSParameterAssert(itemSource); 185 | 186 | if (self = [super init]) { 187 | _itemSource = itemSource; 188 | } 189 | return self; 190 | } 191 | 192 | - (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController { 193 | return _placeholderItem ?: [_itemSource activityViewControllerPlaceholderItem:activityViewController]; 194 | } 195 | 196 | - (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(UIActivityType)activityType 197 | { 198 | TJActivityViewController *const overridableActivityViewController = [activityViewController isKindOfClass:[TJActivityViewController class]] ? (TJActivityViewController *)activityViewController : nil; 199 | 200 | id item = nil; 201 | 202 | if (overridableActivityViewController.threadsafeIsPresented) { 203 | __block TJActivityViewControllerOverrideBlock overrideBlock = nil; 204 | const BOOL activityIncludesRecipient = 205 | #if INCLUDE_RECIPIENTS 206 | overridableActivityViewController.activityIncludesRecipient 207 | #else 208 | NO 209 | #endif 210 | ; 211 | 212 | [overridableActivityViewController.overrideBlocksForMatchBlocks enumerateKeysAndObjectsUsingBlock:^(BOOL (^ _Nonnull matchBlock)(UIActivityType, BOOL), TJActivityViewControllerOverrideBlock replacementBlock, BOOL * _Nonnull stop) { 213 | if (matchBlock(activityType, activityIncludesRecipient)) { 214 | overrideBlock = replacementBlock; 215 | *stop = YES; 216 | } 217 | }]; 218 | 219 | if (overrideBlock) { 220 | BOOL canRunBlock = YES; 221 | if (overridableActivityViewController) { 222 | // Ensure override blocks aren't invoked multiple times. 223 | os_unfair_lock_lock(overridableActivityViewController.lock); 224 | 225 | if (overridableActivityViewController.hasHandledActivities) { 226 | canRunBlock = NO; 227 | } else { 228 | overridableActivityViewController.hasHandledActivities = YES; 229 | } 230 | 231 | os_unfair_lock_unlock(overridableActivityViewController.lock); 232 | } 233 | if (canRunBlock) { 234 | // If this activity type is overridden, call the override block on the main thread 235 | __block dispatch_block_t dismissAndPerformOverrideBlock = ^{ 236 | if (activityViewController.presentingViewController) { 237 | [activityViewController dismissViewControllerAnimated:YES completion:dismissAndPerformOverrideBlock]; 238 | } else { 239 | overrideBlock(activityType, ^(UIActivityType activityType, BOOL completed) { 240 | if (activityViewController.completionWithItemsHandler) { 241 | activityViewController.completionWithItemsHandler(activityType, completed, nil, nil); 242 | } 243 | }); 244 | dismissAndPerformOverrideBlock = nil; 245 | } 246 | }; 247 | if ([NSThread isMainThread]) { 248 | dismissAndPerformOverrideBlock(); 249 | } else { 250 | dispatch_async(dispatch_get_main_queue(), dismissAndPerformOverrideBlock); 251 | } 252 | } 253 | } else { 254 | id (^itemOverrideBlock)(void) = [overridableActivityViewController.itemBlocksForOverriddenActivityTypes objectForKey:activityType]; 255 | if (itemOverrideBlock) { 256 | item = itemOverrideBlock(); 257 | } else { 258 | // Otherwise just return the placeholder item 259 | item = _placeholderItem ?: [_itemSource activityViewController:activityViewController itemForActivityType:activityType]; 260 | } 261 | } 262 | } else { 263 | // Calls for UIActivityTypeCopyToPasteboard sometimes come in before the view controller is presented for the link preview. 264 | // We don't want to inadvertently trigger an override in that case. 265 | item = _placeholderItem; 266 | } 267 | 268 | return item; 269 | } 270 | 271 | - (NSString *)activityViewController:(UIActivityViewController *)activityViewController subjectForActivityType:(UIActivityType)activityType 272 | { 273 | return [_itemSource respondsToSelector:@selector(activityViewController:subjectForActivityType:)] ? [_itemSource activityViewController:activityViewController subjectForActivityType:activityType] : nil; 274 | } 275 | 276 | - (NSString *)activityViewController:(UIActivityViewController *)activityViewController dataTypeIdentifierForActivityType:(UIActivityType)activityType 277 | { 278 | return [_itemSource respondsToSelector:@selector(activityViewController:dataTypeIdentifierForActivityType:)] ? [_itemSource activityViewController:activityViewController dataTypeIdentifierForActivityType:activityType] : nil; 279 | } 280 | 281 | - (UIImage *)activityViewController:(UIActivityViewController *)activityViewController thumbnailImageForActivityType:(UIActivityType)activityType suggestedSize:(CGSize)size 282 | { 283 | return [_itemSource respondsToSelector:@selector(activityViewController:thumbnailImageForActivityType:suggestedSize:)] ? [_itemSource activityViewController:activityViewController thumbnailImageForActivityType:activityType suggestedSize:size] : nil; 284 | } 285 | 286 | #if defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 287 | 288 | - (LPLinkMetadata *)activityViewControllerLinkMetadata:(UIActivityViewController *)activityViewController API_AVAILABLE(ios(13.0)) 289 | { 290 | TJActivityViewController *const overridableActivityViewController = [activityViewController isKindOfClass:[TJActivityViewController class]] ? (TJActivityViewController *)activityViewController : nil; 291 | LPLinkMetadata *metadata = overridableActivityViewController.linkMetadata; 292 | if (!metadata && [_itemSource respondsToSelector:@selector(activityViewControllerLinkMetadata:)]) { 293 | metadata = [_itemSource activityViewControllerLinkMetadata:activityViewController]; 294 | } 295 | return metadata; 296 | } 297 | 298 | #endif 299 | 300 | @end 301 | -------------------------------------------------------------------------------- /TJActivityViewController.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "TJActivityViewController" 4 | 5 | s.version = "1.0.1" 6 | 7 | s.summary = "TJActivityViewController is a handy subclass of UIActivityViewController for simple customization for the iOS share sheet." 8 | 9 | s.homepage = "https://github.com/timonus/TJActivityViewController" 10 | 11 | s.license = { :type => "BSD 3-Clause License", :file => "LICENSE" } 12 | 13 | s.author = { "Tim Johnsen" => "https://twitter.com/timonus" } 14 | 15 | s.platform = :ios, "8.0" 16 | 17 | s.source = { :git => "https://github.com/timonus/TJActivityViewController.git", :tag => "1.0.1" } 18 | 19 | s.source_files = "*.{h,m}" 20 | 21 | s.public_header_files = "*.h" 22 | 23 | s.frameworks = "Foundation", "UIKit" 24 | 25 | s.requires_arc = true 26 | 27 | end --------------------------------------------------------------------------------