我正在创建一个图像拼贴应用程序。我将有多个 UIScrollView 的。 ScrollView 将具有自定义形状的边界,用户将能够动态更改它们相交的形状的角。 ScrollView 有 UIImageView 作为 subview 。
ScrollView 是其他 UIView 的 subview 。我申请了 CAShapeLayer 掩盖这些 UIView 的。这样我就可以毫无问题地屏蔽 ScrollView 。
但问题是,我只能滚动添加的最后一个 ScrollView 的内容。此外,我可以平移和缩放超出蒙版的边界。当我触摸作为蒙版的多边形边界时,我应该只能平移或缩放。
我试过;
scrollView.clipsToBounds = true
scrollView.layer.masksToBounds = true
但结果是一样的。
不幸的是,我无法发布屏幕截图,但是,这是我用来为 UIView 创建蒙版的代码:
func createMask(v: UIView, viewsToMask: [UIView], anchorPoint: CGPoint)
{
let frame = v.bounds
var shapeLayer = [CAShapeLayer]()
var path = [CGMutablePathRef]()
for i in 0...3 {
path.append(CGPathCreateMutable())
shapeLayer.append(CAShapeLayer())
}
//define frame constants
let center = CGPointMake(frame.origin.x + frame.size.width / 2, frame.origin.y + frame.size.height / 2)
let bottomLeft = CGPointMake(frame.origin.x, frame.origin.y + frame.size.height)
let bottomRight = CGPointMake(frame.origin.x + frame.size.width, frame.origin.y + frame.size.height)
switch frameType {
case 1:
// First view for Frame Type 1
CGPathMoveToPoint(path[0], nil, 0, 0)
CGPathAddLineToPoint(path[0], nil, bottomLeft.x, bottomLeft.y)
CGPathAddLineToPoint(path[0], nil, anchorPoint.x, bottomLeft.y)
CGPathAddLineToPoint(path[0], nil, anchorPoint.x, anchorPoint.y)
CGPathCloseSubpath(path[0])
// Second view for Frame Type 1
CGPathMoveToPoint(path[1], nil, anchorPoint.x, anchorPoint.y)
CGPathAddLineToPoint(path[1], nil, anchorPoint.x, bottomLeft.y)
CGPathAddLineToPoint(path[1], nil, bottomRight.x, bottomRight.y)
CGPathAddLineToPoint(path[1], nil, bottomRight.x, anchorPoint.y)
CGPathCloseSubpath(path[1])
// Third view for Frame Type 1
CGPathMoveToPoint(path[2], nil, 0, 0)
CGPathAddLineToPoint(path[2], nil, anchorPoint.x, anchorPoint.y)
CGPathAddLineToPoint(path[2], nil, bottomRight.x, anchorPoint.y)
CGPathAddLineToPoint(path[2], nil, bottomRight.x, 0)
CGPathCloseSubpath(path[2])
default:
break
}
for (key, view) in enumerate(viewsToMask) {
shapeLayer[key].path = path[key]
view.layer.mask = shapeLayer[key]
}
}
那么,如何使 ScrollView 的行为方式使它们仅在其相应的蒙版边界内发生触摸时滚动或缩放内容?
编辑:
根据这个问题的答案:UIView's masked-off area still touchable? mask 只会修改您可以看到的内容,而不是您可以触摸的区域。所以我继承了 UIScrollView 并试图覆盖 hitTest:withEvent: 像这样的方法,
protocol CoolScrollViewDelegate: class {
var scrollViewPaths: [CGMutablePathRef] { get set }
}
class CoolScrollView: UIScrollView
{
weak var coolDelegate: CoolScrollViewDelegate?
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView?
{
if CGPathContainsPoint(coolDelegate?.scrollViewPaths[tag], nil, point, true) {
return self
} else {
return nil
}
}
}
但是通过这个实现,我只能在放大时检查最后一个 ScrollView 和路径边界的变化。例如,如果我放大图像hitTest:withEvent: 方法返回零。
Best Answer-推荐答案 strong>
我会同意@Kendel 在评论中的观点 - 首先它可能是创建 UIScrollView 的一种更简单的方法知道如何用特定形状掩盖自己的子类。将形状逻辑保留在 ScrollView 子类中将使事情保持整洁,并允许您轻松地将触摸限制在形状内(我稍后会谈到)。
从你的描述中很难确切地说出你的形状 View 应该如何表现,但作为一个简短的例子,你的 ShapedScrollView 可能看起来像这样:
import UIKit
class ShapedScrollView: UIScrollView {
// MARK: Types
enum Shape {
case First // Choose a better name!
}
// MARK: Properties
private let shapeLayer = CAShapeLayer()
var shape: Shape = .First {
didSet { setNeedsLayout() }
}
// MARK: Initializers
init(frame: CGRect, shape: Shape = .First) {
self.shape = shape
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// MARK: Layout
override func layoutSubviews() {
super.layoutSubviews()
updateShape()
}
// MARK: Updating the Shape
private func updateShape() {
// Disable core animation actions to prevent changes to the shape layer animating implicitly
CATransaction.begin()
CATransaction.setDisableActions(true)
if bounds.size != shapeLayer.bounds.size {
// Bounds size has changed, completely update the shape
shapeLayer.frame = CGRect(origin: contentOffset, size: bounds.size)
shapeLayer.path = pathForShape(shape).CGPath
layer.mask = shapeLayer
} else {
// Bounds size has NOT changed, just update origin of shape path to
// match content offset - makes it appear stationary as we scroll
var shapeFrame = shapeLayer.frame
shapeFrame.origin = contentOffset
shapeLayer.frame = shapeFrame
}
CATransaction.commit()
}
private func pathForShape(shape: Shape) -> UIBezierPath {
let path = UIBezierPath()
switch shape {
case .First:
// Build the shape path, whatever that might be...
// path.moveToPoint(...)
// ...
}
return path
}
}
因此,使触摸仅在指定的形状内起作用是容易的部分。我们已经有一个形状层的引用,它描述了我们想要限制触摸的形状。 UIView 提供了一种有用的 HitTest 方法,可让您指定是否应将特定点视为该 View 的“内部”:pointInside(_:withEvent .只需将以下覆盖添加到 ShapedScrollView :
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
return CGPathContainsPoint(shapeLayer.path, nil, layer.convertPoint(point, toLayer: shapeLayer), false)
}
这只是说:“如果 point (转换为形状图层的坐标系)在形状的内部 path ,则将其视为在 View 内;否则将其视为在 View 外。”
如果屏蔽自身的 ScrollView 不合适,您仍然可以通过使用 ShapedScrollContainerView: UIView 来采用此技术。与 scrollView 属性(property)。然后,如上将形状 mask 应用到容器上,并再次使用 pointInside(_:withEvent 测试它是否应该响应特定的接触点。
关于swift - 仅当触摸位于自定义形状内时,如何使 UIScrollView 可滚动?,我们在Stack Overflow上找到一个类似的问题:
https://stackoverflow.com/questions/31899016/
|