আইওএসের জন্য ইভেন্ট হ্যান্ডলিং - কীভাবে হিট টেস্ট: উইথ ইভেন্ট: এবং পয়েন্টইনসাইড: উইথ ইভেন্ট: সম্পর্কিত?


145

যদিও বেশিরভাগ অ্যাপলের নথিগুলি খুব ভাল লেখা থাকে, তবে আমি মনে করি আইওএসের জন্য ইভেন্ট হ্যান্ডলিং গাইড ' ব্যতিক্রম is সেখানে বর্ণিত কী হয়েছে তা পরিষ্কারভাবে বোঝা আমার পক্ষে কঠিন।

নথিটি বলে,

হিট-টেস্টিং-এ, উইন্ডোটি hitTest:withEvent:ভিউয়ের স্তরক্রমের শীর্ষ সর্বাধিক দর্শনে কল করে; এই পদ্ধতিটি পুনরাবৃত্তভাবে কল করে এগিয়ে যায়pointInside:withEvent: ক্রমবর্ধমানভাবে ভিউ হায়ারার্কির প্রতিটি দৃশ্যে যা হ্যাঁ ফিরে আসে, শ্রেণিবিন্যাসকে এগিয়ে নিয়ে যাওয়া পর্যন্ত এই স্পর্শটি সংঘটিত হয়েছিল যতক্ষণ না স্পর্শটি ঘটেছিল। এই দৃশ্যটি হিট-পরীক্ষা দর্শন হয়ে যায়।

তাহলে কি এটি কেবল এমন যে hitTest:withEvent:শীর্ষস্থানীয় pointInside:withEvent:দর্শনগুলির দ্বারা সিস্টেমকে বলা হয়, যা সমস্ত সাবভিউয়ের কল করে এবং যদি কোনও নির্দিষ্ট সাবভিউ থেকে প্রত্যাবর্তন হ্যাঁ হয়, তবে pointInside:withEvent:সেই সাবভিউয়ের সাবক্লাসগুলির কলগুলি ?


3
একটি খুব ভাল টিউটোরিয়াল যা আমাকে লিঙ্কটি
এনেবলু

এর সমতুল্য নতুন ডকুমেন্টটি এখন বিকাশকারী
ইউইকিট

উত্তর:


173

এটি বেশ প্রাথমিক প্রশ্ন মনে হচ্ছে। তবে আমি আপনাকে সম্মত করি ডকুমেন্টটি অন্যান্য নথির মতো পরিষ্কার নয়, তাই আমার উত্তর এখানে।

hitTest:withEvent:ইউআইআরআইএসপেন্ডারে বাস্তবায়ন নিম্নলিখিতগুলি করে:

  • এটা কল pointInside:withEvent: এরself
  • যদি hitTest:withEvent:রিটার্নটি না হয় তবে ফিরে আসবেnil । গল্পের শেষ।
  • যদি hitTest:withEvent:রিটার্নটি হ্যাঁ হয় তবে এটি তার পূর্বরূপগুলিতে বার্তা প্রেরণ করে। এটি শীর্ষ-স্তরের সাবভিউ থেকে শুরু হয় এবং অন্য মতামত অবিরত অবধি অবধি অবধি অবিরত থাকেnil অবজেক্ট বা সমস্ত সাবভিউগুলি বার্তাটি গ্রহণ করে না।
  • যদি কোনও সাবভিউ nilপ্রথম বারে কোনও অ- অবজেক্টকে hitTest:withEvent:ফেরত দেয় তবে প্রথমটি সেই বস্তুকে প্রত্যাবর্তন করে। গল্পের শেষ।
  • কোনও সাবভিউ যদি কোনও অ- nilঅবজেক্ট দেয় না, তবে প্রথমটি hitTest:withEvent:দেয়self

এই প্রক্রিয়াটি পুনরাবৃত্তভাবে পুনরাবৃত্তি করে, তাই সাধারণত ভিউ হায়ারার্কির পাতার দৃশ্য অবশেষে ফিরে আসে।

তবে, আপনি অন্যভাবে hitTest:withEventকিছু করতে ওভাররাইড করতে পারেন। অনেক ক্ষেত্রে, ওভাররাইডিং pointInside:withEvent:সহজ এবং এখনও আপনার অ্যাপ্লিকেশনটিতে ইভেন্ট হ্যান্ডলিং টুইঙ্ক করার জন্য পর্যাপ্ত বিকল্প সরবরাহ করে।


আপনি কি hitTest:withEvent:সব সাবভিউ এর অর্থ শেষ পর্যন্ত কার্যকর করা হয়?
realstuff02

