Всем привет.
Недавно мы закончили проект под названием Seat selection. Этот проект посвящен созданию модуля, позволяющего пользователям выбирать место прямо на карте (как выбор места в кинотеатре, но в большем масштабе). Пока что мы подали заявку на один стадион на 40 000 мест. Поскольку это частный проект, я не могу ничего показать по этому поводу.
При запуске прототипа проекта мы знаем, что iOS изначально не поддерживает SVG, поэтому мы используем некоторую библиотеку, чтобы попытаться отобразить и взаимодействовать с файлом SVG, одна из которых — Macaw. К счастью, в то время она работала отлично, плавно и не на что жаловаться на эту библиотеку. Мы успешно сделали прототип с картой на 3000 мест. Но при попытке с картой на 40 000 мест производительность становится очень плохой. Карта отрисовывалась медленно, скаттер реагирует, когда пользователь нажимает на нее. Мы снова пытаемся найти другое решение.
В конце концов, мы выбираем способ загрузки SVG непосредственно в WKWebView и используем Javascripts для обработки взаимодействия пользователей.
Прежде чем погрузиться в технические подробности, я сначала покажу демо. В этой демонстрации мы будем рисовать случайный цвет на пути SVG, когда пользователи нажимают на него, увеличиваем масштаб пути, добавляем представление в центре пути.
Хорошо, поехали
Прежде всего, основная идея заключается в загрузке файла SVG в WKWebView и взаимодействии через Javascript, поэтому нам нужно создать файлы index.html
и main.js
.
Еще одна вещь, которую вам нужно взять на себя, — это пометить идентификатор пути, с которым вы хотите взаимодействовать. В этом примере я отмечу идентификатор пути path-{number}
.
В main.js
нам нужно написать функцию для загрузки файла SVG в body
.
function loadSVG(rawSVG) { document.getElementById("body").innerHTML = rawSVG }
Хорошо, достаточно показать SVG в веб-представлении, теперь мы возвращаемся к iOS. Прежде чем мы начнем, вам нужно кое-что узнать о WKWebView. Некоторый компонент в WKWebView:
- WKWebView — позволяет загружать веб-контент через URL-адрес.
- WKScriptMessage — объект, созданный при получении postMessage()
- WKUserContentController — управляет публикациями и внедрением javascript.
- WKScriptMessageHandler — протокол для доступа к методам делегата WKScriptMessage.
- WKWebViewConfiguration — конфигурация передается в WKWebView.
Я думаю, что этот пост будет слишком длинным, если я объясню их все, поэтому более подробно вы можете прочитать здесь. В этом примере мы будем использовать webview.evaluateJavaScript(anJavascriptFunction)
для вызова функции Javascript из iOS и webkit.messageHandlers.name.postMessage()
для отправки данных из Javascript обратно в iOS.
Прежде всего, вам нужно инициировать WKWebView
class SVGWebView: UIView { private(set) lazy var webView: WKWebView = { let configuration = self.getWebViewConfig() let webView = WKWebView(frame: CGRect(origin: .zero, size: UIScreen.main.bounds.size), configuration: configuration) webView.contentMode = .scaleAspectFit webView.navigationDelegate = self webView.scrollView.showsHorizontalScrollIndicator = false webView.scrollView.showsVerticalScrollIndicator = false webView.translatesAutoresizingMaskIntoConstraints = false return webView }() }
Прежде чем мы сможем взаимодействовать с файлом SVG через Javascript, нам нужно подготовить конфигурацию и передать ее функции инициализации WKWebView. Здесь мы послушаем два обработчика из Javascript — didTapPath
и transferPathsInfo
. Мы будем работать с ними позже в этом посте.
func getWebViewConfig() -> WKWebViewConfiguration { let config = WKWebViewConfiguration() let pref = WKPreferences() config.preferences = pref //listen when user tap a path config.userContentController.add(self, name: "didTapPath") //send all paths to iOS to show in the tableview bellow the view config.userContentController.add(self, name: "transferPathsInfo") return config } extension SVGWebView: WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {} }
И добавьте его в текущий вид:
override init(frame: CGRect) { super.init(frame: frame) self.addSubview(webView) } required init?(coder: NSCoder) { super.init(coder: coder) self.addSubview(webView) }
Далее мы начинаем загружать index.html
в WKWebView.
func loadWebView() { let bundle = Bundle.main guard let path = bundle.path(forResource: "index", ofType: "html") else { return } let url = URL(fileURLWithPath: String(format: "%@", path)) webView.load(URLRequest(url: url)) }
И когда файл index.html
успешно загрузится. Мы вызовем функцию loadSVG
, которую мы написали ранее, чтобы загрузить файл SVG. Чтобы узнать, когда загрузка будет завершена, мы будем соответствовать протоколу WKNavigationDelegate
, у него есть функция didFinish
.
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { loadSVG(svgName: "princess") } func loadSVG(svgName: String) { let rawSVG = readSVGFromFile() let loadSVG = "loadSVG('\(rawSVG)')" webView.evaluateJavaScript(loadSVG) }
Здесь мы используем метод evaluateJavaScript
, этот метод позволяет нам вызывать функцию Javascript. Этот метод принимает строку, которая должна включать функцию javascript и любые аргументы, поэтому любые аргументы, переданные функцией, должны быть преобразованы обратно в строку. Теперь создайте и запустите проект
Круто, работает.
Я думаю, что этот пост достаточно длинный, поэтому мы продолжим взаимодействие в этом посте.