Prevent Users from Modifying Part of the Text in Slcomposeviewcontroller

Prevent users from modifying part of the text in SLComposeViewController

It's a little involved, so bear with me here... and this only works for SLServiceTypeTwitter

For anyone reading this that is interested in using this, I've put a sample project on Github: https://github.com/NSPostWhenIdle/Immutable-SLComposeViewController

The first thing you'll want to do is make sure that your view controller conforms to UITextViewDelegate. You'll also want to create an iVar for a UITextView. You won't actually be creating a text view, but you'll want to have a pointer to assign directly to the text view inside the SLComposeViewController. While you're here make a iVar for the permanent string as well.

@interface ViewController : UIViewController <UITextViewDelegate> //very important!
{
UITextView *sharingTextView;
NSString *permanentText;
}

Then in viewDidLoad you can set up what you want the permanent text to be:

- (void)viewDidLoad
{
[super viewDidLoad];
permanentText = @"http://www.stackoverflow.com/";
}

The code below is a pretty basic IBAction to present the composer with a couple of slight tweaks. First, you'll notice that setInitialText uses a formatted string the append the permanent text to the end of the contents of the text field with a space added in between.

Then comes the important part! I've added a loop to presentViewController:'s completion handler to cycle through some subviews of subviews of subviews in order to identify the UITextView in the composer that contains the sharing text. This needs to be done so you can set that text view's delegate in order to access the UITextViewDelegate method shouldChangeTextInRange.

- (IBAction)exampleUsingFacebook:(UIButton *)sender {

if([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter])
{
SLComposeViewController *sharingComposer = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];

SLComposeViewControllerCompletionHandler __block completionHandler=^(SLComposeViewControllerResult result){
[sharingComposer dismissViewControllerAnimated:YES completion:nil];
};
[sharingComposer setCompletionHandler:completionHandler];
[sharingComposer setInitialText:[NSString stringWithFormat:@"%@ %@",[[self QText]text],permanentText]];

[self presentViewController:sharingComposer animated:YES completion:^{
for (UIView *viewLayer1 in sharingComposer.view.subviews) {
for (UIView *viewLayer2 in viewLayer1.subviews) {
if ([viewLayer2 isKindOfClass:[UIView class]]) {
for (UIView *viewLayer3 in viewLayer2.subviews) {
if ([viewLayer3 isKindOfClass:[UITextView class]]) {
[(UITextView *)viewLayer3 setDelegate:self];
sharingTextView = (UITextView *)viewLayer3;
}
}
}
}
}
}];
}
}

Important: Please note that the above will only work if placed in the completion handler.

Below is an example of how to set up shouldChangeTextInRange to compare the range that the user is attempting to edit to the range that contains your permanent text. By doing so, the user will be able to make changes to any part of the text that they want... except for the part that contains your permanent text. You'll also notice that inside this method I've compared textView to shareingTextView, the pointer we assigned to the text view inside the composer. Doing so will allow you to use other text views within this controller without them following the same rules I've configured for the text view inside the composer.

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if (textView == sharingTextView) {
NSRange substringRange = [textView.text rangeOfString:permanentText];
if (range.location >= substringRange.location && range.location <= substringRange.location + substringRange.length) {
return NO;
}
}
return YES;
}

Hope this helps!

How to disable completion sounds from SLComposeViewController (Facebook, Twitter, ...)?

You can do following:

Make SLComposeViewController not to send tweet when pressed "Send". And send tweet manually.

1. Walk through all views recursively and find button "Send"

// UIButton with width 50px
- (UIButton *)tweetSendButton:(UIView *)view
{
for (UIView * subview in view.subviews)
{
if ([subview isKindOfClass:[UIButton class]]
&& subview.bounds.size.width == 50)
{
return (UIButton *)subview;
}
UIButton * button = [self tweetSendButton:subview];
if (button) return button;
}
return nil;
}

...
UIButton * sendButton = [self tweetSendButton:_tweetController.view];
2. Remove all actions for target SLComposeViewController

NSArray * actions = [sendButton actionsForTarget:_tweetController forControlEvent:UIControlEventTouchUpInside];
for (NSString * action in actions)
[sendButton removeTarget:_tweetController action:NSSelectorFromString(action) forControlEvents:UIControlEventTouchUpInside];
3. Add own action for UIControlEventTouchUpInside event

[sendButton addTarget:self action:@selector(sendCustomTweet:) forControlEvents:UIControlEventTouchUpInside];
4. When "Send" button was pressed use this methods to get text and account (if necessary):

....
UITextView * textView = [self tweetTextView:self.tweetController.view];
UIButton * accountButton = [self twitterAccountButton:self.tweetController.view];

NSString * tweetText = textView.text;
NSString * tweetAccount = [accountButton.titleLabel.text substringFromIndex:1]; // skip @ char
....
5. Here are this methods:

// Single UITextView
- (UITextView *)tweetTextView:(UIView *)view
{
for (UIView * subview in view.subviews)
{
if ([subview isMemberOfClass:[UITextView class]])
return (UITextView *)subview;
UITextView * textView = [self tweetTextView:subview];
if (textView) return textView;
}
return nil;
}

// UIButton witch starts from @
- (UIButton *)twitterAccountButton:(UIView *)view
{
for (UIView * subview in view.subviews)
{
if ([subview isKindOfClass:[UIButton class]])
{
UIButton * button = (UIButton *)subview;
if (button.titleLabel.text && [button.titleLabel.text rangeOfString:@"@"].location == 0)
return button;
}
UIButton * button = [self twitterAccountButton:subview];
if (button) return button;
}
return nil;
}
6. Send tweet manually

- (void)sendTweet:(NSString *)text fromAccount:(NSString *)account withArtwork:(UIImage *)artwork
{
// Create an account store object.
ACAccountStore *accountStore = [[ACAccountStore alloc] init];

// Create an account type that ensures Twitter accounts are retrieved.
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];

// Request access from the user to use their Twitter accounts.
[accountStore requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
if(!granted) return;

// Get the list of Twitter accounts.
NSArray *accountsArray = [accountStore accountsWithAccountType:accountType];

// For the sake of brevity, we'll assume there is only one Twitter account present.
// You would ideally ask the user which account they want to tweet from, if there is more than one Twitter account present.
for(ACAccount *twitterAccount in accountsArray)
{
if (account && ([account compare:twitterAccount.username options:(NSCaseInsensitiveSearch)] != 0))
continue;

// Create a request, which in this example, posts a tweet to the user's timeline.
// This example uses version 1 of the Twitter API.
// This may need to be changed to whichever version is currently appropriate.
NSString * method = artwork ? @"update_with_media" : @"update";
NSURL * url = [NSURL URLWithString:[NSString stringWithFormat:@"https://api.twitter.com/1.1/statuses/%@.json",method,nil]];
TWRequest *postRequest = [[TWRequest alloc] initWithURL:url parameters:@{@"status":text} requestMethod:TWRequestMethodPOST];

// Set the account used to post the tweet.
[postRequest setAccount:twitterAccount];

if (artwork)
{
NSData * data = UIImageJPEGRepresentation(artwork, 0.8);
[postRequest addMultiPartData:data withName:@"media" type:@"JPG"];
}

// Perform the request created above and create a handler block to handle the response.
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if (urlResponse.statusCode == 200)
{
NSLog(@"Tweet sent successfully!");
}
else
{
NSDictionary * json = [NSJSONSerialization JSONObjectWithData:responseData
options:kNilOptions
error:&error];
NSLog(@"Tweet sending failed with error: %@", json);
}
}];
}
}];
}

Tutorial for SLComposeViewController sharing

For details on this framework please see Apple's Social Framework Class Reference