2
হ্যাঁ. hitTest:withEvent:আপনার দর্শনগুলিতে কেবল ওভাররাইড করুন (এবং pointInsideযদি আপনি চান), একটি লগ মুদ্রণ করুন এবং কাকে কাকে ডাকে বলা হয়েছে তা [super hitTest...সন্ধানের hitTest:withEvent:জন্য কল করুন ।
এমএইচসি

পদক্ষেপ 3 করা উচিত নয় যেখানে আপনি উল্লেখ "রিটার্ন হ্যাঁ হয়, তাহলে এটি পাঠায় hitTest: withEvent: ... না এটা করা উচিত pointInside: withEvent আমি ভেবেছিলাম এটা সব subviews করার pointInside পাঠায়?
prostock

ফেব্রুয়ারিতে ফিরে এটি হিট টেস্টটি প্রথম পাঠিয়েছিল: উইথ ইভেন্ট সহ: যার মধ্যে একটি পয়েন্টইনসাইড: উইথ ইভেন্ট: নিজেই প্রেরণ করা হয়েছিল। আমি নিম্নলিখিত এসডিকে সংস্করণগুলির সাথে এই আচরণটি পুনরায় পরীক্ষা করে দেখিনি, তবে আমি মনে করি হিটটেষ্ট পাঠানো: উইন্ডোভেন্ট: আরও বোধগম্য কারণ এটি কোনও ইভেন্টের সাথে সম্পর্কিত কিনা তা একটি উচ্চ-স্তরের নিয়ন্ত্রণ সরবরাহ করে; পয়েন্টআইনসাইড: উইথ এভেনট: ইভেন্টের অবস্থানটি ভিউতে রয়েছে কিনা তা জানিয়েছে, ইভেন্টটি দেখার অন্তর্গত কিনা not উদাহরণস্বরূপ, কোনও সাবভিউ কোনও ইভেন্টের অবস্থান সাবউভিউতে থাকা সত্ত্বেও হ্যান্ডেল করতে না পারে।
এমএইচসি

1
ডাব্লুডব্লিউডিসি2014 সেশন 235 - উন্নত স্ক্রোলভিউ এবং টাচ হ্যান্ডলিং কৌশলগুলি এই সমস্যার জন্য দুর্দান্ত ব্যাখ্যা এবং উদাহরণ দেয়।
antonio081014

297

আমি মনে করি আপনি ভিউ হায়ারার্কির সাথে সাবক্লাসিং বিভ্রান্ত করছেন। ডক যা বলেন তা নিম্নরূপ। বলুন আপনার কাছে এই দৃশ্যপদ্ধতি রয়েছে। শ্রেণিবিন্যাসের দ্বারা আমি শ্রেণিবিন্যাসের কথা বলছি না, তবে নীচের মতামত শ্রেণিবিন্যাসের মধ্যে দেখা হচ্ছে:

+----------------------------+
|A                           |
|+--------+   +------------+ |
||B       |   |C           | |
||        |   |+----------+| |
|+--------+   ||D         || |
|             |+----------+| |
|             +------------+ |
+----------------------------+

বলে আঙ্গুলটা ভিতরে .ুকিয়ে দাও D। এখানে কি ঘটবে:

  1. hitTest:withEvent:বলা হয় A, দেখুন শ্রেণিবদ্ধের শীর্ষ সর্বাধিক দেখুন।
  2. pointInside:withEvent: প্রতিটি দর্শনে পুনরাবৃত্তভাবে বলা হয়।
    1. pointInside:withEvent:বলা হয় A, এবং ফিরে আসেYES
    2. pointInside:withEvent:বলা হয় B, এবং ফিরে আসেNO
    3. pointInside:withEvent:বলা হয় C, এবং ফিরে আসেYES
    4. pointInside:withEvent:বলা হয় D, এবং ফিরে আসেYES
  3. ফিরে আসা মতামতের বিষয়ে YES, স্পর্শটি কোথায় ঘটেছে সেই সংক্ষিপ্তসারটি দেখতে এটি শ্রেণিবিন্যাসের নীচে তাক করবে। এই ক্ষেত্রে, থেকে A, Cএবং D, এটি হবে D
  4. D হিট-পরীক্ষা ভিউ হবে

উত্তরের জন্য ধন্যবাদ. আপনি যা বর্ণনা করেছেন তা হ'ল আমার মনে যা ছিল তবে @ এমএইচসি hitTest:withEvent:বি, সি এবং ডি সম্পর্কে যা বলেছেন তাও আহবান করা হয়েছে। ডি না হলে সি এর একটি পর্যবেক্ষণ হলে কী হবে? আমি মনে করি আমি বিভ্রান্ত হয়ে
পড়েছি

2
আমার অঙ্কনটিতে, ডি সি এর একটি সংক্ষিপ্তসার রয়েছে
pgb

1
করবে না Aআসতে YESপাশাপাশি, ঠিক যেমন Cএবং Dকরে?
মার্টিন উইকম্যান

2
ভুলে যাবেন না যে দৃশ্যগুলি অদৃশ্য হয় (হয়। Hided বা 0.1 এর নীচে অস্বচ্ছতা দ্বারা), বা ব্যবহারকারীর মিথস্ক্রিয়া বন্ধ থাকায় হিট টেস্টের প্রতিক্রিয়া কখনই আসবে না। আমি মনে করি না এই জিনিসগুলিতে প্রথমে হিটটেষ্ট আহ্বান করা হচ্ছে।
জনি

কেবলমাত্র সেই হিটটেষ্টটি যুক্ত করতে চেয়েছিল: উইথ ইভেন্ট: তাদের শ্রেণিবিন্যাসের উপর নির্ভর করে সমস্ত মতামতের উপর কল করা যেতে পারে।
অদিত্যা

47

আমি আইওএস-এ এই হিট-টেস্টিংটি খুব সহায়ক বলে মনে করি

এখানে চিত্র বর্ণনা লিখুন

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) {
        return nil;
    }
    if ([self pointInside:point withEvent:event]) {
        for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
            CGPoint convertedPoint = [subview convertPoint:point fromView:self];
            UIView *hitTestView = [subview hitTest:convertedPoint withEvent:event];
            if (hitTestView) {
                return hitTestView;
            }
        }
        return self;
    }
    return nil;
}

