Как увеличить область нажатия кнопки
Что проверяют
Знаете ли вы, как UIKit решает, какой view получил касание, и умеете ли расширить зону нажатия, не ломая вёрстку. Следом спрашивают, как сделать то же в SwiftUI и почему кнопка у края родителя не реагирует на касание снаружи.
Как отвечать
Отталкивайтесь от Human Interface Guidelines: минимальная зона касания 44×44 point. Способы:
point(inside:with:)в подклассеUIButton: вернутьbounds.insetBy(dx: -10, dy: -10).contains(point). Внешний вид не меняется, растёт только зона. Отступы удобно считать так, чтобы итог был не меньше 44×44;- отступы внутри кнопки: с iOS 15 через
UIButton.Configurationи егоcontentInsets. СтарыйcontentEdgeInsetsпомечен устаревшим, в новом коде он не нужен; - увеличить саму кнопку констрейнтами, оставив иконку маленькой по центру. Самый предсказуемый вариант, и VoiceOver получает честную рамку элемента;
- SwiftUI:
.padding()плюс.contentShape(Rectangle()). БезcontentShapeпрозрачная область вокруг текста или иконки нажатия не ловит.
Объясните механизм. hitTest(_:with:) идёт от окна вниз и для каждого view спрашивает point(inside:with:). Если точка вне границ родителя, до дочерней кнопки проверка не дойдёт, даже если её зона расширена. Поэтому при выходе за пределы superview переопределять приходится и родителя, либо увеличивать кнопку внутри него. Касания также не получают view с isUserInteractionEnabled = false, скрытые и с alpha меньше 0.01.
Где спотыкаются
Расширяют зону через point(inside:with:), а кнопка лежит у края родителя. Касания за границей superview до неё не доходят, и кандидат не может объяснить почему.
Оборачивают кнопку в прозрачный UIView большего размера и ждут, что нажатие по обёртке сработает как нажатие по кнопке. Без жеста или своей логики в hitTest обёртка просто поглотит касание.
Раздувают зоны соседних кнопок так, что они перекрываются. Побеждает та, что выше по порядку отрисовки, и пользователь нажимает не то, что видит. Проверять стоит на реальном устройстве пальцем, а не курсором симулятора.