From 77f1f7040d5c76267321094a6d603d94c2ff40a6 Mon Sep 17 00:00:00 2001 From: Ilya Puchka Date: Thu, 12 Jan 2017 21:35:23 +0100 Subject: [PATCH] extracted helper methods to UITextView extension --- Example/Podfile.lock | 2 +- .../ReadMoreTextView.podspec.json | 2 +- Example/Pods/Manifest.lock | 2 +- Example/Pods/Pods.xcodeproj/project.pbxproj | 90 ++++++++------- ReadMoreTextView.podspec | 2 +- Sources/ReadMoreTextView.swift | 91 ++------------- Sources/UITextView+Extensions.swift | 106 ++++++++++++++++++ 7 files changed, 169 insertions(+), 126 deletions(-) create mode 100644 Sources/UITextView+Extensions.swift diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 4fb8702..27eebd7 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -9,7 +9,7 @@ EXTERNAL SOURCES: :path: .. SPEC CHECKSUMS: - ReadMoreTextView: 8d140284b6621571642fb6824f1774b119358e41 + ReadMoreTextView: 9775d24a0827c3b965789696236b85b7c16e2954 PODFILE CHECKSUM: 72daab87156ddc53981cfb66c0eac16103dd9957 diff --git a/Example/Pods/Local Podspecs/ReadMoreTextView.podspec.json b/Example/Pods/Local Podspecs/ReadMoreTextView.podspec.json index c02f476..50507ea 100644 --- a/Example/Pods/Local Podspecs/ReadMoreTextView.podspec.json +++ b/Example/Pods/Local Podspecs/ReadMoreTextView.podspec.json @@ -19,5 +19,5 @@ "git": "https://github.com/ilyapuchka/ReadMoreTextView.git", "tag": "1.1.0" }, - "source_files": "Sources/ReadMoreTextView.swift" + "source_files": "Sources/*.swift" } diff --git a/Example/Pods/Manifest.lock b/Example/Pods/Manifest.lock index 4fb8702..27eebd7 100644 --- a/Example/Pods/Manifest.lock +++ b/Example/Pods/Manifest.lock @@ -9,7 +9,7 @@ EXTERNAL SOURCES: :path: .. SPEC CHECKSUMS: - ReadMoreTextView: 8d140284b6621571642fb6824f1774b119358e41 + ReadMoreTextView: 9775d24a0827c3b965789696236b85b7c16e2954 PODFILE CHECKSUM: 72daab87156ddc53981cfb66c0eac16103dd9957 diff --git a/Example/Pods/Pods.xcodeproj/project.pbxproj b/Example/Pods/Pods.xcodeproj/project.pbxproj index 383d22b..48d4d5c 100644 --- a/Example/Pods/Pods.xcodeproj/project.pbxproj +++ b/Example/Pods/Pods.xcodeproj/project.pbxproj @@ -7,12 +7,13 @@ objects = { /* Begin PBXBuildFile section */ - 0D54E75E5683453F8710F2F79D1E5CD4 /* ReadMoreTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FEB05108226F9144841322C256FA95C /* ReadMoreTextView.swift */; }; 3597379FC64DCDE942F8E95F45B193E9 /* Pods-ReadMoreTextViewExample-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B8E5C911BA6C82BB46A58A9DAE6DAFF /* Pods-ReadMoreTextViewExample-dummy.m */; }; + 441810EAE3F3920D5348210E5AACF64F /* UITextView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F5C04F3E0018ED544E4A66514840D90 /* UITextView+Extensions.swift */; }; 5BDAD7A35C58867223BDFB0199256FDD /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CBB3DE36805AF21409EC968A9691732F /* Foundation.framework */; }; 6607EFA7CC6A2940E1C9C7B440D8B985 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CBB3DE36805AF21409EC968A9691732F /* Foundation.framework */; }; - CC8A6281EF7BAB4EA92415A00C225856 /* ReadMoreTextView-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = B62238C22C03F5DF69C0D45FBD83EAE9 /* ReadMoreTextView-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - DA071EB6DBAC03FB66A243AE51D6F9E1 /* ReadMoreTextView-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 60EC81998176DDDE3F8CAAE997D03E08 /* ReadMoreTextView-dummy.m */; }; + 9DA62777FE564F447F62CC729272C11C /* ReadMoreTextView-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3526E25B2CC58051C91993A8C358CC91 /* ReadMoreTextView-dummy.m */; }; + CC8A6281EF7BAB4EA92415A00C225856 /* ReadMoreTextView-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = C27167D6C246375AD6F4FDCF9186B0C3 /* ReadMoreTextView-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DDD3C1ACBEA9F0BDBC7B3437133D1AA0 /* ReadMoreTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1609B3C4999713E2C4F0EB2808861DBD /* ReadMoreTextView.swift */; }; E67B6835FB2E53C4B862925A4539AD24 /* Pods-ReadMoreTextViewExample-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = B7D82AAAD19A18B94EDAC41E9F506038 /* Pods-ReadMoreTextViewExample-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; /* End PBXBuildFile section */ @@ -27,26 +28,27 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 15FBCC2E5E4196D7B251B9FD94432897 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 1609B3C4999713E2C4F0EB2808861DBD /* ReadMoreTextView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ReadMoreTextView.swift; sourceTree = ""; }; 196DE5EDC2496F02C6B7E71A521B4274 /* Pods-ReadMoreTextViewExample-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-ReadMoreTextViewExample-acknowledgements.plist"; sourceTree = ""; }; 19BDF3558CAA5431362CCD557177E3A0 /* Pods-ReadMoreTextViewExample-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-ReadMoreTextViewExample-acknowledgements.markdown"; sourceTree = ""; }; - 1FEB05108226F9144841322C256FA95C /* ReadMoreTextView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ReadMoreTextView.swift; sourceTree = ""; }; 2A9384A0F0089F5094E5C89C13713E01 /* ReadMoreTextView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = ReadMoreTextView.framework; path = ReadMoreTextView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2B8E5C911BA6C82BB46A58A9DAE6DAFF /* Pods-ReadMoreTextViewExample-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-ReadMoreTextViewExample-dummy.m"; sourceTree = ""; }; 31C2C5582F899E9E529AE415EC62D533 /* Pods-ReadMoreTextViewExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-ReadMoreTextViewExample.debug.xcconfig"; sourceTree = ""; }; - 40C8C5A52BFE326A6D2E551CBB97390A /* ReadMoreTextView-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ReadMoreTextView-prefix.pch"; sourceTree = ""; }; + 3526E25B2CC58051C91993A8C358CC91 /* ReadMoreTextView-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "ReadMoreTextView-dummy.m"; sourceTree = ""; }; 4219EEDC3BE989ADCFBCF2A711865152 /* Pods_ReadMoreTextViewExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_ReadMoreTextViewExample.framework; path = "Pods-ReadMoreTextViewExample.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; 4686C661354B6F81835B5D3B7AEB075C /* Pods-ReadMoreTextViewExample-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-ReadMoreTextViewExample-resources.sh"; sourceTree = ""; }; 5ABA830DBB4F3F2C1B74C9F462093D49 /* Pods-ReadMoreTextViewExample.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = "Pods-ReadMoreTextViewExample.modulemap"; sourceTree = ""; }; - 60EC81998176DDDE3F8CAAE997D03E08 /* ReadMoreTextView-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "ReadMoreTextView-dummy.m"; sourceTree = ""; }; - 6C2681B8AC509688CD17BA8F3E650A22 /* ReadMoreTextView.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = ReadMoreTextView.modulemap; sourceTree = ""; }; + 5F5C04F3E0018ED544E4A66514840D90 /* UITextView+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "UITextView+Extensions.swift"; sourceTree = ""; }; + 689AC87BE06C7E360AAEEAC1ECADAFD9 /* ReadMoreTextView-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ReadMoreTextView-prefix.pch"; sourceTree = ""; }; 799C9CB346F2178E8EB1A1C71405BF04 /* Pods-ReadMoreTextViewExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-ReadMoreTextViewExample.release.xcconfig"; sourceTree = ""; }; 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; - B62238C22C03F5DF69C0D45FBD83EAE9 /* ReadMoreTextView-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ReadMoreTextView-umbrella.h"; sourceTree = ""; }; B7D82AAAD19A18B94EDAC41E9F506038 /* Pods-ReadMoreTextViewExample-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-ReadMoreTextViewExample-umbrella.h"; sourceTree = ""; }; C027649DC0037F206E0684986D9D7CF2 /* Pods-ReadMoreTextViewExample-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-ReadMoreTextViewExample-frameworks.sh"; sourceTree = ""; }; + C27167D6C246375AD6F4FDCF9186B0C3 /* ReadMoreTextView-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ReadMoreTextView-umbrella.h"; sourceTree = ""; }; + C7B889C0CC27D4ED27ACEB5BF1807D46 /* ReadMoreTextView.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = ReadMoreTextView.modulemap; sourceTree = ""; }; CBB3DE36805AF21409EC968A9691732F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; - D46C2CB9F83362897C29FD75CD09FA0A /* ReadMoreTextView.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = ReadMoreTextView.xcconfig; sourceTree = ""; }; - DFB1DCB4E819EEEDA0992B3D2F7B6759 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DB417DECB634E49A92B283D3E65A170A /* ReadMoreTextView.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = ReadMoreTextView.xcconfig; sourceTree = ""; }; FBFA14730588EA147201B871A9C9800B /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ @@ -70,16 +72,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 0139DBE238703D6E8F9AE7C1931611B0 /* ReadMoreTextView */ = { - isa = PBXGroup; - children = ( - 2737F58909D32D20B784DA79F03C3553 /* Sources */, - EA50F1FC9F38D0A269D42323AE65A1F0 /* Support Files */, - ); - name = ReadMoreTextView; - path = ../..; - sourceTree = ""; - }; 09F2513A67F15497EB6AA766C99B653A /* Targets Support Files */ = { isa = PBXGroup; children = ( @@ -88,13 +80,14 @@ name = "Targets Support Files"; sourceTree = ""; }; - 2737F58909D32D20B784DA79F03C3553 /* Sources */ = { + 0E397243B5B850B567026C8E73A7D3FF /* ReadMoreTextView */ = { isa = PBXGroup; children = ( - 1FEB05108226F9144841322C256FA95C /* ReadMoreTextView.swift */, + 951855D157E243094C230683202DC646 /* Sources */, + 3B71841FF53DF0378694021D594BCD6C /* Support Files */, ); - name = Sources; - path = Sources; + name = ReadMoreTextView; + path = ../..; sourceTree = ""; }; 28D4AC494D794C24B887CB3B089A97CD /* Pods-ReadMoreTextViewExample */ = { @@ -124,10 +117,24 @@ name = Products; sourceTree = ""; }; + 3B71841FF53DF0378694021D594BCD6C /* Support Files */ = { + isa = PBXGroup; + children = ( + 15FBCC2E5E4196D7B251B9FD94432897 /* Info.plist */, + C7B889C0CC27D4ED27ACEB5BF1807D46 /* ReadMoreTextView.modulemap */, + DB417DECB634E49A92B283D3E65A170A /* ReadMoreTextView.xcconfig */, + 3526E25B2CC58051C91993A8C358CC91 /* ReadMoreTextView-dummy.m */, + 689AC87BE06C7E360AAEEAC1ECADAFD9 /* ReadMoreTextView-prefix.pch */, + C27167D6C246375AD6F4FDCF9186B0C3 /* ReadMoreTextView-umbrella.h */, + ); + name = "Support Files"; + path = "Example/Pods/Target Support Files/ReadMoreTextView"; + sourceTree = ""; + }; 4C1CFEDC476ED9B4534BA4721C98A4E2 /* Development Pods */ = { isa = PBXGroup; children = ( - 0139DBE238703D6E8F9AE7C1931611B0 /* ReadMoreTextView */, + 0E397243B5B850B567026C8E73A7D3FF /* ReadMoreTextView */, ); name = "Development Pods"; sourceTree = ""; @@ -151,26 +158,22 @@ ); sourceTree = ""; }; - BC3CA7F9E30CC8F7E2DD044DD34432FC /* Frameworks */ = { + 951855D157E243094C230683202DC646 /* Sources */ = { isa = PBXGroup; children = ( - 7531C8F8DE19F1AA3C8A7AC97A91DC29 /* iOS */, + 1609B3C4999713E2C4F0EB2808861DBD /* ReadMoreTextView.swift */, + 5F5C04F3E0018ED544E4A66514840D90 /* UITextView+Extensions.swift */, ); - name = Frameworks; + name = Sources; + path = Sources; sourceTree = ""; }; - EA50F1FC9F38D0A269D42323AE65A1F0 /* Support Files */ = { + BC3CA7F9E30CC8F7E2DD044DD34432FC /* Frameworks */ = { isa = PBXGroup; children = ( - DFB1DCB4E819EEEDA0992B3D2F7B6759 /* Info.plist */, - 6C2681B8AC509688CD17BA8F3E650A22 /* ReadMoreTextView.modulemap */, - D46C2CB9F83362897C29FD75CD09FA0A /* ReadMoreTextView.xcconfig */, - 60EC81998176DDDE3F8CAAE997D03E08 /* ReadMoreTextView-dummy.m */, - 40C8C5A52BFE326A6D2E551CBB97390A /* ReadMoreTextView-prefix.pch */, - B62238C22C03F5DF69C0D45FBD83EAE9 /* ReadMoreTextView-umbrella.h */, + 7531C8F8DE19F1AA3C8A7AC97A91DC29 /* iOS */, ); - name = "Support Files"; - path = "Example/Pods/Target Support Files/ReadMoreTextView"; + name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ @@ -199,7 +202,7 @@ isa = PBXNativeTarget; buildConfigurationList = 6B436AE262C1BD38DC1FF6ED5A71B638 /* Build configuration list for PBXNativeTarget "ReadMoreTextView" */; buildPhases = ( - 1FEE50214138FC4868D9A8B6BBB6675E /* Sources */, + 699261A580E2FC3706BCB102A6AA10B5 /* Sources */, F8B0B28A27EE2FE981FA074DF02CF642 /* Frameworks */, 1ED0C0170EDB721EF60EBFE2ADC162D0 /* Headers */, ); @@ -258,12 +261,13 @@ /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ - 1FEE50214138FC4868D9A8B6BBB6675E /* Sources */ = { + 699261A580E2FC3706BCB102A6AA10B5 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - DA071EB6DBAC03FB66A243AE51D6F9E1 /* ReadMoreTextView-dummy.m in Sources */, - 0D54E75E5683453F8710F2F79D1E5CD4 /* ReadMoreTextView.swift in Sources */, + 9DA62777FE564F447F62CC729272C11C /* ReadMoreTextView-dummy.m in Sources */, + DDD3C1ACBEA9F0BDBC7B3437133D1AA0 /* ReadMoreTextView.swift in Sources */, + 441810EAE3F3920D5348210E5AACF64F /* UITextView+Extensions.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -365,7 +369,7 @@ }; 4A1DEA6984906477DF79EF0B48C6C290 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D46C2CB9F83362897C29FD75CD09FA0A /* ReadMoreTextView.xcconfig */; + baseConfigurationReference = DB417DECB634E49A92B283D3E65A170A /* ReadMoreTextView.xcconfig */; buildSettings = { "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; @@ -442,7 +446,7 @@ }; A0CA2F9FB0B42835FC8BAA041732768D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D46C2CB9F83362897C29FD75CD09FA0A /* ReadMoreTextView.xcconfig */; + baseConfigurationReference = DB417DECB634E49A92B283D3E65A170A /* ReadMoreTextView.xcconfig */; buildSettings = { "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; diff --git a/ReadMoreTextView.podspec b/ReadMoreTextView.podspec index 039a02e..eebbf35 100644 --- a/ReadMoreTextView.podspec +++ b/ReadMoreTextView.podspec @@ -24,6 +24,6 @@ Pod::Spec.new do |s| s.source = { :git => "https://github.com/ilyapuchka/ReadMoreTextView.git", :tag => s.version } - s.source_files = "Sources/ReadMoreTextView.swift" + s.source_files = "Sources/*.swift" end diff --git a/Sources/ReadMoreTextView.swift b/Sources/ReadMoreTextView.swift index 96f14ca..5208bea 100644 --- a/Sources/ReadMoreTextView.swift +++ b/Sources/ReadMoreTextView.swift @@ -253,44 +253,32 @@ public class ReadMoreTextView: UITextView { #if swift(>=3.0) public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - guard let charIndex = hitTestPointIsInGliphRectAtCharIndex(point: point) else { - return super.hitTest(point, with: event) - } - - if textStorage.attribute(NSLinkAttributeName, at: charIndex, effectiveRange: nil) != nil { - return super.hitTest(point, with: event) - } else if pointIsInReadMoreOrReadLessTextRange(point: point) != nil { + return hitTest(pointInGliphRange: point, event: event) { _ in + guard pointIsInReadMoreOrReadLessTextRange(point: point) != nil else { return nil } return self - } else { - return nil } } public override func touchesEnded(_ touches: Set, with event: UIEvent?) { - defer { super.touchesEnded(touches, with: event) } - guard let point = touches.first?.location(in: self) else { return } - shouldTrim = pointIsInReadMoreOrReadLessTextRange(point: point) ?? shouldTrim + if let point = touches.first?.location(in: self) { + shouldTrim = pointIsInReadMoreOrReadLessTextRange(point: point) ?? shouldTrim + } + super.touchesEnded(touches, with: event) } #else public override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { - guard let charIndex = hitTestPointIsInGliphRectAtCharIndex(point: point) else { - return super.hitTest(point, withEvent: event) - } - - if textStorage.attribute(NSLinkAttributeName, atIndex: charIndex, effectiveRange: nil) != nil { - return super.hitTest(point, withEvent: event) - } else if pointIsInReadMoreOrReadLessTextRange(point: point) != nil { + return hitTest(pointInGliphRange: point, event: event) { _ in + guard pointIsInReadMoreOrReadLessTextRange(point: point) != nil else { return nil } return self - } else { - return nil } } public override func touchesEnded(touches: Set, withEvent event: UIEvent?) { - defer { super.touchesEnded(touches, withEvent: event) } - guard let point = touches.first?.locationInView(self) else { return } - shouldTrim = pointIsInReadMoreOrReadLessTextRange(point: point) ?? shouldTrim + if let point = touches.first?.locationInView(self) { + shouldTrim = pointIsInReadMoreOrReadLessTextRange(point: point) ?? shouldTrim + } + super.touchesEnded(touches, withEvent: event) } #endif @@ -463,35 +451,6 @@ public class ReadMoreTextView: UITextView { return nil } - private func pointIsInTextRange(point aPoint: CGPoint, range: NSRange, padding: UIEdgeInsets) -> Bool { - var boundingRect = layoutManager.boundingRectForCharacterRange(range: range, inTextContainer: textContainer) - boundingRect = boundingRect.offsetBy(dx: textContainerInset.left, dy: textContainerInset.top) - boundingRect = boundingRect.insetBy(dx: -(padding.left + padding.right), dy: -(padding.top + padding.bottom)) - return boundingRect.contains(aPoint) - } - - private func hitTestPointIsInGliphRectAtCharIndex(point aPoint: CGPoint) -> Int? { - let point = CGPoint(x: aPoint.x, y: aPoint.y - textContainerInset.top) - #if swift(>=3.0) - let glyphIndex = layoutManager.glyphIndex(for: point, in: textContainer) - let glyphRect = layoutManager.boundingRect(forGlyphRange: NSMakeRange(glyphIndex, 1), in: textContainer) - if glyphRect.contains(point) { - return layoutManager.characterIndexForGlyph(at: glyphIndex) - } else { - return nil - } - #else - let glyphIndex = layoutManager.glyphIndexForPoint(point, inTextContainer: textContainer) - let glyphRect = layoutManager.boundingRectForGlyphRange(NSMakeRange(glyphIndex, 1), inTextContainer: textContainer) - if CGRectContainsPoint(glyphRect, point) { - return layoutManager.characterIndexForGlyphAtIndex(glyphIndex) - } - else { - return nil - } - #endif - } - } extension String { @@ -499,29 +458,3 @@ extension String { return characters.count } } - -extension NSLayoutManager { - - func characterRangeThatFits(textContainer container: NSTextContainer) -> NSRange { - #if swift(>=3.0) - var rangeThatFits = self.glyphRange(for: container) - rangeThatFits = self.characterRange(forGlyphRange: rangeThatFits, actualGlyphRange: nil) - #else - var rangeThatFits = self.glyphRangeForTextContainer(container) - rangeThatFits = self.characterRangeForGlyphRange(rangeThatFits, actualGlyphRange: nil) - #endif - return rangeThatFits - } - - func boundingRectForCharacterRange(range aRange: NSRange, inTextContainer container: NSTextContainer) -> CGRect { - #if swift(>=3.0) - let glyphRange = self.glyphRange(forCharacterRange: aRange, actualCharacterRange: nil) - let boundingRect = self.boundingRect(forGlyphRange: glyphRange, in: container) - #else - let glyphRange = self.glyphRangeForCharacterRange(aRange, actualCharacterRange: nil) - let boundingRect = self.boundingRectForGlyphRange(glyphRange, inTextContainer: container) - #endif - return boundingRect - } - -} diff --git a/Sources/UITextView+Extensions.swift b/Sources/UITextView+Extensions.swift new file mode 100644 index 0000000..5f6bca6 --- /dev/null +++ b/Sources/UITextView+Extensions.swift @@ -0,0 +1,106 @@ +// +// ReadMoreTextView.swift +// ReadMoreTextView +// +// Created by Ilya Puchka on 06.04.15. +// Copyright (c) 2015 - 2016 Ilya Puchka. All rights reserved. +// + +import UIKit + +extension UITextView { + + /** + Calls provided `test` block if point is in gliph range and there is no link detected at this point. + Will pass in to `test` a character index that corresponds to `point`. + Return `self` in `test` if text view should intercept the touch event or nil otherwise. + */ + public func hitTest(pointInGliphRange aPoint: CGPoint, event: UIEvent?, test: (Int) -> UIView?) -> UIView? { + guard let charIndex = charIndexForPointInGlyphRect(point: aPoint) else { + #if swift(>=3.0) + return super.hitTest(aPoint, with: event) + #else + return super.hitTest(aPoint, withEvent: event) + #endif + } + #if swift(>=3.0) + guard textStorage.attribute(NSLinkAttributeName, at: charIndex, effectiveRange: nil) == nil else { + return super.hitTest(aPoint, with: event) + } + #else + guard textStorage.attribute(NSLinkAttributeName, atIndex: charIndex, effectiveRange: nil) == nil else { + return super.hitTest(aPoint, withEvent: event) + } + #endif + return test(charIndex) + } + + /** + Returns true if point is in text bounding rect adjusted with padding. + Bounding rect will be enlarged with positive padding values and decreased with negative values. + */ + public func pointIsInTextRange(point aPoint: CGPoint, range: NSRange, padding: UIEdgeInsets) -> Bool { + var boundingRect = layoutManager.boundingRectForCharacterRange(range: range, inTextContainer: textContainer) + boundingRect = boundingRect.offsetBy(dx: textContainerInset.left, dy: textContainerInset.top) + boundingRect = boundingRect.insetBy(dx: -(padding.left + padding.right), dy: -(padding.top + padding.bottom)) + return boundingRect.contains(aPoint) + } + + /** + Returns index of character for glyph at provided point. Returns `nil` if point is out of any glyph. + */ + public func charIndexForPointInGlyphRect(point aPoint: CGPoint) -> Int? { + let point = CGPoint(x: aPoint.x, y: aPoint.y - textContainerInset.top) + #if swift(>=3.0) + let glyphIndex = layoutManager.glyphIndex(for: point, in: textContainer) + let glyphRect = layoutManager.boundingRect(forGlyphRange: NSMakeRange(glyphIndex, 1), in: textContainer) + if glyphRect.contains(point) { + return layoutManager.characterIndexForGlyph(at: glyphIndex) + } else { + return nil + } + #else + let glyphIndex = layoutManager.glyphIndexForPoint(point, inTextContainer: textContainer) + let glyphRect = layoutManager.boundingRectForGlyphRange(NSMakeRange(glyphIndex, 1), inTextContainer: textContainer) + if CGRectContainsPoint(glyphRect, point) { + return layoutManager.characterIndexForGlyphAtIndex(glyphIndex) + } + else { + return nil + } + #endif + } + +} + +extension NSLayoutManager { + + /** + Returns characters range that completely fits into container. + */ + public func characterRangeThatFits(textContainer container: NSTextContainer) -> NSRange { + #if swift(>=3.0) + var rangeThatFits = self.glyphRange(for: container) + rangeThatFits = self.characterRange(forGlyphRange: rangeThatFits, actualGlyphRange: nil) + #else + var rangeThatFits = self.glyphRangeForTextContainer(container) + rangeThatFits = self.characterRangeForGlyphRange(rangeThatFits, actualGlyphRange: nil) + #endif + return rangeThatFits + } + + /** + Returns bounding rect in provided container for characters in provided range. + */ + public func boundingRectForCharacterRange(range aRange: NSRange, inTextContainer container: NSTextContainer) -> CGRect { + #if swift(>=3.0) + let glyphRange = self.glyphRange(forCharacterRange: aRange, actualCharacterRange: nil) + let boundingRect = self.boundingRect(forGlyphRange: glyphRange, in: container) + #else + let glyphRange = self.glyphRangeForCharacterRange(aRange, actualCharacterRange: nil) + let boundingRect = self.boundingRectForGlyphRange(glyphRange, inTextContainer: container) + #endif + return boundingRect + } + +}