How to calculate the correct location of a vertical NSRulerMarker? - objective-c

What is the correct formula for the location of an NSRulerMarker on a vertical NSRulerView, if the client view is flipped?
The situation: I have a view (let's call it the "main view") that is embedded in an NSScrollView with rulers, and it has subviews. The user can drag these subviews around, and while dragging, I want to indicate the current position on the rulers.
The main view is flipped: The zero point is top-left.
The horizontal position is pretty simple:
NSPoint scroll = myScrollView.documentVisibleRect.origin;
NSRect rect = mySubView.frame;
rulerMarkerDragLeft.markerLocation = rect.origin.x - scroll.x;
However the same method for the vertical position...
rulerMarkerDragTop.markerLocation = rect.origin.y - scroll.y;
does not work. The marker is only in the correct position when the scrollview has been scrolled down to the extreme bottom. For every n points scrolled back up, the marker location is n points too high. This is independent of the main view's size or the size of the visible area.
I can't seem to wrap my head around this problem; I guess there is a value I need to subtract from my result that expresses how far up the scrollview has been scrolled (or rather, how much further it can be scrolled down), but I don't think I can derive that value from myScrollView.documentVisibleRect...?
I may have overlooked something simple, but I can't find the solution.
Edit 2022-11-02 17:17 CET: Found the problem. I had set the NSRulerViews clientView to the contentView of the window. I am now setting it to the "main view" (ie. the view inside the scroll view), and now it works "automagically": I just set the marker locations to the subviews frame, no correction for scroll position or anything else needed.

The solution was simple: the ruler views' clientView needs to be set to the view that is inside the scroll view, not the main content view of the window.
The positioning of the ruler markers is now very straightforward: you just use the local coordinates inside the view, ie. the subviews' frame values.
No correction for scroll position, view height or such necessary.
My mistake was assigning the window's main content view as the rulers' clientView.

Related

UIScrollView Using Only Interface Builder

I know this question has been answered before, many times, but my use case seems to be just different enough from all of them that I can't quite figure it out.
My Problem
I have a scroll view that is not the same size as its superview. The scroll view has 1 subview that is the same size as it, but it needs to be able to be pinch-zoomed.
Attempted Tutorials:
(1) - Apple's Technical Note - This is done only with code and the examples show only full screen scroll views.
(2) - Natasha The Robot's Article - This was a really well written article but I could not get it to work for me. I think it's due to the fact that her scroll view is full screen.
(3) - Happy Coding Blog Article - Another full screen scroll view
... and lots other tutorials that were very similar to these
My requirements
My scroll view needs to be full width
My scroll view needs to be 40pt from the top and have a 1:1 aspect ratio
My scroll view needs to have one subview that is the exact same size of it but can be pinch-zoomed (aka content size = scroll view size)
I don't think that the size of the scroll view should impact anything, but it appears to.
What I have tried
As all the tutorials above recommended, I have only a single subview of the scroll view and have aptly named it "Content View".
View Controller
|-View
|-ScrollView
|-ContentView
|-ZoomableView
Here is a picture of my constraints:
As you can see, I have an equal width set up from the "Grid" (Zoomable view) to the view controller's view. I've also tried adding an equal width of the content view and the VC's view.
My question
I know I can get this to work with an explicit width and height, but I know I should be able to get it by setting the width equal to the view's width and height equal to the view's width as well (AKA 1:1 aspect ratio). How can I achieve this?
I think I ran into the same problem in the past. What I end up doing was adding a "container" view and use it to set up my width and height equality constraints instead of the view controller's view.
This setup will produce what you want I think: a scrollview with 1 subview that can be pinch-zoomed in the view controller's view (grey in the screenshot) but with a top margin of 40pt.

How to set ContentSize of a UIScrollView which is a sub-view?

I have an iPad app using Storyboards. I have one scene with a UIView, which has an additional UIView on the top half (which I draw on), and a UIScrollView on the bottom half (I needed to keeep them separate). I'm having a hard time figuring out how to address the UIView with this code that I copied from another SO question:
CGFloat scrollViewHeight = 0.0f;
for (UIView* view in scrollView.subviews)
{
scrollViewHeight += view.frame.size.height;
}
[scrollView setContentSize:(CGSizeMake(320, scrollViewHeight))];
The way I read the code above, I'm supposed to use the UIView, but that doesn't make sense, since the UIView covers the entire "scene/window". It makes more sense to me to use the UIScrollView to calculate the ContentSize.
So, what am I supposed to use?
Not completely clear on what you are asking. Once you have a reference to the scroll view, either through tags, properties or some other method, you should set its content size to the width and height of all of its content combined (accounting for overlap, etc). Ex. if you had a single image view that was 600 by 900 points inside the scroll view, you'd set its size to a 600 by 900 CGSize.
That goes through all of the subviews of the scroll view using a for...in loop (aka fast enumeration), adding their heights together, then uses that as the height of the content. I don't think that would be a good way to do it, because that assumes that all the subviews are stacked vertically on top of one another perfectly. It's basically going, "For every subview of my scroll view, add their height to this total and then use that as the content size's height."
The easiest way to manage scroll views is to slap all the content into one UIView, place that in the scroll view, then set the scroll view's content size to the frame of the UIView. If you need a reference to the UIView contained in the scroll view, you can set it to a property or give it a tag and use viewWithTag.

Programmatically reveal a UIView

I am attempting to reveal (through animation) a UIView. Specifically I want to show the center portion of the view and then slowly reveal the outer edges of it (sort of like pulling back a curtain).
My first attempt was to simply set the bounds rect to be smaller and animate it to be the full size of the view's frame, but this did not have the desired effect since by changing the bounds I was also changing the frame.
If what I am trying to do does not sound possible (at least not in a simple manner), at least I would like to be able to have is some way to make the subviews of the main view stationary relative to the screen, NOT their parent view, as the parent resizes (this would give a similar effect).
Any ideas?
Thank you,
-Matt
It definitely is possible. What you need to do is
For the view you're animating, setAutoresizesSubviews:NO and setClipsToBounds:YES.
Set the view's bounds (NOT the frame) to a rect with zero size and origin at the center point of the rect you want the view to occupy when it is fully revealed (in the view's own coordinate system). In other words, startBounds.origin.x should equal half of endBounds.size.width and similarly for y.
Position the view by setting its center (in the parent view's coordinate system).
In an animation block, change the view's bounds to zero origin and full size.
In the animation's completion block, you probably want to setAutoresizesSubviews:YES again.
You may also need to set the view's autoresizing mask to be fully flexible (springs but no struts), depending on what other layout gets triggered as you resize.
Sounds like you want to change its clipping. A cheap (code-wise) way to do that would be to insert the view into a parent view (with autoresizing set to center it), set the parent to clip its children and then animate the parent's frame.

How to make a side-scrolling object?

I would like to make a side-scrolling object that is only 200 pixels wide and 50 pixels tall. This side-scrolling object would contain five different objects that, when scrolled into the middle, act as if selected. How could I go about doing this?
I want sort of the same effect that the iPhone home screen has where it latches on to a page when you slide it. Instead of latching on to the pages though, I want it to latch on to my five different objects.
The side-scrolling behavior is achieved with a UIScrollView with pagingEnabled set to YES. Set the scroll view's width to the size of your pages. Your scroll view delegate can calculate which object is on screen by dividing contentOffset.x by the scroll view's width.
If you want to show several items on the screen at once but still page between the individual items--think of the way the iWork apps show multiple documents, for example--there are three steps involved:
Set the scroll view's width to the width of the objects, not the width of the screen.
Set the scroll view's clipsToBounds property to NO so it displays the objects that aren't within the scroll view's frame.
Subclass UIScrollView and override -pointInside:withEvent: to return YES if the point is within the area you want to respond to touches within. (For example, if you want to respond to touches within the entire width of the screen, just ignore x and make sure y is between the top and bottom of the view.) Use this subclass instead of a standard UIScrollView.

Way to return bounds of non-hidden elements in UIView

Right now, I have a UIScrollView which scales its UIView contents on zoom events. When I hit a certain zoomScale threshold, I want to be able to toggle on and off extra information within the UIViews. Right now, I am simply setting the hidden flag to YES/NO to accomplish this.
However, a problem occurs while attempting to get the bounds of the UIView. The bounds always returns a width and height that extends to include the invisible content.
Is there a way to limit the bounds to only return the size of visible content within a UIView?
You could derive new bounds as you zoom and not rely on the view's automatic bounds adjusting.
OR
If it doesn't make your code too complicated, I would remove those subviews from your resizing view instead of just hiding them.