gpt4 book ai didi

ios - 堆栈 View ScaleAspectFit 蒙版在 Swift 中调整大小

转载 作者:行者123 更新时间:2023-11-29 01:07:35 27 4
gpt4 key购买 nike

我正在堆栈 View 中遮盖图像,由于某些奇怪的原因,我的 mask 未与图像正确对齐/调整大小。

这里演示了当我在堆栈 View 中动态添加该图像的实例时所发生的情况,同时每个 subview 在其边界和间距内调整大小。

Two Circles Five Circles

如您所见,蒙版保留图像的原始大小,而不是调整大小的版本。我尝试了许多不同的宽度和高度变化,包括 bounds.width、layer.frame.width、frame.width、frame.origin.x 等,但没有运气。

Swift 2 中的当前代码:

let testPicture:UIImageView = UIImageView(image: UIImage(named: "myPicture"))
testPicture.contentMode = .ScaleAspectFit
testPicture.layer.borderWidth = 1
testPicture.clipsToBounds = true
testPicture.layer.masksToBounds = true
view.layer.addSublayer(shapeLayer)

var width = testPicture.layer.frame.width
var height = testPicture.layer.frame.height
let center = CGPointMake(width/2, height/2)
let radius = CGFloat(CGFloat(width) / 2)


// Mask
let yourCarefullyDrawnPath = UIBezierPath()
yourCarefullyDrawnPath.moveToPoint(center)
yourCarefullyDrawnPath.addArcWithCenter(center,
radius: radius,
startAngle: 0,
endAngle: CGFloat( (0.80*360.0) * M_PI / 180.0),
clockwise: true)
yourCarefullyDrawnPath.closePath()

let maskPie = CAShapeLayer()
maskPie.frame = testPicture.layer.bounds
testPicture.clipsToBounds = true
testPicture.layer.masksToBounds = true
maskPie.path = yourCarefullyDrawnPath.CGPath
testPicture.layer.mask = maskPie


// Add Into Stackview
self.myStackView.addArrangedSubview(testPicture)
self.myStackView.layoutIfNeeded()

我怀疑我获取了错误的宽度和高度以生成中心和半径变量,尽管在尝试了我能找到的所有不同的宽度和高度之后,我仍然无法获得正确的尺寸。 :-(

最佳答案

您需要获取图像在 ImageView 中占据的帧。

不幸的是,UIImageView 没有为此提供原生支持,但是您可以相当简单地计算它。我已经created a function这将采用给定的外部矩形和给定的内部矩形,并在内部矩形经过方面适合位于外部矩形内后返回内部矩形。

该函数的 Swift 版本看起来像这样:

func aspectFitRect(outerRect outerRect:CGRect, innerRect:CGRect) -> CGRect {

let innerRectRatio = innerRect.size.width/innerRect.size.height; // inner rect ratio
let outerRectRatio = outerRect.size.width/outerRect.size.height; // outer rect ratio

// calculate scaling ratio based on the width:height ratio of the rects.
let ratio = (innerRectRatio > outerRectRatio) ? outerRect.size.width/innerRect.size.width:outerRect.size.height/innerRect.size.height;

// The x-offset of the inner rect as it gets centered
let xOffset = (outerRect.size.width-(innerRect.size.width*ratio))*0.5;

// The y-offset of the inner rect as it gets centered
let yOffset = (outerRect.size.height-(innerRect.size.height*ratio))*0.5;

// aspect fitted origin and size
let innerRectOrigin = CGPoint(x: xOffset+outerRect.origin.x, y: yOffset+outerRect.origin.y);
let innerRectSize = CGSize(width: innerRect.size.width*ratio, height: innerRect.size.height*ratio);

return CGRect(origin: innerRectOrigin, size: innerRectSize);
}

您需要做的另一件事是继承 UIImageView 并覆盖 layoutSubviews 方法。这是因为当您将 ImageView 添加到 UIStackView 时 - 您不再控制 ImageView 的框架。因此,通过重写 layoutSubviews,只要堆栈 View 改变 View 的框架,您就可以更新掩码。

像这样应该可以达到预期的结果:

class MaskedImageView: UIImageView {

let maskLayer = CAShapeLayer()

override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}

override init(image: UIImage?) {
super.init(image: image)
commonInit()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}

private func commonInit() {

// configure your common image view properties here
contentMode = .ScaleAspectFit
clipsToBounds = true

// mask your image layer
layer.mask = maskLayer
}

override func layoutSubviews() {

guard let img = image else { // if there's no image - skip updating the mask.
return
}

// the frame that the image itself will occupy in the image view as it gets aspect fitted
let imageRect = aspectFitRect(outerRect: bounds, innerRect: CGRect(origin: CGPointZero, size: img.size))

// update mask frame
maskLayer.frame = imageRect

// half the image's on-screen width or height, whichever is smallest
let radius = min(imageRect.size.width, imageRect.size.height)*0.5

// the center of the image rect
let center = CGPoint(x: imageRect.size.width*0.5, y: imageRect.size.height*0.5)

// your custom masking path
let path = UIBezierPath()
path.moveToPoint(center)
path.addArcWithCenter(center, radius: radius, startAngle: 0, endAngle: CGFloat(M_PI*2.0*0.8), clockwise: true)
path.closePath()

// update mask layer path
maskLayer.path = path.CGPath
}
}

然后您可以从您的 View Controller 创建您的 ImageView ,并像往常一样将它们添加到您的堆栈 View 。

let stackView = UIStackView()

override func viewDidLoad() {
super.viewDidLoad()

stackView.frame = view.bounds
stackView.distribution = .FillProportionally
stackView.spacing = 10
view.addSubview(stackView)

for _ in 0..<5 {
let imageView = MaskedImageView(image:UIImage(named:"foo.jpg"))
stackView.addArrangedSubview(imageView)
stackView.layoutIfNeeded()
}

}

给我以下结果:

enter image description here


不相关的漫谈...

刚刚在您的代码中注意到您正在这样做:

testPicture.clipsToBounds = true
testPicture.layer.masksToBounds = true

它们做同样的事情。

UIView 只不过是底层 CALayer 的包装器。然而,为了方便起见,一些 CALayer 属性也有一个 UIView 等效项。 UIView 的所有等效操作都是在设置时将消息转发到 CALayer,并在 CALayer 设置为 '得到了。

clipsToBoundsmasksToBounds 是其中一对(尽管令人恼火的是它们不共享相同的名称)。

尝试执行以下操作:

view.layer.masksToBounds = true
print(view.clipsToBounds) // output: true
view.layer.masksToBounds = false
print(view.clipsToBounds) // output: false

view.clipsToBounds = true
print(view.layer.masksToBounds) // output: true
view.clipsToBounds = false
print(view.layer.masksToBounds) // output: false

鉴于您正在使用 UIViewclipToBounds 通常是首选更新属性。

关于ios - 堆栈 View ScaleAspectFit 蒙版在 Swift 中调整大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36162215/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com