iOS - Swift - кликабельные области на UIImage
Я работаю над проектом, в котором мне нужно нанести точки в определенных областях на изображение, представляющее человеческое тело. В Интерфейсном Разработчике я установил контейнер UIView, который занимает большую часть вертикального центра основного вида. В этом представлении контейнера я поместил UIImageView и установил изображение в IB. Графика намного больше, чем UIImageView и контейнер UIView, точнее, она выше. ContentMode UIImageView установлен в AspectFit, потому что я хочу, чтобы изображение не показывалось как больше, чем контейнер.
Код создает несколько экземпляров CGRect, которые являются областями, где касания пользователя что-то значат. Когда пользователь нажимает на представление контейнера, код используется для определения, находится ли точка в одной из областей, и если это так, в центре этой области рисуется точка.
Проблема в том, что когда я запускаю приложение на определенных симуляторах, прямоугольники региона находятся не в нужном месте на изображении. Например, когда я запускаю приложение на iPhone X, прямоугольная область, которая находится на месте для головы, выглядит нормально. Когда я запускаю приложение на iPhone XR, область прямоугольника отключается слева от головы.
Я использую координаты, чтобы определить прямоугольники региона, основанные на том, где, например, человеческая голова находится на изображении. Я чувствую, что это не правильный способ сделать это, поскольку AspectFit для ContentMode изображения, скорее всего, вызывает масштабирование изображения для сохранения аспекта.
Суть в том, что я хочу, чтобы прямоугольник был в нужном месте и размере независимо от масштаба изображения. Не уверен, имеет ли смысл то, как я это делаю, поэтому надеюсь, что появятся некоторые предложения, которые предложат лучший способ сделать это.
Обновление 1: UIImageView прикреплен к окружающему UIView, поэтому его ширина и высота такие же большие, как у контейнера. Поскольку изображение более тонкое, чем UIImageView, изображение отображается по центру. На прикрепленных изображениях фиолетовый фон - это UIImageView, показывающий самый верхний цвет фона UIView.
Обновление 2: я проверил шкалу ширины и высоты и обнаружил, что они разные. Коэффициент масштабирования по ширине равен 1,356565656565, а коэффициент масштабирования по высоте равен 2,104. Я попробовал формулы, приведенные с обоими масштабными коэффициентами, заданными Sweeper, и не повезло.
3 ответа
Вам просто нужно сделать некоторые математики.
На исходном изображении укажите регион, который пользователь может нажать. Запишите его x, y, w, h относительно изображения.
Выясните, насколько уменьшилось изображение при просмотре изображения. Поскольку вы сказали, что изображение выше, чем изображение, изображение подверглось масштабному коэффициенту imageViewHeight / imageHeight
, Теперь мы будем ссылаться на это как scaleFactor
,
Координата Y региона также должна была уменьшиться на scaleFactor
так что вы умножаете regionY
от scaleFactor
получить newY
,
Ширина и высота региона будут делать то же самое, поэтому умножьте их на scaleFactor
и получить newWidth
а также newHeight
,
Координата X области относительно вида изображения немного хитрая. Вам необходимо учитывать количество пустого пространства, созданного представлением изображения, путем его уменьшения. это emptySpace
рассчитывается по (imageViewWidth - newWidth) / 2
, Затем, чтобы вычислить координату X новой области относительно вида изображения, вы делаете emptySpace + X * scaleFactor
,
Теперь прямоугольник (newX, newY, newWidth, newHeight)
это область относительно изображения, которую пользователь может нажать!
Я сделал этот код для прямоугольника в том же положении на каждом устройстве. Чтобы проверить, действительно ли это телефон, я делаю это в viewDidLoad и устанавливаю ограничения на то, что я хочу, и добавляю размер панели вкладок.
@IBOutlet weak var tabMenu: NSLayoutConstraint!
@IBOutlet weak var topConstraint: NSLayoutConstraint!
@IBOutlet weak var bottomConstraint: NSLayoutConstraint!
@IBOutlet weak var backgroundImg: UIImageView!
@IBOutlet weak var rectangleImg: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Check if it's a iPhone X
if #available(iOS 11, *) {
let safeArea = UIApplication.shared.delegate?.window??.safeAreaInsets
// If its a x phone i use safe instead of superView
guard let safe = safeArea else { return }
if safe.bottom > 0 {
topConstraint.constant = safe.top
bottomConstraint.constant = safe.bottom + tabMenu.constant
}
}
}
override func viewDidAppear(_ animated: Bool) {
// Here I set the rectangle to 36% of the width and height (change to what you want)
rectangleImg.frame = CGRect(x: 0, y: 0, width: backgroundImg.frame.width * 0.36,
height: backgroundImg.frame.height * 0.36)
// Last I put the rectangle in the center of background image
rectangleImg.center.x = backgroundImg.center.x
rectangleImg.center.y = backgroundImg.center.y
}
Надеюсь, этот код может помочь!
Корень моей проблемы заключается в том, что у меня было четыре стороны UIImageView, прикрепленные к его виду контейнера. Когда ширина устройства изменилась, оно корректно масштабировало изображение, но это вызвало "растяжение" графики. Это вызвало, например, увеличение ширины головы. На данный момент я решил эту проблему, зафиксировав ширину изображения, чтобы координаты, которые я придумала из исходного изображения, остались без изменений, независимо от устройства.
Для тех, кто читает это, это может показаться не слишком хорошим решением, но я должен согласиться с этим, поскольку у меня довольно агрессивный срок. Я проверил его на нескольких симуляторах устройств, и он работает. Возможно, мне придется вернуться к этому в будущем, но сейчас это работает.
Я также использовал ответ на этот вопрос, чтобы переделать код: создать интерактивную диаграмму тела