Получение событий касания за пределами границ
Ранее были аналогичные вопросы, и я упомянул Захват касаний на подвью вне рамки его супервизора, используя hitTest: withEvent: и Передача событий касания к виду за пределами его родительского вида. Но они, кажется, не отвечают на мою конкретную проблему.
У меня есть настраиваемый элемент управления со следующей структурой:
+-------------------------------+
| UIButton |
+-------------------------------+
| |
| |
| UITableView |
| |
| |
+------------------------- +
Пользовательский элемент управления переопределяет подкласс UIButton
и добавляет UITableView
в качестве своего подвид. Идея состоит в том, чтобы весь контрольный акт выглядел как выпадающий список. Когда нажата кнопка UIButton
, UITableView
выйдет и включит выбор.
Все это отлично работает с hitTest
, переопределенным в UIButton
, как описано в ссылке Apple Q & A выше, если элемент управления является непосредственным дочерним элементом самого верхнего UIView
. Однако, если элемент управления находится в другой иерархии представлений, UITableView
не получает события касания.
Ниже приведен код использования hitTest:
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
// Convert the point to the target view coordinate system.
// The target view isn't necessarily the immediate subview
let pointForTargetView = self.spinnerTableView.convertPoint(point, fromView:self)
if (CGRectContainsPoint(self.spinnerTableView.bounds, pointForTargetView)) {
// The target view may have its view hierarchy,
// so call its hitTest method to return the right hit-test view
return self.spinnerTableView.hitTest(pointForTargetView, withEvent:event);
}
return super.hitTest(point, withEvent: event)
}
Edit:
Я извиняюсь за то, что не смог посмотреть ответы и проверить, разрешают ли они проблему, из-за некоторых других задач, требующих немедленного внимания. Я проверю их и приму помощь. Благодарим за терпение.
Ответы
Ответ 1
Как вы заявили:
Однако, если элемент управления находится в другой иерархии представлений, UITableView не получает события касания.
Даже если вы сделали это работающим, если бы вы рассматривали этот элемент под другим UIView??
Таким образом, у нас есть бремя преобразования точек для иерархии представлений, мое предложение состоит в том, что, как вы можете видеть в этот элемент управления, он добавляет табличное представление в ту же иерархию, где присутствует сам элемент управления. (например, он добавляет представление таблицы на контроллере, где был добавлен этот элемент управления)
Часть из ссылки:
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
[self setupSuggestionList];
[suggestionListView setHidden:NO];
// Add list to the super view.
if(self.dataSourceDelegate && [self.dataSourceDelegate isKindOfClass:UIViewController.class])
{
[((UIViewController *)self.dataSourceDelegate).view addSubview:suggestionListView];
}
// Setup list as per the given direction
[self adjustListFrameForDirection:dropDownDirection];
}
Надеюсь, что это поможет!
Ответ 2
Я думал о UIResponder
, что-то вроде:
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
// Convert the point to the target view coordinate system.
// The target view isn't necessarily the immediate subview
var responder: UIResponder = self
while responder.nextResponder() != nil {
responder = responder.nextResponder()!
if responder is UITableView {
// Got UITableView
break;
}
}
let pointForTargetView = responder.convertPoint(point, fromView:self)
if (CGRectContainsPoint(responder.bounds, pointForTargetView)) {
// The target view may have its view hierarchy,
// so call its hitTest method to return the right hit-test view
return self.responder.hitTest(pointForTargetView, withEvent:event);
}
return super.hitTest(point, withEvent: event)
}
Если этот метод не работает, попробуйте convertPoint
с помощью супервизора:
Если вы уверены, что self.spinnerTableView
не равно nil, и это ваша таблица, выполните следующие действия:
let pointForTargetView = self.spinnerTableView.superView.convertPoint(point, fromView:self)
Ответ 3
Я думаю, что вы должны переопределить pointInside
реализовать:
class Control: UIButton {
var dropdown: Bool = false
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
if dropdown {
return CGRectContainsPoint(CGRect(x: 0, y:0, width: self.bounds.width, self.bounds.height + spinnerTableView.frame.height), point)
}
return super.pointInside(point, withEvent: event)
}
}
как это, управление вызовами наблюдения hittest
при касании внешних границ управления, оно не вернет нуль.
Но все еще есть проблема, если элемент управления находится в другой иерархии представлений, UITableView может не получать события касания.
Я думаю, что эта проблема в норме, вы не можете решить другую точку зренияInside или нет, которая под контролем, если контрольный супервизор hittest
возвращает ноль, метод управления hittest
не будет вызываться.
Если вы не переопределили весь метод pointInside
, который под контролем, но это нереально.
Ответ 4
Вам нужно подклассифицировать UIView и использовать его как ваш корневой режим UIViewController.
class HitTestBottomView: UIView {
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
if let hitTestView = self.getHitTestButtonFrom(self){
let pointForHitTestView = hitTestView.convertPoint(point, fromView: self)
let pointForHitTestTableView = hitTestView.spinnerTableView.convertPoint(point, fromView: self)
if CGRectContainsPoint(hitTestView.bounds, pointForHitTestView) || CGRectContainsPoint(hitTestView.spinnerTableView.bounds, pointForHitTestTableView){
return hitTestView.hitTest(pointForHitTestView, withEvent: event)
}
}
return super.hitTest(point, withEvent: event)
}
func getHitTestButtonFrom(view:UIView) ->HitTestButton?{
if let testView = view as? HitTestButton {
return testView
}
for subView in view.subviews {
if let testView = self.getHitTestButtonFrom(subView) {
return testView
}
}
return nil
}
}
Ответ 5
Вы должны реализовать
func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool
для вашего UIButton
и отметьте границы UITableView
. Поскольку hitTest
сначала проверяет, находится ли эта точка перехода в пределах ваших границ, а затем вызывает hitTest
для вашего представления. поэтому вы должны реализовать pointInside
и вернуть true для своих границ.
WWDC для расширенные виды прокрутки и методы обработки касаний wwdc