From 69ee82df6708431913546a9e074da27a903fe6be Mon Sep 17 00:00:00 2001 From: Victor Smeshkov Date: Mon, 17 Sep 2018 01:04:54 +0300 Subject: [PATCH] Allow hit test layers inside SVGLayer --- SwiftSVG.xcodeproj/project.pbxproj | 8 +++ .../CALayerWithChildHitTest.swift | 52 +++++++++++++++++++ .../CAShapeLayerWithHitTest.swift | 43 +++++++++++++++ SwiftSVG/SVG/Elements/SVGCircle.swift | 2 +- .../SVG/Elements/SVGContainerElement.swift | 2 +- SwiftSVG/SVG/Elements/SVGEllipse.swift | 2 +- SwiftSVG/SVG/Elements/SVGGroup.swift | 2 +- SwiftSVG/SVG/Elements/SVGLine.swift | 2 +- SwiftSVG/SVG/Elements/SVGPath.swift | 2 +- SwiftSVG/SVG/Elements/SVGPolygon.swift | 2 +- SwiftSVG/SVG/Elements/SVGPolyline.swift | 2 +- SwiftSVG/SVG/Elements/SVGRectangle.swift | 2 +- SwiftSVG/SVG/Elements/SVGRootElement.swift | 2 +- SwiftSVG/SVG/Elements/SVGShapeElement.swift | 2 +- 14 files changed, 114 insertions(+), 11 deletions(-) create mode 100644 SwiftSVG/SVG Extensions/CALayerWithChildHitTest.swift create mode 100644 SwiftSVG/SVG Extensions/CAShapeLayerWithHitTest.swift diff --git a/SwiftSVG.xcodeproj/project.pbxproj b/SwiftSVG.xcodeproj/project.pbxproj index dd3e662..50859d7 100644 --- a/SwiftSVG.xcodeproj/project.pbxproj +++ b/SwiftSVG.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 06B8771A214F03AA00CB0628 /* CAShapeLayerWithHitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06B87718214F03AA00CB0628 /* CAShapeLayerWithHitTest.swift */; }; + 06B8771B214F03AA00CB0628 /* CALayerWithChildHitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06B87719214F03AA00CB0628 /* CALayerWithChildHitTest.swift */; }; 174FF06D1EF10691003F3DBE /* PathDLexer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174FF06C1EF10691003F3DBE /* PathDLexer.swift */; }; 175093C11E6C669F00EF1853 /* SVGParserSupportedElements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 175093C01E6C669F00EF1853 /* SVGParserSupportedElements.swift */; }; 175093C31E6C6C2100EF1853 /* SVGElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 175093C21E6C6C2100EF1853 /* SVGElement.swift */; }; @@ -144,6 +146,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 06B87718214F03AA00CB0628 /* CAShapeLayerWithHitTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAShapeLayerWithHitTest.swift; sourceTree = ""; }; + 06B87719214F03AA00CB0628 /* CALayerWithChildHitTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CALayerWithChildHitTest.swift; sourceTree = ""; }; 174FF06C1EF10691003F3DBE /* PathDLexer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PathDLexer.swift; sourceTree = ""; }; 175093C01E6C669F00EF1853 /* SVGParserSupportedElements.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SVGParserSupportedElements.swift; sourceTree = ""; }; 175093C21E6C6C2100EF1853 /* SVGElement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SVGElement.swift; path = SVG/Elements/SVGElement.swift; sourceTree = ""; }; @@ -366,6 +370,8 @@ 38048F321F24210F00179C95 /* CALayer+SVG.swift */, 38048F331F24210F00179C95 /* CAShapeLayer+SVG.swift */, 38048F341F24210F00179C95 /* SVGLayer.swift */, + 06B87719214F03AA00CB0628 /* CALayerWithChildHitTest.swift */, + 06B87718214F03AA00CB0628 /* CAShapeLayerWithHitTest.swift */, 38048F351F24210F00179C95 /* SVGView.swift */, 38048F361F24210F00179C95 /* UIBezierPath+SVG.swift */, 38048F371F24210F00179C95 /* UIView+SVG.swift */, @@ -702,10 +708,12 @@ 39B8D8A71B3C32DD009DAF60 /* String+Subscript.swift in Sources */, 380FED791F07F48500EDB255 /* DispatchQueue+Extensions.swift in Sources */, 38048F381F24210F00179C95 /* CALayer+SVG.swift in Sources */, + 06B8771B214F03AA00CB0628 /* CALayerWithChildHitTest.swift in Sources */, 17CCE7381EE666D6007FA5AD /* Scalar+FromByteArray.swift in Sources */, 39B8D8A91B3C32DD009DAF60 /* Stack.swift in Sources */, 38048F391F24210F00179C95 /* CAShapeLayer+SVG.swift in Sources */, 175093CC1E6C773600EF1853 /* SVGRootElement.swift in Sources */, + 06B8771A214F03AA00CB0628 /* CAShapeLayerWithHitTest.swift in Sources */, 176BC9BC1EE4AE2700F7B54C /* Dictionary+Add.swift in Sources */, 38048F3D1F24210F00179C95 /* UIView+SVG.swift in Sources */, 176BC9B61EE498C700F7B54C /* SVGRectangle.swift in Sources */, diff --git a/SwiftSVG/SVG Extensions/CALayerWithChildHitTest.swift b/SwiftSVG/SVG Extensions/CALayerWithChildHitTest.swift new file mode 100644 index 0000000..a13b8f9 --- /dev/null +++ b/SwiftSVG/SVG Extensions/CALayerWithChildHitTest.swift @@ -0,0 +1,52 @@ +// +// SVGLayer.swift +// SwiftSVG +// +// +// Copyright (c) 2017 Michael Choe +// http://www.github.com/mchoe +// http://www.straussmade.com/ +// http://www.twitter.com/_mchoe +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + + +import Foundation +import QuartzCore + +/*! + * Overrides Apple's CALayer purely to change one method, so that hit-testing + * is done by checking whether the hit point lies: + * + * "inside ANY of my child sub-layers (some of which have over-ridden hit-testing too)" + */ + +open class CALayerWithChildHitTest: CALayer { + + override open func contains(_ p: CGPoint) -> Bool { + for subLayer in sublayers ?? [] { + let pointInSubLayer = convert(p, to: subLayer) + if subLayer.contains(pointInSubLayer) { + return true + } + } + return false + } +} diff --git a/SwiftSVG/SVG Extensions/CAShapeLayerWithHitTest.swift b/SwiftSVG/SVG Extensions/CAShapeLayerWithHitTest.swift new file mode 100644 index 0000000..f9ef3d8 --- /dev/null +++ b/SwiftSVG/SVG Extensions/CAShapeLayerWithHitTest.swift @@ -0,0 +1,43 @@ +// +// SVGLayer.swift +// SwiftSVG +// +// +// Copyright (c) 2017 Michael Choe +// http://www.github.com/mchoe +// http://www.straussmade.com/ +// http://www.twitter.com/_mchoe +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + + +import Foundation +import QuartzCore + +/** + A `CAShapeLayer` subclass that allows you to do perfect "containsPoint" calculations via Apple's API calls + */ + +open class CAShapeLayerWithHitTest: CAShapeLayer { + + override open func contains(_ p: CGPoint) -> Bool { + return path?.contains(p) == true + } +} diff --git a/SwiftSVG/SVG/Elements/SVGCircle.swift b/SwiftSVG/SVG/Elements/SVGCircle.swift index 6fef61f..166c0a2 100644 --- a/SwiftSVG/SVG/Elements/SVGCircle.swift +++ b/SwiftSVG/SVG/Elements/SVGCircle.swift @@ -55,7 +55,7 @@ final class SVGCircle: SVGShapeElement { internal var circleRadius: CGFloat = 0 /// :nodoc: - internal var svgLayer = CAShapeLayer() + internal var svgLayer = CAShapeLayerWithHitTest() /// :nodoc: internal var supportedAttributes: [String : (String) -> ()] = [:] diff --git a/SwiftSVG/SVG/Elements/SVGContainerElement.swift b/SwiftSVG/SVG/Elements/SVGContainerElement.swift index 9c111d6..a43c532 100644 --- a/SwiftSVG/SVG/Elements/SVGContainerElement.swift +++ b/SwiftSVG/SVG/Elements/SVGContainerElement.swift @@ -44,7 +44,7 @@ public protocol SVGContainerElement: SVGElement, DelaysApplyingAttributes, Filla /** The layer that stores all the SVG sublayers */ - var containerLayer: CALayer { get set } + var containerLayer: CALayerWithChildHitTest { get set } } diff --git a/SwiftSVG/SVG/Elements/SVGEllipse.swift b/SwiftSVG/SVG/Elements/SVGEllipse.swift index f488526..8347068 100644 --- a/SwiftSVG/SVG/Elements/SVGEllipse.swift +++ b/SwiftSVG/SVG/Elements/SVGEllipse.swift @@ -58,7 +58,7 @@ final class SVGEllipse: SVGShapeElement { internal var yRadius: CGFloat = 0 /// :nodoc: - internal var svgLayer = CAShapeLayer() + internal var svgLayer = CAShapeLayerWithHitTest() /// :nodoc: internal var supportedAttributes: [String : (String) -> ()] = [:] diff --git a/SwiftSVG/SVG/Elements/SVGGroup.swift b/SwiftSVG/SVG/Elements/SVGGroup.swift index d1dcfbc..47302ff 100644 --- a/SwiftSVG/SVG/Elements/SVGGroup.swift +++ b/SwiftSVG/SVG/Elements/SVGGroup.swift @@ -59,7 +59,7 @@ final class SVGGroup: SVGContainerElement { internal var delayedAttributes = [String : String]() /// A `CALayer` that will hold all sublayers of the `SVGGroup` - internal var containerLayer = CALayer() + internal var containerLayer = CALayerWithChildHitTest() /// :nodoc: internal var supportedAttributes = [String : (String) -> ()]() diff --git a/SwiftSVG/SVG/Elements/SVGLine.swift b/SwiftSVG/SVG/Elements/SVGLine.swift index 4747229..c9f9ae3 100644 --- a/SwiftSVG/SVG/Elements/SVGLine.swift +++ b/SwiftSVG/SVG/Elements/SVGLine.swift @@ -55,7 +55,7 @@ final class SVGLine: SVGShapeElement { internal var start = CGPoint.zero /// :nodoc: - internal var svgLayer = CAShapeLayer() + internal var svgLayer = CAShapeLayerWithHitTest() /// :nodoc: internal var supportedAttributes: [String : (String) -> ()] = [:] diff --git a/SwiftSVG/SVG/Elements/SVGPath.swift b/SwiftSVG/SVG/Elements/SVGPath.swift index 6b1d02a..0d82fad 100644 --- a/SwiftSVG/SVG/Elements/SVGPath.swift +++ b/SwiftSVG/SVG/Elements/SVGPath.swift @@ -59,7 +59,7 @@ final class SVGPath: SVGShapeElement, ParsesAsynchronously, DelaysApplyingAttrib internal var supportedAttributes = [String : (String) -> ()]() /// :nodoc: - internal var svgLayer = CAShapeLayer() + internal var svgLayer = CAShapeLayerWithHitTest() /// :nodoc: internal init() { } diff --git a/SwiftSVG/SVG/Elements/SVGPolygon.swift b/SwiftSVG/SVG/Elements/SVGPolygon.swift index 94532e7..2c4d2c6 100644 --- a/SwiftSVG/SVG/Elements/SVGPolygon.swift +++ b/SwiftSVG/SVG/Elements/SVGPolygon.swift @@ -47,7 +47,7 @@ struct SVGPolygon: SVGShapeElement { internal var supportedAttributes: [String : (String) -> ()] = [:] /// :nodoc: - internal var svgLayer = CAShapeLayer() + internal var svgLayer = CAShapeLayerWithHitTest() /** Function that parses a coordinate string and creates a polygon path diff --git a/SwiftSVG/SVG/Elements/SVGPolyline.swift b/SwiftSVG/SVG/Elements/SVGPolyline.swift index 1662f3d..6bb389d 100644 --- a/SwiftSVG/SVG/Elements/SVGPolyline.swift +++ b/SwiftSVG/SVG/Elements/SVGPolyline.swift @@ -47,7 +47,7 @@ struct SVGPolyline: SVGShapeElement { internal var supportedAttributes: [String : (String) -> ()] = [:] /// :nodoc: - internal var svgLayer = CAShapeLayer() + internal var svgLayer = CAShapeLayerWithHitTest() /** Parses a coordinate string and creates a new polyline based on them diff --git a/SwiftSVG/SVG/Elements/SVGRectangle.swift b/SwiftSVG/SVG/Elements/SVGRectangle.swift index eec7803..9ce9d6f 100644 --- a/SwiftSVG/SVG/Elements/SVGRectangle.swift +++ b/SwiftSVG/SVG/Elements/SVGRectangle.swift @@ -49,7 +49,7 @@ final class SVGRectangle: SVGShapeElement { internal var rectangleRect = CGRect() /// :nodoc: - internal var svgLayer = CAShapeLayer() + internal var svgLayer = CAShapeLayerWithHitTest() /// :nodoc: internal var supportedAttributes: [String : (String) -> ()] = [:] diff --git a/SwiftSVG/SVG/Elements/SVGRootElement.swift b/SwiftSVG/SVG/Elements/SVGRootElement.swift index 480b04f..0354d66 100644 --- a/SwiftSVG/SVG/Elements/SVGRootElement.swift +++ b/SwiftSVG/SVG/Elements/SVGRootElement.swift @@ -47,7 +47,7 @@ struct SVGRootElement: SVGContainerElement { internal var delayedAttributes = [String : String]() // :nodoc: - internal var containerLayer = CALayer() + internal var containerLayer = CALayerWithChildHitTest() // :nodoc: internal var supportedAttributes = [String : (String) -> ()]() diff --git a/SwiftSVG/SVG/Elements/SVGShapeElement.swift b/SwiftSVG/SVG/Elements/SVGShapeElement.swift index 4f98fde..06566b2 100644 --- a/SwiftSVG/SVG/Elements/SVGShapeElement.swift +++ b/SwiftSVG/SVG/Elements/SVGShapeElement.swift @@ -43,7 +43,7 @@ public protocol SVGShapeElement: SVGElement, Fillable, Strokable, Transformable, /** The `CAShapeLayer` that can draw the path data. */ - var svgLayer: CAShapeLayer { get set } + var svgLayer: CAShapeLayerWithHitTest { get set } } extension SVGShapeElement {