Yesterday I failed to get an answer, but perhaps I did not understand the problem deep enough to formulate correct question.
The story is about animating ListBox height. Here are subsequent screenshots:
a) "Medium" is a TextBlock
b) "Medium" TextBlock gets replaced by a ListBox. The user selects an item. That initiates animation of the ListBox.Height. After the animation completes, the ListBox is replaced by original TextBlock.
(Disregard the differences in data. Collection of the images was a painfull process, when I had to work with rendered frames. One of the images was shot for different record.)
This sequence works with occasional flickering. I wanted to know what's going on and after a while I got this screenshot:
What you see is the first frame after Storyboard.Completed event was intercepted. As far I understand this is the final result from the Storyboard.
Notes:
I checked the visual tree at this instant and did not find anything suspiceous.
This is just one of the effects that happen. Another frequent case is a resized 1-line ListBox with blue hatching; in this case all elements above the listbox disappear. 3rd possibility is a diagonal red line over the whole screen.
Here is the code defining the Storyboard:
private Storyboard GetDropDownAnimation(double from, double to)
{
double secs = this.IsExpanded ? 0.2 : 0.4;
CubicEase ease = new CubicEase() { EasingMode = EasingMode.EaseInOut };
DoubleAnimation animation = new DoubleAnimation()
{
Duration = new Duration(TimeSpan.FromSeconds(secs)),
From = from,
To = to,
FillBehavior = FillBehavior.HoldEnd,
EasingFunction = ease
};
Debug.WriteLine("Animation Height {0} -> {1}", from, to);
Storyboard.SetTarget(animation, this);
Storyboard.SetTargetProperty(animation, new PropertyPath("Height"));
Storyboard sb = new Storyboard();
sb.Children.Add(animation);
return sb;
}
I could explain other tricks done (for a long time I was convinced that the problem is there), but it looks like the problem concerns only the animation itself.
Anybody able to explain what's going on?
Have a look at these before you continue this way it might make your life a bit easier :)
That said, have a look at the starting value of the animation (from) and see if it is correct.
If all else fails you could start with a fully transparent listbox.
I made some progress on the problem that may be worth of reporting.
At first I replaced Storyboard by own animations. I started by using CompositionTarget.Rendering callbacks. The code is fairly trivial: In the callback you need to update the ListBox height proportionally to the elapsed time. I double-checked that for each height change there is one LayoutUpdated event, in other words the screen is in sync all the time. (A pleasant surprise.)
The result: Flickering remained.
Conclusion: Storyboard is innocent.
Then I found something interesting:
Because the height manipulation was in my hands I simply rounded the value to the whole multiple of the row height. And guess what - flickering disappeared!
Note the ListBox in question is set up using ItemsSource and DisplayMemberPath that refers to a string property. ItemTemplate is not set. In other words pretty standard ListBox use.
It is not an answer, I know, yet I find it interesting.
Related
What I would like to achieve is something like this:
DevExpress Grid
Table with fixed columns
The table at the above links can have "fixed" columns, which does not scroll with the other content.
I'm aware of NSTableView's floatsGroupRows feature, and of NSScrollView's addFloatingSubview:forAxis: method; but to achieve the above one, these are not enough:
The columns are not NSViews, first of all
The table header and the table content is placed into 2 separate NSClipViews under the NSScrollView (this is the default operation of NSTableView)
So as long I could not find any built in solution for this. My only idea was to use 3 NSTableViews next to each other (+1 for the Left side, +1 for the right side); and sync the vertical scrolling in them manually. How to sync the horizontal scrolling, now that's a harder question. The Left and Right sides should not scroll, so should "float". For the table's content, the NSScrollView's addFloatingSubview:forAxis: method should work IMO(*); but the column headers are different animals. Ok, there still should be a way to achieve this floating behavior via hacking the drawing of the columns...
But still, I did not start to implement the above one, because my NSTableView is slow enough already (NSTableview View Based Scrolling Performance), and I'm sure these plus things would slow it down horribly.
Has anyone any (better) idea how to achieve floating columns in Cocoa?
Any help much appreciated!
Edit
(*): NSScrollView's addFloatingSubview:forAxis: does not work for this. As I see it now, if the NSView given to this method is a subview of an NSTableView, it gets special treatment. Probably the table adds its own logic into; and it turned out now for me, that the NSTableView only can have 1 floating row at a time.
I have achieved synchronizing two NSTableViews vertically. Not really clear why you would need three, but anyways. Note that this is all c# code using the Xamarin.Mac lib. These are wrappers over the native Cocoa platform. You will have to convert this to obj c/swift yourself. This shouldn't be difficult.
In awake from nib:
table1.EnclosingScrollView.ContentView.PostsBoundsChangedNotifications = true;
NSNotificationCenter.DefaultCenter.AddObserver (NSView.BoundsChangedNotification, BoundsDidChangeNotification, table1.EnclosingScrollView.ContentView);
table2.EnclosingScrollView.ContentView.PostsBoundsChangedNotifications = true;
NSNotificationCenter.DefaultCenter.AddObserver (NSView.BoundsChangedNotification, BoundsDidChangeNotification, table2.EnclosingScrollView.ContentView);
So every time one of the tables scrolls, the BoundsDidChangeNotification is called, and it takes care of synchronizing the y axis. Note that this works even when the scroll happens due to inertia scrolling or programmatic changes of the bounds (or when zooming in/out or resizing the views etc). Such events might not always trigger events which are specifically intended for "user scroll", and for this reason, this is the better approach. Bellow is the BoundsDidChangeNotification method:
public void BoundsDidChangeNotification (NSNotification o)
{
if (o.Object == table1.EnclosingScrollView.ContentView) {
var bounds = new CGRect (new CGPoint (table2.EnclosingScrollView.ContentView.Bounds.Left, table1.EnclosingScrollView.ContentView.Bounds.Top), table2.EnclosingScrollView.ContentView.Bounds.Size);
if (bounds == table2.EnclosingScrollView.ContentView.Bounds)
return;
table2.ScrollPoint (bounds.Location);
} else {
var bounds = new CGRect (new CGPoint(table1.EnclosingScrollView.ContentView.Bounds.Left, table2.EnclosingScrollView.ContentView.Bounds.Top), table1.EnclosingScrollView.ContentView.Bounds.Size);
if (table1.EnclosingScrollView.ContentView.Bounds == bounds)
return;
table1.ScrollPoint (bounds.Location);
}
}
Nothing too fancy here... there are some bounds equality checks to avoid an eventual infinite loop (table1 tells table2 to sync and then table2 tels table1 and so on, forever). AFAI remember, if you ScrollPoint to the current scrolled location, bounds did change is not triggered, but since an infinite loop would occur otherwise, I think an extra check is worth it - you can never know what happens in future os x versions.
In my Xcode 6.2 Swift project I have an issue with Auto-layout (I'm also using size classes) that I'm not able to figure out...
In Main.storyboard I have a view containing, among the others interface elements, a UIButton and a UISegmentedControl that are positioned at the same height on the opposite side of the view.
I'm setting manually all the constraints in Interface Builder (none in code) and my every view is working just fine, except in this case (and this particular issue only occurs when I have a long text).
The button is aligned to the left border of the view and its constraints are:
Leading space to superview == 0
Trailing space to segmented controller >= 8
Top and bottom space to other interface elements == 8
The segmented controller (which has 2 segments) is aligned to the right border of the view and its constraints are:
Trailing space to superview == 0
Leading space to button >= 8
Top and bottom space to other interface elements == 8
The button in the storyboard has a title "Some title", but actually the actual title is always set in code in ViewWillAppear:
myButton.setTitle(aStringThatSometimesIsPrettyLong, forState: .Normal)
The visual result I need to achieve (on every possibile device and orientation) is that the Button title I set in code, while it can be displayed in good length in the interface, should never compromise the size of the segmented control, compressing the labels of the two segments.
So, I want the size of the segmented control to be fixed and I'm willing to accept the fact that the Button title, if long, can be truncated with dots.
Instead, no matter what I try (and I'll explain what I've tried in a moment) when the Button title is very long it is not truncated, instead the segmented control is compressed and therefore its two segments labels are truncated.
So far, I've tried, separately and together, these steps:
Adding a width minimum constraint to the segmented control.
Incremented (in steps, up to 1000) the Content compression resistance of the segmented control while decreasing the correspondent value of the button.
Increased (up to 1000) the Content hugging priority of the button.
I think I can't set a maximum width to the Button, because it can stretch depending on the title set in code and, more important, on what device the app is run on.
My biggest issue is that, no matter what I try, when I run the app I always get the same behavior (button title completely shown, un-truncated, and compressed segmented control). It seems like adding these constraints doesn't change anything, and it never happened to me before with Auto-layout... messing up, a lot, but no change adding constraints, this is new!
Maybe the issue is that the button title is set in ViewWillAppear and not in the Storyboard, but my app wouldn't work properly if I couldn't set its title in code.
Last, but pretty important, I have to admit that, while I've managed so far to get Auto-layout and Size classes working on all devices and orientations for all the (over 10) viewcontrollers of my app, I've actually never written a single line of code for Auto.layout and Size classes: I've done everything in Interface Builder and, if possible, I'd really love to continue this way.
Any suggestion would be really, really appreciated!
Thanks in advance,
Cesare
As #KenThomases pointed out, the constraints I was setting were actually right (actually, it also works with less constraints now that, thanks to his answer, I figured out the issue), but the Segmented control wasn't getting its intrinsicContentSize. Fixed that, now everything's fine.
I'm trying to learn auto layout so I can set up a moderately complicated display the way I want. I'm starting with a simple version. At least I thought it was simple.
I have a content view containing a NSScrollView, and a zoom slider. The scroll view is, of course, just a window into a larger 'canvas' on which the user can do things.
I'd like the scroll view to be as big as the window allows, with the slider underneath.
I've tried many things none of which work, in some cases when I resize the window smaller, the scroll view goes on up over the window's top bar, obscuring the title and the red yellow, green, dots.. this is just a grumble, I won't attempt to describe how I got it.
I'm working with Visual Format Language.
The immediate problem: I can only get the thing to work at all if I put in a hard size constraint on the scroll view.
I've got constraints like #"V:|[ScrollView]-[ZoomSlider(==35)]-| and
#"|-20#1000-[ScrollView]-|"
With these, nothing shows at all, until I put a hard size on the scroll view:
For example, #"V:[ScrollView(>=70#20)]" and #"[ScrollView(>=140#20)]" results in a little tiny scroll view (as expected) just above the slider.
Window is resizable, all right.
Is there a simple way to make the scroll view resize to occupy the most space possible when I resize the window? The only way I can think of off hand is to produce metrics for the scroll view based on window size, and use a notification to change the constraints when the window size changes. There should be something simpler!
THanks.ee
OOps. Thought I knew the answer until I started to write it.
AT least here is a partial explanation of things that were causing me problems.
You can't add constraints that position or size the Window's content view. But apparently you can mess them up by deleting them programatically. Some of my problems were solved by getting rid of
[self.myContentView setTranslatesAutoresizingMaskIntoConstraints:NO];
and
[self.myContentView removeConstraints:self.myContentView.constraints];
This left me with a lot of conflicting constraints. I fixed this be eliminating all content window constraints that I could in IB, then by marking the rest as placeholders.
I've got a ways to go before I understand how to use auto layout, but doing it in code is easier (for me) than doing it with IB
In appcelerator, there doesn't seem to be a control to make a collapsible "div", so I thought I would spin one up myself.
1.) Create a parent View(height 50), add a label (Displayed, meant to be clicked), and a picker (hidden) - and put the label & picker in the parent view.
2.) on click of label, animate the parent View to height: 150.
3.) show the picker.
However, the picker gets cutoff at height: 50 (the original size of the View). If I adjust the parent view to an original height of 70, the picker gets cutoff at 70. Is there an issue in the way I'm rendering my view - is there a better way?
options_label.addEventListener('click', function(){
var animation = Titanium.UI.createAnimation();
animation.height = 150
var animationHandler = function() {
animation.removeEventListener('complete',animationHandler);
picker.show()
};
animation.addEventListener('complete',animationHandler);
category_option.animate(animation)
})
I've encountered issues like this in appcelerator a number of times. The fixes usually involve switching around the order of events. Unfortunately, this is more or less a trial and error process, so here goes my best guesses:
Instead of creating the picker at initialisation, and then showing it on animation complete, only create and add the picker to the category_option view in the animationHandler function.
If the above doesn't appeal to you, try forcing the height of the picker to whatever looks right, by setting its height property. This should force it to schedule a relayout, hopefully making it realise that its parent view is now larger.
My best guess as to why this might be happening is that the show() method of the picker is not causing a relayout of the component. This would leave it thinking that it still in a 50 unit heigh view, and crop itself accordingly. I could be completely wrong about that, however, its just speculation.
I would like to create an infinite scrollView (like a slot machine), but without paging. When the user scrolls down, it's easy i just have to increase the contentSize and the scrollView scroll endlessly :
- (void)scrollViewDidScroll:(UIScrollView *)theScrollView {
theScrollView.contentSize = CGSizeMake(45, theScrollView.contentSize.height+45);
}
But how can i create the same effect when the user scrolls upward ? I tried to play with the contentInset but then the contentOfsset doesn't get updated and i end up having weird behaviour.
Do you have any idea how i could achieve that ?
I needed the same, so I created this: http://dev.doukasd.com/2011/04/infinite-scrolling-dial-control-for-ios/
Have a look at the video, I believe it's what you're looking for. Source code is included.
I have developed this kind scroll view. It can scroll infinite.
You can check on github: https://github.com/quangrubify/InfiniteUITableView
I think you should give us more details about the issue. What content do you want the user to see when he is scrolling upwards? You increase the contentSize in scrollViewDidScroll method, but you are not checking the contentOffset, so the contentWill be bigger whenever the user scrolls the scrollView (either way, even horizontal if allowed). Since the contentOffset is already at 0, the user cant scroll upwards because there is nothing that the scroll view can show.
I dont know the content of your scrollView, but I have implemented infinite scrolling horizontally. For details, see: https://stackoverflow.com/a/12856174/936957
PS: Do not use "magic numbers", this is a better alternative:
theScrollView.contentSize = CGSizeMake(theScrollView.contentSize.x, theScrollView.contentSize.height+45);
//Or theScrollView.frame.size.width alternatively