Русский
Русский
English
Статистика
Реклама

Пользовательский интерфейс

Секрет формы иконок iOS это сквиркл? Разбор

01.09.2020 14:23:02 | Автор: admin
Давайте сыграем в игру. У нас есть два ряда знакомых всем пользователям iOS-иконок. На первый взгляд иконки сверху и снизу одинаковые. Но это не так. В одном ряду вы видите настоящие иконки, а в другом подделку.





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

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

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

Но! В интерфейсе iOS нет ни одного квадрата с закруглёнными углами. Все элементы в iOS, это не квадраты и прямоугольники это суперэллипсы!

Сегодня мы поговорим про секреты в дизайнах продуктов Apple. Что такое суперэллипс? В чём магия формы иконок? И почему HomePod это суперяйцо.





Продукты Apple часто получаются противоречивыми и не лишенными недостатков. Но думаю никто не будет спорить, что в плане дизайна Apple заморачиваются наверное больше всех на рынке. И даже форма иконок в iOS взята не с потолка, а вычислена математически.

Она называется суперэллипсом или кривой Ламе, в честь математика Габриеля Ламе, который в 19 веке внёс большой вклад в теорию упругости, разработал общую теорию криволинейных координат, и придумал суперэллипсы. Что же это такое?

Это геометрическая кривая, которая строится при помощи уравнения вот такого вида:



Это уравнение задаёт замкнутую кривую, ограниченную прямоугольником. При этом меняя значение n мы можем регулировать степень выпуклости каждого угла (можно поиграться тут). При n меньше единицы, мы получаем вогнутые стороны, а форма напоминает звезду.



Увеличивая значение n, форма становится всё более выпуклой. При n=2 получается ровный круг. А при n равной 4 или 5 мы получаем привычную всем форму иконки.



У такой формы даже есть своё название сквиркл. И, нет, сквиркл это не ваш любимый раздел на PornHub. Это производное от слов square и circle. Т.е. по русски сквиркл это квадрокруг или квадратный круг.

Кстати, формула описывающая иконку iOS выглядит вот так:



Т.е. иконка в iOS это и не круг и не квадрат, а математически это что-то среднее между ними.

Кольцо и суперяйцо


Историческая справка. Популяризировал суперэллипсы и сквирклы датский ученый Пит Хейн. В 60-х годах он спроектировал транспортное кольцо в Стокгольме в виде суперэллипса со значениями n = 2.5, a/b = 6/5.



Форма развязки стала хитом. И он начал делать, столы, посуду, но вершиной его творчества стало суперяйцо. Как понимаете это трёхмерный суперэллипс, по форме похожий на яйцо.



Кстати, трехмерные суперэллипсы это суперэллипсоиды. А всякие неправильные суперэллипсоиды это суперквадрики.

Непрерывность кривизны


Но вернёмся к иконкам! Чем всё-таки квадрокруг отличается от скругленного квадрата? И чем он не угодил дизайнерами Apple? Давайте посмотрим!

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








Лучше всего это можно визуализировать при помощи вот таких гребней кривизны.



Казалось бы, разница пара пикселей. Но плавные переходы, позволяют добиться так называемой непрерывности кривизны. И это даёт потрясающий эффект форма объекта из искусственной, как бы собранной из разных форм становится естественной и завершенной.

Именно поэтому Apple очень активно использует суперэллипсы не только в интерфейсе, но и в дизайне всех своих продуктов.

Mac Mini, Apple TV, iPhone, iPad, MacBook и конечно HomePod своего рода всё это суперэллипсы. Даже в их новом кампусе нет ни одной прямой стены.

Плавное перетекание форм и отсутствие резких переходов делает продукты Apple не только привлекательными внешне. На этот счёт есть отличная статья Николая Геллара, которую я процитирую:

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

Например если сравнить по дизайну HomePod и Яндекс.Станцию, становится понятно в почему станция выглядит менее привлекательно, её как будто только выпустили с завода. А HomePod будто нашел в своём саду японский мастер суйсэки искусства любования камнями.





Хотя сама Яндекс.Станция девайс отличный. А HomePod красивый, но туповатый (русского не знает).

Примеры


Но, конечно, Apple тоже не сразу к этому пришли. Например, иконки и другие элементы интерфейса стали суперэллипсами только в начиная с iOS 7.



Такая же история с Apple Watch. В Series 4 увеличили не только дисплей, но и обновили форму. Да-да, Apple Watch с четвёртой версии это тоже суперэллипс (как сам экран).




Другие компании


Естественно, не только Apple играется с суперэллипсами. Те же иконки в One UI на смартфонах Samsung, гораздо более явные квадрокруги, чем у Apple. Но, на мой взгляд, у Samsung выбранная форма не работает.

