Connect with us

Разработка

Соединяем две точки с помощью отрезков прямых линий и закругленных углов в SwiftUI

Соединить две точки очень просто — достаточно провести между ними одну или несколько линий.

Опубликовано

/

     
     

Соединить две точки очень просто — достаточно провести между ними одну или несколько линий.

Соединяем две точки с помощью отрезков прямых линий и закругленных углов в SwiftUI

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

Две точки, один угол

Простой подход: UIBezierPath + trimmedPath

UIBezierPath имеет инициализатор init(roundedRect:byRoundingCorners:cornerRadii:), который позволяет нам создать новый объект кривой Безье с прямоугольной траекторией, скругленной по указанным углам.

(Каждый раз, когда я использую что-то из UI, я начинаю задаваться вопросом, наступит ли день, когда SwiftUI сможет догнать UIKit…)

Затем мы объединим его с trimmedPath(from:to:), чтобы извлечь закругленный угол + две прямые линии.

Давайте используем простой пример, в котором у нас будет две точки и один угол, чтобы проверить это!

Соединяем две точки с помощью отрезков прямых линий и закругленных углов в SwiftUI

Выглядит просто! Правда?

Все действительно очень просто, за исключением пары моментов, на которые следует обратить внимание!

Скругление левого нижнего угла

Когда мы создаем наш UIBezierPath, в дополнение к правому верхнему углу topRight, который мы фактически сохраняем, мы также просим его скруглить левый нижний угол bottomLeft.

Зачем нам это нужно, если мы все равно его обрезаем? Давайте проверим, что мы получим, если этого не сделать.

Соединяем две точки с помощью отрезков прямых линий и закругленных углов в SwiftUI

Почему так происходит?

Когда угол скругляется, длина пути уменьшается, и 0.5 уже не обязательно является целевой точкой.

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

Почему мы не можем просто уменьшить 0.5 до какого-нибудь 0.49? Значение будет зависеть от радиуса угла, и, очевидно, мы не хотим пытаться вычислять его каждый раз!

Прямоугольник с нулевым началом

Для прямоугольника, определяющего базовую форму UIBezierPath, мы указали origin в zero и добавили модификатор position к окончательно обрезанному пути.

Почему мы не можем просто использовать start в качестве origin? Если оно у нас есть, нам даже не нужно вычислять позицию.

Соединяем две точки с помощью отрезков прямых линий и закругленных углов в SwiftUI

Выглядят похоже?

С этим есть несколько проблем.

Во-первых, если вы работали с Path и Shape, вы можете знать, что они будут пытаться занять любое свободное место, если мы не установим frame.

Соединяем две точки с помощью отрезков прямых линий и закругленных углов в SwiftUI

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

Соединяем две точки с помощью отрезков прямых линий и закругленных углов в SwiftUI

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

Соединяем две точки с помощью отрезков прямых линий и закругленных углов в SwiftUI

Однако это не сработает, если мы назначили origin в start.

Соединяем две точки с помощью отрезков прямых линий и закругленных углов в SwiftUI

ScaleEffect на Frame

Кроме того, вместо того чтобы применять scale на trimmed пути, я применяю scaleEffect после frame.

Давайте сравним разницу на примере упрощенной версии.

Соединяем две точки с помощью отрезков прямых линий и закругленных углов в SwiftUI

Как видите, сама линия окажется в нужном месте, а frame — нет. На всякий случай, если есть окружающее содержимое, я думаю, что лучше сделать так, как мы сделали.

Больше сегментов линий, больше углов

Если мы посмотрим на другие сервисы для построения графиков или блок-схем, то количество сегментов линий и углов обычно зависит от направления входа и выхода из начальной и конечной точки, при этом мы стараемся не заходить за точку.

Соединяем две точки с помощью отрезков прямых линий и закругленных углов в SwiftUI

Разумеется, существует бесконечное число возможных маршрутов. Кроме того, нам нужно будет произвольно задать некоторые ограничения, например, изгиб прямо посередине или в сторону одного из концов, длину отрезка линии, если мы движемся не в направлении цели из-за направления входа/выхода, и т.д.

Сколько возможностей нам нужно учесть?

Мы можем войти (или выйти) в точку в 4 разных направлениях, значит ли это, что у нас 4 * 4 = 16 случаев?

НЕТ! НЕТ! НЕТ!

Внутри каждого случая у нас также есть 9 подслучаев (почти, некоторые случаи имеют одну и ту же реализацию), описывающих относительное положение между начальной точкой и конечной! Это одни и те же точки? Имеют ли они одинаковые x или y? Или нет?

Давайте рассмотрим один из основных случаев в качестве примера, а остальное вы допишите сами!

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

Для удобства использования я вывожу это в другом View.

Обратите внимание, что даже если start пересекается с end, то есть relativePosition это none, все равно будут некоторые отрезки линий и углы, которые мы должны нарисовать в зависимости от направлений входа и выхода.

Вот простой вид для визуализации 9 подслучаев основного случая: выход из start справа, вход в end сверху.

Соединяем две точки с помощью отрезков прямых линий и закругленных углов в SwiftUI

Конечно, вы можете разделить отрезки прямых разными способами, в разных позициях, например, по направлению к end!

Остальные 15 основных случаев я оставлю на ваше усмотрение, потому что логика, по сути, одна и та же!

Спасибо, что читаете!

Я чувствую, что должны быть какие-то лучшие реализации, но пока я буду придерживаться того, что описал выше!

Счастливого соединения точек!

Источник

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.
Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: