Я собрал пример проекта, репо которого здесь .
В основном вам нужно сделать несколько вещей:
- Рассчитать оба ваш
UIImageView
размер кадра вместе с рамкой UIMage
, отображаемой в scaledAspectFit
. - Создайте два именованных ограничения и динамически переместите вашу кнопку, как только у вас будет ее рамка.
Во-первых, вы должны помнить, что кадры не могут быть установлены до viewDidLayoutSubviews
. Я создаю расширение UIImageView
, которое легко вычисляет, где на самом деле находится кадр UIImage
. (Это старый, но работающий код. Я уверен, что его можно улучшить.)
extension UIImageView {
public var scaleFactor:CGFloat {
guard let image = self.image, self.frame != CGRect.zero else {
return 0.0
}
let frame = self.frame
let extent = image.size
let heightFactor = frame.height/extent.height
let widthFactor = frame.width/extent.width
if extent.height > frame.height || extent.width > frame.width {
if heightFactor < 1 && widthFactor < 1 {
if heightFactor > widthFactor {
return widthFactor
} else {
return heightFactor
}
} else if extent.height > frame.height {
return heightFactor
} else {
return widthFactor
}
} else if extent.height < frame.height && extent.width < frame.width {
if heightFactor < widthFactor {
return heightFactor
} else {
return widthFactor
}
} else {
return 1
}
}
public var imageSize:CGSize {
if self.image == nil {
return CGSize.zero
} else {
return CGSize(width: (self.image?.size.width)!, height: (self.image?.size.height)!)
}
}
public var scaledSize:CGSize {
guard let image = self.image, self.frame != CGRect.zero else {
return CGSize.zero
}
let factor = self.scaleFactor
return CGSize(width: image.size.width * factor, height: image.size.height * factor)
}
}
Для второго пункта необходимо создать две переменные типа NSConstraint
. Я адаптировал мой ответ из двух лет go для этого:
var btnTop:NSLayoutConstraint!
var btnTrailing:NSLayoutConstraint!
И в `viewDidLoad:
button.heightAnchor.constraint(equalToConstant: 20).isActive = true
button.widthAnchor.constraint(equalToConstant: 20).isActive = true
btnTop = button.topAnchor.constraint(equalTo: imageView.topAnchor, constant: 10)
btnTop.isActive = true
btnTrailing = button.trailingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: -10)
btnTrailing.isActive = true
Обратите внимание, что вам нужно кодировать два строки для каждого ограничения! Я так и не понял, почему, но если вы попытаетесь добавить свойство isActive
с фактическим ограничением, компилятор не будет знать правильный тип переменной.
Теперь вы все вместе ie в viewDidLayoutSubviews
:
let scaledSize = imageView.scaledSize
var imageFrame = CGRect(origin: CGPoint.zero, size: scaledSize)
if scaledSize.width == imageView.frame.width {
// image fills view along width, calculate Y constant
imageFrame.origin.y = (imageView.frame.height - scaledSize.height) / 2
} else {
// image fills view along height, calculate X constant
imageFrame.origin.x = (imageView.frame.width - scaledSize.width) / 2
}
//btnTop.constant = imageFrame.width - 30
btnTop.constant = imageFrame.origin.y + 10
btnTrailing.constant = ((imageView.frame.width - imageFrame.width - imageFrame.origin.x) * -1) - 10
Поместить кнопку в верхнем левом углу на намного проще - мне потребовалось добрых 20 минут, чтобы получить правильные расчеты, чтобы сделать их сверху справа!
В моем тестовом проекте я инкапсулировал этот код в repositionCloseButton()
, который будет вызываться каждый раз, когда приложение отображает новое изображение. Это должно работать как в портретной и альбомной ориентации, так и в портретной и альбомной ориентации - расположение кнопки закрытия 20x20 на расстоянии 10 точек от правого верхнего угла изображения.