ios - objective - uiview image



如何在“外觀填充”模式下將UIImageView裁剪為新的UIImage? (1)

讓我們將問題分為兩個部分:

  1. 給定一個UIImageView的大小及其UIImage的大小, 如果UIImageView的內容模式為Aspect Fill, 那麼UIImage中適合UIImageView的部分是什麼? 實際上,我們需要 裁剪原始圖像 以匹配UIImageView實際顯示的圖像。

  2. 給定UIImageView中的任意矩形, 裁剪圖像的 哪一部分(在第1部分中派生)對應?

第一部分是有趣的部分,所以讓我們嘗試一下。 (然後第二部分將變得無關緊要。)

這是我將使用的原始圖像:

https://static1.squarespace.com/static/54e8ba93e4b07c3f655b452e/t/56c2a04520c64707756f4267/1455596221531/

該圖像是1000x611。 這是按比例縮小的樣子(但請記住,我將始終使用 原始 圖像):

但是,我的圖像視圖將為139x182,並設置為“縱橫比填充”。 當顯示圖像時,它看起來像這樣:

我們要解決的問題是:如果我的圖像視圖設置為“縱橫比填充”,則在圖像視圖中顯示 原始 圖像的哪一部分?

開始了。 假設 iv 是圖像視圖:

let imsize = iv.image!.size
let ivsize = iv.bounds.size

var scale : CGFloat = ivsize.width / imsize.width
if imsize.height * scale < ivsize.height {
    scale = ivsize.height / imsize.height
}

let croppedImsize = CGSize(width:ivsize.width/scale, height:ivsize.height/scale)
let croppedImrect =
    CGRect(origin: CGPoint(x: (imsize.width-croppedImsize.width)/2.0,
                           y: (imsize.height-croppedImsize.height)/2.0),
           size: croppedImsize)

因此,現在我們解決了這個問題: croppedImrect 原始 圖像在圖像視圖中顯示的區域。 讓我們繼續使用我們的知識,方法是將圖像實際裁剪為與圖像視圖中顯示的圖像匹配的新圖像:

let r = UIGraphicsImageRenderer(size:croppedImsize)
let croppedIm = r.image { _ in
    iv.image!.draw(at: CGPoint(x:-croppedImrect.origin.x, y:-croppedImrect.origin.y))
}

結果是此圖像(忽略灰色邊框):

但是瞧瞧,這是正確的答案! 我已經從原始圖像中 準確 提取了圖像視圖內部描繪的區域。

因此,現在您擁有了所需的所有信息。 croppedIm 是實際顯示在圖像視圖的裁剪區域中的UIImage。 scale 是圖像視圖和該圖像之間的比例。 因此,您可以輕鬆解決您最初提出的問題! 給定施加在圖像視圖上的任何矩形,在圖像視圖的邊界坐標中,您只需應用比例尺(即,將其所有四個屬性 croppedIm scale 除以)-現在您具有與 croppedIm 相同的矩形。

(觀察到,我們實際上並不需要裁剪原始圖像來獲取 croppedIm ;實際上,知道 如何 進行裁剪就足夠 。重要的信息是 scalecroppedImRectorigin ;鑑於該信息,您可以將矩形應用於圖像視圖,對其進行縮放 並偏移 以獲取所需的原始圖像矩形。)

編輯 我添加了一些截屏視頻,只是為了表明我的方法可以作為概念證明:

編輯 還在這裡創建了一個可下載的示例項目:

https://github.com/mattneub/Programming-iOS-Book-Examples/blob/39cc800d18aa484d17c26ffcbab8bbe51c614573/bk2ch02p058cropImageView/Cropper/ViewController.swift

但是請注意,我不能保證URL會永遠持續下去,因此請閱讀上面的討論以了解所使用的方法。

我正在嘗試使用可放置在 UIImageView 任何位置的覆蓋 UIView 裁剪圖像視圖的子圖像。 我從類似的帖子中藉用了一個解決方案,以解決當UIImageView內容模式為“ Aspect Fit”時如何解決此問題。 提議的解決方案是:

  func computeCropRect(for sourceFrame : CGRect) -> CGRect {

        let widthScale = bounds.size.width / image!.size.width
        let heightScale = bounds.size.height / image!.size.height

        var x : CGFloat = 0
        var y : CGFloat = 0
        var width : CGFloat = 0
        var height : CGFloat = 0
        var offSet : CGFloat = 0

        if widthScale < heightScale {
            offSet = (bounds.size.height - (image!.size.height * widthScale))/2
            x = sourceFrame.origin.x / widthScale
            y = (sourceFrame.origin.y - offSet) / widthScale
            width = sourceFrame.size.width / widthScale
            height = sourceFrame.size.height / widthScale
        } else {
            offSet = (bounds.size.width - (image!.size.width * heightScale))/2
            x = (sourceFrame.origin.x - offSet) / heightScale
            y = sourceFrame.origin.y / heightScale
            width = sourceFrame.size.width / heightScale
            height = sourceFrame.size.height / heightScale
        }

        return CGRect(x: x, y: y, width: width, height: height)
    }

問題在於,當圖像視圖為長寬比填充時使用此解決方案會導致裁剪後的片段與覆蓋 UIView 放置位置不完全 UIView 。 我不太確定如何修改此代碼以適應“長寬比填充”或重新放置疊加層 UIView ,以使其與我要裁剪的部分按1:1對齊。

更新使用下面的馬特的答案解決

class ViewController: UIViewController {

    @IBOutlet weak var catImageView: UIImageView!
    private var cropView : CropView!

    override func viewDidLoad() {
        super.viewDidLoad()

        cropView = CropView(frame: CGRect(x: 0, y: 0, width: 45, height: 45))

        catImageView.image = UIImage(named: "cat")
        catImageView.clipsToBounds = true

        catImageView.layer.borderColor = UIColor.purple.cgColor
        catImageView.layer.borderWidth = 2.0
        catImageView.backgroundColor = UIColor.yellow

        catImageView.addSubview(cropView)

        let imageSize = catImageView.image!.size
        let imageViewSize = catImageView.bounds.size

        var scale : CGFloat = imageViewSize.width / imageSize.width

        if imageSize.height * scale < imageViewSize.height {
            scale = imageViewSize.height / imageSize.height
        }

        let croppedImageSize = CGSize(width: imageViewSize.width/scale, height: imageViewSize.height/scale)

        let croppedImrect =
            CGRect(origin: CGPoint(x: (imageSize.width-croppedImageSize.width)/2.0,
                                   y: (imageSize.height-croppedImageSize.height)/2.0),
                   size: croppedImageSize)

        let renderer = UIGraphicsImageRenderer(size:croppedImageSize)

        let _ = renderer.image { _ in
            catImageView.image!.draw(at: CGPoint(x:-croppedImrect.origin.x, y:-croppedImrect.origin.y))
        }
    }


    @IBAction func performCrop(_ sender: Any) {
        let cropFrame = catImageView.computeCropRect(for: cropView.frame)
        if let imageRef = catImageView.image?.cgImage?.cropping(to: cropFrame) {
            catImageView.image = UIImage(cgImage: imageRef)
        }
    }

    @IBAction func resetCrop(_ sender: Any) {
        catImageView.image = UIImage(named: "cat")
    }
}

最終結果





uiimageview