Как создать общий текстовый редактор с помощью Swift

1656613453 kak sozdat obshhij tekstovyj redaktor s pomoshhyu swift

автор Нео Игадаро

SWAZFPCAD35md8yuYCZJl0AcehqgB4yHQkf8
Фото от rawpixel на Unsplash

Сегодня текстовые редакторы приобретают все большую популярность, независимо от того, встраиваются они в форму комментариев на веб-сайте или используются в качестве блокнота. Есть много разных редакторов по выбору. В этой публикации мы не только узнаем, как создать красивый текстовый редактор для мобильных устройств на iOS, но и сделать возможным совместную работу над заметкой в ​​реальном времени с помощью Pusher.

Обратите внимание, однако, что для простоты программы эта статья не будет рассматривать одновременные редактирования. Поэтому только один человек может редактировать одновременно, пока другие смотрят.

Программа будет работать, инициируя событие, когда вводится некоторый текст. Это событие будет отправлено в Pusher, а затем зафиксировано устройством соавтора и обновится автоматически.

Чтобы продолжить этот урок, вам понадобится следующее:

  1. Cocoapods: установить, запустить gem install cocoapods на вашей машине
  2. Xcode
  3. А Приложение Pusher: Вы можете создать бесплатную учетную запись и приложение здесь
  4. Некоторые знания о Свифт язык
  5. Node.js

Наконец, для выполнения этого руководства необходимо базовое понимание Swift и Node.js.

Начало работы с нашим приложением iOS в Xcode

Запустите Xcode и создайте новый проект. Я позвоню своим Collabo. После выполнения инструкций мастера настройки и открытой рабочей области закройте Xcode, а затем cd к корню вашего проекта и запустите команду pod init. Это должно породить a Podfile для вас. Измените содержимое Podfile:

# Uncomment the next line to define a global platform for your project    platform :ios, '9.0'
    target 'textcollabo' do      # Comment the next line if you're not using Swift and don't want to use dynamic frameworks      use_frameworks!
      # Pods for anonchat      pod 'Alamofire'      pod 'PusherSwift'    end

Теперь запустите команду pod install поэтому менеджер пакетов Cocoapods может привлечь необходимые зависимости. Когда это будет завершено, закройте Xcode (если открыт) и откройте файл .xcworkspace файл, находящийся в корне папки вашего проекта.

Разработка представлений для нашего приложения iOS

Мы собираемся создать несколько представлений для нашей программы iOS. Это будет хребет, к которому мы подключим всю логику. Используя сюжетную доску Xcode, сделайте ваши взгляды похожими на скриншоты ниже.

Это LaunchScreen.storyboard файл. Я только что разработал что-то простое без функциональности.

QzhBjZMrhJiM66feDQLG6npu-aYqA1VEgGlt

Следующая раскадровка, которую мы разработаем, это Main.storyboard. Как известно из названия, он будет основным. Здесь мы имеем все важные взгляды, связанные с определенной логикой.

eibpbvrTnwGPiY9BGpN2dcvELgUgtczDbH-K

Здесь мы имеем три взгляда.

Первый вид создан так, чтобы выглядеть так же, как экран запуска, за исключением кнопки, которую мы связали, чтобы открыть второй вид.

Второй вид – это контроллер навигации. Он прилагается к третьему виду, который является a ViewController. Мы установили третье представление в качестве корневого контроллера для нашего контроллера навигации.

В третьем взгляде мы имеем a UITextView который можно редактировать, который размещается в представлении. Также есть метка, которая должна являться счетчиком символов. Это место, где мы будем увеличивать символы, когда пользователь вводит текст в текстовом представлении.

Кодирование приложения для общего текстового редактора iOS

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

Создайте новый файл класса какао и назовите его TextEditorViewController и свяжите его с третьим представлением в Main.storyboard файл. The TextViewController следует также принять UITextViewDelegate. Теперь ты можешь ctrl+drag в UITextView а также ctrl+drag в UILabel в Main.storyboard файл к TextEditorViewController класс.

