Scroll and zoom to specific CGRect of UIImageView - ios

I have a UIScrollView and a UIImageView inside. The UIScrollView size is 350x350 and the image inside the UIImageView is 350x350.
The UIScrollView has a zoomScale of 4.
I want to show only the specific point of the image rect:
CGRect(x: 100, y: 100, width: 100, height: 100)
For better understanding, only the white rect inside the images:
x:100, y:100, width:100, height:100
x:150, y:150, width:100, height:100
What i've done so far could not do the trick:
self.image.image = UIImage(named: "horse_marked_100x100")
self.imageScroller.maximumZoomScale = 10
self.imageScroller.minimumZoomScale = 1
self.imageScroller.clipsToBounds = true
self.imageScroller.delegate = self
self.imageScroller.isScrollEnabled = false
self.imageScroller.scrollRectToVisible(CGRect(x: 100, y: 100, width: 100, height: 100), animated: false)
self.imageScroller.zoomScale = 4

You can use:
scrollView.zoom(to: CGRect(x: 100, y: 100, width: 100, height: 100), animated: true)
You should only take care to have the desired size covered by the maximumZoomScale of the scroll view. For instance, in this example, you won't be able to zoom in a rect smaller than 35 x 35, because you have a maximumZoomScale of 10. In your example you don't have any problems with this.
Note: the CGRect should be in the coordinate space of the view returned by viewForZooming(in:). This is a UIScrollViewDelegate method which must be implemented in order to enable zooming. Here, you have to simply return the UIImageView inside the scroll view.
UPDATE:
Just to clarify what you tried out so far and why it does not work: scrollView.scrollRectToVisible(_:, animated:) will scroll in a position where the passed CGRect is visible, but without zooming the scroll view. In this example, that CGRect is already completely visible in the scroll view, thus it doesn't have to scroll anywhere else, so you don't see any visible effect.

Related

Can't fully scroll within UIScrollView

I have a UIImageView within a UIScrollView, and I want to be able to scroll around the dimensions of the "regular" photo dimensions. My issue is that I am able to scroll, however when it gets to the top or towards the bottom of the photo, the scroll position does not stay, instead it "bounces" and moves back up a little bit, and does not allow for the photo to stay in that position, how would I go about fixing this?
here is the code below:
scrollView.frame = CGRect(x: self.view.frame.width * 0, y: self.view.frame.height / 3, width: self.view.frame.width, height: self.view.frame.width)
self.view.addSubview(scrollView)
scrollView.delegate = self
let image = imageView.image
imageView.frame = CGRect(x: self.view.frame.width * 0, y: self.view.frame.height * 0, width: (image?.size.width)!, height: (image?.size.height)!)
imageView.frame = scrollView.bounds
imageView.contentMode = .scaleAspectFill
scrollView.addSubview(imageView)
An example would be if you had a full screen photo taken with the camera, and then when the photo is displayed within the view, the whole photo is not able to stay in its position when it is being scrolled.
You are doing a number of things wrong, and as mentioned you should move to auto-layout and constraints, however...
To get your image scrolling the way you are going:
// set scroll view frame to be full width of view, start 1/3 of the way down from the top, and make the height the same as the width
scrollView.frame = CGRect(x: 0, y: self.view.frame.height / 3, width: self.view.frame.width, height: self.view.frame.width)
// add the scroll view to the view
self.view.addSubview(scrollView)
// use "if let" so you're not force-unwrapping optionals
if let image = imageView.image {
// set the imageView's frame to the size of the image
imageView.frame = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
// add the imageView to the scroll view
scrollView.addSubview(imageView)
// set the contentSize of the scroll view - the "scrollable area" - to the size of the image view
scrollView.contentSize = imageView.bounds.size
}
Bouncing is an optional behavior in UIScrollView (and its subclasses, including UiTableView). You can turn it on/off via the bounces property, or in Interface Builder.
I usually bundle desired ScrollView properties together in a convenience method (or you could use UIAppearance) to ensure that all of my views have shared behavior.

Why doesn't view change when I change bounds?

If I change a UIView's bounds from...
v2.bounds = CGRect(0, 0, 70, 70)
to...
v2.bounds = CGRect(-2000, 0, 70, 70)
... nothing happens - the dimensions stay the same upon rendering. Why is this?
To help understand what bounds does, run this sample code in a view controller's viewDidLoad method:
let newView = UIView(frame: CGRect(x: 50, y: 100, width: 30, height: 30))
newView.backgroundColor = UIColor.blue
let secondView = UIView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
secondView.backgroundColor = UIColor.red
newView.addSubview(secondView)
view.addSubview(newView)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
newView.bounds = CGRect(x: 10, y: 0, width: 30, height: 30)
}
Here we're moving the bounds to the right by 10 points, so you'll see the "second view" (which is red) move to the left by 10 points.
Changing the origin of the bounds changes the coordinate system of the view, which affects the location of all of it's subviews. It doesn't affect its origin with respect to its super view, however. For that, you should change the origin of the frame.
Bounds only takes into account width and height, you are only changing the origin in your example, only changing x to be precise. To accomplish this use frame property:
v2.frame = CGRect(-2000, 0, 70, 70)

Unable to properly configure UIScrollView (Offset on top)

I have been fighting with this all morning and can't seem to find a solution. I have created a UIImageView, filled it with red, then added it to a UIScrollView and set the contentSize to the size of the UIImageView. If I print the contentOffset i see (0, 0) and if I print the contentSize and the UIImageView.frame.size they are the same but the red "image" always appears smaller than what the scrollView thinks the contentSize is.
If I scroll all the way to the top I see a cyan stripe about 100 pixels high above the red image and the scroll bar will not make it all the way to the top of what I believe the top of my scroll view to be. Although the top of the scroll bar does line up with the top of my red window so it would seem as though the scroll view is confused as to where it actually lives. Or more likely, I'm confused
Here is my what seems like very simple code...
imgHorizon = UIImage.init(named:"horizon")!
imgBezel = UIImage.init(named:"bezel_transparent")!
imgWings = UIImage.init(named:"wings_transparent")!
imgViewHorizon = UIImageView.init()
imgViewBezel = UIImageView.init()
imgViewWings = UIImageView.init()
svHorizon = UIScrollView.init()
super.init(coder: aDecoder)
imgViewHorizon = UIImageView(frame: CGRect(x: 0, y: 0, width: imgBezel.size.width, height: imgHorizon.size.height))
imgViewHorizon.backgroundColor = UIColor.red
imgViewBezel = UIImageView(frame: CGRect(x: 0, y: 0, width: imgBezel.size.width, height: imgBezel.size.height))
imgViewBezel.contentMode = UIViewContentMode.center
imgViewBezel.clipsToBounds = true
imgViewBezel.image = imgBezel
imgViewWings = UIImageView(frame: CGRect(x: 0, y: 0, width: imgBezel.size.width, height: imgBezel.size.height))
imgViewWings.contentMode = UIViewContentMode.center
imgViewWings.clipsToBounds = true
imgViewWings.image = imgWings
svHorizon = UIScrollView(frame: CGRect(x: 0, y: 0, width: imgBezel.size.width, height: imgBezel.size.width))
svHorizon.contentSize = CGSize(width: imgBezel.size.width, height: imgHorizon.size.height)
svHorizon.contentMode = UIViewContentMode.scaleToFill
svHorizon.bounces = false
svHorizon.backgroundColor = UIColor.cyan
svHorizon.contentOffset = CGPoint(x: 0, y: 0)
svHorizon.addSubview(imgViewHorizon)
addSubview(svHorizon)
addSubview(imgViewBezel)
addSubview(imgViewWings)
From the discussion in the comments it turns out that the Adjust Scroll View Insets option was checked in the attributes inspector of the ViewController. Unchecking it resolved the problem. Have a look at the image below. You need to uncheck the highlighted option.

Stop affine transform from applying on subview

I have a UIView that holds a UILabel inside.
After applying affine transform on the UIView using:
myView.transform = CGAffineTransformMakeScale(4, 4);
My UILabel (which is a sub view to myView) grows as well.
Is there a way to prevent this?
i tried:
1) Using the CGAffineTransformIdentity flag on the label.
2) Adding a superview to myView and adding myView as superview's subview, and the label as a subview to the superview (and not myView).
Non of them seem to be working, the label keeps growing.
Any ideas?
You answered your own question with option 2. Not sure why it's not working since you did not supply any code. The playground code below shows it will work. Uncomment out the last line to transform the subview but not the label.
import UIKit
import XCPlayground
let superview = UIView(frame: CGRect(x: 10, y: 10, width: 200, height: 200))
XCPlaygroundPage.currentPage.liveView = superview
superview.backgroundColor = UIColor.redColor()
let view = UIView(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
view.backgroundColor = UIColor.greenColor()
superview.addSubview(view)
let label = UILabel(frame: CGRect(x: 20, y: 10, width: 40, height: 40))
label.text = "Hello"
superview.addSubview(label)
//view.transform = CGAffineTransformMakeScale(2, 2)

Setting UICollectionView frame at runtime

I am trying to set the frame of my UICollectionView at runtime.
I tried using
mainCollectionView.frame = CGRect(x: 20, y: 20, width: self.view.frame.width-20, height: self.view.frame.width-20)
in my viewDidLoad but unfortunately it will always stay as in IB.
I am not yet using constraints.
Otherwise resizing my cells is working in cellForItem:
collectionCell.frame.size.width = self.view.frame.width/8 * 3
Try to set frame in viewDidLayoutSubviews
you are wrong write height because you are set width at height.
try this line without navigation bar:
mainCollectionView.frame = CGRect(x: 20, y: 20, width: UIScreen.mainScreen().bounds.size.width - (20 * 2), height: UIScreen.mainScreen().bounds.size.height - (20 * 2))
Run the following in one of the LayoutSubviews overrides. This will tell collection view to update its frame.
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
mainCollectionView.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height)
mainCollectionView.collectionViewLayout.invalidateLayout()
}

Resources