본문 바로가기

학습 노트/iOS (2021)

043 ~ 052. Label, Text Field, Text View and Text Input

Label


SingleLine Label

사용하는 씬의 구성은 위와 같다.

Label의 속성들은 위와 같다.

  • Text
    Plain를 선택하면 일반적인 텍스트를 사용한다.
    Attributed를 선택하면 문자열에 속성을 추가할 수 있다.(Attributed String에서 이어진다)
  • Color
    텍스트의 색을 설정한다.
    Background Color와 비교하기 위해 Foreground Color라고 부르기도 한다.

  • Font
    폰트의 크기와 종류, 스타일을 변경할 수 있다.
  • Alignment
    텍스트의 정렬 속성이다.
    기본값은 Netural인데 언어 설정에 따라 글의 정렬이 달라진다.
//
//  SingleLabelViewController.swift
//  HandlingText
//
//  Created by Martin.Q on 2021/08/22.
//

import UIKit

class SingleLabelViewController: UIViewController {
    @IBOutlet weak var Label: UILabel!
    
    @IBAction func redAction(_ sender: Any) {
    }
    @IBAction func blueAction(_ sender: Any) {
    }
    @IBAction func blackAction(_ sender: Any) {
    }
    
    @IBOutlet weak var segment: UISegmentedControl!
    @IBAction func segAction(_ sender: Any) {
    }
    
    @IBAction func stepperAction(_ sender: UIStepper) {
    }
    
    

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

씬과 연결 된 코드는 위와 같다.

 

색 변경하기

@IBAction func redAction(_ sender: Any) {
	Label.textColor = UIColor.systemRed
}
@IBAction func blueAction(_ sender: Any) {
	Label.textColor = UIColor.systemBlue
}
@IBAction func blackAction(_ sender: Any) {
	Label.textColor = UIColor.black
}

textColor 속성의 값을 변경해 주는 것으로 간단하게 색상을 변경할 수 있다.


결과


정렬 변경하기

@IBOutlet weak var segment: UISegmentedControl!
	@IBAction func segAction(_ sender: Any) {
		switch segment.selectedSegmentIndex {
		case 0:
			Label.textAlignment = .left
		case 1:
			Label.textAlignment = .center
		case 2:
			Label.textAlignment = .right
		case 3:
			Label.textAlignment = .justified
		case 4:
			Label.textAlignment = .natural
		default:
			return
	}
}

정렬 속성은 textAlignment이다.
segment의 selectedSegmentIndex를 확인하고, 해당 segment의 상태에 따라 정렬 속성을 변경해 주면 된다.

 open var textAlignment: NSTextAlignment // default is NSTextAlignmentNatural (before iOS 9, the default was NSTextAlignmentLeft)

textAlignment는 위와 같이 NSTextAlignment 속성으로 되어있고,

public enum NSTextAlignment : Int {
	case left = 0 // Visually left aligned
	
	case center = 1 // Visually centered
	
	case right = 2 // Visually right aligned
	
/* !TARGET_ABI_USES_IOS_VALUES */
// Visually right aligned
// Visually centered

	case justified = 3 // Fully-justified. The last line in a paragraph is natural-aligned.

	case natural = 4 // Indicates the default alignment for script
}

해당 속성은 위와 같이 정수의 원시값을 가진 열거형이므로,

@IBOutlet weak var segment: UISegmentedControl!
@IBAction func segAction(_ sender: Any) {
	guard let alignment = NSTextAlignment(rawValue: segment.selectedSegmentIndex) else {
		return
	}
	Label.textAlignment = alignment
}

switch문을 사용하지 않고, 위와 같이 selectedSegmentInsex를 textAlignment와 동기화해 주는 것으로도 구현 가능하다.


결과

 


크기 바꾸기

stepper는 위와 같이 초기값이 10이고, 최소값이 10, 최댓값이 100이며, 5 간격으로 값이 변경하도록 되어있다.

Label.font.pointSize

폰트의 크기는 font.pointSize 속성이 가지고 있다.
단, 해당 속성은 읽기 전용으로 직접 바꿀수가 없으므로, 새 폰트를 생성해서 Label에 할당하는 방식을 사용한다.

@IBAction func stepperAction(_ sender: UIStepper) {
	let new = Label.font.withSize(CGFloat(sender.value))
	Label.font = new
}

UIStepper의 값을 font의 withSize 속성에 전달하여 새로운 크기의 폰트를 만든 뒤,
이 폰트를 Label의 폰트로 설정한다.


결과

 


MultiLine Label

사용할 씬은 위와 같다.

현재 Label에는 위와 같이 한 줄에 감당하기 힘들 양의 텍스트가 지정되어 있고,

이런 경우 위와 같이 '...'으로 내용이 더 있음을 알릴 뿐, 생략하여 표현하게 된다.

Lines 속성을 하나 증가 시킬 때마다, 표현할 수 있는 줄의 수가 늘어나 내용을 조금 더 많이 표시할 수 있고,
0으로 설정하면 줄 수에 관계 없이 모든 내용을 표시할 수 있게 된다.

Line Break

  • Clip
    Lines가 0인 상태에선 의미가 없다.
    표현 범위를 벗어난 텍스트는 말줄임표로 줄이지 않고 그냥 잘라 버린다.
  • Character Wrap
    문자 단위로 줄바꿈을 진행한다.
  • Word Wrap
    단어 단위로 줄바꿈을 진행한다.
  • Truncate Head
    Lines가 0인 상태에선 의미가 없다.
    마지막 문장의 시작 부분을 생략한다.
  • Truncate Middle
    Lines가 0인 상태에선 의미가 없다.
    마지막 문장의 중간을 생략한다.
  • Truncate Tail
    Lines가 0인 상태에선 의미가 없다.
    마지막 문장의 끝을 생략한다.

AutoShirink

  • Minimum Font Scale
    해당 Line 수에서 내용을 전부 표현할 수 있도로 폰트의 크기를 자동으로 변경한다.
    0.1 ~ 1.0 사이의 값을 가지며, 해당 값은 기존 폰트의 크기 대비 비율의 값이다.
    만약 폰트크기를 조절해서도 내용을 전부 표시할 수 없다면 LineBreak 설정 값에 따라 생략을 진행한다.
  • Tighten Letter Spacing
    자간을 줄인다.
    단, 시스템 폰트를 사용하는 경우 줄일 자간이 존재하지 않기 때문에 영향이 없다.
//
//  MultiLableViewController.swift
//  HandlingText
//
//  Created by Martin.Q on 2021/08/22.
//

import UIKit

class MultiLableViewController: UIViewController {
	@IBOutlet weak var label: UILabel!
	@IBOutlet weak var stepperIndex: UILabel!
	@IBOutlet weak var stepper: UIStepper!
	@IBOutlet weak var switchBtn: UISwitch!
	@IBAction func stepperAction(_ sender: UIStepper) {
	}
	@IBAction func switchAction(_ sender: UISwitch) {
	}
	
	@IBOutlet weak var picker: UIPickerView!
	func changeLineBreak() {
	
	}
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}
}

extension MultiLableViewController: UIPickerViewDataSource {
	func numberOfComponents(in pickerView: UIPickerView) -> Int {
		return 1
	}
	
	func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
		return 6
	}
}

extension MultiLableViewController: UIPickerViewDelegate {
	func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
		switch row {
		case 0:
			return "Word Wrap"
		case 1:
			return "Character Wrap"
		case 2:
			return "Clip"
		case 3:
			return "Truncating Head"
		case 4:
			return "Truncating Tail"
		case 5:
		return "Truncating Middle"
			default:
		return nil
		}
	}

	func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
		changeLineBreak()
	}
}

씬과 연결 된 코드는 위와 같다.
extension으로 UIPickerViewDataSource와 UIPickerViewDelegate를 채용하고,
필수 속성을 작성한다.
DataSource의 컴포넌트는 1, 선택 항목은 6이며,
Delegate의 항목의 속성을 기입하고, 항목을 선택할 때 changeLineBreak 함수를 호출한다.

초기화 하기

override func viewDidLoad() {
	super.viewDidLoad()
	stepperIndex.text = "\(label.numberOfLines)"
	stepper.value = Double(label.numberOfLines)
	
	switchBtn.isOn = label.minimumScaleFactor != 0 && label.adjustsFontSizeToFitWidth
	
	picker.selectRow(label.lineBreakMode.rawValue, inComponent: 0, animated: false)
}

뷰가 로드되면, label의 속성 상태와 여러 뷰들의 상태를 동기화해 준다.

이 중 label의 AutoShrink 속성은 제공하지 않으므로,
minimumScalMode과 adjustsFontSizeToFitWidth 속성을 조합해 이를 계산한다.

Lines 변경하기

@IBAction func stepperAction(_ sender: UIStepper) {
	label.numberOfLines = Int(sender.value)
	stepperIndex.text = "\(sender.value)"
}

stepper는 1~10의 값을 가지며 1의 간격으로 값을 변경하도록 설정되어있다.
해당 매커니증으로 label의 numberOfLines 속성을 변경하고, 이를 stpperIndex에 업데이트한다.


결과

 


AutoShirink

@IBAction func switchAction(_ sender: UISwitch) {
	label.minimumScaleFactor = sender.isOn ? 0.5 : 0.0
	label.adjustsFontSizeToFitWidth = sender.isOn
}

AutoShirink 속성을 제공하지 않기 때문에 초기화 때와 마찬가지로
minimumScaleFactor, adjustFontSizeToFitWidth 속성 두 가지로 이를 구현한다.

minimumScaleFactor는 0.0 ~ 1.0 사이의 값을 가지며, 0.0을 저장할 경우 Fixed Scale 설정과 같아진다.
따라서 switch의 값에 따라 0.5의 배율이나 FixedScale로 설정한다.

adjustFontSizeToFitWidth는 Boolean에 따라 minimumScaleFactor의 배율로 크기가 조정된다.
따라서 switch의 값을 그대로 전달한다.


결과

 


func changeLineBreak(with rawValue: Int) {
	if let mode = NSLineBreakMode(rawValue: rawValue) {
		label.lineBreakMode = mode
	}
}

picker는 현재 선택된 항목의 인덱스를 받아와 NSLineBreakMode에 전달한다.

public enum NSLineBreakMode : Int {
	case byWordWrapping = 0 // Wrap at word boundaries, default
	
	case byCharWrapping = 1 // Wrap at character boundaries
	
	case byClipping = 2 // Simply clip
	
	case byTruncatingHead = 3 // Truncate at head of line: "...wxyz"
	
	case byTruncatingTail = 4 // Truncate at tail of line: "abcd..."
	
	case byTruncatingMiddle = 5 // Truncate middle of line:  "ab...yz"
}

NSLineBreakMode는 위와 같이 정수의 원시 값을 가지는 열거형 구조로,
picker에 원시값을 전달하는 것으로 충분하다.


결과

 


Text Field

TextField는 사용자로부터 입력을 받을 때 사용한다.
한 줄 입력에 최적화 되어있다.

  • Text
    TextField에 출력할 문자열을 입력한다.
    단, TextField는 주로 사용자에게 입력을 받기 위해 사용하므로 특별한 이유가 없다면 사용하지 않는다.
  • Color
    색을 설정한다.
  • Font
    폰트를 설정한다.
  • Alignment
    정렬 방식을 결정한다.
  • Placeholder
    어떤 값을 입력해야 하는지에 대한 문구를 입력한다.
    해당 값은 TextField 값에 영향을 미치지 않는다.

//
//  TextFieldViewController.swift
//  HandlingText
//
//  Created by Martin.Q on 2021/08/22.
//

import UIKit

class TextFieldViewController: UIViewController {
    @IBOutlet weak var textField: UITextField!
    @IBOutlet weak var label: UILabel!
    
    @IBAction func printAction(_ sender: Any) {
    }
    

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

사용할 씬의 구성과 연결된 코드는 위와 같다.

 @IBAction func printAction(_ sender: Any) {
 	label.text = textField.text
 }

간단하게 textField의 text 속성을 통해 입력된 값을 받아와 label의 text 속성을 바꿔 주는 것으로,
label의 내용을 입력한 값의 내용으로 대체할 수 있다.

override func viewDidLoad() {
	super.viewDidLoad()
	textField.placeholder = "hahaha"
}

placeholder 속성에 지정할 값을 전달해 변경한다.
뷰가 처음 초기화됐을 때 한 번 실행되는 것으로 충분하기 때문에 viewDidLoad에서 진행한다.


결과

 


Border Style

//
//  BorderTextFieldViewController.swift
//  HandlingText
//
//  Created by Martin.Q on 2021/08/22.
//

import UIKit

class BorderTextFieldViewController: UIViewController {
	@IBOutlet weak var textField: UITextField!
	@IBOutlet weak var segment: UISegmentedControl!
	@IBOutlet weak var switchBtn: UISwitch!
	
	@IBAction func segmentAction(_ sender: UISegmentedControl) {
	}
	@IBAction func switchAction(_ sender: UISwitch) {
	}
	
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}
}

사용할 씬과 연결된 코드는 위와 같다.

@IBAction func segmentAction(_ sender: UISegmentedControl) {
	let option = UITextField.BorderStyle(rawValue: sender.selectedSegmentIndex) ?? .roundedRect
	textField.borderStyle = option
}

segment의 인덱스를 사용해 borderStyle을 지정하고, 이를 textField에 적용한다.

 public enum BorderStyle : Int {
	
	case none = 0
	
	case line = 1
	
	case bezel = 2
	
	case roundedRect = 3
}

UITextField의 BorderStyle 속성은 정수형의 원시 값을 같은 열거형의 구조로,
segment의 정수형 인덱스를 원시 값에 전달하는 것으로 충분하다.


결과

 


None을 사용하는 경우 placeholder조차 없다면 존재하는지 구분하기 힘들어지므로,
CustomDesign을 적용하거나 색상을 넣어 구별할 수 있는 방법을 고안해야 한다.

 

Customizing


Background

Background는 textField의 속성에 영향을 받는다.
기본 설정값인 rounded로 되어 있는 경우 background를 사용할 수 없다.

@IBAction func switchAction(_ sender: UISwitch) {
	textField.isEnabled = switchBtn.isOn
}

스위치와 textField의 isEnabled 속성을 동기화해 주면 Disabled에 설정한 Background 이미지도 확인할 수 있다.


결과

 


Overlay

TextField에는 Clear Button이라는 속성이 존재한다.
해당 설정 값을 바꿈으로써 textField에 입력된 값을 한 번에 지울 수 있는 버튼이 표시된다.


결과

더 아래의 Clear when editing begins 옵션은 편집을 시작하면 이전에 입력됐던 값을 자동으로 비운다.


결과

 


위의 Clear Button처럼 TextField 내에 표시되는 버튼을 overlayView라고 부른다.
TextField의 왼쪽과 오른쪽에 하나씩 추가할 수 있고, interfaceBuilder에서는 삭제 버튼의 표시 방식만 설정할 수 있다.

//
//  OverlayTextFieldViewController.swift
//  HandlingText
//
//  Created by Martin.Q on 2021/08/22.
//

import UIKit

class OverlayTextFieldViewController: UIViewController {
	@IBOutlet weak var textField: UITextField!
		
	@objc func showPredefinedValue() {
		let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
		let one = UIAlertAction(title: "One", style: .default) { (action) in
			self.textField.text = action.title
		}
		let two = UIAlertAction(title: "Two", style: .default) { (action) in
			self.textField.text = action.title
		}
		let three = UIAlertAction(title: "Three", style: .default) { (action) in
			self.textField.text = action.title
		}
			
		alert.addAction(one)
		alert.addAction(two)
		alert.addAction(three)
				
		present(alert, animated: true, completion: nil)
	}
		
	override func viewDidLoad() {
		super.viewDidLoad()
	
	}
}

사용할 씬과 연결된 코드는 위와 같다.

override func viewDidLoad() {
	super.viewDidLoad()
	
	let btn = UIButton(type: .custom)
	btn.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
	btn.setImage(UIImage(named: "menu"), for: .normal)
}

출력할 버튼을 하나 디자인한다.
크기는 20*20으로, 사용하는 이미지는 미리 넣은 이미지이다.

override func viewDidLoad() {
	super.viewDidLoad()
	
	let btn = UIButton(type: .custom)
	btn.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
	btn.setImage(UIImage(named: "menu"), for: .normal)
	btn.addTarget(self, action: #selector(showPredefinedValue), for: .touchUpInside)
}

이후 앞서 작성한 alert와 해당 버튼을 연결한다.

override func viewDidLoad() {
	super.viewDidLoad()
	
	let btn = UIButton(type: .custom)
	btn.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
	btn.setImage(UIImage(named: "menu"), for: .normal)
	btn.addTarget(self, action: #selector(showPredefinedValue), for: .touchUpInside)
	
	textField.leftView = btn
}

이후엔 textField의 leftView나 rightView 속성에 버튼을 추가하면 된다.

leftView와 rightView 속성은 위처럼 UIView 형식을 하고 있어 UIView를 상속하는 모든 View를 할당할 수 있다.

override func viewDidLoad() {
	super.viewDidLoad()
	
	let btn = UIButton(type: .custom)
	btn.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
	btn.setImage(UIImage(named: "menu"), for: .normal)
	btn.addTarget(self, action: #selector(showPredefinedValue), for: .touchUpInside)
	
	textField.leftView = btn
	textField.leftViewMode = .always
}

다음엔 해당하는 View의 모드를 변경해 준다.


결과

 


Text View


TextView는 TextField와는 달리 MultiLine 입력과 스크롤을 지원한다.
또한 텍스트 출력에 주로 사용된다.

TextView의 속성 중 Behavior 속성은 textView의 수정, 선택 가능 여부를 선택한다.

현 상태의 TextView는 모든 내용을 출력하기엔 공간이 모자라다.
따라서 오른쪽처럼 스크롤의 형태로 콘텐츠를 볼 수 있도록 처리한다.

 

TextView의 여백

기본 여백은 top, bottom이 8pt, trailing과 leading이 0pt이다.
여백 설정은 TextContainerInset 속성을 통해 설정한다.
해당 설정은 interfaceBuilder에선 지원하지 않기 때문에 코드로 작성해야 한다.

//
//  TextViewViewController.swift
//  HandlingText
//
//  Created by Martin.Q on 2021/08/22.
//

import UIKit

class TextViewViewController: UIViewController {
    @IBOutlet weak var textView: UITextView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

연결된 코드는 위와 같다.

override func viewDidLoad() {
	super.viewDidLoad()
	textView.textContainerInset = UIEdgeInsets(top: 30, left: 0, bottom: 30, right: 0)
}

textContainerInset에 접근해 설정을 진행하고,
해당 속성은 UIEdgeInset 구조체를 사용해 수치를 지정한다.


결과


좌측의 기존과 우측의 지금을 비교해 봤을 때 상하에 큰 여백이 생긴 것을 확인할 수 있다.
단, 스크롤바는 이전과 같은 위치에서 시작되고, 끝난다.

class TextViewViewController: UIViewController {
	@IBOutlet weak var textView: UITextView!
	
	override func viewDidLoad() {
		super.viewDidLoad()
		textView.textContainerInset = UIEdgeInsets(top: 30, left: 0, bottom: 30, right: 0)
		textView.scrollIndicatorInsets = textView.textContainerInset
	}
}

textContainerInset을 변경했을 경우 scrollIndicatorInset도 동일하게 변경해 줘야
여백을 고려한 ScrollIndicator를 구현할 수 있다.


결과


좌측의 전과 다르게 우측의 지금은 TextContainer의 영역만큼만 Scroll Indicator가 표시된다.

Image attach

//
//  AttatchViewController.swift
//  HandlingText
//
//  Created by Martin.Q on 2021/08/23.
//

import UIKit

class AttachViewController: UIViewController {
	@IBOutlet weak var textView: UITextView!
	
	let image = UIImage(named: "splash")
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}
}

씬의 구성과 연결된 코드는 위와 같다.
'splash'라는 이름의 이미지를 불러오고 있다.

TextView는 텍스트를 출력할 때 TextKit을 사용한다.
내부에는 TextContainer가 존재하고, 보통은 텍스트만 출력하지만 이미지를 함께 표시하는 것도 가능하다.

override func viewDidLoad() {
	super.viewDidLoad()
	let attachment = NSTextAttachment()
}

텍스트에 이미지나 데이터를 추가하고 싶다면 위와 같이 NSTextAttatchment 클래스를 사용해야 한다.

override func viewDidLoad() {
        super.viewDidLoad()
        
        let attachment = NSTextAttachment()
        attachment.image = image
        
        textView.textStorage.insert(attrString: NSAttributedString, at: Int)
    }

NSTextAttachment 속성에 첨부할 이미지를 저장하고,
실질적인 첨부를 위해 textView의 textStorage에 접근해 insert 메서드를 실행한다.
보이는 것과 같이 첨부할 파라미터의 형식은 NSAttributedString 형식으로 전달해야 하므로

override func viewDidLoad() {
	super.viewDidLoad()
	
	let attachment = NSTextAttachment()
	attachment.image = image
	
	let attachmentAttr = NSAttributedString(attachment: attachment)
	
	textView.textStorage.insert(attachmentAttr, at: 0)
}

위처럼 NSAttributedString으로 바꾸어 전달하고, 첨부할 위치를 함께 전달한다.


결과

 


Selection

//
//  SelectionViewController.swift
//  HandlingText
//
//  Created by Martin.Q on 2021/08/23.
//

import UIKit

class SelectionViewController: UIViewController {
	@IBOutlet weak var textView: UITextView!
	@IBOutlet weak var rangeLabel: UILabel!
	
	@IBAction func selectAction(_ sender: Any) {
		let word = "pariatur?"
	}
	
		
	override func viewDidLoad() {
		super.viewDidLoad()
	}
}

사용할 씬과 코드의 연결은 위와 같다.
TextView의 delegate는 해당 씬으로 연결한다.

extension SelectionViewController: UITextViewDelegate {
	func textViewDidChangeSelection(_ textView: UITextView) {
	
	}
}

extension으로 씬의 코드가 UITextViewDelegate를 채용하도록 한 뒤,
textViewDidChangeSelection 메소드를 사용한다.
해당 메서드는 TextView의 특정 범위가 선택되면 호출된다.

extension SelectionViewController: UITextViewDelegate {
	func textViewDidChangeSelection(_ textView: UITextView) {
		textView.selectedRange
	}
}

선택된 범위는 위와 같이 selectedRange를 통해 얻을 수 있고,
해당 속성은 NSRange 형식이다.

extension SelectionViewController: UITextViewDelegate {
	func textViewDidChangeSelection(_ textView: UITextView) {
		let range = textView.selectedRange
		rangeLabel.text = "\(range)"
	}
}

그렇게 받아 온 범위를 Label에 표시한다.


결과

 


선택된 좌표를 Label에 표시하게 된다.

@IBAction func selectAction(_ sender: Any) {
	let word = "pariatur?"
	
	if let text = textView.text as NSString? {
		let range = text.range(of: word)
		textView.selectedRange = range
	}
}

코드를 사용해 텍스트를 선택할 수도 있다.
이때 selectedRange의 형식이 NSRange이므로 NSString으로 변환해 사용하는 것이 제일 간단하다.
원본 텍스트를 불러와 range(of:) 메서드로 범위를 검색하고, 해당 범위를 selectedRange로 설정한다.

단, 이 경우 선택은 되지만 자동으로 해당 지점까지 스크롤되진 않는다.

@IBAction func selectAction(_ sender: Any) {
	let word = "pariatur?"
	
	if let text = textView.text as NSString? {
		let range = text.range(of: word)
		textView.selectedRange = range
		textView.scrollRangeToVisible(range)
	}
}

위처럼 scrollRangeToVisible 메서드를 사용해 해당 범위까지 스크롤할 수 있도록 해야 한다.


결과


해당 범위까지 자동으로 스크롤된다.
단, 선택된 범위가 자동으로 highlihgt 되진 않는다.
TextView는 보기 모드와 편집 모드로 나뉘며,
보기 모드에선 강조되지 않지만, 편집 모드에선 강조가 되는 걸 확인할 수 있다.

DataDetection

씬은 위와 같고,
TextView의 속성에는 DataDetector가 존재한다.
해당 속성엔 다양한 형식의 텍스트를 인식할 수 있는 옵션이 존재한다.
단, 해당 형식을 감지하기 위해선 편집 모드를 비 활성해야 한다.


결과

 


URL을 인식하여 터치하면 사파리를 통해 연결해 주는 것을 확인할 수 있다.
감지된 텍스트를 가장 적합한 앱으로 자동 연결해 주며,
해당 옵션이 켜져 있다고 항상 정확히 인식하는 것은 아님을 명심하자.

//
//  DetectionViewController.swift
//  HandlingText
//
//  Created by Martin.Q on 2021/08/23.
//

import UIKit

class DetectionViewController: UIViewController {
	@IBOutlet weak var textView: UITextView!
	
	override func viewDidLoad() {
		super.viewDidLoad()
		textView.dataDetectorTypes = [.link, .address]
	}
}

코드로는 위와 같이 구현한다.

 

Text Input


키보드를 통해 입력을 받는 모든 컨트롤은 UITextUnputTraits 프로토콜을 구현하고 있다.
따라서 TextView와 TextField에 모두 적용 가능하다.

설정에서 키보드 설정이 활성화되어 있으면 앱에서 재설정할 수 있지만,
비활성화되어 있다면 앱에서는 재설정할 수 없다.

즉 위와 같이 Smart Punctuation 속성이 비활성화되어있다면,
앱에선 해당 속성을 사용할 수 없다.

사용할 씬은 위와 같다.

class InputViewController: UIViewController {
	@IBOutlet weak var textField: UITextField!
	@IBAction func segmentAction(_ sender: UISegmentedControl) {
	}
	
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}
}

연결된 코드는 위와 같다.

Capitalization

대소문자의 처리 방식을 담당한다.

  • Words
    어절의 첫 글자를 대문자로 바꿔준다.
  • Sentences
    문장의 첫 글자를 대문자로 바꿔준다.
  • All Characters
    모든 글자를 대문자로 바꿔준다.
class InputViewController: UIViewController {
	@IBOutlet weak var textField: UITextField!
	@IBAction func segmentAction(_ sender: UISegmentedControl) {
		let type = UITextAutocapitalizationType(rawValue: sender.selectedSegmentIndex) ?? .none
		textField.autocapitalizationType = type
	}
	
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}
}

해당 속성을 설정하기 위해선 autocapitalizationType을 변경해야 한다.

해당 속성은 UITextAutocapitalizationType 형식이며,
따라서 segment의 값을 기준으로 새 속성을 생성해 전달해야 한다.


결과


하지만 제대로 작동하지 않는다.
해당 속성을 변경할 때는 편집 모드 상태에선 적용되지 않기 때문에,
편집모드 종료 > 모드 변경 > 편집모드 재진입의 과정이 필요하다.

@IBAction func segmentAction(_ sender: UISegmentedControl) {
	textField.resignFirstResponder()
	let type = UITextAutocapitalizationType(rawValue: sender.selectedSegmentIndex) ?? .none
	textField.autocapitalizationType = type
	textField.becomeFirstResponder()
}

따라서 위와 같이 코드를 수정하면 정상적으로 설정이 변경된다.


결과

 


Correction

입력되는 문자열을 통해 해당되는 단어를 자동으로 매칭 한다.

  • Default
    context에 따라 자동으로 처리한다.
    대부분의 경우 Yes와 결과가 동일하다.
  • No
    사용하지 않는다면 No를 설정한다.
  • Yes
    결과가 Default와 동일하기 때문에 Yes 보다는 Default로 사용하는 경우가 많다.

//
//  CorrectionViewController.swift
//  HandlingText
//
//  Created by Martin.Q on 2021/08/23.
//

import UIKit

class CorrectionViewController: UIViewController {
	@IBOutlet weak var textField: UITextField!
	
	@IBAction func segmentAction(_ sender: UISegmentedControl) {
	}
	
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}
}

사용할 씬과 연결된 코드는 위와 같다.

class CorrectionViewController: UIViewController {
	@IBOutlet weak var textField: UITextField!
	
	@IBAction func segmentAction(_ sender: UISegmentedControl) {
		textField.resignFirstResponder()
		let option = UITextAutocorrectionType(rawValue: sender.selectedSegmentIndex) ?? .default
		textField.autocorrectionType = option
		textField.becomeFirstResponder()
	}
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}
}

이전과 동일하게 autocorrectionType의 옵션은 UITextAutocorrectionType의 형식으로 되어있다.
따라서 해당 형식의 생성자에 segment의 인덱스를 rawValue로 전달한다.
마찬가지로 설정값을 바꾸기 전, 후로 편집 모드를 껐다 켜 줘야 한다.


결과


Spell Checking

오탈자를 자동으로 검사하고, 수정한다.

//
//  SpellViewController.swift
//  HandlingText
//
//  Created by Martin.Q on 2021/08/23.
//

import UIKit

class SpellViewController: UIViewController {
	@IBOutlet weak var textView: UITextView!
	@IBAction func segmentAction(_ sender: UISegmentedControl) {
	}
	
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}
}

사용할 씬과 연결할 코드는 위와 같다.

Spell Checking설정 또한 세 가지 선택이 가능하다.
해당 옵션은 Correction이 켜지면 자동으로 켜지게 되고,
맞춤법에 어긋나는 지점을 표시하고, 붉은 밑줄을 표시하게 된다.

