boraBong

[iOS] iOS에서 텍스트뷰의 텍스트 내 링크 하이퍼링크로 연결하기 🔗 본문

iOS/Feat

[iOS] iOS에서 텍스트뷰의 텍스트 내 링크 하이퍼링크로 연결하기 🔗

보라봉_ 2022. 2. 16. 04:21
728x90

#Swift how to make hyperlink text in textView

 

안녕하세요!!

보라봉입니다💜

 

오늘은 iOS에서

1️⃣ 텍스트뷰의 텍스트에 링크 형태가 있다면 해당 텍스트를 링크로 변환하는 방법

2️⃣ 변환된 링크를 클릭하여 인앱 브라우저로 연결하는 방법을 알아볼 거에요!!

 

1번을 손쉽게 구현하기 위해서 텍스트를 표현할 때 기본적으로 사용하는 컴포넌트인 UILabel을 사용하지 않고, UITextView를 사용해보려 합니다.

그리고, 2번을 구현하기 위해 SafariServices를 이용하려 합니다 :)

** iOS에서 링크로 웹사이트 연결하는 방법이 궁금하시다면 https://borabong.tistory.com/13 포스트를 참고해주세요!


1️⃣  textView의 text에 link형태의 content가 포함되어 있는 경우 link로 변환하는 방법

위 UI처럼 텍스트뷰의 컨텐츠에 일반 text와 link로 변환될 수 있는 text가 함께 포함되어 있는 경우 link로 변환될 수 있는 text를 변환하는 방법에 대해 알아봅시다!


UITextView에는 UIDataDetectorTypes타입의 dataDetectorTypes라는 프로퍼티가 존재합니다.

UIDataDetectorTypes를 알아보기 위해 공식문서를 들여다보면,


UIDataDetectorTypes

: 텍스트 기반 콘텐츠에서 감지할 정보 유형을 정의하는 상수입니다.

 

 

UIDataDetectorTypes가 감지할 수 있는 정보 유형은 phoneNumber, link, address, calendarEvent, shipmentTrackingNumber, flightNumber, lookupSuggestion, all 이 있네요!

 

💡 우리는 링크 형태의 텍스트를 감지하길 원하기 때문에 UIDataDetectorTypes가 감지가능한 정보 유형 중 link 유형을 사용해볼거에요!

 


< 구현 방법 >

1️⃣ 생성한 textView를 IBOutlet으로 연결

 

2️⃣ textView.text에 link형태가 포함된 텍스트 넣기

ex)

textView.text = "보라봉의 깃헙입니다. https://github.com/hwangJi-dev"

 

3️⃣ 텍스트의 link를 감지하기 위해 textView.dataDetectorTypes를 .link로 설정하기

textView.dataDetectorTypes = .link

 

❓유의할 점!

dataDetectorTypes의 discussion을 읽어보면,

" 이 속성을 사용하여 텍스트 보기에서 자동으로 URL로 변환되어야 하는 데이터 유형(전화번호, http 링크 등)을 지정할 수 있습니다. 탭하면 텍스트 보기가 URL 유형 처리를 담당하는 애플리케이션을 열고 URL을 전달합니다. 텍스트 보기의 isEditable 속성이 true로 설정된 경우 데이터 감지가 발생하지 않습니다. "

 

4️⃣ 따라서! 링크 데이터의 감지를 위해 textView의 isEditable 속성을 false로 설정

textView.isEditable = false

 

전체 코드 첨부 

//
//  ViewController.swift
//  iOS-Practice
//
//  Created by hwangJi on 2022/02/13.
//

import UIKit

class ViewController: UIViewController {

    // MARK: IBOutlet
    @IBOutlet var textView: UITextView!  {
        didSet {
            textView.text = "보라봉의 깃헙입니다. https://github.com/hwangJi-dev"
            textView.isEditable = false
            textView.dataDetectorTypes = .link
        }
    }
    
