I am using TWTweetComposeViewController, when available, to send tweets from inside my iOS app. I pre-populate the view controller with boilerplate text, then let the user modify and send at their discretion. It works great for the most part. Distilled down, it looks like this (with body pointing at a valid NSString):
if (NSClassFromString(#"TWTweetComposeViewController")) {
TWTweetComposeViewController *iOS5twitter = [[TWTweetComposeViewController alloc] init];
[iOS5twitter setInitialText:body];
iOS5twitter.completionHandler = ^(TWTweetComposeViewControllerResult result)
{
[self.viewController dismissViewControllerAnimated:YES completion:nil];
};
[self.viewController presentViewController:iOS5twitter animated:YES completion:nil];
[iOS5twitter release];
}
else {
/* Do something else when the framework is missing */
}
Now if body is too long, i.e., more than 140 characters, the resulting tweet view is empty of any text at all, character countdown set to 140. I might have expected truncation in this case, although it does not appear to be documented in the Class Reference one way or the other what happens when the initial text is too long, but I can accept that I have to do the truncation to maximum tweet length before passing to setInitialText.
What I don't understand is that certain messages which are shorter than 140 characters also produce the empty tweet view.
Initially I saw what seemed to be a perfectly valid string with 139 characters failing. I noticed that shortening the string made it work. But after a great deal of experimenting, I also noticed that replacing a URL which happened to appear inside the text with random text of the same length made it work. In other words, the URL itself is causing a problem.
So I thought maybe there was something weird about the URL I was using, but I distilled it down to this. This one works:
NSString *body = #"............................................................................................................................................";
while this does not
NSString *body = #"............http://a........................................................................................................................";
Observations:
They are both 140 characters long (and report that way in the console with [body length]). The only difference is the presence of something vaguely URL-like embedded in the middle of the second one.
The position of the URL within the string does not seem to matter, but if I change any one of those non-period characters to a period (thus making it not like a URL anymore), it ceases to be broken.
If I shorten the broken one, shaving 14 periods off the end, it works. That is, this particular URL embedded in periods for a total length of 126 characters is fine. 127 or larger is broken. Not clear how, or if, this relates to the length of the URL itself.
Anybody ever seen something like this? Any idea what is going on? Am I doing something wrong, or is the iOS Twitter Framework just broken?
I have run into the exact same problem. It is a known bug in the Twitter framework and is being tracked.
Please see this discussion on dev.twitter.com https://dev.twitter.com/discussions/5024
(I would have posted that as a comment rather than an answer if I could, but I don't have sufficient SO credibility so thought I'd add the below observations as well in case they are of interest).
When just adding text without URLs, the character count works as expected.
Adding a URL with the addURL: method causes 21 characters of the tweet to be used (20 for URL plus a space). Adding the URL in the initial text causes 20 chars to be used for the URL.
When including a single URL (using either method) the framework fails when the total character count exceeds 138 (e.g. 20 for URL + space + 117 chars of initial text) thus losing 2 characters. With just one URL the order of setting the initial text and then adding the URL with addURL: does not make a difference.
When adding 2 URls, it fails when the total character count exceeds 113 this losing 27 characters!
However, with 2 or more, if you add the URLs BEFORE setting the initial text, it fails with a total count of 136. So 2 chars are lost per URL again.
Summary/Workaround - if just including 1 URL then adding it in the initial text will give you one extra character than using the addURL: method, but you will still be short 2 characters overall. If adding 2 or more URLs using addURL: then add them before the initial text, but until the bug is fixed, you will still lose 2 chars per URL.
I have filed a radar, but according to this Can I browse other people's (Apple) bug reports?, the more times a bug is reported the higher priority it is given, so it is worth others filing it as well to increase it's priority.
This seems to be a bug; I sure wish there was a way to directly ask TWTweetComposeViewController how much space is left. Fortunately there is an indirect way to ask. setInitialText: returns NO if the message is too long, so what I've done is brute-force it, chopping off five characters at a time until it returns YES:
- (void)tweetURL:(NSString *)url title:(NSString *)title {
TWTweetComposeViewController *twitter = [[TWTweetComposeViewController alloc] init];
NSString *format = #"“%#” %# /via #DesignSceneApp";
NSString *message = [NSString stringWithFormat:format, title, url]
NSUInteger idx = title.length;
while (![twitter setInitialText:message]) {
idx -= 5;
if (idx > 5) {
message = [NSString stringWithFormat:format,
[NSString stringWithFormat:#"%#…", [title substringToIndex:idx]],
url
];
} else {
// Give up on the title.
message = [NSString stringWithFormat:#"%# /via #DesignSceneApp", url];
[twitter setInitialText:message];
break;
}
}
[self presentViewController:twitter animated:YES completion:nil];
}
I know, it's ugly, right? But at least it allows me to get a reasonable approximation of the max length, so that I truncate no more of the link title than I need to.
Some code excerpt for automatic message trimming:
[tweetSheet addURL:[NSURL URLWithString:#"http://some.nice.url/"]];
if (![tweetSheet setInitialText:message]) {
NSUInteger messageMaxIndex = [message length];
while (messageMaxIndex > 0 && ![tweetSheet setInitialText:[NSString stringWithFormat: #"%# ...", message]]) {
--messageMaxIndex;
message = [message substringToIndex:messageMaxIndex];
};
}
Instead of
[iOS5twitter setInitialText:#"My url is http://something.com. No idea why it is not working"];
Try this
NSString *yourUrlString = #"http://something.com";
NSString *msg= #"My url is %#. No idea why it is not working";
NSString *defaultMessage = [NSString stringWithFormat:msg,yourUrlString];
[iOS5twitter setInitialText:defaultMessage];
I have no idea why it is so but I just faced this problem and tried it and it is working for me.
I had similar problem. Twitter controller doesn't display tweets, that are too long. You can take a substring of a tweet by cutting down to 140 symbols:
[tweetView setInitialText:[self.textToExport substringToIndex:140]];
NSLog(#"tweetView.initialText:%#", [self.textToExport substringToIndex:140]);
try this code
- (IBAction)DeveloperTwitter:(id)sender {
NSString* tweet = #"some tweet goes here..";
if ([TWTweetComposeViewController canSendTweet]) {
TWTweetComposeViewController *twitter = [[TWTweetComposeViewController alloc] init];
[twitter setInitialText:tweet];
[self presentViewController:twitter animated:YES completion:nil];
twitter.completionHandler = ^(TWTweetComposeViewControllerResult res) {
if(res == TWTweetComposeViewControllerResultDone) {
// sent ...
}
[self dismissModalViewControllerAnimated:YES];
};
}
else {
tweet = [NSString stringWithFormat:#"%#%#", #"http://twitter.com/home?status=", tweet];
tweet = [tweet stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString: tweet]];
}
tweet=nil;
}
Related
I am using an app to lock, unlock, and open the trunk of my car. The only problem is that I can't figure out how to modify the Xcode project so there are 3 buttons. Basically right now if I type "U" then enter- the car unlocks, "L" then enter- the car locks, and "T" then enter- the trunk opens. I want to add three buttons that simulate these three things and eliminate the typing all together. If you want to see my adruino or xcode project code I can upload those. I have put some code about the text box below.
BOOL)textFieldShouldReturn:(UITextField *)textField
{
NSString *text = textField.text;
NSNumber *form = [NSNumber numberWithBool:NO];
NSString *s;
NSData *d;
if (text.length > 16)
s = [text substringToIndex:16];
else
s = text;
d = [s dataUsingEncoding:NSUTF8StringEncoding];
if (bleShield.activePeripheral.state == CBPeripheralStateConnected) {
[bleShield write:d];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:text, TEXT_STR, form, FORM, nil];
[tableData addObject:dict];
[_tableView setContentOffset:CGPointMake(0, CGFLOAT_MAX)];
NSLog(#"%f", _tableView.contentOffset.y);
[self.tableView reloadData];
}
textField.text = #"";
return YES;
Thanks for the help!
Your view controller probably has a textFieldShouldReturn method which is taking the string value from the text field and building a parameter to a call that initiates sending the command. If not this method then perhaps its action method linked to the text field.
You'll need to duplicate parts of that code into a method that receives a string parameter instead of taking it from the text field, say named sendLockCommand:(NSString *)commandString (assuming you're coding in Objective-C, also like that repo).
Make action methods for your buttons, something like lockDoors, unlockDoors, openTrunk, in each call [self sendLockCommand:#"L"], each with the appropriate string. Wire up the buttons to those actions and you're good to go.
I am using buttons and I assigned tag 0 to 10 . Then I made an action to get the clicked button's tag, and now I want to display the tag in a label . Also I have a cancel button C. If user wants to delete any number, he can click C button that I want to remove number from the label .
This is my screenshot to touch the number
- (void)viewDidLoad {
[super viewDidLoad];
self.title = #"Ezywire";
addnum=[[NSMutableArray alloc]init];
numbber=[[NSString alloc]init];
}
- (IBAction)NumberAction:(id)sender {
NSInteger tagvalue = [sender tag];
NSString *current=[NSString stringWithFormat:#"%ld", (long)tagvalue];
[addnum addObject:current];
NSString *temp;
for ( int i=0; i<[addnum count]; i++) {
numbber=[numbber stringByAppendingString:[addnum objectAtIndex:i]];
}
NSLog(#"data===%#",numbber);
ValueLable.text= numbber;
}
But in the label I am getting repeated number like this. How to implement this.
For example if user enters 2 then in the label
2
then he enters 7 then in the label
27
then he entered 9 then in the label
279
........ like this .
If user clicks C, then it remove from label last value is (last value removed)
27
The problem in your code is that numbber is initialized when the view is loaded, and never gets cleared again. However, each time a button is pressed, the whole addnum of digits gets appended to num again, creating repeated digits.
Fix this by removing num as an instance variable, making it a local to NumberAction: method, and setting it to an empty string every time the number is pressed.
Since you are planning to support the clearing action as well, you should make a private method that combines the digits from addnum array into a string. This way your NumberAction: and ClearAction would share the code that formats the array and sets the label. Your NumberAction: method would append a number and call FormatAndSetLabel, while the ClearAction method would remove the last digit if it is available, and call FormatAndSetLabel as well:
- (IBAction)NumberAction:(id)sender {
NSInteger tagvalue = [sender tag];
NSString *current=[NSString stringWithFormat:#"%ld", (long)tagvalue];
[addnum addObject:current];
[self FormatAndSetLabel];
}
- (IBAction)ClearAction:(id)sender {
if (!addnum.count) return;
[addnum removeLastObject];
[self FormatAndSetLabel];
}
-(void)FormatAndSetLabel {
NSMutableString *temp = [NSMutableString string];
for ( int i=0; i<[addnum count]; i++) {
[temp appendString:addnum[i]];
}
ValueLable.text= temp;
}
Also it might be interesting for you to have a look at Paul's Hegarty Stanford iOS development course (iPad and iPhone Application Development, Fall 2011)
https://itunes.apple.com/ru/itunes-u/ipad-iphone-application-development/id473757255?mt=10
Calculator app is used here as an example. Must see for the beginners.
So I'm making a game that involves wireless communication between multiple iPhones, with one being the host. I am attempting to do so via the MultipeerConnectivity framework, and I've made a MCManager class (an instance of which I put into appDelegate so it's available throughout the app) to handle sending data from one system to another. This is how sending is implemented in my code:
- (void) sendState: (NSString*) str;
//used by the host to send commands to the other connected systems
{
if(appDelegate.mcManager.connected && iAmHost){
NSData *dataToSend = [str dataUsingEncoding: NSUTF8StringEncoding];
NSArray *allPeers = appDelegate.mcManager.session.connectedPeers;
NSError *error;
[appDelegate.mcManager.session sendData:dataToSend
toPeers:allPeers
withMode:MCSessionSendDataReliable
error:&error];
if (error) {
NSLog(#"%#", [error localizedDescription]);
}
}
}
and when the subordinate systems receive the data, MCManager sends the Notification Center a notification and my class, which is looking for that particular notification, grabs it and executes this:
-(void)didReceiveDataWithNotification:(NSNotification *)notification{
if(!iAmHost){
NSData *receivedData = [[notification userInfo] objectForKey:#"data"];
NSString *action = [[NSString alloc] initWithData: receivedData encoding:NSUTF8StringEncoding];
NSLog(#"Recieved:");
NSLog(action); //for debugging purposes, and figuring out timing
//decide how to act depending on the string given
if([action containsString:#"ChangeMaxScore"]){
//the string was formatted as, for example, "ChangeMaxScore105"
NSString* valueStr = [action substringFromIndex:14];
maxScore = (int)[valueStr integerValue];
[self changeMaxScore]; //this method changes the label text that shows the user the value of maxScore
}
else if([action containsString:#"ChangePlayerNo"]){
//strings are formatted as "ChangePlayerNo2" for the second segment in a segmented control with the segments "2", "3", "4"
//so it would be referring to four players
NSString *valueStr = [action substringFromIndex:14];
[playerNumberSegmentedControl setSelectedSegmentIndex: [valueStr integerValue]];
playerNumber = playerNumberSegmentedControl.selectedSegmentIndex + 2;
[self changePlayerNumber];
//Players can either be human or a type of AI (AI-1,AI-2,etc.)
//the segmented control where you choose this is invisible unless that player number is playing
//so this method sets that segmented control visible and interactable (by adding it to the view)
//and removes those segmented controls not in use from the view
}
else if([action containsString:#"ChangeP0State"]){
//changes the value of the first player's segmented control (Human, AI-1, AI-2, etc.
NSString* valueStr = [action substringFromIndex:13];
AIControl0.selectedSegmentIndex = (int)[valueStr integerValue];
}
...
else if([action containsString:#"StartGame"])
[self newGame];
//this method starts the game and, in the process, pushes another view controller
}
}
My issue is that these actions, on the receiving end, are very laggy. For changing the number of players, for instance, the receiver NSLogs "Received: ChangePlayerNo1", and the segmented control on-screen changes its selected segment to the second one, but the stuff that's supposed to show up at that point...doesn't. And when I send the "StartGame" command, the receiver NSLogs that it has received it, I have to wait thirty seconds for it to actually start the game like it was asked.
This delay makes it very hard to test whether my wireless methods are working or not (it works on the host's side, mostly because the host is changing them manually, not responsively - also, all of this works on the other side of my program, which is just the game without wireless support, with several players/AIs on a single screen) and, if not fixed, will definitely prevent the app from being used easily.
I'm curious what causes this and what I can do to fix it. Thank you!
Using SLComposeViewController, I notice curious behavior when posting to Facebook if both the image and the URL are present. Specifically, if you have both image and URL, the URL appears in the body of the Facebook post in the view of the SLComposeViewController, immediately after the initialText if I do the following:
SLComposeViewController *controller = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
NSString *text = #"This is a test Facebook post with SLComposeViewController.";
NSURL *url = [NSURL URLWithString:#"http://http://stackoverflow.com/questions/12503287/tutorial-for-slcomposeviewcontroller-sharing"];
UIImage *image = ...;
[controller setInitialText:text];
[controller addURL:url];
[controller addImage:image];
[self presentViewController:controller animated:YES completion:nil];
That's obviously a hassle because the if the URL is long, the initial text is pushed off of the visible portion of the view of SLComposeViewController and I only see the latter portion of the URL:
If I repeat this process, this time not adding the image to the post, the text of the URL conveniently does not show up in the body at all (even though it shows up properly online).
Bottom line, only if there is an image and and URL does the URL show up in the body of the post. And I see this same pattern when I use FBNativeDialogs.
Is there any way to stop that behavior with SLComposeViewController, so that I can have both image and URL connected to the Facebook post without exposing the user to the web site's long, ugly URL? Clearly I can use any of the non-SLComposeViewController solutions (e.g. design my own UI for composing the Facebook post, using Facebook's deprecated Feed Dialog, etc.). Just wondering if I'm overlooking some obvious SLComposeViewController solution.
In the end, I've given up on the SLComposeViewController (as well as the FBNativeDialogs). They present a nice, integrated feel, but given that my posts invariably include both photo and URL, this really doesn't work. On top of that, the posts were not correctly attributed as being from my app.
So, in the end, I've written my own user interface and use the the Facebook SDK 3.1 FBRequestConnection as outlined here. I think it's a little silly that we all have to make our own UI because the weaknesses in the native UI, but it is what it is.
The new dialog seems to be designed around you providing most of the share data as tags on the website the provided link points to. Like this:
Title:
<title>TITLE</title>
Description:
<meta name="description" content="DESCRIPTION">
Image:
<meta property="og:image" content="IMAGE_URL">
App ID:
<meta property="fb:app_id" content="APP_ID">
This way you only need to provide the initial text and an URL. You can look how the official Youtube app does it's Facebook sharing for an example.
TWTweetComposeViewController *twitter = [[TWTweetComposeViewController alloc] init];
NSString *format = #"“%#” %# /via #DesignSceneApp";
NSString *message = [NSString stringWithFormat:format, title, url]
NSUInteger idx = title.length;
while (![twitter setInitialText:message]) {
idx -= 5;
if (idx > 5) {
message = [NSString stringWithFormat:format,
[NSString stringWithFormat:#"%#…", [title substringToIndex:idx]],
url
];
} else {
// Give up on the title.
message = [NSString stringWithFormat:#"%# /via #DesignSceneApp", url];
[twitter setInitialText:message];
break;
}
}
[self presentViewController:twitter animated:YES completion:nil];
I'm trying to determine the user language in iOS. StackOverflow has several answers on this topic which has greatly helped me out, such as this one: Getting current device language in iOS?
I can successfully retrieve the value I'm looking for in NSLog (i.e. "en" or "de") but every time I question this with an if/then statement it doesn't appear to work. I have this in my viewDidLoad for testing:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *myLanguage = [[NSLocale preferredLanguages] objectAtIndex:0];
NSLog(#"The current languague is %#.", myLanguage);
if (myLanguage == #"de") {
self.myLabel.text = #"German";
} else if (myLanguage == #"en") {
self.myLabel.text = #"English";
} else {
self.myLabel.text = #"didn't work";
}
}
No matter if the device is set to English or German only the last else statement is displayed. NSLog however correctly displays either en or de.
What am I doing wrong?
NSString comparison is done with isEqualToString: method. In your code you are comparing two different NSString objects, while instead you have to compare the contents of each one of them.
If you have two objects of any kind, they are always different, even if all their members have the same values. That's why methods like this exist, to compare objects based on their members.
Replace:
if (myLanguage == #"de")
with
if ([myLanguage isEqualToString:#"de"])
and the same for the else ifs in your code.