Synchronise animation with top of a keyboard with accessory view - ios

I have a UITextField which has an accessory view. I also have another view which resides at the bottom of the screen. I want this view to animate up as the keyboard shows, which I have done plenty of times by utilizing the UIKeyboardWillShowNotification. However, this time it is a little special since I want the bottom view to follow the top of the keyboard and not the top of the accessory view.
Obviously I have tried this solution:
[UIView animateWithDuration:keyboardAnimationDuration delay:0 options:keyboardAnimationCurve animations:^{
viewBottomConstraint.constant = keyboardHeight - accessoryViewHeight;
[self.overviewViewController.view layoutIfNeeded];
} completion:nil];
(keyboardAnimationDuration, keyboardAnimationCurve and keyboardHeight all come from the userInfo dictionary of the keyboard notification)
This solution gives me the correct end position of the bottom view, but the animation is slightly off since the keyboard (including the accessory view) has to traverse a longer distance in the same duration (to the top of the accessory view instead of to the top of the keyboard).
I have considered calculating a relative animation duration for my bottom view like this:
keyboardToAccessoryRatio = (keyboardHeight - accessoryViewHeight) / keyboardHeight
relativeDuration = keyboardAnimationDuration * keyboardToAccessoryRatio
And then have the animation start with a delay of keyboardAnimationDuration - relativeDuration. However, this would only work nicely if the keyboard was using the UIViewAnimationOptionCurveLinear curve, but it is, in fact, using UIViewAnimationCurveEaseInOut.
So any other suggestions?
PS. I cannot simply add my button view to the accessory view as they are actually in separate view controllers. The animation is done as a part of a custom transition within a UIViewControllerAnimatedTransitioning class.

In the end I gave up and added the accessory view as a regular view which I made follow the keyboard.

Related

iMessage Extension: UITextView in Child View Controller, scrolling behaviour has multiple issues

I have a very basic view controller in an iMessage Extension. (Expanded view of course). I am aware that you can not have keyboard input int the Compact view...
The UITextView is constrained to the Element above it, and below it. It has a set height and width. The constraints are all in order, and are not broken.
All the options on the textview in interface builder have been left as DEFAULTS.
The following problems occur with the UITextView:
When I begin editing, the cursor is barely visible. (on the bottom of the field, half way cut off vertically).
Typing the first letter brings the cursor to the vertical centre of the Text View.
Putting a new line and then a character, puts the cursor back down again.
Then typing subsequent characters after that will bring the cursor to the Vertical Centre again. And every other character scrolls the whole text view to the bottom again. So you get this weird bounce up and down behaviour with every key stroke.
The Scroll Bar visual hint on the right side does not reflect the proper height of the text view, when I scroll. (The scroll bar only goes down half way - of the text view's height.)
I expect the UITextView to work like this by default:
Cursor should go to the TOP of the text view, when activated and editing.
Text View should not jump when typing keys after a new line.
Vertical Scroll Bar indicator should show the full range of the text field, and not half of it.
So, obviously the internal Scroll View inside the Text View has a problem figuring out the sizes of things.
Does anyone know, if this is an iMessage specific issue, or whether anyone has had this problem outside of iMessage as well, and how to fix?
My IB constraints (The second view down is the text view):
Video Demo of the problem:
https://youtu.be/1bkvHnkXLWM
UPDATE: I am using Child View Controllers to embed my View Controller inside the Root Controller. I have tried a blank Hello World application, and the UITextView works normally by default. So the issue, therefore is related to the way i'm embedding a Child View Controller.
This is the code I'm using to embed my Child View Controller:
- (void)showViewController:(UIViewController*)vcToShow isPop:(BOOL)isPop
{
NSLog(#"Presenting Controller: %#", vcToShow);
[self.activeVC willMoveToParentViewController:nil];
[self addChildViewController:vcToShow];
vcToShow.view.translatesAutoresizingMaskIntoConstraints = NO;
vcToShow.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.rootView addControls:#[vcToShow.view]
align:VerticalAlignStretchToFullHeight];
UIViewController* currentVC = self.activeVC;
self.activeVC = vcToShow;
vcToShow.view.alpha = 0.0f;
[self transitionFromViewController:currentVC
toViewController:vcToShow
duration:0.3
options:UIViewAnimationOptionTransitionNone
animations:^{
vcToShow.view.alpha = 1.0f;
}
completion:^(BOOL finished) {
[currentVC removeFromParentViewController];
[vcToShow didMoveToParentViewController:self];
[self updateScreenState];
if (isPop) {
[self removeReferenceToController:currentVC];
}
}];
}
I have a solution.
Instead of using Interface Builder to place my UITextView, I used my own programmatic constraint generation method to create and place the UITextView, only after ViewDidAppear.
My suspicion is that at the time Interface builder rendered the Child VC's view, it did not have everything populated that the UITextView's internal scroll view needed. So the scroll view got initialized with wrong dimension values, and therefore would glitch out on anything scrolling related. After doing everything in viewDidAppear programmatically (in the Child VC), the text view now scrolls properly, and the cursor is always at the beginning, working as expected.
The update code:
Took UITextView out of interface builder completely.
Added it programmatically (called from viewDidAppear):
self.textView = [[UITextView alloc] init];
self.textView.translatesAutoresizingMaskIntoConstraints = NO;
CGFloat topPadding = self.instructionLabel.frame.origin.y + self.instructionLabel.frame.size.height + TEXT_VIEW_TOP_PADDING;
// This custom method generates Visual Constraint format for me
// so i don't have to write manual individual constraint syntax.
[self.rootView addControls:#[self.textView]
align:VerticalAlignTop
withHeight:TEXT_VIEW_HEIGHT
verticalPadding: 0
horizontalPadding: 20
topPadding: topPadding];

Animate a view on key window while dragging another view with UILongPressGestureRecognizer

I have added UILongPressGestureRecognizer to a view, and after long press event, the user can drag the view.
I have added a view on the key window (need the custom view over the navigation and tab bar controllers, that is why need to add it on key window) using below code:
// add the custom view on the key window.
UIWindow *currentWindow = [UIApplication sharedApplication].keyWindow;
[currentWindow addSubview:customView];
Now, when I am dragging the first view beyond the (screenSize/2) point, I want the customView to animate from the right side of the view. I tried the following code to present the view:
// animate the customView view.
[UIView animateWithDuration:0.50f animations:^{
customView.frame = CGRectMake(SCREEN_WIDTH - customViewWidth, 0, customViewWidth, SCREEN_HEIGHT);
} completion:nil];
But the view does not appear neither it animates. I even tried to use the dispatch_async(dispatch_get_main_queue(),{}); but still no success.
Any help will be appreciated.
You should use UIAttachmentBehavior.
It specifies a dynamic connection between two dynamic items, or between a dynamic item and an anchor point. When a dynamic item moves, either by tracking a gesture or via other input, any attached dynamic item also moves—if possible given its other dynamic parameters and boundaries. You can configure an attachment behavior using its length, damping, and frequency properties.

UIButton created outside view not working

I have an application I'm making in storyboards. I want to have a tutorial-type of view. I decided to go with a freeform view controller and am filling it with 600x600 views that count as the pages. The problem I'm having is that when I have a UI button animate to the next page, the buttons that were created outside of the visible view don't seem to work. I even moved a view over so the button was half-visible and only half the button works when I move the view over.
Here's my next page code:
- (void)nextPage {
if (scrolling) return;
scrolling = YES;
[UIView animateWithDuration:0.3 animations:^{
CGRect frame = self.tutorialView.frame;
frame.origin.x -= 50; //frame.size.width;
[self.tutorialView setFrame:frame];
}];
scrolling = NO;
}
I currently have it moving only 50px instead of the whole page for testing purposes.
For testing purposes, I've started it half-way through and only half the button works. I've started it with the second view on top of the other one halfway visible and the same thing happens (only half the button works). Otherwise, when I tap the next button on the first view the buttons on the second view don't work (buttons created outside the initial view).
Views don't receive touch events where they're outside the bounds of their superview. You'll need to increase the button's superview's size.
You can visualize this behavior by setting clipsToBounds = YES - then you'll only see the touchable area of your button.
(You can override this behavior, but you probably shouldn't.)

Xcode 5 - Swipe switch view with constant background

I understand how to implement UISwipeGestureRecognizer, but I do not understand how to use multiple views properly in collaboration with it.
My ultimate goal is to provide a sleek interface where buttons or swiping may be used to change views. I am hoping for a constant background rather than a background image show it is transferring to the exact same image again. I am using storyboarding currently where each view has its own controller, and I would like (if possible) to keep it as close to this format for visual clarity of what is going on.
I have read that subviews may be my best hope for this sort of scenario (keeping them to the right until a swipe is called), but that would break up the current storyboarding structure I am using, and most certainly ruin any visual clarity.
Does anyone have any suggestions?
I did same programmatically, but you can achieve same using storyboard
1. You need base view which will keep same background
2. Add 3 Views (or 2 depending on ur requirement) Left, Middle, Right with transparent backgrounds as follows in viewDidLoad of base view controller
[self addChildViewController:myChildViewController];
[myChildViewController view].frame = CGMakeRect(x,y,width,height);//Left one will have x = -320, y=0 ,Middle x= 0, y= 0, Right x = 32O etc.
/* Add new view to source view */
[self.view addSubview:[myChildViewController view]];
/* Inform 'didMoveToParentViewController' to newly added view controller */
[myChildViewController didMoveToParentViewController:self];
3. In handle Swipe method change frames of view with animation which will make it look like swiping page
e.g
//swipe left, get right view in middle
[UIView animateWithDuration:0.2 animations:^{
CGRect frame = rightViewController.view.frame;
frame.origin.x = 0;
frame.origin.y = 0;
rightViewController.view.frame = frame;
CGRect middleViewFrame = middleViewController.view.frame;
middleViewFrame.origin.x = -320;
middleViewController.view.frame = middleViewFrame;
}];
You could use a navigation to do this. If you don't want the navigation bar, you can hide it. You can connect the left swipe gesture recognizer to a segue to the next controller, and connect the right swipe gesture recognizer to an unwind segue (or call popViewControllerAnimated: from its action method). If you want a constant background, then you need to add that to the navigation controller's view (as a background color), and have the child view controller's views be transparent (or partially so). This code in the navigation controller's root view controller will add the background:
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationController.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"img.tiff"]];
}
I know exactly what you're talking about and I'm actually doing the same with my current project.
I accomplished this by using Interface Builder and a single view controller. Basically I placed a UIImageView in the view and set it to the background you wish, then I put a transparent UIScrollView on top of it. Inside that scroll view I put the two views I want to scroll horizontally through.
Don't forget to enable UIScrollView paging. In my case, I had two table views. Starting from the first one, if you swiped from right to left it would scroll to the next table view, and if you swiped left to right it would scroll back to the first.