    // MARK: Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

 


2️⃣  변환된 링크를 클릭하여 인앱 브라우저로 연결하는 방법

 

 

위 UI처럼 변환된 link text를 인앱브라우저로 연결하는 방법에 대해 알아봅시다!


textView의 대리자를(delegate) 위임해주고,

UITextViewDelegate 프로토콜을 채택해주면 
텍스트뷰 내 URL link와 interaction하는 메서드(지정된 텍스트 보기가 지정된 텍스트 범위에서 지정된 URL과 지정된 유형의 사용자 상호 작용을 허용하는지 여부를 대리자에게 묻는 메서드)를 사용할 수 있습니다.

 

해당 메서드를 더 알아보기 위해 공식문서를 들여다보면,


textView(_:shouldInteractWith:in:interaction:)

: 지정된 텍스트 보기가 지정된 텍스트 범위에서 지정된 URL과 지정된 유형의 사용자 상호 작용을 허용하는지 여부를 대리자에게 묻는 메서드입니다. 

 

 

💡 먼저 함수의 반환 타입부터 살펴보겠습니다!

👉🏻 URL의 interaction이 허용되는 경우 true를, interaction이 허용되지 않는 경우 false를 반환하라고 하네요!

-> 그렇다면 interaction은 default값이 정해져 있는 걸까요?? 조금 뒤에 더 자세히 알아보도록 합시다!

 

💡 discussion을 살펴보면

" 이 메서드는 URL 링크와의 첫 번째 상호 작용에서만 호출됩니다. 예를 들어, 이 메서드는 사용자가 URL과의 첫 번째 상호 작용을 통해 수행할 수 있는 작업 목록을 표시하려는 경우 호출됩니다. 사용자가 목록에서 열기 작업을 선택하면 "open"이 동일한 URL과의 두 번째 상호 작용을 나타내므로 이 메서드가 호출되지 않습니다. "

라는데! 잠시 후에 interaction에 대해 알아보고 나서 이 discussion을 이해할 수 있게 될겁니다!

 

❓유의할 점!

👉🏻 Link는 선택가능해야 웹으로 연결하든, 어떤 액션을 취할 수 있으니 textView.isSelectable속성을 true로 설정해주어야 하고, 위에 다뤘던 isEditable속성은 false로 설정해주어야 합니다!

 

💡 파라미터

 

 

textView(_:shouldInteractWith:in:interaction:)의 매개변수로는 textView, URL, characterRange, interaction이 있네요! 다른 매개변수들은 익숙한데, 낯선 매개변수가 하나 보입니다!

 

interaction.. 이 친구의 값를 어찌저찌 이용해서 링크도 연결하고 하는 것 같은 느낌이 드는데.. 자세히 들여보니 요 interaction친구는 UITextItemInteraction 타입의 value를 가진다고 하네요! 그럼 UITextItemInteraction 타입을 한번 들여다볼까요?

 

UITextItemInteraction

: 사용자가 URL 또는 텍스트 첨부 파일과의 상호 작용 유형을 나타내는 상수입니다.

 

enum 타입의 상수네요! 그럼 어떠한 case들을 가지고 있는지 살펴봅시다!

 

public enum UITextItemInteraction : Int {

    
    case invokeDefaultAction = 0

    case presentActions = 1

    case preview = 2
}

 

< 테스트 >

기본 제공되는 interaction들을 사용하기 위해 return 값은 true로 설정해주었고,

각 case별 테스트를 위해 print문을 통해 interaction.rawValue를 콘솔에 찍어보았습니다.

/// shouldInteractWith URL - 텍스트뷰 내 link와 interact하는 메서드
    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
        print("interactionCase::::", interaction.rawValue)
        return true
    }

 

⚙️ case 0 : invokeDefaultAction

 

단순 링크 클릭의 default value는 역시나 case0번인 invokeDefaultAction이었습니다.

여기서 interaction의 default value가 외부 사파리 앱으로 연결되는 방식이라는 것을 알 수 있네요!

 

 

⚙️ case 1 : presentActions

 

링크를 꾹 클릭해서 목록 보기를 수행했을 때의 interaction value는 case1번인 presentActions였습니다.

 