সুইফট 4 সম্পাদনা করুন:

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    if self.point(inside: point, with: event) {
        return super.hitTest(point, with: event)
    }
    guard isUserInteractionEnabled, !isHidden, alpha > 0 else {
        return nil
    }

    for subview in subviews.reversed() {
        let convertedPoint = subview.convert(point, from: self)
        if let hitView = subview.hitTest(convertedPoint, with: event) {
            return hitView
        }
    }
    return nil
}

সুতরাং আপনাকে এটি ইউআইভিউর একটি সাবক্লাসে যুক্ত করতে হবে এবং আপনার শ্রেণিবিন্যাসের সমস্ত দৃষ্টিভঙ্গি এটি থেকে উত্তরাধিকারসূত্রে পাওয়া উচিত?
গুগ

21

উত্তরের জন্য ধন্যবাদ, তারা আমাকে "ওভারলে" মতামত দিয়ে পরিস্থিতি সমাধান করতে সহায়তা করেছে।

+----------------------------+
|A +--------+                |
|  |B  +------------------+  |
|  |   |C            X    |  |
|  |   +------------------+  |
|  |        |                |
|  +--------+                | 
|                            |
+----------------------------+

ধরে নিন X- ব্যবহারকারীর স্পর্শ। pointInside:withEvent:উপর Bআয় NO, তাই hitTest:withEvent:আয় AUIViewশীর্ষস্থানীয় সর্বাধিক দৃশ্যমান দৃশ্যে আপনাকে স্পর্শ পাওয়ার দরকার হলে ইস্যুটি পরিচালনা করতে আমি বিভাগটি লিখেছিলাম ।

- (UIView *)overlapHitTest:(CGPoint)point withEvent:(UIEvent *)event {
    // 1
    if (!self.userInteractionEnabled || [self isHidden] || self.alpha == 0)
        return nil;

    // 2
    UIView *hitView = self;
    if (![self pointInside:point withEvent:event]) {
        if (self.clipsToBounds) return nil;
        else hitView = nil;
    }

    // 3
    for (UIView *subview in [self.subviewsreverseObjectEnumerator]) {
        CGPoint insideSubview = [self convertPoint:point toView:subview];
        UIView *sview = [subview overlapHitTest:insideSubview withEvent:event];
        if (sview) return sview;
    }

    // 4
    return hitView;
}
  1. লুকানো বা স্বচ্ছ দর্শনগুলির জন্য বা userInteractionEnabledসেট করা দর্শনের জন্য আমাদের স্পর্শ ইভেন্টগুলি প্রেরণ করা উচিত নয়NO ;
  2. যদি স্পর্শটি ভিতরে থাকে self,self সম্ভাব্য ফলে বিবেচনা করা হবে।
  3. হিট জন্য পুনরাবৃত্তভাবে সমস্ত সাবভিউ দেখুন। যদি থাকে তবে তা ফিরিয়ে দিন।
  4. অন্য পদক্ষেপ 2 এর ফলাফলের উপর নির্ভর করে স্ব স্ব বা শূন্য ফিরে আসুন।

দ্রষ্টব্য, [self.subviewsreverseObjectEnumerator]শীর্ষ থেকে নীচে পর্যন্ত পর্যায়ক্রমিক অনুসরণ অনুসরণ করা দরকার। এবং clipsToBoundsমুখোশযুক্ত সাবভিউ পরীক্ষা না করে তা পরীক্ষা করে দেখুন।

