I am animating the height change of a NSTableView based row. I am using noteHeightOfRowsWithIndexesChanged for that row to reduce the height by -
NSAnimationContext.runAnimationGroup({ (context: NSAnimationContext) -> Void in
context.duration = 0.5
self.table.noteHeightOfRowsWithIndexesChanged(NSIndexSet(index: rowIndex!))
}, completionHandler: nil)
This works perfectly when the row has a small size and the row's full frame is visible.
If the row height is tall and the row frame is not completely visible, it causes an issue: the reload of multiple rows, breaking the smooth height reduce animation and causing a lot of flickering.
What is the correct way of reducing the height of a row that has a big height(with animation)?
Related
On a Mac, Mail and Finder have a solid looking scroll on their table views when the up or down arrow is held. The row highlight sits flush with the top or bottom of the column and the rows step through with no animation.
8 years ago it seems that it was hard to not do this. Now I can't seem to stop scrollRowToVisible on an NSOutlineView animating.
I have tried wrapping the call with NSAnimationContext.beginGrouping() or CATransaction.begin() etc to set any animation duration to 0.0 but no luck.
Is there anyway to make this call snap - or should I be using something a little lower level?
EDIT
Here is my code. The duration has no effect here. There are always a few frames of scroll animation, and the endpoint of the animation is slightly irregular (i.e. the bottom edge of the scrolled to view is not always aligned with the bottom edge).
if selectedRows != outlineView.selectedRowIndexes {
outlineView.selectRowIndexes(selectedRows, byExtendingSelection: false)
// I would love this not to animate like in mail, but it cannot be stopped!!!
if selectedRows.one {
NSAnimationContext.beginGrouping()
NSAnimationContext.current.allowsImplicitAnimation = false
NSAnimationContext.current.duration = 0
outlineView.scrollRowToVisible(selectedRows.first!)
NSAnimationContext.endGrouping()
}
}
Using runAnimationGroup has the same result:
NSAnimationContext.runAnimationGroup( { current in
current.allowsImplicitAnimation = false
current.duration = 0
outlineView.scrollRowToVisible(selectedRows.first!)
}, completionHandler: nil)
I have variable height rows in my table but I don't see why this would make a difference. From the above code, the change in selection is always highlighted before any movement in the table, further indication that the scroll animation is not being removed.
I had this problem myself, and solved it by subclassing NSClipView and overriding func scroll(to newOrigin: NSPoint) like this:
override func scroll(to newOrigin: NSPoint) {
super.setBoundsOrigin(newOrigin)
}
This should disable smooth scroll entirely, which is the animation effect you are describing, for the scroll view that houses your subclassed clip view.
I have a tableView with a tableHeaderView that is assigned through a nib file. This tableHeaderView contains a TextView (orange background) that is sized with a height constraint using sizeThatFits.
let textViewSize = titleView.sizeThatFits(CGSize(width: titleView.frame.size.width, height: CGFloat(MAXFLOAT)))
textViewHeightConstraint.constant = textViewSize.height
The constraints of this header view are setup to properly drive it’s height.
When the tableView sizes the frame for the tableHeaderView i am getting very strange results from the headerView.systemLayoutSizeFitting when scrolling in the UITextView is no enabled.
if let headerView = tableView.tableHeaderView {
let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
var frame = headerView.frame
frame.size.height = height
if headerView.frame.height != height {
headerView.frame = frame
tableView.tableHeaderView = headerView
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
}}
If scrolling in the textView is enabled the tableHeaderView scales as expected, but the textView is cutting off the top line.
If scrolling for the textView is disabled (desired setting) then the height for this headerView is calculated to be larger than the size required. The length of text seems to multiply this effect.
I should also add that the TextView is being assigned attributed text with various paragraph and line spacing options.
Any suggestions as to what could be going on are much appreciated.
I have resolved my issue. I am still not sure why the above problems were occurring, but when I applied the header view in a storyboard (vs loading it in via a nib file and assigning it to tableView.tableHeaderView at runtime) all of my autolayout issues disappeared.
Still would like to know why it was mis-behaving and if there is a way to get my table header views to work through a xib files
The goal: Have a scroll view that displays an array of uiimageviews (photos) that you can horizontally scroll through them
How I understand to do this: Make the frame (CGRect) of each uiimageview the height and width of the scroll view, the y value to 0 on each, and set the first imgViews x value to 0. For every imgView after that, add the width of the scrollview to the x value. In theory, this would line the imgViews (Photos) up next to each other horizontally and not allow for any vertical scrolling or zooming, purely a horizontal photo viewer.
The storyboard setup: I am creating my scrollview in a xib file (It’s a custom uiCollectionViewCell), with these constraints:
— Top space to cell (0)
— Trailing space to cell (0)
— Leading space to cell (0)
— Height of 400
— Bottom space to a view (0)
—— (See Below for img)
Laying out the UIImgViews:
func layoutScrollView() {
for (index, img) in currentImages.enumerate() {
let imgView = UIImageView(frame: CGRect(x: CGFloat(index) * scrollView.bounds.width, y: CGFloat(0), width: scrollView.bounds.width, height: scrollView.bounds.height))
imgView.contentMode = .ScaleAspectFill
imgView.image = img
scrollView.addSubview(imgView)
scrollView.contentSize = CGSize(width: imgView.frame.width * CGFloat(index), height: scrollView.bounds.height)
scrollView.setNeedsLayout()
}
}
My suspicion: I suspect the issue is stemming from the auto layout constraints i’ve specified, but (considering Im asking a SO question) not sure
If there is a better way to do this (really the correct way) please let me know! I have been trying to wrap my head around this for a few days now.
I appreciate all responses! Thanks for reading
EDIT #1
I tried paulvs approach of setting setNeedsLayout & layoutIfNeeded before the "for" loop, and still no luck. Here is (out of three images selected) the second photo displaying. It seems that both the first and second photos are way longer than the content view and that would move the middle view over (Squished).
Your code looks fine except for a few details (that may be causing the problem):
Add:
view.setNeedsLayout()
view.layoutIfNeeded()
before accessing the scrollView's frame (a good place would be before the for-loop).
This is because when using Autolayout, if you access a view's frame before the layout engine has performed a pass, you will get incorrect frames sizes/positions.
Remove these lines from inside the for-loop:
scrollView.contentSize = CGSize(width: imgView.frame.width * CGFloat(index), height: scrollView.bounds.height)
scrollView.setNeedsLayout()
and place this line after (outside) the for loop:
scrollView.contentSize = CGSize(width: imgView.frame.width * CGFloat(currentImages.count), height: scrollView.bounds.height)
I created a 10 000 x 10 000 pixel canvas, placed it inside a scroll pane with dimensions 300 x 300. This was implemented fine. The whole grid was drawn and I could scroll through it.
I'm tasked to instead create the same functionality, except with a 300 x 300 canvas instead. As speed is important in this program, I'm told this would work much faster.
My problem is that when I do this, I lose access to the scroll bars on the bottom and right of the scroll pane. How do I regain access to those scroll bars, to physically scroll?
The canvas is updating at 60 steps per second. What's being drawn is a square grid from the information in a 2D array.
Since the the viewport handling will be done by the drawing logic, it's rather easy to create a "ScrollPane" yourself using a GridPane:
public static ScrollBar createScrollBar(Orientation orientation, double fullSize, double canvasSize) {
ScrollBar sb = new ScrollBar();
sb.setOrientation(orientation);
sb.setMax(fullSize - canvasSize);
sb.setVisibleAmount(canvasSize);
return sb;
}
Canvas canvas = new Canvas();
canvas.setHeight(300);
canvas.setWidth(300);
ScrollBar vScroll = createScrollBar(Orientation.VERTICAL, 10000, 300);
ScrollBar hScroll = createScrollBar(Orientation.HORIZONTAL, 10000, 300);
GridPane scrollPane = new GridPane();
scrollPane.addColumn(0, canvas, hScroll);
scrollPane.add(vScroll, 1, 0);
I´m trying to create a drop down menu that will display a set of controls. I got the animation working but the whole "button" moves with the animation.
My button is a UIView which is named settingsPanel, this is my code so far:
[UIView animateWithDuration:1.0f animations:^{
self.settingsPanel.bounds = CGRectMake(self.settingsPanel.bounds.origin.x, self.settingsPanel.bounds.origin.y, self.settingsPanel.bounds.size.width, self.settingsPanel.bounds.size.height + 300);
}];
I just want the height of the panel to change, and it does, but the whole view moves up a bit as well.
How can I create a animation that justs increases the height downwards?
You need to set the frame, not the bounds.
[UIView animateWithDuration:1.0f animations:^{
self.settingsPanel.frame =
CGRectMake(self.settingsPanel.frame.origin.x,
self.settingsPanel.frame.origin.y,
self.settingsPanel.frame.size.width,
self.settingsPanel.frame.size.height + 300);
}];
Frame is an external coordinate. When you change the frame, you change the actual position of the view within its superview, so you can keep the origin and width the same and just increase the height.
Bounds is an internal coordinate. When you change the bounds, the system has to decide what to do about the frame; its solution is that the change happens around a stationary center position. So the top moves upward as the height grows downward.