Animate UITextview change out, Keep keyboard in place

I have a UITextview, becomes first responder and keyboard is presented. Presently, I have buttons in inputAccessoryView toolbar that exchange the text forward and reverse through an array of strings.
I have the process working with no issues, so of course I am inclined to break it. My wish is to slide the textview left and right like a carousel to make it more clear to the user that next or previous string is coming and going. The current system simply replaces the text with no animation.
My first thought was to create a UINavigationController, give it an array of UIViewControllers that present the UITextviews. The navigation controllers view is only as big as the textview and I add it as a subview to my full view (which is itself in a navigation controller). I got this working fairly completely, the navigation bar is hidden and it looks no different than the original textview except that the textview now slides off to the right or left, depending if I am pushing or popping.
The problem with that is that the keyboard slides off along with the dismissed view controller, then the new textview in the new controller becomes first responder and the keyboard returns. Close, but no cigar.
I considered using page view controller but it seems it will have the same issue. I think I may have to go back to the single textview and animate the whole process directly with static screen grabs. That is completely beyond my experience level and I am think there must be a simpler way.
Can anyone suggest a simple way to keep that keyboard present while the views are swapped as described? Suggestions on other angles of attacking this?
Seems like an awful lot of overhead for a simple animation.
Try something like this (assuming ARC):
typedef enum _eDirection
{
rightToLeft = -1,
leftToRight = 1
} eDirection;
- (void) animateTextFields:(eDirection)direction
{
CGFloat distance = self.window.bounds.size.width;
UITextField *oldTextField = thisView.textField;
CGRect oldTFRect = oldTextField.frame;
CGRect newTFRect = CGRectOffset(oldTFRect, -direction * distance, 0);
UITextField *newTextField = [[UITextField alloc] initWithFrame:newTFRect];
[newTextField setText:#"whatever"];
[self addSubview:newTextField];
[UIView animateWithDuration:0.3f
animations:^{
[oldTextField setFrame:CGRectOffset(oldTextField.frame, direction * distance, 0)];
[newTextField setFrame:CGRectOffset(newTextField.frame, direction * distance, 0)];
} completion:^(BOOL finished) {
[self setTextField:newTextField];
[self removeSubview:oldTextField];
[newTextField becomeFirstResponder];
}];
}
(Disclaimer: as with all code typed off the top of one's head, etc., etc.)
This method would create a new textField, animate it horizontally onto the screen while moving the existing one off the screen in the direction you give, and then assigns the new textField as the current one and makes it the firstResponder.

Resources