Additional tutorials:

  1. http://soulwithmobiletechnology.blogspot.com/2012/07/tutorial-how-to-use-inbuilt.html
  2. http://www.mobile.safilsunny.com/iphone/integrating-facebook-ios/
  3. https://rudeboy-quickies.blogspot.com/2012/06/steps-to-integrate-facebook-in-ios6.html

For this example, we will be using the SLComposeViewController's SLServiceTypeFacebook. If you wish to use Twitter or SinaWeibo just change out the SLServiceType to one of the following:

  • SLServiceTypeFacebook
  • SLServiceTypeSinaWeibo
  • SLServiceTypeTwitter

iOS 6 has made it very easy to post directly to Facebook, Twitter or Sina Weibo using the SLComposeViewController. This works very similarly to iOS 5's TWTweetComposeViewController.

First, in your view controller's header file (.h) #import the Social Framework and the Accounts Framework.

#import <Social/Social.h>

#import <Accounts/Accounts.h>

Here we will declare a simple UIButton and an IBAction that we will later link to that button and a void (sharingStatus) which will be used to check that the selected sharing service is available.

@interface ViewController : UIViewController

@property (weak, nonatomic) IBOutlet UIButton *easyFacebookButton;
- (IBAction)facebookPost:(id)sender;
- (void)sharingStatus;
@end

@implementation ViewController

Then, in your implementation file (.m), we'll start by implementing the (sharingStatus) void that we declared in the header file. sharingStatus uses SLComposeViewController's isAvailableForServiceType BOOL to return whether or not you can post to the service specified in its argument. In this case, we will use the service type SLServiceTypeFacebook. If the service is available the post button will be enabled with an alpha value of 1.0f, and if the service isn't available the button will be disabled its alpha value set to 0.5f.

- (void)sharingStatus {
if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]) {
NSLog(@"service available");
self.easyFacebookButton.enabled = YES;
self.easyFacebookButton.alpha = 1.0f;
} else {
self.easyFacebookButton.enabled = NO;
self.easyFacebookButton.alpha = 0.5f;
}
}

Here we will set up the IBAction that will call up the composer. For good practice, we will check isAvailableForServiceType again to avoid calling up the composer for a service type that isn't available. (Incase something went wrong during the last check, or if availability somehow changed in the fraction of a second in between tapping the post button and the composers all/init. The code below has been set up to display a Facebook composers sheet with text, an image, and a link. This action also utilises a completion handler for the composer's cancelled and done results.

- (IBAction)facebookPost:(id)sender {

if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]) {

SLComposeViewController *mySLComposerSheet = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];

[mySLComposerSheet setInitialText:@"iOS 6 Social Framework test!"];

[mySLComposerSheet addImage:[UIImage imageNamed:@"myImage.png"]];

[mySLComposerSheet addURL:[NSURL URLWithString:@"http://stackoverflow.com/questions/12503287/tutorial-for-slcomposeviewcontroller-sharing"]];

[mySLComposerSheet setCompletionHandler:^(SLComposeViewControllerResult result) {

switch (result) {
case SLComposeViewControllerResultCancelled:
NSLog(@"Post Canceled");
break;
case SLComposeViewControllerResultDone:
NSLog(@"Post Sucessful");
break;

default:
break;
}
}];

[self presentViewController:mySLComposerSheet animated:YES completion:nil];
}
}

In viewWillAppear we will register an observer to ACAccountStoreDidChangeNotification so the application can be notified when account information changes. This observer will then be removed in viewDidDisappear.

- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sharingStatus) name:ACAccountStoreDidChangeNotification object:nil];
}

- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:ACAccountStoreDidChangeNotification];
}

And finally, open up interface builder and add a UIButton which will be the post button. Then in the connections inspector link the IBOutlet and IBAction we created earlier to the button, and that's it! You're done!

enter image description here



Related Topics



Leave a reply



Submit