Swift - программно щелкнуть по точке на экране

tl;dr- Как я могу программно выполнить собственное касание в определенной точке на экране?

В веб-представлении есть HTML, который содержит Iframe, поэтому код и элементы веб-представления недоступны, а отправка сообщений в JS также невозможна. В веб-просмотре есть кнопка по конкретным координатам. Как его нажать программно?


person Luda    schedule 28.06.2016    source источник
comment
Не могли бы вы предоставить больше информации о том, что вы хотите и что вы пробовали сейчас?   -  person Mayank Jain    schedule 28.06.2016
comment
Без приватного API я думаю, что нельзя. За исключением того, что вы вызываете непосредственно функцию (или делегат), которая обрабатывает касание.   -  person Duyen-Hoa    schedule 28.06.2016
comment
И если то, что вы хотите сделать, не находится в тестовой схеме. В этом случае вы можете использовать для этого какой-нибудь фреймворк, например KIF.   -  person Duyen-Hoa    schedule 28.06.2016
comment
@MayankModi, у меня есть кнопка в веб-просмотре. Я знаю, где конкретно находится кнопка, и мне нужно ее программно нажать, но я не могу получить доступ к коду веб-просмотра. Поэтому я хочу имитировать прикосновение. (обновил вопрос)   -  person Luda    schedule 28.06.2016
comment
Почему вы не могли просмотреть подвиды страницы и найти один из них по соответствующей координате?   -  person Feldur    schedule 28.06.2016
comment
Зачем вам нужно обнаружить кнопку? вы можете использовать методы делегата? Можете ли вы описать, что именно вы хотите?   -  person Mayank Jain    schedule 29.06.2016
comment
Попробуйте это. Это быстрая версия, если хотите: buttonObj.sendActionsForControlEvents(.TouchUpInside) .   -  person Dershowitz123    schedule 30.06.2016
comment
Вы нашли решение? Ищу безуспешно. Пожалуйста, уточните, решена ли ваша проблема.   -  person Masoud Dadashi    schedule 24.05.2017


Ответы (4)


Если вы не хотите имитировать прикосновение на уровне JavaScript, вы можете использовать эту extension для UITouch и UIEvent, просто подключите его к Swift:

func performTouchInView(view: UIView) {
    let touch = UITouch(inView: view)
    let eventDown = UIEvent(touch: touch)

    touch.view!.touchesBegan(eventDown.allTouches()!, withEvent: eventDown)

    touch.setPhase(.Ended)
    let eventUp = UIEvent(touch: touch)

    touch.view!.touchesEnded(eventUp.allTouches()!, withEvent: eventUp)
}

Ваш синтаксис может отличаться в зависимости от используемой версии Swift. Здесь я делаю принудительную распаковку, но вы должны получить общее представление о том, что происходит.

person JAL    schedule 30.06.2016
comment
@Luda, ты добавил расширение в свое приложение через соединительный заголовок? - person JAL; 30.06.2016
comment
@Luda Глядя на частный заголовок UITouch, я вижу, что есть переменные, которые обрабатывают позицию касания: _locationInWindow , _preciseLocationInWindow и т. д. Вы можете попробовать переопределить _setLocation:preciseLocation:inWindowResetPreviousLocation: или _setLocationInWindow:resetPrevious:, чтобы явно задать точку касания. Просто выставьте эти интерфейсы в файле Objective-C. - person JAL; 30.06.2016
comment
@Luda Я не переводил расширение. Расширение добавляет дополнительные методы в UITouch и UIEvent. Если вы подключитесь к Swift, реализация расширения будет выглядеть именно так. Чтобы переопределить другие методы, которые я упомянул, просто укажите их в расширении интерфейса. - person JAL; 30.06.2016
comment
@Luda, ты добавил в свой проект файлы TouchSynthesis.h и TouchSynthesis.m? Я не могу воспроизвести проблему, я только что создал пустой проект Swift и смог без проблем скомпилировать код в своем ответе. - person JAL; 30.06.2016
comment
Что такое performTouchLeftInView? О, я вижу, это метод класса для UIEvent, а не UITouch. Используйте UIEvent.performTouchLeftInView(someView). - person JAL; 30.06.2016
comment
@Luda У меня нет и не возникает никаких проблем. Вот что я сделал: 1. Создайте новое приложение Swift с одним представлением. 2. Добавьте соединительный заголовок. 3. Добавьте TouchSynthesis.h и TouchSynthesis.m в проект. 4. Добавьте #import "TouchSynthesis.h" к связующему заголовку. 5. Добавьте код Swift в моем ответе на контроллер представления. - person JAL; 30.06.2016
comment
@Luda Я также должен отметить, что я использую Xcode 7.3.1 со Swift 2.2. - person JAL; 30.06.2016
comment
Jal, я попытался воспроизвести ваши шаги до 4 и построил. 1. Создайте новое приложение Swift с одним представлением. 2. Добавьте связующий заголовок. 3. Добавьте TouchSynthesis.h и TouchSynthesis.m в проект. 4. Добавьте #import TouchSynthesis.h в соединительный заголовок. Затем я собрал его, но он не скомпилировался. - person Luda; 03.07.2016
comment
Здесь есть ссылка на этот код stackoverflow.com/a/1975901/1341180, и вы можете видеть, что люди говорят, что это не так. компиляция тоже. Может быть, вы можете загрузить свой образец защиты на GitHub - person Luda; 03.07.2016
comment
@Люда, ты прав. Я использовал это в проекте Cocopods, и во время его компиляции произошел сбой во время выполнения. Попробовал добавить это в пустой проект и получил кучу ошибок компиляции. Не уверен, почему. Все еще расследуют... - person JAL; 06.07.2016
comment
Будет ли приложение, использующее это, пройти через процесс отправки приложения Apple? - person Geoff H; 12.03.2019

Чтобы имитировать прикосновение, вам нужно будет написать функцию javascript на вашей веб-странице, которая может нажимать кнопку от вашего имени.

Предположим, что кнопка на веб-сайте, загруженная в iFrame, имеет следующий код:

<a href="#" id="MagicButton" onclick="targetFunction();">Click Me!</a>

И iFrame закодирован так на вашей веб-странице:

<iframe id="MyIFrame" src="http://www.stackoverflow.com" width="200" height="200"></iframe>

На вашей веб-странице добавьте следующую функцию javascript для вызова события нажатия на встроенную кнопку:

<script>
    function myJavaScriptFunction(){
        //get handle for the iFrame element
        var iFrame = document.getElementById('MyIFrame');
        var htmlDoc = iFrame.contentDocument ? iFrame.contentDocument : iFrame.contentWindow.document;
        var magicButton = htmlDoc.getElementById('MagicButton');
        magicButton.click();
    }
</script>

Возвращаясь к вашему коду Swift, вам нужно будет использовать следующий API:

func evaluateJavaScript(_ javaScriptString: String,
      completionHandler completionHandler: ((AnyObject?,
                                 NSError?) -> Void)?) 

Вы можете выполнить свою функцию javascript, вызвав следующую функцию после загрузки страницы:

   myWKWebview.evaluateJavaScript("myJavaScriptFunction(argument1, argument2);",
                                                               completion:{ _ in })
person Myles Eynon    schedule 30.06.2016
comment
У меня вообще нет доступа к JS. Кодируется из соображений безопасности. Решение должно быть родным только для iOS. - person Luda; 30.06.2016

Таким образом, цель состояла в том, чтобы имитировать событие сенсорного экрана в webView.

Структура

ViewController-
  self.glassView-|
     self.webView-|
       self.view.

Я пишу этот класс, но еще не тестирую:

class ViewController: UIViewController,UIWebViewDelegate {

    @IBOutlet weak var webView: UIWebView!
    @IBOutlet weak var glassView: UIView!
    var portionRect: CGRect!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.webView.delegate = self
        let url = NSURL (string: "http://www.tiscali.it");
        let requestObj = NSURLRequest(URL: url!)
        self.webView.loadRequest(requestObj)
        self.view.userInteractionEnabled = true
    }

    ////////////////////////////////////////////////
    // UIWebViewDelegate

    func webViewDidStartLoad(webView: UIWebView) {
        UIApplication.sharedApplication().networkActivityIndicatorVisible = true
    }


    func webViewDidFinishLoad(webView: UIWebView) {
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false
        if self.webView.loading {
            return
        } else {
            simulateTap()
        }
    }

    func webView(webView: UIWebView, didFailLoadWithError error: NSError?) {
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false
    }

    func simulateTap() {
        let pointOnTheScreen = CGPointMake(156.0, 506.6)
         self.glassView.hitTest(pointOnTheScreen, withEvent: nil)
         self.glassView.overlapHitTest(pointOnTheScreen, withEvent: nil)            
         print("tap on screen")
    }

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        if let touch = touches.first {
            let touchLocation = touch.locationInView(self.view)
            print("point touched: \(touchLocation)")
        }
        super.touchesBegan(touches, withEvent:event)
    }
}

extension UIView {
    func overlapHitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
        // 1
        if !self.userInteractionEnabled || self.hidden || self.alpha == 0 {
            return nil
        }
        //2
        var hitView: UIView? = self
        if !self.pointInside(point, withEvent: event) {
            if self.clipsToBounds {
                return nil
            } else {
                hitView = nil
            }
        }
        //3
        for subview in self.subviews.reverse() {
            let insideSubview = self.convertPoint(point, toView: subview)
            if let sview = subview.overlapHitTest(insideSubview, withEvent: event) {
                return sview
            }
        }
        return hitView
    }
}
person Alessandro Ornano    schedule 05.07.2016
comment
Поправьте меня, если я ошибаюсь: вы настраиваете распознаватель жестов касания, а затем отвечаете на него в функции handleTap. Но как вызвать кран? - person Luda; 06.07.2016
comment
Вы хотите вызывать автоматически или вручную (addTarget)? - person Alessandro Ornano; 06.07.2016
comment
Я хочу вызвать тап программно - person Luda; 06.07.2016
comment
Я полностью переписал свой ответ, потому что я неправильно понимаю, чего вы хотите достичь. Я еще не тестирую этот класс, дайте мне знать, если он может помочь. - person Alessandro Ornano; 06.07.2016

Вы также можете попробовать хит-тест:

    let pointOnTheScreen = CGPointMake(50, 50)
    view.hitTest(pointOnTheScreen, withEvent: nil)
person NFerocious    schedule 06.07.2016
comment
Нашел это объяснение: stackoverflow.com/a/4961484/1341180 Не знаю, как это может мне помочь... - person Luda; 06.07.2016