I try to make a tiny animation under IOS7. My view controller has 2 views: a tableview and a mapview. I have a button, that switches between 'show map full screen' and 'show map and tableview' state.
Here is my experimental code:
float mapNewX = self.mapView.frame.origin.x == 0 ? 375 : 0;
float mapNewWidth = mapNewX == 0 ? self.view.frame.size.width : self.view.frame.size.width - mapNewX;
[UIView animateKeyframesWithDuration:0.1 delay:0.0 options:UIViewAnimationCurveEaseOut animations:^
{
CGRect mapViewFrame = self.mapView.frame;
mapViewFrame.origin.x = (mapNewX);
mapViewFrame.size.width = mapNewWidth;
self.mapView.frame = mapViewFrame;
}
completion:^(BOOL finished)
{
NSLog(#"OK");
}];
It works, but when I rotate my device, it switches back to the original state. So, when I have full map display, when rotating device, it switches back to tableview + mapview on the screen.
The same happens, if I come back from another view controller by navigation controller. If I tap detail disclosure of a map pin, it switches back to the original layout.
What should I do? Thanks very much!
Update: here are the screenshots before/after rotation (some sensitive data is masked out)
If you loose frame changes after rotation or screen navigation, it probably relates to view controller's layout methods viewWillLayoutSubviews, viewDidLayoutSubviews or life cycle methods viewWillAppear:, viewDidAppear:. Check implementations of these methods if you override them.
Because you use auto layout, it's better to animate its constraints instead of frames.
As far as I understood, your task is to move left side of a map to reveal a table view underneath (opened/closed states). With autolayout your screen might look as simple as this:
(UIImageView view plays a role of a map)
UITableView constraints
constant width constraint
top, leading and bottom space to superview constraints set to zero
UIImageView constraints
top, bottom and trailing space to superview constraints set to zero
leading space to superview set to table width (opened state, animate this constraint)
To implement desired effect we need to animate leading space to superview constraint (selected on a the screenshot). We create an outlet to this constraint and change its constant property to opened/closed state.
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *mapLeadingSpace;
Set initial state of the menu before it will appear on screen. We set a default value, but you can restore an appropriate boolean setting from NSUserDefaults.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.mapLeadingSpace.constant = 0; // default is closed
}
Next, toggle animation on bar button press.
- (IBAction)toggle:(id)sender
{
self.mapLeadingSpace.constant = self.mapLeadingSpace.constant == 0
? self.tableView.frame.size.width // opened
: 0; // closed
[UIView animateWithDuration:.2 animations:^{
[self.imageView layoutIfNeeded];
}];
}
Using
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
Create a variable to indicate whether it is full screen map or split view, then run the routine once rotation is completed? Not sure how hi-tech of a solution that is, but it's what i'd do! :)
I had a similar split views (2 UItableViews), here is how i managed to fix it.
First, have a global static variable lets call it static BOOL _isViewSplitted
its static so when you push or pop viewContorller is stays in memory.
Override didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
check your variable here and if YES, split them if NO, just return.
Also, you have to check (do the same as above) in viewWillAppear when the viewController is popped.
You could probably fix this by coding your constraints and then animating them.
Something like this (the values in this code doesn't match the ones in your example):
[subview setTranslatesAutoresizingMaskIntoConstraints:NO];
[parentview setTranslatesAutoresizingMaskIntoConstraints:NO];
[parentview addSubview:subview];
NSDictionary *views = NSDictionaryOfVariableBindings(subview);
[parentview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[subview]"
options:0
metrics:nil
views:views]];
NSArray *firstConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[subview]"
options:0
metrics:nil
views:views];
[parentview addConstraints:firstConstraints];
[parentview layoutSubtreeIfNeeded];
[parentview removeConstraints:firstConstraints];
NSArray *secondConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-100-[subview]-100-|"
options:0
metrics:nil
views:views];
[parentview addConstraints:secondConstraints];
subview.superview.wantsLayer = YES;
[NSAnimationContext runAnimationGroup:^(NSAnimationContext* context) {
[context setDuration:.3];
[context setAllowsImplicitAnimation:YES];
[context setTimingFunction: [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
[subview layoutSubtreeIfNeeded];
} completionHandler:^{
[[self delegate] createOptionMenuForPDFViewWithInvoice:invoice andSuperView:parentview];
}];
Tom, your big problem (IMHO) is that you are calling this code that you've provided in your question too often. Every time its going to toggle the map from full screen to split because it makes this decision from where it is now. Why don't you just have a boolean ivar you check? you can flip this in your button action to toggle the maps full screen state, then it won't matter if you repeatedly call this code on reorient etc, just alter the condition in your turnery operators at the start there to this new boolean instead of querying whether the map is full screen or not
This question stood a greater chance of being answered relatively quickly without the need for a bounty had the following information been provided in the original post (in general, this might be a good set of guidelines for any post related to layout in iOS):
(1.) is Auto Layout enabled in the storyboard?
(2.) are you using a mixed (hybrid) approach or pure Auto Layout? in other words, which views have their translatesAutoresizingMaskIntoConstraints set to YES?
(3.) if any layout methods or rotation callback methods are overridden, provide that implementation
(4.) show us your constraints
I was able to figure out the answer to #1 from the comments.
My answer given the little I know about the problem:
If you are using Auto Layout on the map view, then setting its frame in the animation block won't permanently re-position the view because the map view's constraints haven't been changed. Instead, update the map view's constraints before the animation block, then send layoutIfNeeded to self.view inside the animation block.
// update constraints…
[UIView animateWithDuration:0.5 animations:^{
[self.view layoutIfNeeded];
}];
Also give a try to this in viewDidload of your Viewcontroller class
if ([self respondsToSelector:#selector(edgesForExtendedLayout)]) {
self.edgesForExtendedLayout = UIRectEdgeNone;
}
Related
I am trying to hide a UIView but nothing work so far. The UIView is at the bottom of a UITableView, not inside it as a cell. Here is what I tried so far:
self.viContainerLocation.hidden = YES;
self.viContainerLocation.alpha=0.0;
CGRect Conframe = self.viContainerLocation.frame;
Conframe.size.height = self.view.bounds.size.height;
self.viContainerLocation.frame = Conframe;
self.tableView.translatesAutoresizingMaskIntoConstraints=NO;
__block CGRect frame=self.tableView.frame;
frame.size.height= self.view.frame.size.height-frame.origin.y;
frame.size.width=self.view.frame.size.width;
self.tableView.frame=frame;
viContainerLocation is the UIView I am trying to hide. The above code hide all elements inside the UIView, but leaves a white space with the dimensions of the UIView.
For a reason I don't understand, viContainerLocation is hidden when I use this code:
[UIView transitionWithView:self.viContainerLocation
duration:0.0
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{ }
completion:^(BOOL finish){
__block CGRect frame=self.tableView.frame;
frame.size.height= self.view.frame.size.height-frame.origin.y;
frame.size.width=self.view.frame.size.width;
self.tableView.frame=frame;
}];
But using this code the white space gets displayed of a fraction of a second and it doesn't look good.
How can I make it go away from the beginning ?
You are misunderstanding something.
When hidding an UIView, the space occupied by this view is still visible.
If you want your tableview filling the missing space, the quickest way is to ember your tableview and your view into a UIStackView.
In a UIStackView when you set one subview to isHidden=true, the view is removed, and other views fill the left space.
More infos here : https://developer.apple.com/documentation/uikit/uistackview
Hi i am very new for Auto-layouts and in my project i have added Two UIViews programmatically using Auto-layouts and i have added two UIButton which are Next and Back button and when i click Next button i push MyFirst UIView to second UIView using UIView animations and when i click Back button i push back from Second UIView to First UIView ok Everything is all right based on my code
But here my main problem is when i change First UIView orientation in simulator at portrait to landscape then Second UIView overlapped on my First UIView, And i know that it is must be Constrains issue i mean i have to remove and adding Constraints each time when we change Orientation for this i have tried below code but that's not working please help me and for this i have tried since long time but no one saying right answers using constrains
when we run program at portrait mode screen is coming like image1 and when i change land scape mode second UIview overlapped on my first UIview like below second image that's my main problem
my code:-
#import "SubViewController.h"
#interface SubViewController (){
UIButton * GoNext;
UIButton * GoBack;
NSArray * FHorizental;
NSArray * FVertical;
NSArray * SHorizental;
NSArray * SVertical;
}
#end
#implementation SubViewController
#synthesize MyFisrtView,MySecondView;
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"How are you");
[self callAutolayouts];
MyFisrtView.hidden = NO;
MySecondView.hidden = YES;
}
-(void)callAutolayouts{
NSLog(#"Hi");
MyFisrtView = [[UIView alloc] init];
MyFisrtView.backgroundColor = [UIColor orangeColor];
MyFisrtView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:MyFisrtView];
MySecondView = [[UIView alloc] init];
MySecondView.backgroundColor = [UIColor lightGrayColor];
MySecondView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:MySecondView];
//Applying autolayouts for MyFirstView and MySecondView
NSDictionary * HeaderDictionary = NSDictionaryOfVariableBindings(MyFisrtView,MySecondView);
//Appliying Autolayouts for FirstView
FHorizental =[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"H:|-0-[MyFisrtView]-0-|"]
options:0
metrics:nil
views:HeaderDictionary];
FVertical = [NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"V:|-0-[MyFisrtView]-0-|"]
options:0
metrics:nil
views:HeaderDictionary];
[self.view addConstraints:FHorizental];
[self.view addConstraints:FVertical];
//Appliying Autolayouts for SEcondView
SHorizental =[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"H:|-0-[MySecondView]-0-|"]
options:0
metrics:nil
views:HeaderDictionary];
SVertical = [NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"V:|-0-[MySecondView]-0-|"]
options:0
metrics:nil
views:HeaderDictionary];
[self.view addConstraints:SHorizental];
[self.view addConstraints:SVertical];
GoNext = [UIButton buttonWithType: UIButtonTypeRoundedRect];
GoNext.frame = CGRectMake(50, 50, 100, 18);
[GoNext setTitle:#"Next" forState:UIControlStateNormal];
[GoNext addTarget:self action:#selector(GoNext:) forControlEvents:UIControlEventTouchUpInside];
GoNext.backgroundColor = [UIColor lightGrayColor];
[MyFisrtView addSubview:GoNext];
GoBack = [UIButton buttonWithType: UIButtonTypeRoundedRect];
GoBack.frame = CGRectMake(50, 50, 100, 18);
[GoBack setTitle:#"Back" forState:UIControlStateNormal];
[GoBack addTarget:self action:#selector(GoBack:) forControlEvents:UIControlEventTouchUpInside];
GoBack.backgroundColor = [UIColor orangeColor];
[MySecondView addSubview:GoBack];
}
-(void)GoNext:(id)sender{
MySecondView.hidden = NO;
MySecondView.frame=CGRectMake(248, 0, self.view.frame.size.width, self.view.frame.size.height); // starting visible position
[UIView animateWithDuration:0.5f
delay:0.0f
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
[self.view removeConstraints:self.view.constraints];
[self callAutolayouts];
[self.view setNeedsLayout];
[MySecondView setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)]; // final visible position
}
completion:nil];
}
-(void)GoBack:(id)sender{
MySecondView.frame=CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height); // starting visible position
[UIView animateWithDuration:0.5f
delay:0.0f
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
[self.view removeConstraints:self.view.constraints];
[self callAutolayouts];
[self.view setNeedsLayout];
[MySecondView setFrame:CGRectMake(MyFisrtView.frame.size.width, 0, self.view.frame.size.width, self.view.frame.size.height)]; // final visible position
}
completion:nil];
}
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
if(toInterfaceOrientation == UIInterfaceOrientationPortrait){
NSLog(#"portrait");
[self.view removeConstraints:self.view.constraints];
[self callAutolayouts];
[self.view setNeedsLayout];
}
else if(toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) {
NSLog(#"LandscapeRight");
[self.view removeConstraints:self.view.constraints];
[self callAutolayouts];
[self.view setNeedsLayout];
}
else if(toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
NSLog(#"LandscapeLeft");
[self.view removeConstraints:self.view.constraints];
[self callAutolayouts];
[self.view setNeedsLayout];
}
}
#end
image1:-
image2:-
First you have to remove the constraints and then add agin as you did in your question.
You could follow a tutorial on auto layout so that you can get a little know how about it. Here's a link.
Another option is the use of springs and struts.
I hope that answers your question.
Based on your comments below I'm updating my answer but leaving the original below as it may help you or others down the road.
Your issue isn't really Auto Layout then, your real issue is an architecture design issue. Every view that comes onto screen should have a different view controller running it. It sounds like you are drawing one view that is double the width of the device, one half (I'm guessing the left half) has the next button on it while the other half includes the back button. As soon as your app runs on a screen with a different size this will break unless you are checking for the screen size. Even then it breaks (as you have seen) when the device is rotated.
You need to make two different view controller subclasses, one to run the 'Next' view and one for the 'Back' view then you will never have the both views onscreen because the device got rotated.
The really hard way to get the effect you want is to do it all in code like you've tried thus far. If there is some strange reason that you just have to code it all instead of using the storyboard that was included when you started the project you will need to look at the animation methods of UIView (specifically look at the documentation for + (void)transitionFromView: toView: duration: options: completion: if you want something like a flip or page curl). If you want to slide between the two views you should really look into UINavigationController as it does all that for you. The other option would be when the Next button gets pressed create the second view controller, who will create the second view, and set it's frame to be off screen. Then animate the frame of the view to the onscreen position. Back would have to animate the frame of the Back view off the screen again and remove the view. It will take a lot of code to get all of this working which will mean reading a lot of documentation, I highly recommend using a storyboard to take care of this issue.
If you want the easy way use the storyboard and drag out a second view controller. Add a button to each scene in whatever location you want them to be in and set the text as desired. Control drag from the Next button to the second scene and select the kind of transition you would like. Add the following code to the view controller running the view with the Next button
#IBAction func exitToViewController (sender: UIStoryboardSegue){ }
Control drag from the Back button to the Exit icon (top of the scene all the way to the right, it's red with a white box and an arrow point to the right) select exitToViewController. Select one of the scenes and select Editor -> Resolve Auto Layout Issues -> Reset To Suggested Constraints (the second one under all views). It takes five minutes to do and works wonderfully.
If you need help with any of the tasks mentioned in the Storyboard route consider finding a beginner level tutorial on Xcode (there is a pretty good overview class on Coursera.org that isn't terribly long but covers many of the basics called,Foundations of Objective-C App Development and it's free if you don't care about a verified certificate.
Original Answer
Is there a reason the UI has to be built programmatically?
Building it in IB will make your life much easier as you can build the base version of the UI and then create overrides for specific size classes (look at the bottom of IB where it says w Any h Any and change the selection to be compact height). Once in the compact height make any modifications you need to the constraints and visibility of UI elements. The system should take care of things from there (you might have to manually hide and show the correct UI elements as need for the orientations).
When building in IB one of the Assistant editors is preview. You can select various devices and change the orientation between landscape and portrait, this will allow you to see in real time what your view is going to do when run for real rather than making a change in code and then running the app again. You can add one of each device if you want and they can all have their own orientation allowing you to see the UI in landscape and portrait at the same time.
If it does has to be done in code and you have to actually remove the unused elements from the UI rather than hiding them you will have to set all the constraints for the landscape orientation every time the device rotates to that orientation, the same is true for the portrait orientation. The best thing to do is to make the constraint changes in an animation block rather than manually updating the frames, this will cause the views to animate into place (you can do any alpha adjustments at the same time to fade elements in and out).
I have a scenario where I needed to add an animation inside the Navigation Bar, but the simulator doesn't animate. I add a UIView, add a label/text field and create outlets for the constraints and update them with "UIView animateWithDuration:" Instead, the simulator just updates the constraints and displays the view accordingly, but without any animation. I have tried it by modifying both constraints as well as frames (separately).
[self.view layoutIfNeeded];
[UIView animateWithDuration: 3
delay: 0.0
options: UIViewAnimationOptionCurveEaseInOut
animations: ^{
self.constraint.constant += 60;
NSLog(#"inside animation");
[self.view layoutIfNeeded];
}
completion:nil];
This is shown when I increment the width constraint by "60" in viewDidLoad: . And it comes out non-animated.
When I configure the constraints (increment width by "60") through a button click, even a static updation doesn't happen.
The same code works perfectly outside the navigation bar inside the UIView, though.
Appreciate any help...
If your navigation bar contains some animation, then you could try out drawing custom navigation bar. Here's what you've to do :
In your view controller in viewDidLoad() method add the following code
self.navigationController.navigationBarHidden = YES;
After that using a UIView create a custom navigation bar with frame adjusted to be visible and top.
After that add components on the view and start working with animations.
You can achieve that by taking custom UIView and create width NSLayoutConstraint outlet to it and in code you can handle the constant property of this variable by calling UpdateConstraintsIfNeeded or UpdateViewConstraints method before calling animation code
Ok, I've solved this at last. Phew...
What I did was...
Added a view in place of barButtonItem (let's call this "superView") and over it another view (and let's call this "subView") so that I get the following configuration.
Constraints were set between subView and objects on top of it. Now, I created outlets (superView and subView) for superView and subView, made them transparent (optional, of course).
Useful note: Set the superView's width to the default bar button item's width and the subView's to the screen width. Disable "Clip Subviews" on superView but enable on the subView.
Added animations as needed where I had wanted, but layoutIfNeeded on the "superView", not the parent view.
[self.superView layoutIfNeeded];
Good luck.
I have a UILabel inside of a UITableViewCell. I am trying to animate the label moving to the right when the user taps the cell. I have this code:
CGRect otherFrame = cellLabel.frame;
otherFrame.origin.x +=50;
[UIView animateWithDuration:1.0f animations:^{
cellLabel.frame = otherFrame;
}];
The odd thing that's happening is that the label is jumping 50 pixels to the left and animating back to its origin (where it was before the action began).
I actually had this working earlier in the week and didn't have any trouble with it, and after scouring through the revision history, I can't figure out where I've gone wrong. I must be missing something stupid.
EDIT:
Based on the answer from Jakub, I found that this works:
[UIView animateWithDuration:0.01f animations:^{}
completion:^(BOOL finished)
{
CGRect otherFrame = cellLabel.frame;
otherFrame.origin.x += 50;
[UIView animateWithDuration:1.0f animations:^{
cellLabel.frame = otherFrame;
}];
}];
Oddly, if I move all of the logic into a completion handler that performs a new animation after the first one completes (without the first actually doing anything), everything animates properly. This is super hacky, and I'm not at all happy with it.
Can anyone think of what would cause the initial animation to move the frame to the negative offset of the intended destination, only to animate it back to its origin, and why triggering a new animation as the completion handler of an empty animation would work?
EDIT 2:
I am going to mark Duncan's answer as the right one because it pointed me in the right direction, but I am still baffled why/how these symptoms can happen:
The animation moves the frame to the negative offset of the
destination, then animates it back to the origin (rather than from
the origin to the destination)
Running an empty animation block but adding another animation to the first block's completion handler animates correctly
Info for the attempted answers:
I have to use auto layout for the project, and as far as I know, I
can't disable it for a single view
I am not putting anything onto a background thread. I didn't try specifically dispatching to the main thread, but I think I was already there. Isn't all visible UI always on the main thread? It was animating, just not as expected.
The reason I didn't go the route of changing constraints to begin with is that I am using prototype cells and IB doesn't let you create IBOutlets from prototype cells. There's a bit of work to walk a constraint list and detect a specific one, so I left the constraints blank and tried to animate the frame (and as I said, it was working earlier in the week -- and still worked when animating from an animation block's completion handler).
So the final solution was to add constraints to the container cell (the important one here being the leading), then to animate, I had to find the constraint:
NSLayoutConstraint *titleLeadingConstraint = nil;
for( NSLayoutConstraint *constraint in cellLabel.superview.constraints )
{
if( constraint.firstItem == cellLabel && constraint.firstAttribute == NSLayoutAttributeLeading )
{
titleLeadingConstraint = constraint;
}
}
Then set the constraint constant:
titleLeadingConstraint.constant = 55.0;
Then set the animation block:
[UIView animateWithDuration:1.0f animations:^{
[self.view layoutIfNeeded];
}];
This solution should be more future proof (and robust, reliable and stable) than moving the frame, but it turned out to be a fair amount of work in discovery.
Do you have auto layout set in your storyboard or XIB by mistake? (It is on by default). If so you either need to turn AutoLayout off or animate a constraint rather than manipulating your view's frame.
Where are you calling this code?
Try calling UIView animateWithDuration:... method on the main thread, in a
dispatch_async(dispatch_get_main_queue(), ^{
// Your animation here
});
If this method is not executed on the main thread, it usually jumps to the final stage of the animation.
You should reset the origin after animation.
I need to change the width of a subview depending on, say, available disk space. So I have a selector that's called upon the view controller's viewWillLayoutSubviews as such:
CGRect rect = self.usageView.bounds;
rect.size.width = self.someCalculatedwidth;
[self.usageView setFrame:rect];
[self.usageView setNeedsDisplay];
Really, I just want to change the width of the subview. It works if the view controller is the initial controller; however, if it is transitioned from, say, a Push Segue it stopped working. What happens is I'd see my desired width rendered for moment but then it gets changed to the original width as per its blueprint on the Storyboard.
The behavior feels like the iOS caches the original (storyboard) view in a block during the push segue animation, and upon completion it executes the block which doesn't have my calculations above; thereby overridden the desired view. Any idea?
There are two approaches.
The first approach is to circumvent the problem, with a side benefit of cool animation. Since I have no clue when or how many times or exactly how iOS enforces the auto-constraints, I simply delay my UIView modifications and smooth it out with animation. So I wrap view animation around the code in my selector:
[UIView animateWithDuration:.25 animations:^{
CGRect rect = self.usageView.bounds;
rect.size.width = self.someCalculatedwidth;
[self.usageView setFrame:rect];
[self.usageView setNeedsDisplay];
}];
Then I'd call my selector in my controller's viewDidAppear:animated:
[self performSelector:#selector(resetUsageView) withObject:self afterDelay:0.0];
Now most would say this is a cop-out or a kludge. And that's fair.
In the second approach I attack the root of the problem - auto layout/constraints. I add a Width constraint to my usageView in the storyboard, and connect its IBOutlet to my code.
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *usageViewWidthConstraint;
Then I modify the constraint as per the calculated width in my controller's viewWillAppear:animated:
[self.usageViewWidthConstraint setConstant:self.someCalculatedWidth];
Voila! A one liner.
Now what if I want animation given I got a taste of it from the first approach? Well, constraint changes are outside the realm of animation so basically I'd need a combination of the first and second approaches. I change the UIView's frame for animation, and modify the constraint as well to "set the record straight":
[UIView animateWithDuration:.25 animations:^{
CGRect rect = self.usageView.bounds;
rect.size.width = self.someCalculatedwidth;
[self.usageView setFrame:rect];
[self.usageViewWidthConstraint setConstant:self.someCalculatedWidth]; // <<<<
[self.usageView setNeedsDisplay];
}];