Layout does not update when playing animation - unity3d-gui

I have a ui panel with an AnimatorController.
The panel contains an image with an AspectRatioFitter component (the one that comes with the unity ui system). The aspect ratio fitter is set to "Fit In Parent" and therefor have one public float variable called "Aspect Ratio" to control the aspect.
The animator controller start an animation that change the value "Aspect Ratio" over time.
The problem is that the size of the rect transform does not update accordingly to the new aspect ratio. Not during the animation, nor when the animation finishes. It never applies the new value.
I can see it work, if I for example change the value manually, or if I disable and then enable the AspectRatioFitter component manually.
For testing purposes I have tried calling the following on the GameObject in an Update() method. But it will not update the aspect:
LayoutRebuilder.MarkLayoutForRebuild(this.transform as RectTransform);
LayoutRebuilder.ForceRebuildLayoutImmediate(this.transform as RectTransform);
Canvas.ForceUpdateCanvases();
this.GetComponent<Image>().SetAllDirty();
this.GetComponent<Image>().SetLayoutDirty();
this.GetComponent<Image>().Rebuild(CanvasUpdate.LatePreRender);
this.GetComponent<Image>().Rebuild(CanvasUpdate.Layout);
this.GetComponent<Image>().Rebuild(CanvasUpdate.MaxUpdateValue);
this.GetComponent<Image>().Rebuild(CanvasUpdate.PostLayout);
this.GetComponent<Image>().Rebuild(CanvasUpdate.Prelayout);
this.GetComponent<Image>().Rebuild(CanvasUpdate.PreRender);
So why is the layout not updated? And how can I solve this?

Related

Codenameone Slider cannot set height

I am finding it difficult to set the right look and feel of a CN1 Slider control. Essentially i want it to look like the one in the CN1 Default Demo of the Theme window, and have it with the Thumb image to allow the user to set the scroll.
But when i code it up as per online examples, it comes out skinny on the simulator (as below)
...but doesn't display the line at all on the actual iphone device.
When i start messing with the methods, such as 'getSliderEmptyUnselectedStyle()' then this makes the background show and can see the progress but its tall and i cannot seem to shrink it.
Container container = new Container(new FlowLayout());
container.getAllStyles().setBgTransparency(255);
container.getAllStyles().setBgColor(0xffffff);
Slider slider = new Slider();
// slider.getAllStyles().setFgColor(0);
// slider.getAllStyles().setMarginLeft(0);
// slider.getSliderEmptyUnselectedStyle().setBgTransparency(255);
// slider.getSliderFullUnselectedStyle().setBgTransparency(255);
// slider.getSliderEmptySelectedStyle().setBgTransparency(255);
// slider.getSliderFullSelectedStyle().setBgTransparency(255);
// slider.getSliderEmptyUnselectedStyle().setBgColor(0xffffff);
// slider.getSliderFullUnselectedStyle().setBgColor(0x42B72A);
// slider.getSliderEmptySelectedStyle().setBgColor(0xffffff);
// slider.getSliderFullSelectedStyle().setBgColor(0x42B72A);
Style sliderStyle = UIManager.getInstance().getComponentStyle("Label");
sliderStyle.setFgColor(0);
slider.setThumbImage(
FontImage.createMaterial(FontImage.MATERIAL_RADIO_BUTTON_CHECKED, sliderStyle, 4).toImage());
slider.setMinValue(0);
slider.setIncrements(25);
slider.setProgress(entity.getCompletion_pct());
slider.setEditable(true);
container.add(slider);
return container;
How can i get it pretty much to the CN1 example?
Thanks
The slider in the example used a 9-piece border or a 3-piece border both of which have a fixed minimum height/width. This minimal height is applied to the way the slider is rendered.
Your code can be made thicker with padding but it will create a problem when you add a thumb. Once you add the thumb it will increase the overall size and make the entire height of the thumb have that background. So you need to style this via the designer and for this specific case you would want to use an image border that is carefully measured to align with the thumb image.
Also notice you used getAllStyles() which is wrong. Slider is a special case and uses the Slider and SliderFull UIID so effectively the component has two UIID's and two separate sets of Style objects.

How do I efficiently manage screen size changes and redrawing with storyboards, initWithCoder, viewDidLoad and viewDidLayoutSubviews?

I am using storyboard layouts to set up view layout.
I am supporting both iPhone and iPad layouts.
When the view is created with initWithCoder:, it is initially created with the frame size of the device I was last looking at in Interface Builder.
If I am designing with the iPhone X layout in interface builder and then build and run on an iPad, the view is initially created with iPhone X screen dimensions. Then viewDidLayoutSubviews: is called and it updates the screen dimensions to the correct iPad size.
The subviews are using drawRect inside UIViews to draw the view graphics. I am doing this so I can change graphic colors via code. I change the color variable and then call setNeedsDispay on the view to redraw the view with new colors using CGGraphicsContext commands.
It also allows me to draw any graphic image at any size. And with lots of graphics that means I don't have to include all the different images at 1x, 2x and 3x sizes in my bundle. It's all drawn dynamically.
Some of these images are laid out when the view loads and not in Interface Builder. So I check the screen size and draw the button size and position accordingly.
What happens is, viewDidLoad is called and it draws the graphics based on the initial screen size.
Then viewDidLayoutSubviews is called and I have to update the drawing of the subviews I placed manually repositioning them based on new screen dimensions and then calling the drawRect on them. I feel like this is just unnecessary extra work for the device.
In addition to that, viewDidLayoutSubviews is called for other reasons then just resizing the view on initial load of the viewController. So then each time it's called it will go redraw the subviews, even if they don't need it.
And, if the device I am running on is the same as the device I was using in Interface Builder, it doesn't call the viewDidLayoutSubviews. I can't just let the view layout the subviews there because there is no guarantee it will be called.
My solution so far is creating a variable to track the screen width. I set the variable in viewDidLoad. If it creates the view at iPhone X size, my screenWidthTracker = 1125. When viewDidLayoutSubviews is called I compare the current screen size to screenWidthTracker.
if (self.view.frame != screenWidthTracker) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateView" object:self];
};
if the view has changed size, it sends a message to redraw views. Any views I have placed manually as subviews are registered to listen for #"updateView".
Is there a better way to manage this? Is there a method that gets called ONLY when screen dimensions change and not when its updating the position of other subviews? Should I be utilizing viewDidAppear? I feel as though that is too late in the chain and I don't want the user to see button size updates.
In general, from the description you've given, I'd say that at the very most, you should be doing as much size work as possibly by using autolayout (which you can configure entirely within the storyboard) and then implementing only drawRect and viewDidLayoutSubviews — and the latter only if you need to.
A very common strategy is to use a boolean flag to set up initial conditions in the first call to viewDidLayoutSubviews, and not use it thereafter. Screen size changes after launch (not at launch), such as rotation, are detected by implementing willTransition(to:with:) — and even then you should check to see that the old size and new size are not the same (180 degree rotation) and do nothing if they are.

View not resizing if done in the "viewWillAppear" or "viewDidLoad" method

I am developing a game: I need to load the user's score (locally and if internet is available, download it from the server) and resize an UIView based on the value.
The problem is that when the score is loaded locally (calling the "resizeUserBar" from viewWillAppear), no view gets resized. But if the same method ("resizeUserBar") is called after the score gets downloaded from the server, it gets resized properly. I have debugged the frame's values and they are correct in both cases.
Here's the code I am using to resize:
CGRect frame = firstBar.frame;
frame = CGRectMake(frame.origin.x, frame.origin.y, firstBarSpace, frame.size.height);
firstBar.frame = frame;
frame = secondBar.frame;
frame = CGRectMake(firstBar.frame.origin.x + firstBarSpace, frame.origin.y, secondBarSpace, frame.size.height);
secondBar.frame = frame;
Autolayout is active on my Storyboard but I'm not using it yet. There are no constraints on the views used in the method above.
Thank you
Thank you
You say:
Autolayout is active on my Storyboard but I'm not using it yet. There
are no constraints on the views used in the method above.
In Xcode 5, if you have Auto Layout turned on but don't set any constraints, the compiler will automatically generate fixed size and position constraints for you at build time.
Turn off Auto Layout and you'll see that your resizing will start to work. Or, setup some actual constraints.
Update: More information…
Auto Layout Guide: Working with Constraints in Interface Builder on Apple.com
When you drag out an element from the Object Library and drop it on
the Interface Builder canvas, it starts out unconstrained to make it
easy to prototype your interface by dragging elements around. If you
build and run without adding any constraints to an element, you’ll
find that Interface Builder fixes the element’s width and height, and
pins its position relative to the top left corner of the superview;
this means that resizing the window doesn’t move or resize the
elements. To make your interface react correctly to changes in size or
orientation, you need to start adding constraints.

How can I set the end dragging velocity of a UICollectionView with custom flow layout?

I'm working on a UICollectionView with a custom flow layout subclass which, among other things, does some custom "paging". Everything's fine but for the fact that depending on how I drag, when I release and after - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity gets called, the collection view (or some part of the UICollectionViewFlowLayout which I did not yet know I need to override) is controlling the velocity with which the animation of an item snapping happens.
That is, if I slightly offset an item from the center of the
collection view qnd release, it snaps back to its position pretty
quickly (desired).
But If I drag the item, say, half way past the collection view's
frame and/or change swiping directions while still dragging and then
release, the "snap" animation takes too long (not desired: I'd like the velocity to adjust so that the end drag animation takes the same amount of time always, regardless of distance).
I tried modifying the decelerationRate of the collection view but it doesn't seem to do anything. And I'm thinking of writing my own animation block in one of the collection view delegate methods, but I'm wondering if there is a different way (perhaps from within the flow layout subclass?).
Well, actually setting self.collectionView.decelerationRate = 0.; seems to work for now. It at least does not decelerate the scrolling and so it looks like constant velocity which is not exactly what I wanted but feels almost right.

IKImageView zooming controlled by an NSSlider

What's the best practice for setting zoom factor of an image within IKImageView via NSSlider?
I was able to bind a slider either to zoom in OR zoom out action of an IKImageView.
Obviously, what I'd rather see is a single slider controlling both those actions.
Best, if image is refreshed after each change of the slider (continuously, even if a mouse button is not released yet).
This demo explains a lot: ImageKitDemo
In particular, this snippet is what I was looking for:
- (IBAction) zoomSliderDidChange:(id)sender
{
[addProductPhotoImageView setZoomFactor:[sender floatValue]];
}
The Bindings way would be to bind both the Zoom Factor of the IK image view and the Value of the slider to the same property of your controller. When the slider changes the value of the property, the image view will be notified, and will go get the new value from your controller.
One advantage of this way is that you can add more ways of zooming in and out and the value in the slider won't go stale. For one example, if IKImageView adds pinch-zooming (or if it has it already—I don't have multi-touch on my Mac), the user can zoom that way and the slider will update automatically. That won't happen with the IBAction solution.
Another example would be Zoom In and Zoom Out menu commands (perhaps with ⌘+ and ⌘- keyboard shortcuts) that send action messages to your controller. Your controller would respond by increasing or decreasing the value of the property (using a setter method it implements). With Bindings, both the image view and the slider will update for free. Without Bindings, you would have to explicitly talk to both the image view and the slider, telling one to update its zoom factor and the other to update its slider.
A third example would be a “Zoom factor: X%” display in a corner of your window. With Bindings, this can update for free no matter how the user zooms the image: moving the slider thumb, pinching/unpinching the image, or pressing a menu item. Without Bindings, this would be yet another thing you have to talk to in your (at least three) change-the-zoom-value action methods.