📌 여기서 목록 보기를 수행 후 Open Link를 눌렀을 때 아까 case0번과 동일한 interaction인 외부 사파리 링크로 연결 동작이 수행되었지만 콘솔에는 case 1번만 찍히고(링크 꾹 눌러 목록 열기 동작에 관한 interaction만 찍힘) 외부 링크로 연결되는 case0번에 대한 textView interaction 메서드는 호출되지 않았네요!

 

 

🤗 여기서 아까 살펴본 discussion을 다시 들여다볼까요~? 

" 이 메서드는 URL 링크와의 첫 번째 상호 작용에서만 호출됩니다. 예를 들어, 이 메서드는 사용자가 URL과의 첫 번째 상호 작용을 통해 수행할 수 있는 작업 목록을 표시하려는 경우 호출됩니다. 사용자가 목록에서 열기 작업을 선택하면 "open"이 동일한 URL과의 두 번째 상호 작용을 나타내므로 이 메서드가 호출되지 않습니다. "

네! 이러한 이유에서 메서드가 호출되지 않았던 것입니다 :) 이제 완벽히 이해가 되네요!!

 


 

🙋🏻‍♀️ 그런데, 저희의 처음 목표는 인앱 브라우저에서 링크를 연결하는 것이었는데, textView에서 제공하는 interaction의 open link 방식은 외부 사파리앱과의 링크 연동이네요..! 😅😅

 

🙋🏻‍♀️🙋🏻‍♀️ 외부 사파리앱으로 링크 연결을 하고 싶으신 분들은 메서드의 return value를 true로 설정해주시고 여기서 코드를 마무리해주시면 됩니다!

/// shouldInteractWith URL - 텍스트뷰 내 link와 interact하는 메서드
    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
        return true
    }

 

 

💡 그렇다면, 인앱에서 링크를 열기 위해서는 어떻게 해야 할까요 ?

👉🏻 바로 네! 지정된 URL interaction을 사용하지 않기 위해 return value를 false로 설정해주고, 해당 메서드 내에 원하는 동작(SFSafariController로 인앱에서 링크를 연결)을 코드로 작성해주면 되겠죠!


< 구현 방법 >

1️⃣ import SafariServices 

import SafariServices

 

2️⃣ textView의 대리자 위임 (delegate 설정)

textView.delegate = self

 

3️⃣ SFSafariViewController 이용하여 인앱에서 링크 연결 및 textView(_:shouldInteractWith:in:interaction:) 메서드의 return value false로 설정

/// shouldInteractWith URL - 텍스트뷰 내 link와 interact하는 메서드
    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
        let safariView: SFSafariViewController = SFSafariViewController(url: URL)
        self.present(safariView, animated: true, completion: nil)
        return false
    }

 

 

 

전체 코드 첨부 

//
//  ViewController.swift
//  iOS-Practice
//
//  Created by hwangJi on 2022/02/13.
//

import UIKit

class ViewController: UIViewController {

    // MARK: IBOutlet
    @IBOutlet var textView: UITextView!  {
        didSet {
            textView.delegate = self
            textView.isSelectable = true
            textView.isEditable = false
            textView.dataDetectorTypes = .link
        }
    }
    
    // MARK: Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

// MARK: - UITextViewDelegate
extension ViewController: UITextViewDelegate {

/// shouldInteractWith URL - 텍스트뷰 내 link와 interact하는 메서드
    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
        let safariView: SFSafariViewController = SFSafariViewController(url: URL)
        self.present(safariView, animated: true, completion: nil)
        return false
    }
}

 

 

이렇게 오늘은 swift에서 텍스트뷰의 텍스트 내 링크를 하이퍼링크로 연결하는 방법을 알아봤습니다!!

유익한 정보가 되었으면 좋겠네요:)

도움이 되셨다면 공감 꾸욱 눌러주세요 👍🏻💜

 

 

https://github.com/hwangJi-dev

 

hwangJi-dev - Overview

hwangJi-dev has 14 repositories available. Follow their code on GitHub.

github.com

 

반응형
Comments