class SpellViewController: UIViewController {
	@IBOutlet weak var textView: UITextView!
	@IBAction func segmentAction(_ sender: UISegmentedControl) {
		let option = UITextSpellCheckingType(rawValue: sender.selectedSegmentIndex) ?? .default
		textView.spellCheckingType = option
	}
	
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}
}

코드는  spellCheckingType 속성에 UITextSpellCheingType 형식으로 이전과 동일한 방식으로 작성된다.


결과

 


Secure Text Entry

가장 아래쪽에 존재하는 이 옵션은 비밀번호 등 유출의 위험이 존재하는 텍스트 입력 시,
자동으로 가리게 되는 옵션이다.

//
//  SecureViewController.swift
//  HandlingText
//
//  Created by Martin.Q on 2021/08/23.
//

import UIKit

class SecureViewController: UIViewController {
	@IBOutlet weak var textField: UITextField!
	
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}
	
}

사용할 씬과 코드는 위와 같다.

//
//  SecureViewController.swift
//  HandlingText
//
//  Created by Martin.Q on 2021/08/23.
//

import UIKit

class SecureViewController: UIViewController {
	@IBOutlet weak var textField: UITextField!
	
	
	override func viewDidLoad() {
		super.viewDidLoad()
		textField.isSecureTextEntry = true
	}
	
}