Кроме того, вам следует импортировать PusherSwift и AlamoFire библиотеки в TextViewController. После того как вы закончите, у вас должно быть что-то близко к этому:

import UIKit    import PusherSwift    import Alamofire
    class TextEditorViewController: UIViewController, UITextViewDelegate {        @IBOutlet weak var textView: UITextView!        @IBOutlet weak var charactersLabel: UILabel!    }

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

import UIKit    import PusherSwift    import Alamofire
    class TextEditorViewController: UIViewController, UITextViewDelegate {        static let API_ENDPOINT = "
        @IBOutlet weak var textView: UITextView!
        @IBOutlet weak var charactersLabel: UILabel!
        var pusher : Pusher!
        var chillPill = true
        var placeHolderText = "Start typing..."
        var randomUuid : String = ""    }

Теперь мы разделим логику на три части:

  1. Просмотр и клавиатура событий
  2. Методы UITextViewDelegate
  3. Обработка событий Pusher.

Просмотр и клавиатура событий

Откройте TextEditorViewController и обновите его с помощью следующих методов:

override func viewDidLoad() {        super.viewDidLoad()        // Notification trigger        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)        // Gesture recognizer        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tappedAwayFunction(_:))))        // Set the controller as the textView delegate        textView.delegate = self        // Set the device ID        randomUuid = UIDevice.current.identifierForVendor!.uuidString        // Listen for changes from Pusher        listenForChanges()    }    override func viewWillAppear(_ animated: Bool) {        super.viewWillAppear(animated)        if self.textView.text == "" {            self.textView.text = placeHolderText            self.textView.textColor = UIColor.lightGray        }    }    func keyboardWillShow(notification: NSNotification) {        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {            if self.charactersLabel.frame.origin.y == 1.0 {                self.charactersLabel.frame.origin.y -= keyboardSize.height            }        }    }    func keyboardWillHide(notification: NSNotification) {        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {            if self.view.frame.origin.y != 1.0 {                self.charactersLabel.frame.origin.y += keyboardSize.height            }        }    }

В viewDidLoad метод мы зарегистрировали функции клавиатуры, чтобы они реагировали на события клавиатуры. Мы также добавили средства распознавания жестов, которые будут закрывать клавиатуру, когда вы прикасаетесь за пределами UITextView. И мы установили textView делегировать самому контролеру. Наконец, мы вызвали функцию прослушивания новых обновлений (мы создадим ее позже).

В viewWillAppear методом, мы просто сломали UITextView чтобы иметь текст-заполнитель, поскольку по умолчанию файл UITextView нет этой функции. Интересно почему, Apple…

В keyboardWillShow и keyboardWillHide функции мы сделали так, чтобы отметка подсчета символов поднималась вместе с клавиатурой и опускалась вместе с ней соответственно. Это не позволит клавиатуре закрыть этикетку, когда она активна.

Методы UITextViewDelegate

Обновите TextEditorViewController со следующим:

func textViewDidChange(_ textView: UITextView) {        charactersLabel.text = String(format: "%i Characters", textView.text.characters.count)
        if textView.text.characters.count >= 2 {            sendToPusher(text: textView.text)        }    }
    func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {        self.textView.textColor = UIColor.black
        if self.textView.text == placeHolderText {            self.textView.text = ""        }
        return true    }
    func textViewDidEndEditing(_ textView: UITextView) {        if textView.text == "" {            self.textView.text = placeHolderText            self.textView.textColor = UIColor.lightGray        }    }
    func tappedAwayFunction(_ sender: UITapGestureRecognizer) {        textView.resignFirstResponder()    }

The textViewDidChange метод просто обновляет метку количества символов, а также посылает изменения в Pusher с помощью нашего серверного API (который мы создадим в минуту).

The textViewShouldBeginEditing получено из UITextViewDelegate и она запускается, когда текстовое представление собирается редактировать. Здесь мы в основном играем с заполнителем, так же, как и textViewDidEndEditing метод.

Наконец, в tappedAwayFunction мы определяем обратный вызов события жеста, который мы зарегистрировали в предыдущем разделе. В методе мы в основном отбрасываем клавиатуру.

Обработка событий Pusher

Обновите контроллер с помощью следующих методов:

func sendToPusher(text: String) {        let params: Parameters = ["text": text, "from": randomUuid]
        Alamofire.request(TextEditorViewController.API_ENDPOINT + "/update_text", method: .post, parameters: params).validate().responseJSON { response in            switch response.result {
            case .success:                print("Succeeded")            case .failure(let error):                print(error)            }        }    }
    func listenForChanges() {        pusher = Pusher(key: "PUSHER_KEY", options: PusherClientOptions(            host: .cluster("PUSHER_CLUSTER")        ))
        let channel = pusher.subscribe("collabo")        let _ = channel.bind(eventName: "text_update", callback: { (data: Any?) -> Void in
            if let data = data as? [String: AnyObject] {                let fromDeviceId = data["deviceId"] as! String
                if fromDeviceId != self.randomUuid {                    let text = data["text"] as! String                    self.textView.text = text                    self.charactersLabel.text = String(format: "%i Characters", text.characters.count)                }            }        })
        pusher.connect()    }

В sendToPusher метода, мы присылаем полезную нагрузку в нашу серверную программу с помощью AlamoFireкоторый, в свою очередь, пришлет его в Pusher.

В listenForChanges Затем мы прослушиваем изменения в тексте и, если таковые имеются, применяем изменения в текстовом представлении.

? Рemember заменить ключ и кластер на фактическое значение, которое вы получили из панели инструментов Pusher.

Если вы внимательно придерживались учебника, то ваш TextEditorViewController должно выглядеть примерно так:

import UIKit    import PusherSwift    import Alamofire
    class TextEditorViewController: UIViewController, UITextViewDelegate {        static let API_ENDPOINT = "
        @IBOutlet weak var textView: UITextView!
        @IBOutlet weak var charactersLabel: UILabel!
        var pusher : Pusher!
        var chillPill = true
        var placeHolderText = "Start typing..."
        var randomUuid : String = ""
        override func viewDidLoad() {            super.viewDidLoad()
            // Notification trigger            NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)            NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
            // Gesture recognizer            view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tappedAwayFunction(_:))))
            // Set the controller as the textView delegate            textView.delegate = self
            // Set the device ID            randomUuid = UIDevice.current.identifierForVendor!.uuidString
            // Listen for changes from Pusher            listenForChanges()        }
        override func viewWillAppear(_ animated: Bool) {            super.viewWillAppear(animated)
            if self.textView.text == "" {                self.textView.text = placeHolderText                self.textView.textColor = UIColor.lightGray            }        }
        func keyboardWillShow(notification: NSNotification) {            if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {                if self.charactersLabel.frame.origin.y == 1.0 {                    self.charactersLabel.frame.origin.y -= keyboardSize.height                }            }        }
        func keyboardWillHide(notification: NSNotification) {            if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {                if self.view.frame.origin.y != 1.0 {                    self.charactersLabel.frame.origin.y += keyboardSize.height                }            }        }
        func textViewDidChange(_ textView: UITextView) {            charactersLabel.text = String(format: "%i Characters", textView.text.characters.count)
            if textView.text.characters.count >= 2 {                sendToPusher(text: textView.text)            }        }
        func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {            self.textView.textColor = UIColor.black
            if self.textView.text == placeHolderText {                self.textView.text = ""            }
            return true        }
        func textViewDidEndEditing(_ textView: UITextView) {            if textView.text == "" {                self.textView.text = placeHolderText                self.textView.textColor = UIColor.lightGray            }        }
        func tappedAwayFunction(_ sender: UITapGestureRecognizer) {            textView.resignFirstResponder()        }
        func sendToPusher(text: String) {            let params: Parameters = ["text": text, "from": randomUuid]
            Alamofire.request(TextEditorViewController.API_ENDPOINT + "/update_text", method: .post, parameters: params).validate().responseJSON { response in                switch response.result {
                case .success:                    print("Succeeded")                case .failure(let error):                    print(error)                }            }        }
        func listenForChanges() {            pusher = Pusher(key: "PUSHER_KEY", options: PusherClientOptions(                host: .cluster("PUSHER_CLUSTER")            ))
            let channel = pusher.subscribe("collabo")            let _ = channel.bind(eventName: "text_update", callback: { (data: Any?) -> Void in
                if let data = data as? [String: AnyObject] {                    let fromDeviceId = data["deviceId"] as! String
                    if fromDeviceId != self.randomUuid {                        let text = data["text"] as! String                        self.textView.text = text                        self.charactersLabel.text = String(format: "%i Characters", text.characters.count)                    }                }            })
            pusher.connect()        }    }

Прекрасно! Теперь нам нужно сделать бэкенд программы.

Создание серверной программы Node

Теперь, когда мы закончили работу с частью Swift, мы можем сосредоточиться на создании бэкенда Node.js для программы. Мы будем использовать Express, чтобы быстро что-нибудь запустить.

Создайте каталог для веб-приложения и создайте несколько новых файлов.

The index.js файл:

let path = require('path');    let Pusher = require('pusher');    let express = require('express');    let bodyParser = require('body-parser');    let app = express();    let pusher = new Pusher(require('./config.js'));
    app.use(bodyParser.json());    app.use(bodyParser.urlencoded({ extended: false }));
    app.post('/update_text', function(req, res){      var payload = {text: req.body.text, deviceId: req.body.from}      pusher.trigger('collabo', 'text_update', payload)      res.json({success: 200})    });
    app.use(function(req, res, next) {        var err = new Error('Not Found');        err.status = 404;        next(err);    });
    module.exports = app;
    app.listen(4000, function(){      console.log('App listening on port 4000!');    });

В вышеприведенном файле JS мы используем Express для создания простой программы. В /update_text маршрута, мы просто получаем полезную нагрузку и передаем его Pusher. Ничего сложного там нет.

Создать package.json файл также:

{      "main": "index.js",      "dependencies": {        "body-parser": "^1.17.2",        "express": "^4.15.3",        "path": "^0.12.7",        "pusher": "^1.5.1"      }    }

The package.json файл, где мы определяем все зависимости NPM.

Последний созданный файл a config.js файл. Вот где мы определим значение конфигурации для нашей программы Pusher:

module.exports = {      appId: 'PUSHER_ID',      key: 'PUSHER_KEY',      secret: 'PUSHER_SECRET',      cluster: 'PUSHER_CLUSTER',      encrypted: true    };

? Рemember заменить ключ и кластер на фактическое значение, которое вы получили из панели инструментов Pusher.

Теперь беги npm install в каталоге, а затем node index.js после завершения установки npm. Вы должны увидеть Приложение прослушивает порт 4000! сообщения.

TGJKpixfoUkwEIal-2tDPo25Rb7Bo8BuEWMa

Тестирование программы

После того, как у вас запущен веб-сервер локального узла, вам нужно будет внести некоторые изменения, чтобы ваше приложение могло общаться с локальным веб-сервером. В info.plist файл, внесите следующие изменения:

FARWGcIZQDh0lMGVsdWlhAmDwm1FIon921ch

Благодаря этому изменению вы можете создать и запустить свою программу, и она будет общаться непосредственно с вашим локальным веб-приложением.

Вывод

В этой статье мы рассказали, как создать текстовый редактор для совместной работы в реальном времени на iOS с помощью Pusher. Надеемся, вы узнали кое-что, следуя учебнику. Для практики можно расширить статусы, чтобы поддерживать больше экземпляров.

Эта публикация была впервые опубликована в Pusher.

Добавить комментарий

Ваш адрес email не будет опубликован.