ব্যবহার:

  1. আপনার সাবক্ল্যাস করা ভিউতে বিভাগটি আমদানি করুন।
  2. প্রতিস্থাপন hitTest:withEvent:এই সঙ্গে
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    return [self overlapHitTest:point withEvent:event];
}

অফিসিয়াল অ্যাপলের গাইড খুব ভাল চিত্রও সরবরাহ করে।

আশা করি এটি কারও সাহায্য করবে।


অ্যামেজিং! পরিষ্কার যুক্তি এবং গ্রেট কোড স্নিপেটের জন্য ধন্যবাদ, আমার হেড-স্ক্র্যাচার সমাধান করেছেন!
থম্পসন

@ লায়ন, সুন্দর উত্তর। এছাড়াও আপনি প্রথম ধাপে রঙ সাফ করার জন্য সমতাটি পরীক্ষা করতে পারেন।
অ্যাকোয়ারিয়াম_মুজ

3

এটি এই স্নিপেটের মতো দেখায়!

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if (self.hidden || !self.userInteractionEnabled || self.alpha < 0.01)
    {
        return nil;
    }

    if (![self pointInside:point withEvent:event])
    {
        return nil;
    }

    __block UIView *hitView = self;

    [self.subViews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {   

        CGPoint thePoint = [self convertPoint:point toView:obj];

        UIView *theSubHitView = [obj hitTest:thePoint withEvent:event];

        if (theSubHitView != nil)
        {
            hitView = theSubHitView;

            *stop = YES;
        }

    }];

    return hitView;
}

আমি উত্তরটি বোঝার পক্ষে এটি সবচেয়ে সহজ বলে মনে করি এবং এটি আমার আসল আচরণের পর্যবেক্ষণগুলির সাথে খুব ঘনিষ্ঠতার সাথে মেলে। পার্থক্যটি হ'ল বিপরীত ক্রমে সংক্ষিপ্তসারগুলি পর্যালোচনা করা হয়েছে, সুতরাং সামনের কাছাকাছি সংক্ষিপ্তসারগুলি তাদের পিছনের ভাইবোনদের পছন্দকে স্পর্শ করে।
ডগলাস হিল

@ ডগলাসহিল আপনার সংশোধন করার জন্য ধন্যবাদ। শুভেচ্ছা
হিপ্পো

1

@ লিওনের স্নিপেট একটি কবজির মতো কাজ করে। আমি এটিকে ২.১ স্যুইফ্টে পোর্ট করেছিলাম এবং এটি ইউআইভিউতে এক্সটেনশন হিসাবে ব্যবহার করি। কারওর প্রয়োজন হলে আমি এটি এখানে পোস্ট করছি।

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
    }
}

এটি ব্যবহার করতে, কেবল হিটটেষ্টকে ওভাররাইড করুন: পয়েন্ট: আপনার ইউভিউতে নিম্নরূপে ইভেন্ট:

override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
    let uiview = super.hitTest(point, withEvent: event)
    print("hittest",uiview)
    return overlapHitTest(point, withEvent: event)
}

0

ক্লাস ডায়াগ্রাম

হিট টেস্টিং

একটি First Responder

First Responderএক্ষেত্রে গভীরতম UIView point()পদ্ধতিটি সত্য যা প্রত্যাবর্তন করেছে

func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?
func point(inside point: CGPoint, with event: UIEvent?) -> Bool

অভ্যন্তরীণ hitTest()মত দেখাচ্ছে

hitTest() {

    if (isUserInteractionEnabled == false || isHidden == true || alpha == 0 || point() == false) { return nil }

    for subview in subviews {
        if subview.hitTest() != nil {
            return subview
        }
    }

    return nil

}

এ টাচ ইভেন্ট প্রেরণ করুন First Responder

//UIApplication.shared.sendEvent()

//UIApplication, UIWindow
func sendEvent(_ event: UIEvent)

//UIResponder
func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?)

আসুন উদাহরণটি একবার দেখুন

প্রতিক্রিয়াশীল চেইন

//UIApplication.shared.sendAction()
func sendAction(_ action: Selector, to target: Any?, from sender: Any?, for event: UIEvent?) -> Bool

উদাহরণ দেখুন

class AppDelegate: UIResponder, UIApplicationDelegate {
    @objc
    func foo() {
        //this method is called using Responder Chain
        print("foo") //foo
    }
}

class ViewController: UIViewController {
    func send() {
        UIApplication.shared.sendAction(#selector(AppDelegate.foo), to: nil, from: view1, for: nil)
    }
}

[অ্যান্ড্রয়েড অন টাচ]

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.