해당 속성은 isSecureTextEntry 속성으로 설정한다.


결과


오른쪽과 같이 입력되는 텍스트를 자동으로 가려준다.
또한, 자동완성 기능이나 복사와 같은 기능이 비활성화된다.

 

Smart Punctuation

공백과 구두점 등을 자동으로 입력해 주는 기능으로 Smart Dashes, Smart Insert, Smart Quotes 세 가지의 옵션으로 제공한다.
마찬가지로 Dfault, No, Yes의 세가지 선택지가 주어지며, 특징은 비슷하다.

//
//  SPunctuationViewController.swift
//  HandlingText
//
//  Created by Martin.Q on 2021/08/23.
//

import UIKit

class SPunctuationViewController: UIViewController {
	@IBOutlet weak var textField: UITextField!


	override func viewDidLoad() {
		super.viewDidLoad()
		textField.smartQuotesType = .default
		textField.smartDashesType = .default
		textField.smartInsertDeleteType = .default
	}
}

각각의 옵션에 접근해서 설정한다.

  • Smart Dashes
    연속해서 입력한 하이픈이 mdash나 ndash로 치환된다.
  • Smart Insert
    문자열을 추가하거나 삭제할 때 공백을 자동으로 추가한다.
  • Smart Quotes
    인용부호 등을 언어 설정에 적합한 문자로 치환한다.