Avatar ·

Как скопировать текст в буфер обмена (UIPasteboard) на Swift?

Всем привет! Я новичок в iOS-разработке на Swift, и у меня возникла, казалось бы, простая проблема. Мне нужно, чтобы при нажатии на кнопку текст из текстового поля (UITextField) или просто строка, которую я задаю программно, копировалась в буфер обмена, чтобы пользователь мог потом вставить её в другое приложение. Я нашёл в документации класс UIPasteboard, но примеров кода на Swift совсем мало, в основном на Objective-C. Я пробовал написать что-то вроде: ```swift @IBAction func copyButtonTapped(_ sender: UIButton) { let textToCopy = myTextField.text UIPasteboard.general.string = textToCopy } ``` Но это не сработало — ничего не копируется. Может быть, я неправильно обращаюсь к свойству `string`? Или нужно делать какие-то дополнительные настройки? Ещё я слышал, что в iOS 14+ появились какие-то уведомления о вставке из буфера обмена, и это может влиять на работу. Подскажите, пожалуйста, как правильно и надёжно скопировать текст в буфер обмена на Swift, чтобы это работало на всех версиях iOS, начиная с 12-й? Желательно с простым и понятным примером для кнопки. <<>>

Копирование текста в буфер обмена с помощью UIPasteboard

Ваш код в целом верен, но есть несколько нюансов, которые стоит учесть. Основной класс для работы с буфером обмена в iOS — UIPasteboard. Его синглтон general предоставляет доступ к системному буферу обмена. Вот правильный и полный пример для кнопки:

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var textField: UITextField!
    @IBOutlet weak var copyButton: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Дополнительная настройка, если нужно
    }
    
    @IBAction func copyButtonTapped(_ sender: UIButton) {
        // 1. Проверяем, что текст не nil и не пустой
        guard let text = textField.text, !text.isEmpty else {
            // Можно показать alert, что нечего копировать
            print("Текст пуст")
            return
        }
        
        // 2. Копируем в буфер обмена
        UIPasteboard.general.string = text
        
        // 3. (Опционально) Показываем пользователю обратную связь
        // Например, меняем текст кнопки на "Скопировано!" на секунду
        let originalTitle = copyButton.title(for: .normal)
        copyButton.setTitle("Скопировано!", for: .normal)
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
            self.copyButton.setTitle(originalTitle, for: .normal)
        }
    }
}

Почему ваш код мог не работать?

  • Проверка на nil: Если textField.text равен nil, присвоение UIPasteboard.general.string = nil просто очистит буфер обмена. Всегда проверяйте значение через guard или if let.
  • Соединение IBOutlet: Убедитесь, что textField и copyButton правильно подключены в Storyboard или XIB. Если соединение отсутствует, код не выполнится.
  • Вызов на главном потоке: Все UI-операции должны выполняться на главном потоке. Ваш @IBAction обычно вызывается оттуда, но если вы вызываете копирование из фоновой очереди (например, после URLSession), оберните в DispatchQueue.main.async.

Дополнительные возможности UIPasteboard

Вы можете копировать не только строки, но и другие типы данных:

// Копирование изображения
UIPasteboard.general.image = UIImage(named: "example")

// Копирование URL
UIPasteboard.general.url = URL(string: "https://example.com")

// Копирование нескольких объектов (например, для богатого текста)
let items: [[String: Any]] = [
    [UIPasteboard.typeAutomatic: "Текст", 
     UIPasteboard.typeAutomatic: UIImage(named: "icon") as Any]
]
UIPasteboard.general.items = items

Важное замечание о конфиденциальности (iOS 14+)

Начиная с iOS 14, система показывает уведомление, когда приложение читает данные из буфера обмена. Это не влияет на запись (копирование) — вы по-прежнему можете писать в UIPasteboard без каких-либо уведомлений. Однако, если ваше приложение читает буфер обмена (например, UIPasteboard.general.string), пользователь увидит предупреждение. Старайтесь читать буфер только в ответ на явное действие пользователя (например, нажатие кнопки "Вставить").

Совет для начинающих

Для большей надёжности и читаемости кода рекомендую вынести логику копирования в отдельный метод:

func copyToClipboard(_ text: String) {
    guard !text.isEmpty else { return }
    UIPasteboard.general.string = text
    // Дополнительно: вибрация или анимация для тактильного отклика
    let feedbackGenerator = UIImpactFeedbackGenerator(style: .medium)
    feedbackGenerator.prepare()
    feedbackGenerator.impactOccurred()
}

Таким образом, ваш код станет более модульным и его легко будет переиспользовать в других частях приложения.

<<>>

Альтернативный подход: использование UIPasteboard с типом данных

Вместо установки свойства string напрямую, можно использовать метод setValue(_:forPasteboardType:). Это даёт больше контроля над форматом данных и может быть полезно, если вы работаете с разными типами контента (например, HTML или RTF).

@IBAction func copyWithType(_ sender: UIButton) {
    guard let text = textField.text else { return }
    let pasteboard = UIPasteboard.general
    pasteboard.setValue(text, forPasteboardType: "public.plain-text")
}

Когда это может пригодиться? Если вы копируете форматированный текст (например, из UILabel с атрибутами), вы можете указать тип "public.html" или "public.rtf". Это позволит другим приложениям (например, Pages или Mail) правильно распознать форматирование.

Ещё один вариант — использование обёртки Pasteboard для SwiftUI. Если вы пишете на SwiftUI (iOS 14+), можно использовать модификатор .onTapGesture и встроенный UIPasteboard:

import SwiftUI

struct ContentView: View {
    @State private var text = "Привет, мир!"
    
    var body: some View {
        Text(text)
            .onTapGesture {
                UIPasteboard.general.string = text
            }
    }
}

Этот подход более лаконичный, но требует импорта UIKit в SwiftUI-проекте (что обычно делается автоматически). Помните, что в SwiftUI нет прямого аналога UIPasteboard, поэтому приходится использовать UIKit.