• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

swift - 仅当触摸位于自定义形状内时,如何使 UIScrollView 可滚动?

[复制链接]
菜鸟教程小白 发表于 2022-10-22 09:02:38 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题

我正在创建一个图像拼贴应用程序。我将有多个 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-推荐答案


我会同意@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/

回复

使用道具 举报

懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关注0

粉丝2

帖子830918

发布主题
阅读排行 更多
广告位

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap