Как изменить размер UIView, перетаскивая его края?
В своем приложении для iPad я хочу, чтобы пользователи могли изменять размер UIView
перетаскивая вид с его краев. Я буду использовать iOS 5 SDK, так какой же самый чистый подход для этого? Есть ли альтернативы для достижения этой цели, не касаясь прикосновений Began, TouchMoved и т. Д.?
4 ответа
Я предполагаю, что ваш пользовательский интерфейс включает в себя какие-то маркеры по сторонам вида и прикрепляет простой UIPanGestureRecognizer
с этими ручками управления делает всю проблему довольно легко.
В методе действия из распознавателя жестов просто получите -translationInView:
относительно размера, который вы изменяете, сохраняйте исходный кадр, когда состояние распознавателя жестов UIGestureRecognizerStateBegan
и постоянно корректировать кадр вида, пока состояние UIGestureRecognizerStateChanged
,
Вы можете сделать это, проверив точку начала касания. Если он попадает в один из четырех ваших углов, вы можете изменить размер в зависимости от расстояния между точкой начала касания и текущей точкой касания. (Если точка начала касания не достигла угла, мы просто перемещаем вид вместо изменения размера.)
Определите размер ваших перетаскиваемых углов.
CGFloat kResizeThumbSize = 45.0f;
Добавьте эти переменные экземпляра в ваш класс, чтобы отслеживать состояние касания и то, как мы изменяем размеры.
@interface MY_CLASS_NAME : UIView {
BOOL isResizingLR;
BOOL isResizingUL;
BOOL isResizingUR;
BOOL isResizingLL;
CGPoint touchStart;
}
Обработка сенсорного запуска / изменения событий.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
touchStart = [[touches anyObject] locationInView:self];
isResizingLR = (self.bounds.size.width - touchStart.x < kResizeThumbSize && self.bounds.size.height - touchStart.y < kResizeThumbSize);
isResizingUL = (touchStart.x <kResizeThumbSize && touchStart.y <kResizeThumbSize);
isResizingUR = (self.bounds.size.width-touchStart.x < kResizeThumbSize && touchStart.y<kResizeThumbSize);
isResizingLL = (touchStart.x <kResizeThumbSize && self.bounds.size.height -touchStart.y <kResizeThumbSize);
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint touchPoint = [[touches anyObject] locationInView:self];
CGPoint previous = [[touches anyObject] previousLocationInView:self];
CGFloat deltaWidth = touchPoint.x - previous.x;
CGFloat deltaHeight = touchPoint.y - previous.y;
// get the frame values so we can calculate changes below
CGFloat x = self.frame.origin.x;
CGFloat y = self.frame.origin.y;
CGFloat width = self.frame.size.width;
CGFloat height = self.frame.size.height;
if (isResizingLR) {
self.frame = CGRectMake(x, y, touchPoint.x+deltaWidth, touchPoint.y+deltaWidth);
} else if (isResizingUL) {
self.frame = CGRectMake(x+deltaWidth, y+deltaHeight, width-deltaWidth, height-deltaHeight);
} else if (isResizingUR) {
self.frame = CGRectMake(x, y+deltaHeight, width+deltaWidth, height-deltaHeight);
} else if (isResizingLL) {
self.frame = CGRectMake(x+deltaWidth, y, width-deltaWidth, height+deltaHeight);
} else {
// not dragging from a corner -- move the view
self.center = CGPointMake(self.center.x + touchPoint.x - touchStart.x,
self.center.y + touchPoint.y - touchStart.y);
}
}
Я обновил код выше, используя enum
,
class ResizableView: UIView {
enum Edge {
case topLeft, topRight, bottomLeft, bottomRight, none
}
static var edgeSize: CGFloat = 44.0
private typealias `Self` = ResizableView
var currentEdge: Edge = .none
var touchStart = CGPoint.zero
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
touchStart = touch.location(in: self)
currentEdge = {
if self.bounds.size.width - touchStart.x < Self.edgeSize && self.bounds.size.height - touchStart.y < Self.edgeSize {
return .bottomRight
} else if touchStart.x < Self.edgeSize && touchStart.y < Self.edgeSize {
return .topLeft
} else if self.bounds.size.width-touchStart.x < Self.edgeSize && touchStart.y < Self.edgeSize {
return .topRight
} else if touchStart.x < Self.edgeSize && self.bounds.size.height - touchStart.y < Self.edgeSize {
return .bottomLeft
}
return .none
}()
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let currentPoint = touch.location(in: self)
let previous = touch.previousLocation(in: self)
let originX = self.frame.origin.x
let originY = self.frame.origin.y
let width = self.frame.size.width
let height = self.frame.size.height
let deltaWidth = currentPoint.x - previous.x
let deltaHeight = currentPoint.y - previous.y
switch currentEdge {
case .topLeft:
self.frame = CGRect(x: originX + deltaWidth, y: originY + deltaHeight, width: width - deltaWidth, height: height - deltaHeight)
case .topRight:
self.frame = CGRect(x: originX, y: originY + deltaHeight, width: width + deltaWidth, height: height - deltaHeight)
case .bottomRight:
self.frame = CGRect(x: originX, y: originY, width: currentPoint.x + deltaWidth, height: currentPoint.y + deltaWidth)
case .bottomLeft:
self.frame = CGRect(x: originX + deltaWidth, y: originY, width: width - deltaWidth, height: height + deltaHeight)
default:
// Moving
self.center = CGPoint(x: self.center.x + currentPoint.x - touchStart.x,
y: self.center.y + currentPoint.y - touchStart.y)
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
currentEdge = .none
}
}
currentEdge
сохраняет состояние прикосновения пользователя.
Это решение Swift 4.2, которое работает с AutoLayout & Constraint-Animation.
Поскольку при работе с AutoLayout рекомендуется анимировать ограничения, а не фактический размер прямоугольника, это решение делает именно это.
Дополнительные возможности:
- Изменение размера только одной стороны, когда не по краю.
- Переместите весь прямоугольник на ощупь в середине этого.
Посмотрите видео об этом здесь: https://imgur.com/a/CrkARLi
Импорт состоит в том, чтобы связать ограничения как выходы, анимировать их.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var topConstraint: NSLayoutConstraint!
@IBOutlet weak var rightConstraint: NSLayoutConstraint!
@IBOutlet weak var leftConstraint: NSLayoutConstraint!
@IBOutlet weak var bottomConstraint: NSLayoutConstraint!
@IBOutlet weak var rect: UIView!
struct ResizeRect{
var topTouch = false
var leftTouch = false
var rightTouch = false
var bottomTouch = false
var middelTouch = false
}
var touchStart = CGPoint.zero
var proxyFactor = CGFloat(10)
var resizeRect = ResizeRect()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first{
let touchStart = touch.location(in: self.view)
print(touchStart)
resizeRect.topTouch = false
resizeRect.leftTouch = false
resizeRect.rightTouch = false
resizeRect.bottomTouch = false
resizeRect.middelTouch = false
if touchStart.y > rect.frame.minY + (proxyFactor*2) && touchStart.y < rect.frame.maxY - (proxyFactor*2) && touchStart.x > rect.frame.minX + (proxyFactor*2) && touchStart.x < rect.frame.maxX - (proxyFactor*2){
resizeRect.middelTouch = true
print("middle")
return
}
if touchStart.y > rect.frame.maxY - proxyFactor && touchStart.y < rect.frame.maxY + proxyFactor {
resizeRect.bottomTouch = true
print("bottom")
}
if touchStart.x > rect.frame.maxX - proxyFactor && touchStart.x < rect.frame.maxX + proxyFactor {
resizeRect.rightTouch = true
print("right")
}
if touchStart.x > rect.frame.minX - proxyFactor && touchStart.x < rect.frame.minX + proxyFactor {
resizeRect.leftTouch = true
print("left")
}
if touchStart.y > rect.frame.minY - proxyFactor && touchStart.y < rect.frame.minY + proxyFactor {
resizeRect.topTouch = true
print("top")
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first{
let currentTouchPoint = touch.location(in: self.view)
let previousTouchPoint = touch.previousLocation(in: self.view)
let deltaX = currentTouchPoint.x - previousTouchPoint.x
let deltaY = currentTouchPoint.y - previousTouchPoint.y
if resizeRect.middelTouch{
topConstraint.constant += deltaY
leftConstraint.constant += deltaX
rightConstraint.constant -= deltaX
bottomConstraint.constant -= deltaY
}
if resizeRect.topTouch {
topConstraint.constant += deltaY
}
if resizeRect.leftTouch {
leftConstraint.constant += deltaX
}
if resizeRect.rightTouch {
rightConstraint.constant -= deltaX
}
if resizeRect.bottomTouch {
bottomConstraint.constant -= deltaY
}
UIView.animate(withDuration: 0.25, delay: 0, options: UIView.AnimationOptions.curveEaseIn, animations: {
self.view.layoutIfNeeded()
}, completion: { (ended) in
})
}
}
}
Я поместил это как рабочий проект на Github тоже: https://github.com/ppoh71/resizeRectangleOnTouchDrag
Быстрая версия решения @Prerna Chavan, решение Prerna не определяет, касается ли пользователь прикосновения к краям, оно обнаруживает только углы, однако приведенный ниже код обнаруживает все
class OverlayView: UIView {
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
static var kResizeThumbSize:CGFloat = 44.0
private typealias `Self` = OverlayView
var imageView = UIImageView()
var isResizingLeftEdge:Bool = false
var isResizingRightEdge:Bool = false
var isResizingTopEdge:Bool = false
var isResizingBottomEdge:Bool = false
var isResizingBottomRightCorner:Bool = false
var isResizingLeftCorner:Bool = false
var isResizingRightCorner:Bool = false
var isResizingBottomLeftCorner:Bool = false
//Define your initialisers here
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let currentPoint = touch.location(in: self)
isResizingBottomRightCorner = (self.bounds.size.width - currentPoint.x < Self.kResizeThumbSize && self.bounds.size.height - currentPoint.y < Self.kResizeThumbSize);
isResizingLeftCorner = (currentPoint.x < Self.kResizeThumbSize && currentPoint.y < Self.kResizeThumbSize);
isResizingRightCorner = (self.bounds.size.width-currentPoint.x < Self.kResizeThumbSize && currentPoint.y < Self.kResizeThumbSize);
isResizingBottomLeftCorner = (currentPoint.x < Self.kResizeThumbSize && self.bounds.size.height - currentPoint.y < Self.kResizeThumbSize);
isResizingLeftEdge = (currentPoint.x < Self.kResizeThumbSize)
isResizingTopEdge = (currentPoint.y < Self.kResizeThumbSize)
isResizingRightEdge = (self.bounds.size.width - currentPoint.x < Self.kResizeThumbSize)
isResizingBottomEdge = (self.bounds.size.height - currentPoint.y < Self.kResizeThumbSize)
// do something with your currentPoint
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let currentPoint = touch.location(in: self)
// do something with your currentPoint
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let currentPoint = touch.location(in: self)
// do something with your currentPoint
isResizingLeftEdge = false
isResizingRightEdge = false
isResizingTopEdge = false
isResizingBottomEdge = false
isResizingBottomRightCorner = false
isResizingLeftCorner = false
isResizingRightCorner = false
isResizingBottomLeftCorner = false
}
}
Просто small fixed
добавленный мной в ответ @Peter Pohlmann, который не позволяет перетаскивать или перемещать представление за пределыview's frame
Единственный бит в override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first{
let currentTouchPoint = touch.location(in: self.view)
let previousTouchPoint = touch.previousLocation(in: self.view)
let deltaX = currentTouchPoint.x - previousTouchPoint.x
let deltaY = currentTouchPoint.y - previousTouchPoint.y
if resizeRect.middelTouch {
if topConstraint.constant + deltaY > 0 && leftConstraint.constant + deltaX > 0 && rightConstraint.constant - deltaX > 0 && bottomConstraint.constant - deltaY > 40 {
topConstraint.constant += deltaY
leftConstraint.constant += deltaX
rightConstraint.constant -= deltaX
bottomConstraint.constant -= deltaY
}
}
if resizeRect.topTouch {
if topConstraint.constant + deltaY > 0 {
topConstraint.constant += deltaY
}
}
if resizeRect.leftTouch {
if leftConstraint.constant + deltaX > 0 {
leftConstraint.constant += deltaX
}
}
if resizeRect.rightTouch {
if rightConstraint.constant - deltaX > 0 {
rightConstraint.constant -= deltaX
}
}
if resizeRect.bottomTouch {
if bottomConstraint.constant - deltaY > 0 {
bottomConstraint.constant -= deltaY
}
}
UIView.animate(withDuration: 0.25, delay: 0, options: UIView.AnimationOptions.curveLinear, animations: {
self.view.layoutIfNeeded()
}, completion: { (ended) in
})
}
}