PageRenderTime 27ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Three20UI/Sources/TTPostController.m

https://github.com/GetMoPix/three20
Objective C | 621 lines | 406 code | 147 blank | 68 comment | 57 complexity | e6187ebafd51cca4028de47310b0701e MD5 | raw file
  1. //
  2. // Copyright 2009-2011 Facebook
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. //
  16. #import "Three20UI/TTPostController.h"
  17. // UI
  18. #import "Three20UI/TTNavigator.h"
  19. #import "Three20UI/TTPostControllerDelegate.h"
  20. #import "Three20UI/TTActivityLabel.h"
  21. #import "Three20UI/TTView.h"
  22. #import "Three20UI/UIViewAdditions.h"
  23. // UINavigator
  24. #import "Three20UINavigator/TTGlobalNavigatorMetrics.h"
  25. // UICommon
  26. #import "Three20UICommon/TTGlobalUICommon.h"
  27. #import "Three20UICommon/UIViewControllerAdditions.h"
  28. // Style
  29. #import "Three20Style/TTGlobalStyle.h"
  30. #import "Three20Style/TTStyleSheet.h"
  31. // Core
  32. #import "Three20Core/TTGlobalCoreLocale.h"
  33. #import "Three20Core/TTCorePreprocessorMacros.h"
  34. #import "Three20Core/NSStringAdditions.h"
  35. #import "Three20Core/TTGlobalCore.h"
  36. static const CGFloat kMarginX = 5.0f;
  37. static const CGFloat kMarginY = 6.0f;
  38. ///////////////////////////////////////////////////////////////////////////////////////////////////
  39. ///////////////////////////////////////////////////////////////////////////////////////////////////
  40. ///////////////////////////////////////////////////////////////////////////////////////////////////
  41. @implementation TTPostController
  42. @synthesize result = _result;
  43. @synthesize textView = _textView;
  44. @synthesize originView = _originView;
  45. @synthesize delegate = _delegate;
  46. ///////////////////////////////////////////////////////////////////////////////////////////////////
  47. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
  48. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  49. if (self) {
  50. self.navigationItem.leftBarButtonItem =
  51. [[[UIBarButtonItem alloc] initWithBarButtonSystemItem: UIBarButtonSystemItemCancel
  52. target: self
  53. action: @selector(cancel)] autorelease];
  54. self.navigationItem.rightBarButtonItem =
  55. [[[UIBarButtonItem alloc] initWithTitle: TTLocalizedString(@"Done", @"")
  56. style: UIBarButtonItemStyleDone
  57. target: self
  58. action: @selector(post)] autorelease];
  59. }
  60. return self;
  61. }
  62. ///////////////////////////////////////////////////////////////////////////////////////////////////
  63. - (id)initWithNavigatorURL:(NSURL*)URL query:(NSDictionary*)query {
  64. self = [self initWithNibName:nil bundle:nil];
  65. if (self) {
  66. if (nil != query) {
  67. _delegate = [query objectForKey:@"delegate"];
  68. _defaultText = [[query objectForKey:@"text"] copy];
  69. self.navigationItem.title = [query objectForKey:@"title"];
  70. self.originView = [query objectForKey:@"__target__"];
  71. NSValue* originRect = [query objectForKey:@"originRect"];
  72. if (nil != originRect) {
  73. _originRect = [originRect CGRectValue];
  74. }
  75. }
  76. }
  77. return self;
  78. }
  79. ///////////////////////////////////////////////////////////////////////////////////////////////////
  80. - (void)dealloc {
  81. TT_RELEASE_SAFELY(_result);
  82. TT_RELEASE_SAFELY(_defaultText);
  83. TT_RELEASE_SAFELY(_originView);
  84. TT_RELEASE_SAFELY(_textView);
  85. TT_RELEASE_SAFELY(_navigationBar);
  86. TT_RELEASE_SAFELY(_innerView);
  87. TT_RELEASE_SAFELY(_activityView);
  88. [super dealloc];
  89. }
  90. ///////////////////////////////////////////////////////////////////////////////////////////////////
  91. ///////////////////////////////////////////////////////////////////////////////////////////////////
  92. #pragma mark -
  93. #pragma mark Private
  94. ///////////////////////////////////////////////////////////////////////////////////////////////////
  95. - (NSString*)stripWhitespace:(NSString*)text {
  96. if (nil != text) {
  97. NSCharacterSet* whitespace = [NSCharacterSet whitespaceCharacterSet];
  98. return [text stringByTrimmingCharactersInSet:whitespace];
  99. } else {
  100. return @"";
  101. }
  102. }
  103. ///////////////////////////////////////////////////////////////////////////////////////////////////
  104. - (void)showKeyboard {
  105. UIApplication* app = [UIApplication sharedApplication];
  106. _originalStatusBarStyle = app.statusBarStyle;
  107. _originalStatusBarHidden = app.statusBarHidden;
  108. if (!_originalStatusBarHidden) {
  109. #ifdef __IPHONE_3_2
  110. if ([app respondsToSelector:@selector(setStatusBarHidden:withAnimation:)])
  111. [app setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
  112. else
  113. #endif
  114. [app setStatusBarHidden:NO animated:YES];
  115. [app setStatusBarStyle:UIStatusBarStyleBlackTranslucent animated:YES];
  116. }
  117. [_textView becomeFirstResponder];
  118. }
  119. ///////////////////////////////////////////////////////////////////////////////////////////////////
  120. - (void)hideKeyboard {
  121. UIApplication* app = [UIApplication sharedApplication];
  122. #ifdef __IPHONE_3_2
  123. if ([app respondsToSelector:@selector(setStatusBarHidden:withAnimation:)])
  124. [app setStatusBarHidden:_originalStatusBarHidden withAnimation:UIStatusBarAnimationSlide];
  125. else
  126. #endif
  127. [app setStatusBarHidden:_originalStatusBarHidden animated:YES];
  128. [app setStatusBarStyle:_originalStatusBarStyle animated:NO];
  129. [_textView resignFirstResponder];
  130. }
  131. ///////////////////////////////////////////////////////////////////////////////////////////////////
  132. - (CGAffineTransform)transformForOrientation {
  133. return TTRotateTransformForOrientation(TTInterfaceOrientation());
  134. }
  135. ///////////////////////////////////////////////////////////////////////////////////////////////////
  136. - (void)showActivity:(NSString*)activityText {
  137. if (nil == _activityView) {
  138. _activityView = [[TTActivityLabel alloc] initWithStyle:TTActivityLabelStyleWhiteBox];
  139. [self.view addSubview:_activityView];
  140. }
  141. if (nil != activityText) {
  142. _activityView.text = activityText;
  143. _activityView.frame = CGRectOffset(CGRectInset(_textView.frame, 13, 13), 2, 0);
  144. _activityView.hidden = NO;
  145. _textView.hidden = YES;
  146. self.navigationItem.rightBarButtonItem.enabled = NO;
  147. } else {
  148. _activityView.hidden = YES;
  149. _textView.hidden = NO;
  150. self.navigationItem.rightBarButtonItem.enabled = YES;
  151. }
  152. }
  153. ///////////////////////////////////////////////////////////////////////////////////////////////////
  154. - (void)layoutTextEditor {
  155. CGFloat keyboard = TTKeyboardHeightForOrientation(TTInterfaceOrientation());
  156. _screenView.frame = CGRectMake(0, _navigationBar.bottom,
  157. self.view.orientationWidth,
  158. self.view.orientationHeight - (keyboard+_navigationBar.height));
  159. _textView.frame = CGRectMake(kMarginX, kMarginY+_navigationBar.height,
  160. _screenView.width - kMarginX*2,
  161. _screenView.height - kMarginY*2);
  162. _textView.hidden = NO;
  163. }
  164. ///////////////////////////////////////////////////////////////////////////////////////////////////
  165. - (void)showAnimationDidStop {
  166. _textView.hidden = NO;
  167. [self.superController viewDidDisappear:YES];
  168. }
  169. ///////////////////////////////////////////////////////////////////////////////////////////////////
  170. - (void)dismissAnimationDidStop {
  171. if ([_delegate respondsToSelector:@selector(postController:didPostText:withResult:)]) {
  172. [_delegate postController:self didPostText:_textView.text withResult:_result];
  173. }
  174. TT_RELEASE_SAFELY(_originView);
  175. [self dismissPopupViewControllerAnimated:NO];
  176. }
  177. ///////////////////////////////////////////////////////////////////////////////////////////////////
  178. - (void)fadeOut {
  179. _originView.hidden = NO;
  180. TT_RELEASE_SAFELY(_originView);
  181. [UIView beginAnimations:nil context:nil];
  182. [UIView setAnimationDelegate:self];
  183. [UIView setAnimationDidStopSelector:@selector(fadeAnimationDidStop)];
  184. [UIView setAnimationDuration:TT_TRANSITION_DURATION];
  185. self.view.alpha = 0;
  186. [UIView commitAnimations];
  187. [self hideKeyboard];
  188. }
  189. ///////////////////////////////////////////////////////////////////////////////////////////////////
  190. - (void)fadeAnimationDidStop {
  191. [self dismissPopupViewControllerAnimated:NO];
  192. }
  193. ///////////////////////////////////////////////////////////////////////////////////////////////////
  194. - (void)dismissWithCancel {
  195. if ([_delegate respondsToSelector:@selector(postControllerDidCancel:)]) {
  196. [_delegate postControllerDidCancel:self];
  197. }
  198. BOOL animated = YES;
  199. [self.superController viewWillAppear:animated];
  200. [self dismissPopupViewControllerAnimated:animated];
  201. }
  202. ///////////////////////////////////////////////////////////////////////////////////////////////////
  203. ///////////////////////////////////////////////////////////////////////////////////////////////////
  204. #pragma mark -
  205. #pragma mark UIViewController
  206. ///////////////////////////////////////////////////////////////////////////////////////////////////
  207. - (void)loadView {
  208. [super loadView];
  209. self.view.frame = [UIScreen mainScreen].applicationFrame;
  210. self.view.backgroundColor = [UIColor clearColor];
  211. self.view.autoresizesSubviews = YES;
  212. _innerView = [[UIView alloc] init];
  213. _innerView.backgroundColor = [UIColor blackColor];
  214. _innerView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
  215. _innerView.autoresizesSubviews = YES;
  216. [self.view addSubview:_innerView];
  217. _screenView = [[TTView alloc] init];
  218. _screenView.backgroundColor = [UIColor clearColor];
  219. _screenView.style = TTSTYLE(postTextEditor);
  220. _screenView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
  221. _screenView.autoresizesSubviews = YES;
  222. [self.view addSubview:_screenView];
  223. _textView = [[UITextView alloc] init];
  224. _textView.delegate = self;
  225. _textView.font = TTSTYLEVAR(font);
  226. _textView.textColor = [UIColor blackColor];
  227. _textView.contentInset = UIEdgeInsetsMake(0, 4, 0, 4);
  228. _textView.keyboardAppearance = UIKeyboardAppearanceAlert;
  229. _textView.backgroundColor = [UIColor clearColor];
  230. [self.view addSubview:_textView];
  231. _navigationBar = [[UINavigationBar alloc] init];
  232. _navigationBar.barStyle = UIBarStyleBlackOpaque;
  233. _navigationBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  234. [_navigationBar pushNavigationItem:self.navigationItem animated:NO];
  235. [_innerView addSubview:_navigationBar];
  236. }
  237. ///////////////////////////////////////////////////////////////////////////////////////////////////
  238. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
  239. return TTIsSupportedOrientation(interfaceOrientation);
  240. }
  241. ///////////////////////////////////////////////////////////////////////////////////////////////////
  242. - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
  243. duration:(NSTimeInterval)duration {
  244. [UIView beginAnimations:nil context:nil];
  245. [UIView setAnimationDuration:TT_TRANSITION_DURATION];
  246. self.view.transform = [self transformForOrientation];
  247. self.view.frame = [UIScreen mainScreen].applicationFrame;
  248. _innerView.frame = self.view.bounds;
  249. [self layoutTextEditor];
  250. [UIView commitAnimations];
  251. }
  252. ///////////////////////////////////////////////////////////////////////////////////////////////////
  253. - (UIView*)rotatingHeaderView {
  254. return _navigationBar;
  255. }
  256. ///////////////////////////////////////////////////////////////////////////////////////////////////
  257. ///////////////////////////////////////////////////////////////////////////////////////////////////
  258. #pragma mark -
  259. #pragma mark UIViewController (TTCategory)
  260. ///////////////////////////////////////////////////////////////////////////////////////////////////
  261. - (BOOL)persistView:(NSMutableDictionary*)state {
  262. [state setObject:[NSNumber numberWithBool:YES] forKey:@"__important__"];
  263. NSString* delegate = [[TTNavigator navigator] pathForObject:_delegate];
  264. if (delegate) {
  265. [state setObject:delegate forKey:@"delegate"];
  266. }
  267. [state setObject:self.textView.text forKey:@"text"];
  268. NSString* title = self.navigationItem.title;
  269. if (title) {
  270. [state setObject:title forKey:@"title"];
  271. }
  272. return [super persistView:state];
  273. }
  274. ///////////////////////////////////////////////////////////////////////////////////////////////////
  275. - (void)restoreView:(NSDictionary*)state {
  276. [super restoreView:state];
  277. NSString* delegate = [state objectForKey:@"delegate"];
  278. if (delegate) {
  279. _delegate = [[TTNavigator navigator] objectForPath:delegate];
  280. }
  281. NSString* title = [state objectForKey:@"title"];
  282. if (title) {
  283. self.navigationItem.title = title;
  284. }
  285. _defaultText = [[state objectForKey:@"text"] retain];
  286. }
  287. ///////////////////////////////////////////////////////////////////////////////////////////////////
  288. ///////////////////////////////////////////////////////////////////////////////////////////////////
  289. #pragma mark -
  290. #pragma mark TTPopupViewController
  291. ///////////////////////////////////////////////////////////////////////////////////////////////////
  292. - (void)showInView:(UIView*)view animated:(BOOL)animated {
  293. [self retain];
  294. [self.superController viewWillDisappear:animated];
  295. UIWindow* window = view.window ? view.window : [UIApplication sharedApplication].keyWindow;
  296. self.view.transform = [self transformForOrientation];
  297. self.view.frame = [UIScreen mainScreen].applicationFrame;
  298. [window addSubview:self.view];
  299. if (_defaultText) {
  300. _textView.text = _defaultText;
  301. } else {
  302. _defaultText = [_textView.text retain];
  303. }
  304. _innerView.frame = self.view.bounds;
  305. [_navigationBar sizeToFit];
  306. _originView.hidden = YES;
  307. if (animated) {
  308. _innerView.alpha = 0;
  309. _navigationBar.alpha = 0;
  310. _textView.hidden = YES;
  311. CGRect originRect = _originRect;
  312. if (CGRectIsEmpty(originRect) && _originView) {
  313. originRect = _originView.screenFrame;
  314. }
  315. if (!CGRectIsEmpty(originRect)) {
  316. _screenView.frame = CGRectOffset(originRect, 0, -TTStatusHeight());
  317. } else {
  318. [self layoutTextEditor];
  319. _screenView.transform = CGAffineTransformMakeScale(0.00001, 0.00001);
  320. }
  321. [UIView beginAnimations:nil context:nil];
  322. [UIView setAnimationDuration:TT_TRANSITION_DURATION];
  323. [UIView setAnimationDelegate:self];
  324. [UIView setAnimationDidStopSelector:@selector(showAnimationDidStop)];
  325. _navigationBar.alpha = 1;
  326. _innerView.alpha = 1;
  327. if (originRect.size.width) {
  328. [self layoutTextEditor];
  329. } else {
  330. _screenView.transform = CGAffineTransformIdentity;
  331. }
  332. [UIView commitAnimations];
  333. } else {
  334. _innerView.alpha = 1;
  335. _screenView.transform = CGAffineTransformIdentity;
  336. [self layoutTextEditor];
  337. [self showAnimationDidStop];
  338. }
  339. [self showKeyboard];
  340. }
  341. ///////////////////////////////////////////////////////////////////////////////////////////////////
  342. - (void)dismissPopupViewControllerAnimated:(BOOL)animated {
  343. if (animated) {
  344. [self fadeOut];
  345. } else {
  346. UIViewController* superController = self.superController;
  347. [self.view removeFromSuperview];
  348. [self release];
  349. superController.popupViewController = nil;
  350. [superController viewDidAppear:animated];
  351. }
  352. }
  353. ///////////////////////////////////////////////////////////////////////////////////////////////////
  354. ///////////////////////////////////////////////////////////////////////////////////////////////////
  355. #pragma mark -
  356. #pragma mark UIAlertViewDelegate
  357. ///////////////////////////////////////////////////////////////////////////////////////////////////
  358. - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
  359. if (buttonIndex == 0) {
  360. [self dismissWithCancel];
  361. }
  362. }
  363. ///////////////////////////////////////////////////////////////////////////////////////////////////
  364. ///////////////////////////////////////////////////////////////////////////////////////////////////
  365. #pragma mark -
  366. #pragma mark Public
  367. ///////////////////////////////////////////////////////////////////////////////////////////////////
  368. - (UITextView*)textView {
  369. self.view;
  370. return _textView;
  371. }
  372. ///////////////////////////////////////////////////////////////////////////////////////////////////
  373. - (UINavigationBar*)navigatorBar {
  374. if (!_navigationBar) {
  375. self.view;
  376. }
  377. return _navigationBar;
  378. }
  379. ///////////////////////////////////////////////////////////////////////////////////////////////////
  380. - (void)setOriginView:(UIView*)view {
  381. if (view != _originView) {
  382. [_originView release];
  383. _originView = [view retain];
  384. _originRect = CGRectZero;
  385. }
  386. }
  387. ///////////////////////////////////////////////////////////////////////////////////////////////////
  388. - (void)post {
  389. BOOL shouldDismiss = [self willPostText:_textView.text];
  390. if ([_delegate respondsToSelector:@selector(postController:willPostText:)]) {
  391. shouldDismiss = [_delegate postController:self willPostText:_textView.text];
  392. }
  393. if (shouldDismiss) {
  394. [self dismissWithResult:nil animated:YES];
  395. } else {
  396. [self showActivity:[self titleForActivity]];
  397. }
  398. }
  399. ///////////////////////////////////////////////////////////////////////////////////////////////////
  400. - (void)cancel {
  401. if (TTIsStringWithAnyText(_textView.text)
  402. && !_textView.text.isWhitespaceAndNewlines
  403. && !(_defaultText && [_defaultText isEqualToString:_textView.text])) {
  404. UIAlertView* cancelAlertView = [[[UIAlertView alloc] initWithTitle:
  405. TTLocalizedString(@"Cancel", @"")
  406. message:TTLocalizedString(@"Are you sure you want to cancel?", @"")
  407. delegate:self cancelButtonTitle:TTLocalizedString(@"Yes", @"")
  408. otherButtonTitles:TTLocalizedString(@"No", @""), nil] autorelease];
  409. [cancelAlertView show];
  410. } else {
  411. [self dismissWithCancel];
  412. }
  413. }
  414. ///////////////////////////////////////////////////////////////////////////////////////////////////
  415. - (void)dismissWithResult:(id)result animated:(BOOL)animated {
  416. [_result release];
  417. _result = [result retain];
  418. [self.superController viewWillAppear:animated];
  419. if (animated) {
  420. if ([_delegate respondsToSelector:@selector(postController:willAnimateTowards:)]) {
  421. CGRect rect = [_delegate postController:self willAnimateTowards:_originRect];
  422. if (!CGRectIsEmpty(rect)) {
  423. _originRect = rect;
  424. }
  425. }
  426. CGRect originRect = _originRect;
  427. if (CGRectIsEmpty(originRect) && _originView) {
  428. originRect = _originView.screenFrame;
  429. }
  430. _originView.hidden = NO;
  431. _activityView.hidden = YES;
  432. _textView.hidden = YES;
  433. [UIView beginAnimations:nil context:nil];
  434. [UIView setAnimationDuration:TT_TRANSITION_DURATION];
  435. [UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
  436. [UIView setAnimationDelegate:self];
  437. [UIView setAnimationDidStopSelector:@selector(dismissAnimationDidStop)];
  438. if (!CGRectIsEmpty(originRect)) {
  439. _screenView.frame = CGRectOffset(originRect, 0, -TTStatusHeight());
  440. } else {
  441. _screenView.transform = CGAffineTransformMakeScale(0.00001, 0.00001);
  442. }
  443. _innerView.alpha = 0;
  444. _navigationBar.alpha = 0;
  445. [UIView commitAnimations];
  446. } else {
  447. [self dismissAnimationDidStop];
  448. }
  449. [self hideKeyboard];
  450. }
  451. ///////////////////////////////////////////////////////////////////////////////////////////////////
  452. - (void)failWithError:(NSError*)error {
  453. [self showActivity:nil];
  454. NSString* title = [self titleForError:error];
  455. if (title.length) {
  456. UIAlertView* alertView = [[[UIAlertView alloc] initWithTitle:TTLocalizedString(@"Error", @"")
  457. message:title delegate:nil cancelButtonTitle:TTLocalizedString(@"Ok", @"")
  458. otherButtonTitles:nil] autorelease];
  459. [alertView show];
  460. }
  461. }
  462. ///////////////////////////////////////////////////////////////////////////////////////////////////
  463. - (BOOL)willPostText:(NSString*)text {
  464. return YES;
  465. }
  466. ///////////////////////////////////////////////////////////////////////////////////////////////////
  467. - (NSString*)titleForActivity {
  468. return nil;
  469. }
  470. ///////////////////////////////////////////////////////////////////////////////////////////////////
  471. - (NSString*)titleForError:(NSError*)error {
  472. return nil;
  473. }
  474. @end