---
title: "Non-selectable UITextViews and URL interactions"
slug: non-selectable-uitextviews-and-url-interactions
section: tech
date: 2017-06-04T11:58:00.000Z
canonical: https://roland.leth.ro/blog/tech/non-selectable-uitextviews-and-url-interactions
---

Say we have some HTML content we want to display in a `UITextView`:

```swift
if let stringData = string.data(using: .utf16),
	let attributedString = try? NSAttributedString(
		data: stringData,
		options: [.documentType: NSAttributedString.DocumentType.html],
		documentAttributes: nil) {
	attributedText = attributedString
}
else {
	attributedText = nil
}
```

For added complexity, let's assume the `UITextView` is inside a `UIScrollView`, meaning we'll most likely want the it to be non-editable,  non-selectable and, for our case, non-scrollable as well. And here comes the tricky part: if we set its `isSelectable` property to `false`, `URLs` won't be tappable anymore, so we can't do that; we'll have to work around having `isSelectable == true`.

The first steps are to set the `tintColor` to `.clear` (the caret bugs sometimes), and reset the text selection, as it happens:

```swift
func textViewDidChangeSelection(_ textView: UITextView) {
	DispatchQueue.main.async {
		textView.selectedTextRange = nil
	}
}
```

This will "disable" text selection, but the loupe will still show up. If we want that to go away as well, we'll need to create a subclass, and use a little hack:

```swift
override func caretRect(for position: UITextPosition) -> CGRect {
	return CGRect(x: 0, y: contentSize.height * 1.25,
                  width: 0, height: 0)
}
```

This will position the loupe in the given `rect`, which should be offscreen. In my case, I had a full-width, almost full-height `UITextView`, and these numbers worked just fine, but you'll need to adjust them according to your needs, based on `contentSize`, `height` and position. I'm not sure why, but giving `x` any value has no impact.

I've read and tried all other solutions involving `canPerformAction`, `selectionRects`, setting `UIMenuController.shared.isMenuVisible` to `false`, or intercepting touches, but I found this solution to be the most reliable one, even though it's basically a hack.

*Update, Jun 4, 2017*: As [Saoud](https://twitter.com/sdrzn) pointed [out](https://twitter.com/sdrzn/status/871405771946250240), there's a better [approach](https://medium.com/@sdrzn/swifty-approach-to-handling-uitextviews-with-links-in-cells-d89e0e4b83d1). A bit more code, but I feel it's more appropriate, and it's _hack free_. Clearly this wasn't part of the things I tried in my "intercepting touches" attempts.