Maintain scroll position in UIWebView when resizing - objective-c

I have a UIWebView which resizes when the device is rotated. When the webview is resized the scroll position from the top of the page remains the same, but since the height of the content is changing, you end up in a different place at the end of the rotation.
The content of the webview is text and images which are not being scaled to fit. At a smaller width the text breaks more making it taller.
Is there a good way to maintain the scroll position?

One way to kind of work around this problem is to adjust the contentOffset of the webviews's scrollview after a rotation. Due to changes in line breaks in texts the overall size of the page may change during a rotation.
So to solve this, you have to save the old size of the webviews content in the "willRotateToInterfaceOrientation"-method, then calculate the relative change in the "didRotateFromInterfaceOrientation"-method and adjust the scrollviews contentOffset. (That 'almost' solve the problem - i say almost because due to the changes in line breaks, you might end up one or two lines off your desired position.)
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
oldSize = self.webView.scrollView.contentSize;
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
CGSize newSize = self.webView.scrollView.contentSize;
float xFactor = newSize.width / oldSize.width;
float yFactor = newSize.height / oldSize.height;
CGPoint newOffset = webView.scrollView.contentOffset;
newOffset.x *= xFactor;
newOffset.y *= yFactor;
webView.scrollView.contentOffset = newOffset;
}

Take a look at my solution: https://github.com/cxa/WebViewContentPositioner. In short, I use document.caretRangeFromPoint(0, 0) to capture the Range, and insert an element to track its position. After resizing, use the captured Range info to decide position.

Related

NSView not scaling all content

I'm trying to scale an NSView for a larger display when I'm testing on my Mac screen, so I can see everything in proportion.
NSRect wFrame = self.window.frame;
wFrame.origin.x = 200;
wFrame.origin.y = 100; // from bottom of my screen
wFrame.size.width = 1080 * 0.5; // so it will fit on my screen
wFrame.size.height = 1920 * 0.5;
[self.window setFrame:wFrame display:YES]; // the window is smaller, but not everything is scaled (e.g., font sizes)
That creates the correct size window, and most of the contents are drawn at half size. But, some things are not scaled.
An NSButton's title text is still the original, large size. If the button contains an image, the image is scaled properly.
The content of a WebView are not rendered at the small size.
How can I scale all of the content, including button titles and Webview content?
I found the answer here: https://stackoverflow.com/a/33293423/236415
I wrapped my full sized NSView (and its subviews) in an NSScrollView (sized for my screen) and then set the magnification to 0.5.

Get image from AVCaptureVideoPreviewLayer

I'm essentially cloning Cropping a captured image exactly to how it looks in AVCaptureVideoPreviewLayer since asking the original poster if they found a solution isn't an "answer" and I am unable to comment yet because I don't have enough reputation...
The app I'm building will always be in portrait mode because rotation isn't important in this case.
I have an AVCaptureSession with the AVCaptureVideoPreviewLayer connected to a UIView of size 320x240 that is positioned against the top layout guide.
I have capturing the input working but the image that I'm receiving is skewed and shows a lot more than the portion I'm displaying. How can I capture just the area that is shown in my AVCaptureVideoPreviewLayer?
Have a look at AVCaptureVideoPreviewLayer s
-(CGRect)metadataOutputRectOfInterestForRect:(CGRect)layerRect
This method lets you easily convert the visible CGRect of your layer to the actual camera output.
One caveat: The physical camera is not mounted "top side up", but rather rotated 90 degrees to the right. (So if you hold your iPhone - Home Button right, the camera is actually top side up).
Keeping this in mind, you have to convert the CGRect the above method gives you, to crop the image to exactly what is on screen.
Example:
CGRect visibleLayerFrame = THE ACTUAL VISIBLE AREA IN THE LAYER FRAME
CGRect metaRect = [self.previewView.layer metadataOutputRectOfInterestForRect:visibleLayerFrame];
CGSize originalSize = [originalImage size];
if (UIInterfaceOrientationIsPortrait(_snapInterfaceOrientation)) {
// For portrait images, swap the size of the image because
// here, the output image is actually rotated relative to what you see on screen.
CGFloat temp = originalSize.width;
originalSize.width = originalSize.height;
originalSize.height = temp;
}
// metaRect is fractional, that's why we multiply here
CGRect cropRect;
cropRect.origin.x = metaRect.origin.x * originalSize.width;
cropRect.origin.y = metaRect.origin.y * originalSize.height;
cropRect.size.width = metaRect.size.width * originalSize.width;
cropRect.size.height = metaRect.size.height * originalSize.height;
cropRect = CGRectIntegral(cropRect);
This may be a bit confusing, but what made me really understand this is this:
Hold your device "Home Button right" -> You'll see the x - axis actually lies along the "height" of your iPhone, while the y - axis lies along the "width" of your iPhone. That's why for portrait images, you have to swap the size ;)

CGRect not completely formed before arranging UI elements

I have the following code
- (void) setTargetGoalFrameToLeftOfWindow: (UIView*) goalView orientation: (UIInterfaceOrientation) orientation {
[goalView setTransform:CGAffineTransformMakeRotation(-M_PI_2)];
CGRect goalFrame = goalView.frame;
CGRect windowFrame = [self getWindowFrame];
CGFloat topMargin;
if (UIInterfaceOrientationIsLandscape(orientation)) {
topMargin = (windowFrame.size.width - goalFrame.size.height) / 2.0;
} else {
topMargin = (windowFrame.size.height - goalFrame.size.height) / 2.0;
}
goalView.frame = CGRectMake(GOAL_MARGIN, topMargin, goalFrame.size.width, goalFrame.size.height);
}
I am creating frame with CGRect and I am using it display my UI elements like labels, buttons etc. I am calculating position based on the window size so that they are in appropriate positions in different orientations. When my app is running, I click on home button. When I open the app again, my UI elements are messed up. They are not in proper positions. The method I mentioned above gets invoked every time I change the orientation and open the app. So, this is getting invoked when I reopen the app. But the problem is that, even before the frame is completely formed, it is taking the width and height at that particular point and calculating positions of my UI elements. This is leading to messed up UI. Is there any way where in I can restrict it to take width and height only after the frame is completely formed? Thanks!

Infinite horizontal scrolling UIScrollView

I have a UIScrollView whose content size is 1200x480. I have some image views on it, whose width adds up to 600. When scrolling towards the right, I simply increase the content size and set the offset so as to make everything smooth (I then want to add other images, but that's not important right now). So basically, the images currently in the viewport remain somewhere on the left, eventually to be removed from the superview.
Now, the problem that I have happens when scrolling towards the left. What I do is I move the images to the end of the content size (so add 600 to each image view's origin.x), and then set the content offset accordingly. It works when the finger is on the screen and the user drags (scrollView.isTracking = YES). When the user scrolls towards the left and lets go (scrollView.isTracking = NO), the image views end up moving too fast towards the right and disappear almost instantly. Does anyone know how I could have the images move nicely and not disappear even when the user's not manually dragging the view and has already let go?
Here's my code for dragging horizontally:
-(void) scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint offset = self.scrollView.contentOffset;
CGSize size = self.scrollView.contentSize;
CGPoint newXY = CGPointMake(size.width-600, size.height-480);
// this bit here allows scrolling towards the right
if (offset.x > size.width - 320) {
[self.scrollView setContentSize:CGSizeMake(size.width+320, size.height)];
[self.scrollView setContentOffset: offset];
}
// and this is where my problem is:
if (offset.x < 0) {
for (UIImageView *imageView in self.scrollView.subviews) {
CGRect frame = imageView.frame;
[imageView setFrame:CGRectMake
(frame.origin.x+newXY.x, frame.origin.y, 200, frame.size.height)];
}
[self.scrollView setContentOffset:CGPointMake(newXY.x+offset.x, offset.y)];
}
}
EDIT: This is now working - I had a look at StreetScroller and it's all good now.
However, I now want to zoom in on the scrollview, but viewForZoomingInScrollView is never called. Is it not possible to zoom in on a scrollview with a large content size?
There are some approaches floating around here. Just use the site search …
If you want an more "official" example created by Apple take a look at the StreetScroller Demo. For some more information about this example take a look at last years WWDC session no. 104 Advanced Scroll View Techniques.
There is also an UIScrollView subclass on Github called BAGPagingScrollView, which is paging & infinite, but it has a few bugs you have to fix on your own, because it's not under active development (especially the goToPage: method leads to problems).

UIScrollView: handling of dynamic content?

I've a Storyboard with a UIScrollView which contains two UILabels, a UIImageView and a UITextView. The content of the UIImageView and UITextView is dynamic and so are their height.
Currently I'm doing this inside my viewDidLoad to adjust the size of the UITextView after the dynamic text is inserted:
CGRect frame = self.textView.frame;
frame.size.height = self.textView.contentSize.height;
self.textView.frame = frame;
Is this the way to change its height?
My next problem is to set the content size for the UIScrollView, to activate the scrolling. Is there a smart way to get the height of all its content or do I have to calculate the height for each element and set the sum of this as the content size of the UIScrollView?
IF you had no space in between your objects, you could make a for loop in your scrollView.subviews and add up all the heights to set as the contentSize.
As you probably don't have everything tight together, you're probably better by getting the bottom most object and adding up it's frame.origin.y and it's frame.size.height (maybe you want to have some extra space in here, but that's up to you) and that will give you your contentSize.height to keep everything in there.