I am working in pdf editing project, in that I have to add signature in pdfview and after that need to resize and delete that signature. Now for this I am adding one scrollview with UIView on top of pdfview with transparent alpha and add image view to that UIView but I had an issue with zooming. How can I get how much zoom pdfview zooming when user pinch zoom. I want to find pdfview's zoom contentInset, so I can set my own scrollview's contentInset.
try to get pdfview scale factor when pdfview zoom with :
#objc func pdfViewScaleChange(notification:NSNotification){
print(self.pdfView.scaleFactor)
}
But Still I can't find How can I zoom and set my UIScrollview as pdfview zoom.
Your PDFView has a property called scaleFactorForSizeToFit, which is the scale factor that PDFKits autoScales would use for scaling the current document and layout.
What this means is that if you use the autoScales property when you set your pdf you will be able to calculate an amount to scale your UIScrollview by.
Here is an example of what I mean for some more context:
//wherever you set your pdf (doesn't have to be viewDidAppear)
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
pdfView.document = PDFDocument(data: data)
pdfView.autoScales = true
//Add the observer here to detect when your pdf is zoomed in or out
NotificationCenter.default.addObserver(self,
selector: #selector(pdfPageWasZoomed),
name: Notification.Name.PDFViewPageChanged,
object: nil)
}
//wherever you want to zoom use this
func getScrollViewScaleFactor() -> CGFloat {
return (pdfView.scaleFactor / pdfView.scaleFactorForSizeToFit)
}
//Objc function which is called everytime the zoom changes
#objc private func pdfPageWasZoomed() {
//Here use the function getScrollViewScaleFactor() to get a factor to scale in or out by
}
Something like this should solve your problem.
Related
I am trying to figure out how to add pinch/zoom capabilities to an imageView,
but specifically to one that I added to the scrollView programmatically. I was able to find some great examples on SO to pinch and zoom an image using a scrollview with viewForZooming, but in that instance I had to return the image view being pinched and zoomed, which doesn't work if I am returning it programatically.
My ultimate goal is to have an array of images where the user can scroll left and right to see all the images AND be able to zoom in on them, basically just as if they were flipping through their photoStream. I found an ok tutorial for adding the images dynamically for scrolling here https://www.codementor.io/taiwoadedotun/ios-swift-implementing-photos-app-image-scrolling-with-scroll-views-bkbcmrgz5#comments-bkbcmrgz5 but I am not clear how to add the viewForZooming since the image views are being .addSubview dynamically in a loop.
I created a little example with a collection view of 0-n images associated to a post. Once the collectionViewCell with the image is tapped a hidden scrollView appears with a new dynamic UIImageView added as a subView. All works great but I don't know how to add the pinch/zoom now.
#objc func imageTapped(_ sender: UITapGestureRecognizer) {
print("BlipeFile Image Tapped")
let imageView = sender.view as! UIImageView
let newImageView = UIImageView(image: imageView.image)
//newImageView.frame = UIScreen.main.bounds
newImageView.contentMode = .scaleAspectFit
newImageView.clipsToBounds = true
newImageView.layer.borderColor = UIColor.gray.cgColor
newImageView.layer.borderWidth = 3.0
newImageView.frame = self.view.frame
newImageView.backgroundColor = .black
newImageView.isUserInteractionEnabled = true
newImageView.image = imageView.image
let tap = UITapGestureRecognizer(target: self, action: #selector(dismissFullscreenImage))
newImageView.addGestureRecognizer(tap)
scroller.isHidden = false
scroller.addSubview(newImageView)
}
#objc func dismissFullscreenImage(_ sender: UITapGestureRecognizer) {
scroller.isHidden = true
self.navigationController?.isNavigationBarHidden = false
self.tabBarController?.tabBar.isHidden = false
sender.view?.removeFromSuperview()
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return image //Can't return dynamically created newImageView?
}
My ultimate goal is to have an array of images where the user can scroll left and right to see all the images AND be able to zoom in on them
Apple has explained many times, in WWDC videos and in sample code that you can download, how this is done. Basically it’s just a scroll view in a scroll view. The outer scroll view permits scrolling horizontally. The inner scroll view contains an image view and permits zooming.
So instead of adding an image view, add a scroll view containing an image view.
Relatively new to iOS apps, and swift, so I'm a little lost on how to proceed.
Basically, I have a UIImageView, and i'm able to pinch to zoom/scroll, as my UIImageView is in a UIScrollView, but I'd like to add a double tap on the UIImage to zoom in as well.
I'm able to zoom in (just set the zoomScale to 3.0 for now), but I'd like to have a smooth transition. I did see the zoom(to:, animated:) method, but I wasn't quite sure on how to zoom into the center of the view.
Anyways, here's what I have thus far.
I've created the tapGestureRecognizer, and assigned it to the UIImageView
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapImageViewGesture))
self.Image1.isUserInteractionEnabled = true
self.Image1.addGestureRecognizer(tapGesture)
and below is the function to perform the zoom
func tapImageViewGesture(sender: UITapGestureRecognizer) {
print("Touched")
self.pinchZoomScroll1.zoomScale = 3.0
}
Is there a simple way I can have some sort of a smooth animation of zooming in?, or should I go with using the zoom function instead, as it already supports animation?
Thank you!!
Either you can set set animation true by using this :
self.pinchZoomScroll1.setZoomScale(3.0, animated: true)
or you can also use scrollView delegate func by setting scrollViewminimumScale and max scale:
pinchZoomScroll1.minimumZoomScale = 1.0
pinchZoomScroll1.maximumZoomScale = 6.0
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return Image1
}
So I have a big image in an UIImageView (Swift) with "Aspect Fill"
But What I want is to have like an Animation or Ken Burn Effect on the Image.
In the Image Below... What I want is to go from Red to Yellow seen the image.
Take a normal uiview give it proper height width as per ur wish... Take an imageview inside of it and give the height, x coordinate and y coordinate same as uiview and keep the width as much wide you want to keep the image....
Use UIView.animateWithDuration method and in its animation block set the final desired frame such that the end of image view comes equal to the end of uiview... The image view will slide outside of vision of the uiview as you desire...
You can even repeatedly animate from left to right calling animation into each others completion block
I found an extension for UIImageView called UIImageViewAligned. You can use the alignment properties provided by the extension and animate the changes.
import UIKit
import UIImageViewAligned
class ViewController: UIViewController {
// MARK: Outlets
#IBOutlet var imageView: UIImageViewAligned!
// MARK: Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
imageView?.contentMode = .scaleAspectFill
imageView?.alignLeft = true
imageView?.alignRight = false
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 5) {
self.imageView?.alignLeft = false
self.imageView?.alignRight = true
}
}
}
The problem in short, related to working with pan gesture inside a scrollView.
I have a canvas(which is an UIView itself but bigger in size) where i am drawing some UIView objects with pan gesture enabled over each of them(Each little UIView Objects I am talking about, are making using another UIView class).
Now the canvas can be bigger in height and width...which can be changed as per the user input.
So to achieve that I have placed the canvas inside a UIScrollView. Now the canvas is increasing or decreasing smoothly.
Those tiny UIView objects on the canvas can be rotated also.
Now the problem.
If I am not changing the canvas size(static) i.e. if its not inside the scrollview then each UIView objects inside the canvas are moving superbly and everything is working just fine with the following code.
If the canvas is inside the UIScrollView then the canvas can be scrollable right? Now inside the scrollview if I am panning the UIView objects on the canvas then those little UIView objects are not following the touch of the finger rather than its moving on another point when touch is moving on the canvas.
N.B. - Somehow I figured out that I need to disable the scrolling of the scrollview when any of the subviews are getting touch. For that thing I have implemented NSNotificationCenter to pass the signal to the parent viewController.
Here is the code.
This functions are defined inside the parent viewController class
func canvusScrollDisable(){
print("Scrolling Off")
self.scrollViewForCanvus.scrollEnabled = false
}
func canvusScrollEnable(){
print("Scrolling On")
self.scrollViewForCanvus.scrollEnabled = true
}
override func viewDidLoad() {
super.viewDidLoad()
notificationUpdate.addObserver(self, selector: "canvusScrollEnable", name: "EnableScroll", object: nil)
notificationUpdate.addObserver(self, selector: "canvusScrollDisable", name: "DisableScroll", object: nil)
}
This is the Subview class of the canvas
import UIKit
class ViewClassForUIView: UIView {
let notification: NSNotificationCenter = NSNotificationCenter.defaultCenter()
var lastLocation: CGPoint = CGPointMake(0, 0)
var lastOrigin = CGPoint()
var myFrame = CGRect()
var location = CGPoint(x: 0, y: 0)
var degreeOfThisView = CGFloat()
override init(frame: CGRect){
super.init(frame: frame)
let panRecognizer = UIPanGestureRecognizer(target: self, action: "detectPan:")
self.backgroundColor = addTableUpperViewBtnColor
self.multipleTouchEnabled = false
self.exclusiveTouch = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func detectPan(recognizer: UIPanGestureRecognizer){
let translation = recognizer.translationInView(self.superview!)
self.center = CGPointMake(lastLocation.x + translation.x, lastLocation.y + translation.y)
switch(recognizer.state){
case .Began:
break
case .Changed:
break
case .Ended:
notification.postNotificationName("EnableScroll", object: nil)
default: break
}
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
notification.postNotificationName("DisableScroll", object: nil)
self.superview?.bringSubviewToFront(self)
lastLocation = self.center
lastOrigin = self.frame.origin
let radians:Double = atan2( Double(self.transform.b), Double(self.transform.a))
self.degreeOfThisView = CGFloat(radians) * (CGFloat(180) / CGFloat(M_PI) )
if self.degreeOfThisView != 0.0{
self.transform = CGAffineTransformIdentity
self.lastOrigin = self.frame.origin
self.transform = CGAffineTransformMakeRotation(CGFloat(M_PI_4))
}
myFrame = self.frame
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
notification.postNotificationName("EnableScroll", object: nil)
}
}
Now the scrollView is disabling its scroll perfectly whenever one of the UIView object is receiving touch over the canvas which is inside the scrollview but sometimes those UIView objects are not properly following the touch location over the canvas/screen.
I am using Swift 2.1 with Xcode 7 but anyone can tell me the missing things of mine or the solution using Objective-c/Swift?
Where do you set the lastLocation? I think it would be better for you to use locationInView and compute the translation by yourself. Then save the lastLocation on every event that triggers the method.
Also you might want to handle the Cancel state as well to turn the scrolling back on.
All of this does seem a bit messy though. The notifications are maybe not the best idea in your case nor is putting the gesture recognizers on the subviews. I think you should have a view which handles all those small views; it should also have a gesture recognizer that can simultaneously interact with other recognizers. When the gesture is recognized it should check if any of the subviews are hit and decide if any of them should be moved. If it should be moved then use the delegate to report that the scrolling must be disabled. If not then cancel the recognizer (disable+enable does that). Also in most cases where you put something movable on the scrollview you usually want a long press gesture recognizer and not a pan gesture recognizer. Simply use that one and set some very small minimum press duration. Note that this gesture works exactly the same as the pan gesture but can have a small delay to be detected. It is very useful in these kind of situations.
Update (The architecture):
The hierarchy should be:
View controller -> Scrollview -> Canvas view -> Small views
The canvas view should contain the gesture recognizer that controls the small views. When the gesture begins you should check if any of the views are hit by its location by simply iterating through the subviews and check if their frame contains a point. If so it should start moving the hit small view and it should notify its delegate that it has began moving it. If not it should cancel the gesture recognizer.
As the canvas view has a custom delegate it is the view controller that should implement its protocol and assign itself to the canvas view as a delegate. When the canvas view reports that the view dragging has begin it should disable the scrollview scrolling. When the canvas view reports it has stopped moving the views it should reenable the scrolling of the scroll view.
Create this type of view hierarchy
Create a custom protocol of the canvas view which includes "did begin dragging" and "did end dragging"
When the view controller becomes active assign self as a delegate to the canvas view. Implement the 2 methods to enable or disable the scrolling of the scroll view.
The canvas view should add a gesture recognizer to itself and should contain an array of all the small movable subviews. The recognizer should be able to interact with other recognizers simultaneously which is done through its delegate.
The Canvas gesture recognizer target should on begin check if any of the small views are hit and save it as a property, it should also save the current position of the gesture. When the gesture changes it should move the grabbed view depending on the last and current gesture location and re-save the current location to the property. When the gesture ends it should clear the currently dragged view. On begin and end it should call the delegate to notify the change of the state.
Disable or enable the scrolling in the view controller depending on the canvas view reporting to delegate.
I think this should be all.
Good Evening!
I am trying to zoom UIScrollView, more specific all sub-views placed inside.
As far I understand, I need to implement UIScrollViewDelegate method:
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return subView // <--- subview inside my scroll view
}
In my test projects zoom simply won't work until I do this. But, with this technique I can only zoom (1) single view. My scroll-view have dozens of sub-views which I want to be zoomed too. I can solve this by placing one UIView inside UIScrollView and then other sub-views inside this container view, but I will like to ask is there better solution?
I have used both automatic mechanism of pinch-to-zoom of scrollview and later played with my own pinch gesture recogniser:
override func viewDidLoad() {
super.viewDidLoad()
...
let pinchGesture = UIPinchGestureRecognizer(target: self, action: "pinchHandler:")
scrollView.addGestureRecognizer(pinchGesture)
.
.
.
func pinchHandler(recognizer: UIPinchGestureRecognizer)
{
if recognizer.state == UIGestureRecognizerState.Began
{
if recognizer.scale < 1.0 {
scrollView.setZoomScale(1, animated: true)
} else {
scrollView.setZoomScale(2, animated: true)
}
}
}
In both cases, only 1 view is zoomed (one passed as result of viewForZoomingInScrollView function)
Thank you.