Во-первых, потому, что иконки закруглены уж очень сильно, из-за чего они выглядят неустойчиво, как будто в любой момент они могут укатиться с рабочего стола. Кстати, в физическом мире такой же ошибкой грешит колонка от Mail.ru, она просто неустойчивая.



Но это не самое главное. Основная проблема в интерфейсе Samsung. Я говорю про отсутствие единства. Плавные суперэллипсы тут соседствуют с грубоватыми скруглёнными. Как внутри интерфейса так и в дизайне самих девайсов.



А именно единство подхода начиная от самого простого элемента интерфейса, заканчивая дизайном всей линейки продуктов, делает устройства Apple такими привлекательными и доставляющими в использовании.Да, наверное, это звучит рекламно. Но, мне пофиг, просто хочется отдать должное дотошности подхода ребят из Купертино.

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

Реализация двойной панели инструментов в QT

06.03.2021 18:05:18 | Автор: admin

Ссылка на исходный код

Задача и требования

  • Создание двойного "тулбара" (панели инструментов), положение разделителя которого меняло бы соотношение полей сплиттераа.
    Иными словами разделитель должен перетягиваться мышкой.

  • Положение разделителя тулбаров так же должно зависеть от положения сплиттера.

  • При наведении курсора на разделитель тулбаров должен меняться тип курсора на горизонтальный QT::SizeHorCursor

  • При перетягивании разделителя курсор так же должен менять свой тип на горизонтальный

  • В целом данный тулбар должен соответствовать типичному представлению пользователя о данном типе тулбаров, это сделает интерфейс простым и понятным

  • Приложение должно давать возможность пользователю менять размер окна

Реализация

Первый шаг

Создаем Qt Widgets application. Основы cpp и h файлов, с которыми мы работаем, сгенерируются без нашего участия.

Object Inspector

Выполнение задачи необходимо начать с построения структуры интерфейса.

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

Параметры виджетов

У виджетов MainWindow, centralwidget и у обоих тулбаров задаем параметр Mouse tracking, как true. Это позволит перехватывать события нажатия мыши, отпускания мыши и передвижения мыши над тулбарами.

У обоих списков, что лежат в сплиттере, задаем параметр минимальной ширины. В моем случае этот параметр равен 200 пикселям. У сплиттера задаем параметр childrenCollapsible, как false. Эти действия необходимы для того, чтобы не было возможности "схлопнуть" одну из секций сплиттера или увести тулбар за пределы экрана. Ниже покажу: как это связано с реализацией cpp файла.

У тулбаров нужно установить параметр movable, как false, чтобы убрать дефолтное задание размеров тулбаров пользователем.

MainWindow.h

private:    Ui::MainWindow *ui;    int timerId = 0;    bool toolbar_dragged = false;

Под Ui::MainWindow *ui создаем две переменные: timerId и toolbar_dragged.
timerId необходима для хранения времени таймера, которое мы будем использовать.
toolbar_dragged определяет: тянет ли пользователь за разделитель

public:    MainWindow(QWidget *parent = nullptr);    ~MainWindow();private slots:    void on_splitter_splitterMoved(int pos, int index, bool windowResized  = true);    void mouseMoveEvent(QMouseEvent *event);    void mouseReleaseEvent(QMouseEvent *e);    void mousePressEvent(QMouseEvent *event);    void timerEvent(QTimerEvent *event);    void resizeEvent(QResizeEvent* event);

Задаем слоты и указываем входной параметр слота on_splitter_splitterMoved windowResized, как параметр, по умолчанию заданный, как true. В resizeEvent, событии изменения размера окна, будет вызываться on_splitter_splitterMoved при помощи emit. Позиция сплиттера при изменении окна меняется, потому и должна меняться позиция разделителя тулбаров. Но в случае изменения размера окна перетягивания разделителя не происходит, toolbar_dragged == false. Потому для случая, когда окно меняет размер, необходимо данное входное условие. Подробнее об этом в cpp файле.

MainWindow.cpp

В файл необходимо включить две библиотеки:

QMouseEvent нужна для обработки сообщений мыши
QWidget необходима для работы с виджетами приложения

#include <QMouseEvent>#include <QWidget>

Рассмотрим конструктор главного окна:

centralWidget()->layout()->setContentsMargins убирает отступы от границ окна, созданные добавлением layout-а, как центрального виджета.
setSpacing(2) нужен потому, что иконки на этом тулбаре не передают событие передвижения мыши в MainWindow, Отступ в 2 пикселя вполне достаточен, чтобы избавиться от этой проблемы.

