为什么CGPath包含测试有时会在路径外的某个点返回true?

时间:2018-10-13 21:12:56

标签: ios swift core-graphics hittest cgpath

我有一个UIView的子类,其中包含一堆使用CGPAth绘制的图形。我需要知道何时触摸触及这些路径之一,以及哪个路径。

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    for t in touches {
        for p in myDrawnPaths {
           if(p.contains(t.location(in: self)) {
              doStuffWith(p)
           }
        }
    }
}

有时,这段代码会导致doStuffWith()在多条路径上执行,包括远离命中位置的路径。我做了一些检查,发现应该受到影响的路径确实有些奇怪:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    for t in touches {
        for p in myDrawnPaths {
           print(p.contains(t.location(in: self))) //true
           print(p.boundingBox.contains(t.location(in: self))) //false!
        }
    }
}

嗯?边界框不应该包含整个路径,这意味着路径内的点是否一定在边界框内?

仅在此类测试中出现问题-当我使用CoreGraphics API绘制相同的CGPath并为其设置动画时,所有显示都是正确的。

更新

我试图在Playgrounds中通过简单的实验路径重现该问题,但是无法做到,所以我不得不从我的实际应用程序中提取这个问题。

(75.0, 264.0) is in 
Path 0x600003643960:
  moveto (252.02, 287.067)
    lineto (259.489, 286.324)
    lineto (259.567, 286.754)
    lineto (263.438, 286.167)
    lineto (268.131, 285.503)
    lineto (268.17, 285.268)
    lineto (268.248, 284.681)
    lineto (269.069, 283.235)
    lineto (269.851, 282.57)
    lineto (269.773, 280.576)
    lineto (270.399, 279.95)
    lineto (270.829, 279.833)
    lineto (270.907, 278.425)
    lineto (271.494, 277.252)
    lineto (271.924, 277.486)
    lineto (272.002, 277.721)
    lineto (272.315, 277.799)
    lineto (273.058, 277.408)
    lineto (272.901, 273.85)
    lineto (271.65, 270.643)
    lineto (270.751, 267.085)
    lineto (269.812, 265.834)
    lineto (268.796, 265.13)
    lineto (268.17, 265.56)
    lineto (266.645, 266.264)
    lineto (265.902, 268.219)
    lineto (264.846, 269.666)
    lineto (264.416, 269.9)
    lineto (263.83, 269.666)
    curveto (263.83, 269.666) (262.813, 269.079) (262.891, 268.845)
    curveto (262.969, 268.61) (263.087, 266.889) (263.087, 266.889)
    lineto (264.416, 266.381)
    lineto (264.729, 265.052)
    lineto (264.964, 264.035)
    lineto (265.902, 263.409)
    lineto (265.785, 259.499)
    lineto (265.159, 258.6)
    lineto (264.651, 258.287)
    lineto (264.338, 257.466)
    lineto (264.651, 257.153)
    lineto (265.276, 257.27)
    lineto (265.355, 256.644)
    lineto (264.338, 255.784)
    lineto (263.83, 254.767)
    lineto (262.813, 254.767)
    lineto (261.053, 254.181)
    lineto (258.903, 252.851)
    lineto (257.847, 252.851)
    lineto (257.612, 253.086)
    lineto (257.221, 252.891)
    lineto (256.009, 251.991)
    lineto (254.875, 252.695)
    lineto (253.741, 253.594)
    lineto (253.858, 255.002)
    lineto (254.249, 255.119)
    lineto (255.07, 255.315)
    lineto (255.266, 255.628)
    lineto (254.249, 255.941)
    lineto (253.233, 256.058)
    lineto (252.646, 256.762)
    lineto (252.529, 257.583)
    lineto (252.646, 258.209)
    lineto (252.763, 260.359)
    lineto (251.356, 261.18)
    lineto (251.121, 261.102)
    lineto (251.121, 259.46)
    lineto (251.629, 258.521)
    lineto (251.864, 257.583)
    lineto (251.551, 257.27)
    lineto (250.808, 257.583)
    lineto (250.417, 259.225)
    lineto (249.361, 259.655)
    lineto (248.657, 260.398)
    lineto (248.579, 260.789)
    lineto (248.814, 261.102)
    lineto (248.579, 262.119)
    lineto (247.68, 262.314)
    lineto (247.68, 262.745)
    lineto (247.993, 263.683)
    lineto (247.563, 266.068)
    lineto (246.937, 267.632)
    lineto (247.172, 269.47)
    lineto (247.367, 269.9)
    lineto (247.054, 270.839)
    lineto (246.937, 271.152)
    lineto (246.82, 272.208)
    lineto (248.227, 274.554)
    lineto (249.361, 277.095)
    lineto (249.948, 278.972)
    lineto (249.635, 280.81)
    lineto (249.244, 283.156)
    lineto (248.306, 285.19)
    lineto (248.188, 286.246)
    lineto (246.937, 287.458)
    closepath
  moveto (233.916, 259.147)
  moveto (233.407, 258.717)
  moveto (232.703, 254.65)
  moveto (231.257, 254.142)
  moveto (230.592, 253.242)
  moveto (225.665, 252.148)
  moveto (224.57, 251.717)
  moveto (221.403, 250.857)
  moveto (218.352, 250.466)
  moveto (216.827, 248.394)
  moveto (217.101, 248.198)
  moveto (218.157, 247.885)
  moveto (219.565, 246.986)
    lineto (219.565, 246.595)
    lineto (219.799, 246.36)
    lineto (222.145, 245.969)
    lineto (223.084, 245.226)
    lineto (224.804, 244.405)
    lineto (224.883, 243.897)
    lineto (225.626, 242.763)
    lineto (226.33, 242.45)
    lineto (226.838, 241.746)
    lineto (227.737, 240.847)
    lineto (229.458, 239.908)
    lineto (231.296, 239.713)
    lineto (231.726, 240.143)
    lineto (231.608, 240.534)
    lineto (230.162, 240.925)
    lineto (229.575, 242.137)
    lineto (228.676, 242.45)
    lineto (228.48, 243.388)
    lineto (227.542, 244.64)
    lineto (227.424, 245.656)
    lineto (227.737, 245.852)
    lineto (228.128, 245.422)
    lineto (229.536, 244.288)
    lineto (230.044, 244.796)
    lineto (230.944, 244.796)
    lineto (232.195, 245.187)
    lineto (232.782, 245.617)
    lineto (233.368, 246.83)
    lineto (234.424, 247.885)
    lineto (235.949, 247.807)
    lineto (236.535, 247.416)
    lineto (237.161, 247.924)
    lineto (237.787, 248.12)
    lineto (238.295, 247.807)
    lineto (238.725, 247.807)
    lineto (239.351, 247.416)
    lineto (240.915, 246.008)
    lineto (242.245, 245.578)
    lineto (244.825, 245.461)
    lineto (246.585, 244.718)
    lineto (247.602, 244.21)
    lineto (248.188, 244.288)
    lineto (248.188, 246.517)
    lineto (248.384, 246.634)
    lineto (249.518, 246.947)
    lineto (250.261, 246.751)
    lineto (252.646, 246.126)
    lineto (253.076, 245.696)
    lineto (253.663, 245.891)
    lineto (253.663, 248.628)
    lineto (254.914, 249.84)
    lineto (255.422, 250.075)
    lineto (255.931, 250.466)
    lineto (255.422, 250.583)
    lineto (255.109, 250.466)
    lineto (253.663, 250.271)
    lineto (252.842, 250.505)
    lineto (251.942, 250.427)
    lineto (250.691, 251.014)
    lineto (249.987, 251.014)
    lineto (247.719, 250.505)
    lineto (245.686, 250.583)
    lineto (244.943, 251.6)
    lineto (242.205, 251.835)
    lineto (241.267, 252.148)
    lineto (240.837, 253.36)
    lineto (240.328, 253.79)
    lineto (240.133, 253.712)
    lineto (239.546, 253.086)
    lineto (237.787, 254.025)
    lineto (237.552, 254.025)
    lineto (237.122, 253.399)
    lineto (236.809, 253.477)
    lineto (236.066, 255.198)
    lineto (235.675, 256.762)
    lineto (234.424, 259.46)
    closepath
  moveto (222.849, 237.367)
  moveto (223.553, 236.545)
  moveto (224.413, 236.233)
  moveto (226.525, 234.708)
  moveto (227.424, 234.473)
  moveto (227.62, 234.668)
  moveto (225.626, 236.663)
  moveto (224.335, 237.406)
  moveto (223.514, 237.758)
    closepath
  moveto (257.221, 250.31)
  moveto (257.456, 251.287)
  moveto (258.707, 251.365)
  moveto (259.215, 250.896)
    curveto (259.215, 250.896) (259.176, 250.31) (259.059, 250.271)
    curveto (258.942, 250.192) (258.433, 249.528) (258.433, 249.528)
    lineto (257.573, 249.606)
    lineto (256.947, 249.684)
    lineto (256.83, 250.114)

我们看到,路径中的所有点的X坐标都在200 s之内,但是X坐标为75的点被计算在该路径之内。

如果我在路径上使用close命令,问题就消失了,但这带来了其他问题:

  1. CGPath的contains在开放路径上如何工作,以及在哪里记录(如果有的话)?

  2. 这为什么会影响contains方法的结果,而不影响屏幕上显示的内容?例如,如果我告诉相应的CALayer用某种颜色填充,那么该颜色不会溢出超出其应有的范围。

1 个答案:

答案 0 :(得分:1)

在操场上进行了实验后,我发现对于x值大于或等于CGRect.contains(:)的点,maxX方法返回false。或大于或等于maxY的的y值。

事实证明,CGRect的documentation表示类似的内容:

如果点的坐标位于矩形内或最小X或最小Y边上,则认为该点在矩形内。

我认为文档的措辞很差,因为最大X和最大Y边缘的交点不包括相同。

下面是证明这一点的代码:

let rect = CGRect(x: 0, y: 0, width: 200, height: 200)
rect.contains(CGPoint(x: 0, y: 0))                 // true
rect.contains(CGPoint(x: rect.midX, y: 0))         // true
rect.contains(CGPoint(x: 0, y: rect.midY))         // true
rect.contains(CGPoint(x: 200, y: 200))             // false
rect.contains(CGPoint(x: 200, y: 0))               // false
rect.contains(CGPoint(x: 0, y: 200))               // false
rect.contains(CGPoint(x: 0, y: rect.maxY))         // false
rect.contains(CGPoint(x: rect.maxX, y: 0))         // false
rect.contains(CGPoint(x: rect.maxX, y: rect.maxY)) // false
rect.maxX                                          // 200
rect.maxY                                          // 200

此外,documentation指出boundingBox包含路径的所有点,包括贝塞尔曲线和二次曲线的控制点。您可能要改用boundingBoxOfPath