Ios UIImageView:如何在平移和平移之后捕获可见内容;快速移动
我有一个UIImageView,它由ImagePicker加载,带有一张照片(3k x 4k大小),使用AspectFit模式填充到视图中。然后,我用手势识别器平移和缩放图像,该识别器可以调整视图的变换比例和平移。这很有效,下面是代码:Ios UIImageView:如何在平移和平移之后捕获可见内容;快速移动,ios,swift,uiimageview,cgaffinetransform,Ios,Swift,Uiimageview,Cgaffinetransform,我有一个UIImageView,它由ImagePicker加载,带有一张照片(3k x 4k大小),使用AspectFit模式填充到视图中。然后,我用手势识别器平移和缩放图像,该识别器可以调整视图的变换比例和平移。这很有效,下面是代码: @objc private func startZooming(_ sender: UIPinchGestureRecognizer) { let scaleResult = sender.view?.transform.scaledBy(x: sender
@objc private func startZooming(_ sender: UIPinchGestureRecognizer) {
let scaleResult = sender.view?.transform.scaledBy(x: sender.scale, y: sender.scale)
guard let scale = scaleResult, scale.a > 1, scale.d > 1 else { return }
sender.view?.transform = scale
sender.scale = 1
}
@objc private func startPanning(_ sender: UIPanGestureRecognizer) {
let translate = sender.translation(in: mainImageView)
let center = mainImageView.center
mainImageView.center = CGPoint(x: center.x + translate.x, y: center.y + translate.y)
self.currImageTranslation.x += translate.x
self.currImageTranslation.y += translate.y
sender.setTranslation(CGPoint(x: 0, y: 0), in: mainImageView)
}
我的问题是,我需要转换产生的视觉结果作为CGImage输入视觉推理引擎。当我从UIImageView获取图像时:
let image = myUIImageView.image // I get the original untransformed image
当我尝试时:
UIGraphicsBeginImageContextWithOptions(image!.size, false, 0.0)
myImageView.layer.render(in: UIGraphicsGetCurrentContext()!)
let newImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
let rect = mainImageView.bounds
let scale = UIScreen.main.scale
var t = self.currImageTranslation
let tScale = mainImageView.transform.a
let zoomOffsetFactorX = ((t.x / rect.size.width) * tScale)
let zoomOffsetFactorY = ((t.y / rect.size.height) * tScale)
t.x = t.x - zoomOffsetFactorX
t.y = (t.y + 20.0) + zoomOffsetFactorY
// create a context
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
let context = UIGraphicsGetCurrentContext()!
let transform = mainImageView.transform
let imrect = CGRect(origin: t, size: rect.size)
context.concatenate(transform)
let tempImage = mainImageView.image
tempImage!.draw(in: imrect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
新图像也是原始图像(未转换)
当我尝试时:
UIGraphicsBeginImageContextWithOptions(image!.size, false, 0.0)
myImageView.layer.render(in: UIGraphicsGetCurrentContext()!)
let newImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
let rect = mainImageView.bounds
let scale = UIScreen.main.scale
var t = self.currImageTranslation
let tScale = mainImageView.transform.a
let zoomOffsetFactorX = ((t.x / rect.size.width) * tScale)
let zoomOffsetFactorY = ((t.y / rect.size.height) * tScale)
t.x = t.x - zoomOffsetFactorX
t.y = (t.y + 20.0) + zoomOffsetFactorY
// create a context
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
let context = UIGraphicsGetCurrentContext()!
let transform = mainImageView.transform
let imrect = CGRect(origin: t, size: rect.size)
context.concatenate(transform)
let tempImage = mainImageView.image
tempImage!.draw(in: imrect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
新图像将正确转换,但仅在比例=1.0时,缩放或不缩放将创建分数图像,即剪裁到零原点
我想生成一个用户在该ImageView屏幕上看到的UIImage。有人能帮忙吗
在@DonMag慷慨的建议下,我尝试了以下方法:
func enableZoom() {
let pinchGesture = UIPinchGestureRecognizer(target: self,
action: #selector(startZooming(_:)))
let panGesture = UIPanGestureRecognizer(target: self,
action: #selector(startPanning(_:)))
containerView.isUserInteractionEnabled = true
containerView.addGestureRecognizer(pinchGesture)
containerView.addGestureRecognizer(panGesture)
}
@objc private func startZooming(_ sender: UIPinchGestureRecognizer) {
let scaleResult = sender.view?.transform.scaledBy(x: sender.scale, y: sender.scale)
guard let scale = scaleResult, scale.a > 1, scale.d > 1 else { return }
sender.view?.transform = scale
newImage()
sender.scale = 1
}
@objc private func startPanning(_ sender: UIPanGestureRecognizer) {
let translate = sender.translation(in: mainImageView)
let center = mainImageView.center
mainImageView.center = CGPoint(x: center.x + translate.x, y: center.y + translate.y)
self.currImageTranslation.x += translate.x
self.currImageTranslation.y += translate.y
sender.setTranslation(CGPoint(x: 0, y: 0), in: mainImageView)
newImage()
}
//MARK:- create cropped/panned image and infer
func newImage() -> Void {
let format = UIGraphicsImageRendererFormat()
// we want a 1:1 points-to-pixels output
format.scale = 1
let renderer = UIGraphicsImageRenderer(size: containerView.bounds.size, format: format)
let image = renderer.image { ctx in
containerView.drawHierarchy(in: containerView.bounds, afterScreenUpdates: true)
}
self.currCGImage = image.cgImage
//repeatInference() // runs only once every 0.3 s
return
}
现在,“新形象”正确地反映了泛感觉创造的翻译,但似乎忽略了尺度上的细微变化。drawHierarchy似乎忽略了UIView中强加的转换。我哪里出错了?我不完全确定您在用转换代码做什么,但这可能对您有用——而且更简单 如果我理解正确,您有一个带有
.aspectFit
的UIImageView
,它位于UIView
的“容器”中,您可以对图像视图应用缩放和平移变换:
let format = UIGraphicsImageRendererFormat()
// we want a 1:1 points-to-pixels output
format.scale = 1
let renderer = UIGraphicsImageRenderer(size: containerView.bounds.size, format: format)
let image = renderer.image { ctx in
containerView.drawHierarchy(in: containerView.bounds, afterScreenUpdates: true)
}
下面是一个完整的示例实现:
class TransImageViewController: UIViewController {
let containerView = UIView()
let imageView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
guard let img = UIImage(named: "bkg_2400x1600") else {
fatalError("Could not load the image!!!")
}
let stack = UIStackView()
stack.spacing = 20
stack.distribution = .fillEqually
let b1 = UIButton()
let b2 = UIButton()
[b1, b2].forEach { b in
b.backgroundColor = .red
b.setTitleColor(.white, for: .normal)
b.setTitleColor(.gray, for: .highlighted)
}
b1.setTitle("Transform", for: [])
b2.setTitle("Capture", for: [])
stack.addArrangedSubview(b1)
stack.addArrangedSubview(b2)
[stack, containerView, imageView].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
}
view.addSubview(stack)
view.addSubview(containerView)
containerView.addSubview(imageView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// buttons stack at top
stack.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
stack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
stack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
// container 400x500 centered
containerView.widthAnchor.constraint(equalToConstant: 400),
containerView.heightAnchor.constraint(equalToConstant: 500),
containerView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
containerView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
// imageView constrained all 4 sides to container
imageView.topAnchor.constraint(equalTo: containerView.topAnchor),
imageView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
imageView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
imageView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
])
containerView.clipsToBounds = true
imageView.contentMode = .scaleAspectFit
imageView.image = img
view.backgroundColor = .blue
containerView.backgroundColor = .yellow
imageView.backgroundColor = .orange
b1.addTarget(self, action: #selector(self.doTransform), for: .touchUpInside)
b2.addTarget(self, action: #selector(self.grabImage), for: .touchUpInside)
}
@objc func doTransform() -> Void {
var t = CGAffineTransform.identity
t = t.scaledBy(x: 4.0, y: 4.0)
t = t.translatedBy(x: -120, y: 40)
imageView.transform = t
}
@objc func grabImage() -> Void {
let format = UIGraphicsImageRendererFormat()
// we want a 1:1 points-to-pixels output
format.scale = 1
let renderer = UIGraphicsImageRenderer(size: containerView.bounds.size, format: format)
let image = renderer.image { ctx in
containerView.drawHierarchy(in: containerView.bounds, afterScreenUpdates: true)
}
// do what you want with the resulting image
print("Resulting image size:", image.size)
}
}
将此图像用作我的“bkg_2400x1600”图像资源:
上面的代码是这样开始的(我的“容器”视图是400x500分):
点击“转换”按钮适用于。缩放比(x:4.0,y:4.0)
和。平移比(x:-120,y:40)
:
然后点击“捕获”按钮,我可以看到这个400x500像素的图像:
@DonMag让我走上了正确的道路。然而,为了在抓取的图像中获得正确的缩放,我需要将scaleBy变换应用于UIImageView,而不是containerview。此外,我还需要将格式比例设置为pinchGesture中的当前比例因子。以下是工作版本:
func enableZoom() {
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(startZooming(_:)))
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(startPanning(_:)))
containerView.isUserInteractionEnabled = true
containerView.addGestureRecognizer(pinchGesture)
containerView.addGestureRecognizer(panGesture)
}
@objc private func startZooming(_ sender: UIPinchGestureRecognizer) {
// let scaleResult = sender.view?.transform.scaledBy(x: sender.scale, y: sender.scale)
// guard let scale = scaleResult, scale.a > 1, scale.d > 1 else { return }
// sender.view?.transform = scale
let scaleResult = mainImageView?.transform.scaledBy(x: sender.scale, y: sender.scale)
guard let scale = scaleResult, scale.a > 1, scale.d > 1 else { return }
mainImageView.transform = scale
currImageScale = sender.scale
newImage()
sender.scale = 1
}
@objc private func startPanning(_ sender: UIPanGestureRecognizer) {
let translate = sender.translation(in: mainImageView)
let center = mainImageView.center
mainImageView.center = CGPoint(x: center.x + translate.x, y: center.y + translate.y)
self.currImageTranslation.x += translate.x
self.currImageTranslation.y += translate.y
sender.setTranslation(CGPoint(x: 0, y: 0), in: mainImageView)
newImage()
}
//MARK:- create cropped/panned image and infer
func newImage() -> Void {
let format = UIGraphicsImageRendererFormat()
// we want a 1:1 points-to-pixels output
format.scale = currImageScale
let renderer = UIGraphicsImageRenderer(size: containerView.bounds.size, format: format)
let image = renderer.image { ctx in
containerView.drawHierarchy(in: containerView.bounds, afterScreenUpdates: true)
}
self.currCGImage = image.cgImage
repeatInference() // runs only once every 0.3 s
return
}
为了澄清。。。您想要原始图像的大小吗?还是屏幕尺寸?例如,如果您的scrollview在@2x设备上是200x200分,并且您的zoom显示的是3k x 4k图像的1600x1600部分。。。是否希望结果是1600x1600矩形缩小为400x400像素图像?还是要从原始图像中剪裁1600x1600像素的矩形?感谢您的回复!为了尝试澄清,我没有使用卷轴和播放内容大小。只需在图像视图中对UIImage进行变换/转换。uiimageview在plus手机上约为440x500,这是我想要退回的尺寸。本质上类似于视图的位图。推理机将根据需要进行缩放/裁剪。这个答案有部分答案。肯定没有一个简短的答案。看起来很完美!将在几个小时内进行测试,并将很快标记为正确。非常感谢!我修改了ViewController,将目标图像视图放置在容器UIView中,并将userInteractions/GestureRecognitors设置为容器UIView。抓取的图像现在在帧中按预期移动,但是,收缩手势(即缩放或放大容器)在屏幕上确实会改变,但不会改变抓取的图像。抓取的图像不会放大。我正在修改OP以包含当前的相关代码。