I'm about to implement a Facebook-like side navigation for an iPhone App. I can slide it in and out without problems but when I want to click a button or anything in that navigation view nothing happens.
Here is the code for my side navigation view I initialize in viewDidLoad:
self.sideNavigationView = [[UIView alloc] initWithFrame:CGRectMake(self.navigationController.view.frame.size.width, 20, 238, self.navigationController.view.frame.size.height-20)];
[self.navigationController.view addSubview:self.sideNavigationView];
Then I have the following method to slide it in:
- (IBAction) openMenuBar: (id) sender {
if (!self.sideNavigationIsExpanded) {
self.sideNavigationView.frame = CGRectMake(self.navigationController.view.frame.size.width, 20, 238, self.navigationController.view.frame.size.height-20);
}
CGRect destination = self.navigationController.view.frame;
CGRect sideNavigationDestination = self.sideNavigationView.frame;
sideNavigationDestination.size.width = 238;
// Slide back
if (self.sideNavigationIsExpanded) {
sideNavigationDestination.origin.x = self.view.frame.size.width;
destination.origin.x = 0;
sideNavigationDestination.origin.y = 20;
self.sideNavigationIsExpanded = NO;
}
// Slide in
else {
sideNavigationDestination.origin.x = self.view.frame.size.width;
destination.origin.x = - sideNavigationDestination.size.width;
sideNavigationDestination.origin.y = 20;
self.sideNavigationIsExpanded = YES;
self.sideNavigationView.hidden = NO;
}
[UIView animateWithDuration:0.25 animations:^{
self.sideNavigationView.frame = sideNavigationDestination;
self.navigationController.view.frame = destination;
} completion:^(BOOL finished) {
if (!self.sideNavigationIsExpanded)
self.sideNavigationView.hidden = YES;
self.view.userInteractionEnabled = !self.sideNavigationIsExpanded;
self.sideNavigationView.userInteractionEnabled = self.sideNavigationIsExpanded;
}];
}
I tried several combinations of userInteractionEnabled = YES at every subview... Changed nothing...
Could it be that the subviews of my sideNavigationView aren't selectable because when I initialize it it is out of sight? I tried initializing it with a frame in sight (CGRectMake(0,0,238,300)) and that worked. -_-
So how could I solve my problem?
As sideNavigationView is a subview of self.view then this line:
self.view.userInteractionEnabled = !self.sideNavigationIsExpanded;
is preventing touches from reaching your sideNavigationView at all. A view will not pass a touch on to its subviews if its own userInteractionEnabled property is set to NO, even though you have set the subview's property to YES in the next line.
As for initialising off-screen, I don't believe this will have any bearing on whether the view can receive touches once it is moved back onscreen.
Related
I am developing iPad application in landscape mode.I have set in all view controller as return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);.
but when I give change view frame through following code,it is not changing , it occupies full screen of ipad.How can i overcome this problem , any help please?I tried view.frame also.
-(IBAction)category_Click:(id)sender
{
CGRect rect = _categoryController.view.bounds;
rect.origin.x = 0;
rect.origin.y = rect.origin.y;
rect.size.width = 1024;
rect.size.height = 650;
_categoryController.view.bounds = rect;
_categoryController.view.bounds = CGRectMake(rect.origin.x, rect.origin.y,1024, 650);
[self presentModalViewController:_categoryController animated:YES];
}
Have you tried adjusting the frame after the viewController is presented? I believe that the presentModalViewController:animated: method may sometimes alter the frame during its execution. You may even need to wait until the animation is finished.
If it possible, perhaps try adjusting the view's frame in the '-(void)viewDidAppear:' method of the _categoryController's class.
Generally, though, I have found that changing the appearance of a modal view controller in a smooth way is difficult.
Check modalPresentationStyle property
_categoryController.modalPresentationStyle = UIModalPresentationFormSheet;
_categoryController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:_categoryController animated:YES];
_categoryController.view.superview.frame = CGRectMake(0, 0, 818, 739);
_categoryController.view.superview.center = self.view.center;
I'm not having any success trying to change the frame size or the hidden property of views in the contentViewController of my UIPopover. I tried to just alter the frame size of a subview of the contentViewController.view programmatically, but I couldn't alter it. Whatever frame the xib initialized it retained. Now I'm tryin 2 subviews with correct frame sizes for each device orientation, and useing the hidden property to make the view with the correct frame size visible. The xib initially has each ".hidden:YES". I'm not able to use ".hidden:NO" make one of them visible.
Specifically I've a UIImageView in the popover that I want as large as possible in each orientation, while retaining the aspect ratio of the picture it contains.
Some code...
UIInterfaceOrientation interfaceOrientation = [self interfaceOrientation];
// If a saveOrPrintPopover is not already showing, create it.
if(self.saveOrPrintPopover){
[self closeSaveOrPrintPopover];
keyImageView.image = appDelegate.lineImage;
appDelegate.lineImage = NULL;
content.printView.image = NULL;
}else{
//////// UIPopoverController created here !
saveOrPrintPopover = [[UIPopoverController alloc] initWithContentViewController:content];
content.view.frame = CGRectMake(0, 0, 1024, 748);
SaveOrPrintViewController *cntrler = (SaveOrPrintViewController *)saveOrPrintPopover.contentViewController;
if (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
saveOrPrintPopover.popoverContentSize = CGSizeMake(748, 940);
cntrler.landscapeBackingView.hidden = YES;
cntrler.portraitBackingView.hidden = NO;
}
else {
saveOrPrintPopover.popoverContentSize = CGSizeMake(984, 664);
cntrler.landscapeBackingView.hidden = NO;
cntrler.portraitBackingView.hidden = YES;
}
[content.view setNeedsLayout];
[content.view layoutIfNeeded];
saveOrPrintPopover.delegate = self;
appDelegate.lineImage = keyImageView.image;
keyImageView.image = NULL;
}
[self showPopoverFromBarButtonItem];
You can change size of popover:
[saveOrPrintPopover setPopoverContentSize:CGSizeMake(320, 560)];
And your content view should be configured properly to autoresize.
I'm writing an iPhone app that (like most apps) supports auto-rotation: You rotate your phone, and its views rotate and resize appropriately.
But I am assigning a custom view to navigationItem.titleView (the title area of the navigation bar), and I can't get that view to resize correctly when the phone rotates.
I know what you're thinking, "Just set its autoresizingMask to UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight," but it's not that simple. Of course, if I don't set my view's autoresizingMask, then my view doesn't resize; and I want it to resize.
The problem is, if I do set its autoresizingMask, then it resizes correctly as long as that view is visible; but the titleView's size gets messed up in this scenario:
Run the app, with the phone held in portrait mode. Everything looks good.
Do something that causes the app to push another view onto the navigation stack. E.g. click a table row or button that causes a call to [self.navigationController pushViewController:someOtherViewController animated:YES].
While viewing the child controller, rotate the phone to landscape.
Click the "Back" button to return to the top-level view. At this point, the title view is messed up: Although you are holding the phone in landscape mode, the title view is still sized as if you were holding it in portrait mode.
Finally, rotate the phone back to portrait mode. Now things get even worse: The title view shrinks in size (since the navigation bar got smaller), but since it was already too small, now it is much too small.
If you want to reproduce this yourself, follow these steps (this is a bit of work):
Make an app using Xcode's "Navigation-based Application" wizard.
Set it up so that the top-level table view has rows that, when you click them, push a detail view onto the navigation stack.
Include this code in both the top-level view controller and the detail view controller:
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
Include this code in only the top-level view controller:
- (void)viewDidLoad {
[super viewDidLoad];
// Create "Back" button
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:#"Master"
style:UIBarButtonItemStylePlain target:nil action:nil];
self.navigationItem.backBarButtonItem = backButton;
[backButton release];
// Create title view
UILabel* titleView = [[[UILabel alloc] initWithFrame:CGRectMake(0,0,500,38)] autorelease];
titleView.textAlignment = UITextAlignmentCenter;
titleView.text = #"Watch this title view";
// If I leave the following line turned on, then resizing of the title view
// messes up if I:
//
// 1. Start at the master view (which uses this title view) in portrait
// 2. Navigate to the detail view
// 3. Rotate the phone to landscape
// 4. Navigate back to the master view
// 5. Rotate the phone back to portrait
//
// On the other hand, if I remove the following line, then I get a different
// problem: The title view doesn't resize as I want it to when I:
//
// 1. Start at the master view (which uses this title view) in portrait
// 2. Rotate the phone to landscape
titleView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.navigationItem.titleView = titleView;
}
Finally, follow my repro steps.
So ... am I doing something wrong? Is there a way to make my titleView always resize correctly?
You should also set the contentMode of the UIImageView to get the titleView properly displayed in landscape and/or portrait mode :
imgView.contentMode=UIViewContentModeScaleAspectFit;
The whole sequence: (self is a UIViewController instance)
UIImageView* imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"myCustomTitle.png"]];
imgView.autoresizingMask=UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
imgView.contentMode=UIViewContentModeScaleAspectFit;
self.navigationItem.titleView = imgView;
[imgView release];
I had something similar - but it was returning (popping) to root view controller. Ultimately, I went with the following for popping:
[[self navigationController] setNavigationBarHidden:YES animated:NO];
[[self navigationController] popViewControllerAnimated:YES];
[[self navigationController] setNavigationBarHidden:NO animated:NO];
And it worked. There may have been a better way but - after all the hours I'd already spent on this issue - this was good enough for me.
I dealt with this same issue by keeping track of the customView's initial frame, then toggling between that and a scaled CGRect of the initial frame in a -setLandscape method on a UIButton subclass. I used the UIButton subclass as navigationItem.titleView and navigationItem.rightBarButtonItem.
In UIButton subclass -
- (void)setLandscape:(BOOL)value
{
isLandscape = value;
CGFloat navbarPortraitHeight = 44;
CGFloat navbarLandscapeHeight = 32;
CGRect initialFrame = // your initial frame
CGFloat scaleFactor = floorf((navbarLandscapeHeight/navbarPortraitHeight) * 100) / 100;
if (isLandscape) {
self.frame = CGRectApplyAffineTransform(initialFrame, CGAffineTransformMakeScale(scaleFactor, scaleFactor));
} else {
self.frame = initialFrame;
}
}
Then in the InterfaceOrientation delegates I invoked the -setLandscape method on the customViews to change their sizes.
In UIViewController -
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[self updateNavbarButtonsToDeviceOrientation];;
}
- (void)updateNavbarButtonsToDeviceOrientation
{
ResizeButton *rightButton = (ResizeButton *)self.navigationItem.rightBarButtonItem.customView;
ResizeButton *titleView = (ResizeButton *)self.navigationItem.titleView;
if (self.interfaceOrientation == UIDeviceOrientationPortrait || self.interfaceOrientation == UIDeviceOrientationPortraitUpsideDown) {
[rightButton setLandscape:NO];
[titleView setLandscape:NO];
} else {
[rightButton setLandscape:YES];
[titleView setLandscape:YES];
}
}
(Answering my own question)
I got this working by manually keeping track of the titleView's margins (its distance from the edges of the navigtion bar) -- saving when the view disappears, and restoring when the view reappears.
The idea is, we aren't restoring the titleView to the exact size it had previously; rather, we are restoring it so that it has the same margins it had previously. That way, if the phone has rotated, the titleView will have a new, appropriate size.
Here is my code:
In my view controller's .h file:
#interface MyViewController ...
{
CGRect titleSuperviewBounds;
UIEdgeInsets titleViewMargins;
}
In my view controller's .m file:
/**
* Helper function: Given a parent view's bounds and a child view's frame,
* calculate the margins of the child view.
*/
- (UIEdgeInsets) calcMarginsFromParentBounds:(CGRect)parentBounds
childFrame:(CGRect)childFrame {
UIEdgeInsets margins;
margins.left = childFrame.origin.x;
margins.top = childFrame.origin.y;
margins.right = parentBounds.size.width -
(childFrame.origin.x + childFrame.size.width);
margins.bottom = parentBounds.size.height -
(childFrame.origin.y + childFrame.size.height);
return margins;
}
- (void)viewDidUnload {
[super viewDidUnload];
titleSuperviewBounds = CGRectZero;
titleViewMargins = UIEdgeInsetsZero;
}
- (void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// Keep track of bounds information, so that if the user changes the
// phone's orientation while we are in a different view, then when we
// return to this view, we can fix the titleView's size.
titleSuperviewBounds = self.navigationItem.titleView.superview.bounds;
CGRect titleViewFrame = self.navigationItem.titleView.frame;
titleViewMargins = [self calcMarginsFromParentBounds:titleSuperviewBounds
childFrame:titleViewFrame];
}
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Check for the case where the user went into a different view, then
// changed the phone's orientation, then returned to this view. In that
// case, our titleView probably has the wrong size, and we need to fix it.
if (titleSuperviewBounds.size.width > 0) {
CGRect newSuperviewBounds =
self.navigationItem.titleView.superview.bounds;
if (newSuperviewBounds.size.width > 0 &&
!CGRectEqualToRect(titleSuperviewBounds, newSuperviewBounds))
{
CGRect newFrame = UIEdgeInsetsInsetRect(newSuperviewBounds,
titleViewMargins);
newFrame.size.height =
self.navigationItem.titleView.frame.size.height;
newFrame.origin.y = floor((newSuperviewBounds.size.height -
self.navigationItem.titleView.frame.size.height) / 2);
self.navigationItem.titleView.frame = newFrame;
}
}
}
For IOS5 onwards, as this is an old question...This is how I accomplished the same issue with the title text not aligning properly.
[[UINavigationBar appearance] setTitleVerticalPositionAdjustment:2 forBarMetrics:UIBarMetricsLandscapePhone];
Tested on ios5/6 sims works fine.
This is what I did:
self.viewTitle.frame = self.navigationController.navigationBar.frame;
self.navigationItem.titleView = self.viewTitle;
The viewTitle is a view created in the xib, it takes the size of the navigationBar and after it has been added the titleView adjust the size to leave room to the back button. Rotations seem to work fine.
I had had same problem, but I seem to get workaround with following code.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
UIView *urlField = self.navigationItem.leftBarButtonItem.customView;
CGRect frame = urlField.frame;
frame.size.width = 1000;
urlField.frame = frame;
}
In my case, the custom view is a UITextField, but I hope this will help you.
Heey,
In my iPad application I have a UIPopoverController with a UIViewController containing some textfields.
When the keyboard comes up, the Popover gets animated to fit. Does anybody know how to disable this?
Thanks
I dont think you can except make the popovers smaller in height...Its done like that so when your keyboard pops up none of the popover gets covered by it (thus it shrinks back), however i have found it to be annoying at times since it messed with table views (not making them able to scroll all the way and having to resize them)
It would be great if it did actually animate to better part of the screen, I think you mean it actually shrinks the popUp which is mostly not good.(which i see during rotation in my case). You can not keep the popUp from squishing, UNLESS you move a view. The best way to handle this is to temporarily move your entire main UIView of the whole screen up with the keyBoard by the difference between the size of your pop up, and how much that pop up would shrink if you did not move it up... you can't just move it up by the size of your keyboard, because popUps up high would then also be effected. This code below is for when the keyboard rotated, similar code for when it is first introduced. easy to do, EXCept for when you rotate the screen, then things get tricky...
in otherwords, sometimes your UIView will not move at all, sometimes it will move up by a good 170 points.
//-----------------finding the keyboard top (also used on "didRotate")----very handy,
// took me hours to figure out an absolute always work topOfKeyboard, so here you go.-------
//--------------------------------------------------------------------------------
- (void)keyboardWillShow:(NSNotification*)notification
{
NSLog(#" keyboardWillShow");
UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
NSDictionary* info = [notification userInfo];
keyboardRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
keyboardRect = [(UIView*)keyWindow convertRect:keyboardRect toView:mainViewController.view];
keyboardIsVisible = TRUE;
topOfKeyboard = keyboardRect.origin.y;
the tricky part is finding the PopUp bottom, because there appears to be code in the popup itself or the convertRect:toView: code that makes the origin flaky , (if you try to "view" origin after a "convertRect:toView:" code), it wants to move and be in different spots during rotation,(or one of it's super views) so bottom calc comes out different some times,(not predicable) because of async process of the rotation of different elements possibly because the popUp itself has many superviews down in the pop up. (to see problem in action with pop up and, must have keyboard effecting popup, move the whole view up then log the "origin" and "size" of popUp after the "convertRect:toView:" code)... the "origin" i'm talking about is the origin of the frame after it is translated to the main view (or atleast a couple views up) with the "convertRect:toView:" code....
update:(it appears if you move down about 3 superviews from the popUp, the flakiness goes away... (this code below was in a "didRotate" ipad type of code. That is that the popUp has several superviews before you can get to the one that is projected ontop of the proper frame
UIPopoverController probably should have a property that has the origin that is predicted after a rotation in it, in a type of "will rotate" kind of code, (because of the keyboard problem), instead of just the "popoverContentSize", (also should include the popoverContentSize that would have been without the keyboard as another variable, ".. anyway This is how I had to do it, see code below.
//--------------------------------------------------------------------------------
- (void) checkRotation
{
NSLog(#" ");
NSLog(#"checkRotation");
if (wasOffset)
{
wasOffset = false;
[UIImageView beginAnimations:nil context:NULL];
[UIImageView setAnimationDuration:0.2f];
CGRect frame = carousel.frame;
frame.origin.y += offset;
carousel.frame = frame;
[UIImageView commitAnimations];
[popPickerController presentPopoverFromRect:zoneButton.frame inView:[zoneButton superview] permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
if (popPickerController.popoverVisible)
{
if (keyboardIsVisible)
{
wasOffset = false;
[popPickerController presentPopoverFromRect:zoneButton.frame inView:[zoneButton superview]
permittedArrowDirections:UIPopoverArrowDirectionAny animated:NO];
upView3 = [[[popPickerController.contentViewController.view superview] superview] superview]; //hey it works... :o)
//NSLog(#" ");
//NSLog(#"upView3.frame.origin.x = %f",upView3.frame.origin.x);
//NSLog(#"upView3.frame.origin.y = %f",upView3.frame.origin.y);
//NSLog(#"upView3.frame.size.height = %f",upView3.frame.size.height);
//NSLog(#"upView3.frame.size.width = %f",upView3.frame.size.width);
//NSLog(#" ");
popUpRect.origin.x = upView3.frame.origin.x;
popUpRect.origin.y = upView3.frame.origin.y;
popUpRect.size.height = popUpSize.height;
popUpRect.size.width = popUpSize.width; //you must save the size because the keyboard destroys it before you can use it. very tricky....
//NSLog(#" ");
//NSLog(#"popUpRect.origin.x = %f",popUpRect.origin.x);
//NSLog(#"popUpRect.origin.y = %f",popUpRect.origin.y);
//NSLog(#"popUpRect.size.height = %f",popUpRect.size.height);
//NSLog(#"popUpRect.size.width = %f",popUpRect.size.width);
//NSLog(#" ");
//NSLog(#" ");
//NSLog(#"keyboardIsVisible = %d", keyboardIsVisible);
//NSLog(#" ");
//NSLog(#"keyboardRect.origin.x = %f",keyboardRect.origin.x);
//NSLog(#"keyboardRect.origin.y = %f",keyboardRect.origin.y);
//NSLog(#"keyboardRect.size.height = %f",keyboardRect.size.height);
//NSLog(#"keyboardRect.size.width = %f",keyboardRect.size.width);
//NSLog(#"topOfKeyboard = %f",topOfKeyboard);
CGFloat bottomOfPicker = popUpRect.origin.y + popUpRect.size.height - amountShadowCanEncroach;
//NSLog(#" ");
//NSLog(#"bottomOfPicker = %f",bottomOfPicker);
//NSLog(#"topOfKeyboard = %f",topOfKeyboard);
//NSLog(#" ");
if (bottomOfPicker > topOfKeyboard)
{
wasOffset = true;
offset = bottomOfPicker - topOfKeyboard;
NSLog(#"offset = %f",offset);
[UIImageView beginAnimations:nil context:NULL];
[UIImageView setAnimationDuration:0.2f];
CGRect frame = carousel.frame;
frame.origin.y -= offset;
carousel.frame = frame;
[UIImageView commitAnimations];
}
}
[popPickerController presentPopoverFromRect:zoneButton.frame inView:[zoneButton superview] permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
}
and moving the main UIView back
//-----------------------------------------------------------------------------
- (void) searchDidEndEditing
{
NSLog(#"searchDidEndEditing");
keyboardIsVisible = false;
if (wasOffset)
{
wasOffset = false;
[UIImageView beginAnimations:nil context:NULL];
[UIImageView setAnimationDuration:0.2f];
CGRect frame = mainView.frame;
frame.origin.y += offset;
mainView.frame = frame;
[UIImageView commitAnimations];
if (zoneButton.selected)
[popPickerController presentPopoverFromRect:zoneButton.frame inView:[zoneButton superview] permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
}
I'm using a UITextView to roughly replicate the SMS text box above the keyboard. I'm using a UITextView instead of a field so that it can expand with multiple lines.
The problem is that, in my UITextView, the correction suggestions pop up below the text, causing them to be partially obscured by the keyboard.
In the SMS app, the suggestions pop up above the text. The placement does not appear to be a property of UITextView, or UITextInputTraits.
Any idea how to replicate this behavior? Thanks!
The problem is that the Keyboard is implemented as a separate UIWindow, rather than as a view within the main UIWindow, so layout with it is tricky. Here are some pointers in the right direction:
Hunt through the application's -windows property to find the private UITextEffectsWindow window and figure out its frame. This is the keyboard
Hunt through the TextView's subviews to find the private UIAutocorrectInlinePrompt view. This is the autocorrect bubble.
Move that subview into a separate wrapper view (added to the TextView) and then move that wrapper view so it's above the above-mentioned keyboard window.
You'll notice two mentions of "private" above. That carries all the relevant caveats. I have no idea why Apple has allowed the problem to persist when even their apps have had to work around it.
By doing the search for the UIAutocorrectInlinePrompt in an overridden or swizzled layoutSubViews it is possible to alter the layout of the correction so that it appears above. You can do this without calling any private APIs by looking for the subs views of particular classes positioned in a way you'd expect them. This example works out which view is which, checks to see that the correction is not already above the text and moves the correction above, and draws it on the window so that it is not bounded by the UITextView itself. Obviously if apple change the underlying implementation then this will fail to move correction. Add this to your overriden or swizzled layoutSubViews implementation.
- (void) moveSpellingCorrection {
for (UIView *view in self.subviews)
{
if ([[[view class] description] isEqualToString:#"UIAutocorrectInlinePrompt"])
{
UIView *correctionShadowView = nil; // [view correctionShadowView];
for (UIView *subview in view.subviews)
{
if ([[[subview class] description] isEqualToString:#"UIAutocorrectShadowView"])
{
correctionShadowView = subview;
break;
}
}
if (correctionShadowView)
{
UIView *typedTextView = nil; //[view typedTextView];
UIView *correctionView = nil; //[view correctionView];
for (UIView *subview in view.subviews)
{
if ([[[subview class] description] isEqualToString:#"UIAutocorrectTextView"])
{
if (CGRectContainsRect(correctionShadowView.frame,subview.frame))
{
correctionView = subview;
}
else
{
typedTextView = subview;
}
}
}
if (correctionView && typedTextView)
{
CGRect textRect = [typedTextView frame];
CGRect correctionRect = [correctionView frame];
if (textRect.origin.y < correctionRect.origin.y)
{
CGAffineTransform moveUp = CGAffineTransformMakeTranslation(0,-50.0);
[correctionView setTransform: moveUp];
[correctionShadowView setTransform: moveUp];
CGRect windowPos = [self convertRect: view.frame toView: nil ];
[view removeFromSuperview];
[self.window addSubview: view];
view.frame = windowPos;
}
}
}
}
}
}
Actually doing
textview.scrollEnabled = NO;
will set the bubble on top of the text... the caveat is that you lose scrolling, in my case it wasn't a problem due to havinng a textfield only for input purposes with character limit
Actually, the keyboard simply uses the result of -[UITextInput textInputView] to determine where to put the correction view (and to ask if your view supports correction). So all you need to do is this:
- (UIView *)textInputView {
for (UIWindow *window in [UIApplication sharedApplication].windows) {
if ([window isKindOfClass:NSClassFromString(#"UITextEffectsWindow")] &&
window != self.window) {
return window;
}
}
// Fallback just in case the UITextEffectsWindow has not yet been created.
return self;
}
Note that you'll likely also need to update -[UITextInput firstRectForRange:] to use the coordinate system of the window / device, so you can do this:
- (CGRect)firstRectForRange:(CoreTextTokenTextRange *)range {
CGRect firstRect = [self firstRectForRangeInternal:range];
return [self convertRect:firstRect toView:[self textInputView]];
}
(In the above context, self is a class that implements UITextInput).
If the bottom of your UITextView clears the keyboard, you should be able to just resize your UITextView to be tall enough to see the corrections. The corrections themselves don't display outside of the UITextView's frame.
If you want to mimic what you are getting in the SMS app (corrections above), you'll probably have to roll your own.
Putting the below method, adjustAutocorrectPromptView in layoutSubviews worked for me in portrait and landscape. I have a category that provides the bottom and top methods on view but you get the idea.
NSArray * subviewsWithDescription(UIView *view, NSString *description)
{
return [view.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:#"class.description == '%#'", description]]];
}
- (void) adjustAutocorrectPromptView;
{
UIView *autocorrectPromptView = [subviewsWithDescription(self, #"UIAutocorrectInlinePrompt") lastObject];
if (! autocorrectPromptView)
{
return;
}
UIView *correctionShadowView = [subviewsWithDescription(autocorrectPromptView, #"UIAutocorrectShadowView") lastObject];
if (! correctionShadowView)
{
return;
}
UIView *typedTextView = nil; //[view typedTextView];
UIView *correctionView = nil; //[view correctionView];
for (UIView *subview in subviewsWithDescription(autocorrectPromptView, #"UIAutocorrectTextView"))
{
if (CGRectContainsRect(correctionShadowView.frame,subview.frame))
{
correctionView = subview;
}
else
{
typedTextView = subview;
}
}
if (correctionView && typedTextView)
{
if (typedTextView.top < correctionView.top)
{
correctionView.bottom = typedTextView.top;
correctionShadowView.center = correctionView.center;
}
}
}
Make sure your view controller delegate is listening to the notification when the keyboard pops up so that you resize your UITextView so that the keyboard doesn't obscure the UITextView. Then your correction won't be obscured by the keyboard. See:
http://www.iphonedevsdk.com/forum/iphone-sdk-development/12641-uitextview-scroll-while-editing.html
Here is a copy of the code from that page in case the original link is broken:
// the amount of vertical shift upwards keep the Notes text view visible as the keyboard appears
#define kOFFSET_FOR_KEYBOARD 140.0
// the duration of the animation for the view shift
#define kVerticalOffsetAnimationDuration 0.50
- (IBAction)textFieldDoneEditing:(id)sender
{
[sender resignFirstResponder];
}
- (IBAction)backgroundClick:(id)sender
{
[latitudeField resignFirstResponder];
[longitudeField resignFirstResponder];
[notesField resignFirstResponder];
if (viewShifted)
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:kVerticalOffsetAnimationDuration];
CGRect rect = self.view.frame;
rect.origin.y += kOFFSET_FOR_KEYBOARD;
rect.size.height -= kOFFSET_FOR_KEYBOARD;
self.view.frame = rect;
[UIView commitAnimations];
viewShifted = FALSE;
}
}
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
if (!viewShifted) { // don't shift if it's already shifted
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:kVerticalOffsetAnimationDuration];
CGRect rect = self.view.frame;
rect.origin.y -= kOFFSET_FOR_KEYBOARD;
rect.size.height += kOFFSET_FOR_KEYBOARD;
self.view.frame = rect;
[UIView commitAnimations];
viewShifted = TRUE;
}
return YES;
}