MainWindow::MainWindow(QWidget *parent)    : QMainWindow(parent)    , ui(new Ui::MainWindow){    ui->setupUi(this);    centralWidget()->layout()->setContentsMargins(0, 0, 0, 0);    //this is nessesary    this->ui->toolBar_2->layout()->setSpacing(2);}

Функция on_splitter_splitterMoved задает фиксированный размер левого тулбара. Второй тулбар автоматически подстраивается под размер первого. Данный размер можно задавать многократно, что мы и делаем. Обычными resize и move методами это сделать нельзя.

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

void MainWindow::on_splitter_splitterMoved(int pos, int /*index*/, bool windowResized){    if (((pos>this->ui->listWidget->minimumSize().width()) &&        (pos<(this->width() - this->ui->listWidget_2->minimumSize().width()))) || windowResized)    {        this->ui->toolBar->setMaximumSize(pos, ui->toolBar->rect().height());        this->ui->toolBar->setMinimumSize(pos, ui->toolBar->rect().height());    }}

Функция mouseReleaseEvent вызывается, когда пользователь отпускает мышь. После этого нужно приветси курсор к типу Qt::ArrowCursor и задать соответствующую переменную toolbar_dragged, как false.

void MainWindow::mouseReleaseEvent(QMouseEvent* /*e*/){    if (toolbar_dragged)    {        toolbar_dragged = false;        this->setCursor(Qt::ArrowCursor);    }}

mousePressEvent перехватывает событие нажатия мыши пользователем.

При срабатывании данной функции, если мышь находится на промежутке +-20 пикселей от разделителя сплиттера, на высоте тулбаров, toolbar_dragged становится true, а тип курсора меняется на соответствующий перетягиванию.

void MainWindow::mousePressEvent(QMouseEvent *event){    QList<int> currentSizes = this->ui->splitter->sizes();    if ((this->ui->toolBar_2->underMouse()) &&        (event->buttons() == Qt::LeftButton) &&        (event->pos().x() < (currentSizes[0]+20)))    {        this->ui->toolBar_2->movableChanged(true);        toolbar_dragged = true;        this->setCursor(Qt::SizeHorCursor);    }    else if ((this->ui->toolBar->underMouse()) &&             (event->buttons() == Qt::LeftButton) &&             (event->pos().x() > (currentSizes[0]-20)))    {        this->ui->toolBar->movableChanged(true);        toolbar_dragged = true;        this->setCursor(Qt::SizeHorCursor);    }}

mouseMoveEvent вызывается при перемещении мыши. Если происходит перетягивание разделителя (toolbar_dragged), то вызывает функцию on_splitter_splitterMoved для изменения размера тулбара, предварительно поменяв размеры сплиттера.

В противном случае, если перетягивания не происходит, но мышь находится на промежутке -2 +5 пикселей от разделителя сплиттера, на высоте тулбаров, с отступами от горизонтальных границ в два пикселя, то тип курсора меняется на SizeHorCursor. Если эти отступы не сделать, то мышь не будет менять тип с SizeHorCursor на ArrowCursor, даже если задать Mouse tracking параметр, как true, всем другим виджетам.

void MainWindow::mouseMoveEvent(QMouseEvent* event){    QList<int> currentSizes = this->ui->splitter->sizes();    if (toolbar_dragged)    {        QList<int> currentSizes = this->ui->splitter->sizes();        currentSizes[0] = event->pos().x();        currentSizes[1] = this->width() - event->pos().x();        this->ui->splitter->setSizes(currentSizes);        emit on_splitter_splitterMoved(event->pos().x(), 1, false);    }    else if ((event->pos().y() > (2+this->ui->toolBar->y())) &&             (event->pos().y() < (this->ui->toolBar->height()-2+this->ui->toolBar->y())) &&             (event->pos().x() < (currentSizes[0]+5)) &&             (event->pos().x() > (currentSizes[0]-10)))    {        this->setCursor(Qt::SizeHorCursor);    }    else    {        this->setCursor(Qt::ArrowCursor);    }}

resizeEvent - событие изменения размера окна. В этом событии нельзя вызвать on_splitter_splitterMoved, потому необходимо сделать таймер, который будет "выходить за пределы" resizeEvent, работать вне ее.

void MainWindow::resizeEvent(QResizeEvent* event){        if (timerId)        {            killTimer(timerId);            timerId = 0;        }        // delay beetween ends of resize and your action        timerId = startTimer(1);    QMainWindow::resizeEvent(event);}

timerEvent меняет размеры тулбара через on_splitter_splitterMoved. При изменении размера окна, положение разделителя сплиттера определяется автоматически

void MainWindow::timerEvent(QTimerEvent *event){    QList<int> currentSizes = this->ui->splitter->sizes();    this->ui->toolBar_2->adjustSize();    emit on_splitter_splitterMoved(currentSizes[0], 1,true);    killTimer(event->timerId());    timerId = 0;}
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru