NSSplitView: Divider Thickness changes during drag on retina screens - objective-c

Context
I have an NSSplitView in my app that has 3 panes. I've subclassed NSSplitView and it has a single override:
-(CGFloat) dividerThickness { return 1.0f; }
I have disabled ALL the delegate methods that constrain divider min/max points and resize subviews, etc.
The Problem
When I drag any divider on a retina screen, the divider's thickness fluctuates during the drag. See this video: http://incident57.com/divider.mp4
This happens no matter what divider thickness I use---the thickness always varies by about 1px during drag.
This DOES NOT HAPPEN on non-retina screens. It also DOES NOT HAPPEN when the window itself is resized. The divider width only fluctuates when the divider itself is dragged.
(If you look really closely, you can see that there's some blank, white space between the views where it looks like NSSplitView should be drawing the divider, but isn't. Look at the end of the brown selected table row in the video.)
What I Need
How do I fix this so that the divider width stays constant during drags on retina screens?

I solved this problem by implementing this NSSplitViewDelegate method:
- (CGFloat) splitView:(NSSplitView *)splitView constrainSplitPosition:(CGFloat)proposedPosition ofSubviewAt:(NSInteger)dividerIndex
{
return floor(proposedPosition);
}
It's also worth pointing out that this issue only manifests if the subviews within the panes of the NSSplitView are NSScrollViews. Normal NSViews inside the panes will not cause the divider-width fluctuation.

Layer-backing the split view (i.e. [splitView setWantsLayer:YES]) should also fix this.
Since scroll views will often auto-layerback themselves, their layer overlaps the drawn split view divider when non point aligned.

Related

Increase UINavigationBar title font size on scroll

I saw this effect today where the navigation bar title seemingly starts within the view, then shrinks and moves upwards into the navigation bar's title as you scroll the page, it then reverses to its original state when scrolled back to the top.
Does anyone have any insight on how this is done? Is a navigation bar used at all, or is it being mocked using a UIView that shrinks in height and the background colour darkens? Perhaps the title is a label converted to a UIImage and scaled down rather than the font size decreasing?
Just speculating on possible techniques.
Would love to get some opinions on this. Thanks in advance.
Yes, You can change the size of the font and the origin of the Navigation Bar in accordance with your gesture recognizer.
navigationBar.frame.origin.y = -10
will shift the Navigation bar up by 10 points. The font can be changed using
if let font = UIFont(name: "Lato-Light.ttf", size: 34) {
UINavigationBar.appearance().titleTextAttributes = [NSFontAttributeName: font]
}
This format is because using a forced unwrap ! will crash the app with UIFont
In this context, font can be a variable where you call the normal init with same typeface and different size.
These two operations should be performed whenever the gesture recognizer updates its value or scroll position. You may or may not have to redraw the view, however.

Setting UIScrollView's origin for pinch gestures

I'm creating a scroll view for displaying a very large view, and I need both scroll and zoom functionality (just like an image viewer). Here are the steps that I've taken:
In interface builder, I've put a scroll viewer to the view controller.
I've added a pinch gesture recognizer to the scroll viewer.
I've connected the gesture recognizer's action to the code to handle the gesture events.
When the view controller is loaded, I change my view's origin to the center (viewer is my scroll viewer): self.viewer.contentOffset = CGPointMake(384, 512);
In my code for the handler, I handled the event as such:
(startScale is 1.0 in the beginning)
- (IBAction)handlePinch:(UIPinchGestureRecognizer *)sender {
if(sender.state == UIGestureRecognizerStateEnded){
startScale *= sender.scale;
}else{
float result = sender.scale * startScale;
self.viewer.transform = CGAffineTransformMakeScale(result, result);
}
}
When I run the app, the gesture is recognized and scaling works correctly, however, the whole view scales with respect to the 0,0 point of the screen (top left). I want it to scale with respect to the middle point that I'm applying the gesture, just as a natural pinch gesture for zooming into a photo.
I've also tried setting self.viewer.frame's origin, but nothing changed. I've searched about the problem and found these:
How to set a UIView's origin reference? (already tried)
https://stackoverflow.com/questions/13163279/pinch-punch-gestures-center (about my problem, but unanswered)
UIPinchGestureRecognizer for zooming and panning an image in xcode (looks like an overkill, too complicated for me, and I'm not sure if this would really help my situation)
How can I achieve natural pinching with my scroll view, what am I doing wrong?
Thanks,
Can.
Well, the answer to the problem is very simple: Remove the pinch gesture altogether. The benefit of using a UIScrollView is that it handles the panning/zooming internally, and you have to do nothing
Edit: To make sure the content is scaled properly, you are going to need a UIView (called contentView or whatever you want) where you put all the content, and then on the delegate method of your UIScrollView do this:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return contentView;
}
This should solve your problem
Edit 2: Also remember to set the minimum / maximum zoom scales for your UIScrollView

Starting Position of NSSplitView divider

How can I set the start position of a NSSplitView?
The closest thing I've found that looks like it would work is setPosition
//Set splitView position.
[splitView setPosition:330 ofDividerAtIndex:0];
This doesn't seem to do anything though, my splitview still starts with the divider in the center.
ANy ideas?
You don't set the position of the divider, you set the sizes of your NSSplitView's subviews. The divider is then repositioned automatically.
This is how I positioned my divider and subview size (in swift):
let subview: NSView = mySplitView.subviews[1] as NSView
subview.setFrameSize(NSMakeSize(subview.frame.size.width, 100))
In the view's class housing the split view
override func viewWillAppear() {
self.mySplitView.setPosition(120, ofDividerAtIndex: 0)
}
or wherever you want it to start.
NSSplitView needs initial non-sized bounds to make them layout correctly.
If your view has zero-size, then it will not show expected layout.
The best way is providing non-zero layout (this is what IB does), but sometimes this is impossible.
If you cannot provide non-zero size, then I think you have to provide proper - (void)splitView:(NSSplitView *)splitView resizeSubviewsWithOldSize:(NSSize)oldSize delegate method implementation to layout everything manually yourself. (this is my current best practice)
Maybe that is the center? If splitView is correctly hooked up to your split view, that code should work. You should probably log [splitView minPossiblePositionOfDividerAtIndex:0] and [splitView maxPossiblePositionOfDividerAtIndex:0] before trying to set the position of the divider so you know the possible values.

How to control nested scrollviews

In my iphone application I want to use 2 scrollviews and they have some images inside. Well, my question is when I scroll vertically on my first scrollview I want to explore the content of it however when I scroll horizontally I want to move to my second scrollview. I hope I explained clearly.
Well, I tried to use 3 scrollviews first of them located on the background, others are located on the first scrollview but I can only control the background scrollview or the others at once.
Is there a way to control first one horizontally and the others vertically.sorry for my english, hope it makes sense.
I have two recommendations.
1) Scrollviews can scroll horizontally and vertically - so you dont need two of them if you have content in a vertical direction and content in a horizontal direction. You can use one.
2) If for some reason you really do need 2, then you can detect a horizontal swipe by subclassing UIScrollView and switch to the other.
Remember that a UIScrollView will scroll in any direction that exceeds its contentSize. So all you need to do in the first case (1) is take the view that is inside say scrollview 2 (the horizontal scrollview) and put that view in the scroll view to the left or the right outside of the scrollviews viewport when the user scrolls they will see that view and can of course scroll vertically there as well.
If you use method 2 - make sure that the content size of scrollview one is at leat a few pixels more wide than the content size so that you can detect a horizontal swipe then invoke the coe to switch to your other scroll view. If you dont subclass UIScrollview to get the swipe you probably wont get the event. So do that add a little to the width of that view and then look for a value less than the left edge of the scroll view and switch to the other scroll view. You can do the same in reverse to go back to the previous scrollview.
I hope this helps - sorry no code at the moment, but I do have code working on iOS and OSX that does this.
You can distinguish both UIScrollView via if statement
Set delegate of both UIScrollView
Then compare your scrollView in its delegate method. You can change your delegate method according to your requirement -
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if(firstScrollView == scrollView)
{
//Do your work for firstScrollView
}
if(secondScrollView == scrollView)
{
//Do your work for secondScrollView
}
}
try to do in delegates methords
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
set some conditions like
if(myScroll1)
{
//scroll vatical.
}
if(myScroll2)
{
//scroll horizontal.
}
this is not complete code this is an idea Best of luck..

NSScrollView clipping overlaid UI elements

I have a button that sits on top of an NSScrollView, not within. When the scrollview scrolls, the button get's clipped with part of the button going along with the scrolling and the other part staying positioned.
To better describe the issue here's a video of the issue:
http://dl.dropbox.com/u/170068/ScrollTest.mov
The planned goal was to have a button sit in the top right corner of a text view but stay there when the text view scrolls. So if anyone has any thoughts on how to achieve this it would be greatly appreciated.
You should subclass NSScrollView and override "tile" method to position sub-controls of the scroll view.
- (void)tile
{
[super tile];
if (subControl)
{
NSRect subControlFrame = [subControl frame];
// adjust control position here in the scrollview coordinate space
// move controls
[subControl setFrame:subControlFrame];
}
}
I have used this way to implement a custom ScrollView with zoom control and background color selector embedded.
Overlapping views isn't recommended for non-layer-backed views. I think Interface Builder will even warn you about this. The easiest way to work around this would be to make your button layer-backed.