سؤال

في تطبيق الكاكاو الخاص بي ، أحتاج إلى nscell مخصص لـ nstableView. هذه تحتوي الفئة الفرعية NSCELL على NSBUTTONCELL مخصص للتعامل مع نقرة (واثنين أو ثلاثة nstextfieldcells للمحتويات النصية). ستجد مثالًا مبسطًا على الكود الخاص بي أدناه.

@implementation TheCustomCell

- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
   // various NSTextFieldCells
   NSTextFieldCell *titleCell = [[NSTextFieldCell alloc] init];
   ....
   // my custom NSButtonCell
   MyButtonCell *warningCell = [[MyButtonCell alloc] init];
   [warningCell setTarget:self];
   [warningCell setAction:@selector(testButton:)];
   [warningCell drawWithFrame:buttonRect inView:controlView];
}

المشكلة التي علقت بها هي: ما هي الطريقة الأفضل/الصحيحة للحصول على هذا الزر (بشكل أدق: nsbuttoncell) داخل هذا nscell للعمل بشكل صحيح؟ "العمل" يعني: تشغيل رسالة الإجراء المعينة وإظهار الصورة البديلة عند النقر عليها. خارج المربع ، لا يفعل الزر أي شيء عند النقر عليه.

من الصعب العثور على المعلومات / القراءات حول هذا الموضوع. أشارتني المنشورات الوحيدة التي وجدتها على الشبكة إلى التنفيذ

- (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)untilMouseUp; 

هل هذه هي الطريقة الصحيحة للقيام بذلك؟؟؟ تنفيذ TrackMouse: في NSCELL المحتوية؟ ثم إعادة توجيه الحدث إلى NSButtoncell؟ كنت أتوقع أن تعرف NSButtoncell نفسها ما يجب القيام به عند النقر عليه (ورأيت Trackmouse: الأساليب أكثر في وظائف مع تتبع حركات الماوس حقًا - وليس كعجلة تدريب لسلوك النقر "القياسي"). لكن يبدو أنه لا يفعل ذلك عند تضمينه في خلية نفسها ... يبدو أنني لم أكن أسيء الصورة الكبيرة على الخلايا المخصصة ، حتى الآن ؛-)

سأكون سعيدًا إذا تمكن شخص ما من الإجابة على هذا (أو يوجهني إلى بعض البرنامج التعليمي أو ما شابه) من تجربته الخاصة - وأخبرني إذا كنت على المسار الصحيح.

شكرا مقدما ، توبي

هل كانت مفيدة؟

المحلول

الحد الأدنى من المتطلبات هي:

  • بعد الفأر الأيسر لأسفل على الزر ، يجب أن يظهر مضغوطًا كلما انتهى الماوس.
  • إذا كان الماوس بعد ذلك يطلق على الزر ، فيجب أن ترسل خليتك رسالة الإجراء المناسبة.

لجعل الزر يبدو مضغوطًا ، تحتاج إلى تحديث خلية الزر highlighted الممتلكات حسب الاقتضاء. إن تغيير الحالة وحدها لن ينجز هذا ، ولكن ما تريده هو أن يتم تسليط الضوء NSOnState.

لإرسال رسالة الإجراء ، يجب أن تكون على دراية عند إصدار الماوس ، ثم استخدامه -[NSApplication sendAction:to:from:] لإرسال الرسالة.

من أجل أن تكون في وضع يسمح لها بإرسال هذه الرسائل ، ستحتاج إلى ربط طرق تتبع الأحداث التي توفرها NSCell. لاحظ أن جميع أساليب التتبع هذه ، باستثناء النهائي ، -stopTracking:... الطريقة ، أعد منطقية للإجابة على السؤال ، "هل تريد الاستمرار في تلقي رسائل التتبع؟"

التطور النهائي هو أنه من أجل إرسال أي رسائل تتبع على الإطلاق ، تحتاج إلى تنفيذ -hitTestForEvent:inRect:ofView: وإرجاع مناكب bitmass المناسب من NSCellHit... القيم. على وجه التحديد ، إذا كانت القيمة التي تم إرجاعها لا تحتوي على NSCellHitTrackableArea قيمة فيه ، لن تحصل على أي رسائل تتبع!

لذلك ، على مستوى عالٍ ، سيبدو تنفيذك شيئًا مثل:

- (NSUInteger)hitTestForEvent:(NSEvent *)event
                       inRect:(NSRect)cellFrame
                       ofView:(NSView *)controlView {
    NSUInteger hitType = [super hitTestForEvent:event inRect:cellFrame ofView:controlView];

    NSPoint location = [event locationInWindow];
    location = [controlView convertPointFromBase:location];
    // get the button cell's |buttonRect|, then
    if (NSMouseInRect(location, buttonRect, [controlView isFlipped])) {
        // We are only sent tracking messages for trackable areas.
        hitType |= NSCellHitTrackableArea;
    }
    return hitType;
}

+ (BOOL)prefersTrackingUntilMouseUp {
   // you want a single, long tracking "session" from mouse down till up
   return YES;
}

- (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView {
   // use NSMouseInRect and [controlView isFlipped] to test whether |startPoint| is on the button
   // if so, highlight the button
   return YES;  // keep tracking
}

- (BOOL)continueTracking:(NSPoint)lastPoint at:(NSPoint)currentPoint inView:(NSView *)controlView {
   // if |currentPoint| is in the button, highlight it
   // otherwise, unhighlight it
   return YES;  // keep on tracking
}

- (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:(NSView *)controlView mouseIsUp:(BOOL)flag {
   // if |flag| and mouse in button's rect, then
   [[NSApplication sharedApplication] sendAction:self.action to:self.target from:controlView];
   // and, finally,
   [buttonCell setHighlighted:NO];
}

نصائح أخرى

نقطة NSCell الفئات الفرعية هي فصل المسؤولية عن تقديم عناصر واجهة المستخدم المشتركة والتعامل معها (عناصر التحكم) عن مسؤوليات التسلسل الهرمي المرئي والحدث في NSView الطبقات. يسمح هذا الاقتران بكل واحد لتوفير تخصص وتغير أكبر دون إعاقة الآخر. انظر إلى العدد الكبير من NSButton الحالات يمكن للمرء أن يخلق في الكاكاو. تخيل عدد NSButton الفئات الفرعية التي يمكن أن توجد إذا كان هذا الانقسام في الوظائف غائبا!

باستخدام لغة نمط التصميم لوصف الأدوار: NSControl تعمل بمثابة واجهة ، وإخفاء تفاصيل تكوينها من عملائها وإصدار الأحداث وتقديم الرسائل إليها NSCell مثيل الذي يعمل كمندوب.

لان خاصتك NSCell الفئة الفرعية تشمل الأخرى NSCell مثيلات الفئة الفرعية داخل تكوينها ، لم تعد تتلقى رسائل الحدث هذه مباشرة من NSControl مثيل هو في عرض التسلسل الهرمي. وبالتالي ، لكي تتلقى هذه الحالات الخلوية رسائل الأحداث من سلسلة مستجيب الأحداث (من التسلسل الهرمي للعرض) ، تحتاج مثيل الخلية الخاص بك إلى تمرير تلك الأحداث ذات الصلة. أنت تقوم بإعادة إنشاء عمل NSView التسلسل الهرمي.

هذا ليس بالضرورة شيئًا سيئًا. عن طريق تكرار سلوك NSControlNSView فئة فائقة) ولكن في NSCell النموذج ، يمكنك تصفية الأحداث التي تم نقلها إلى خلايا فرعية حسب الموقع أو نوع الحدث أو أي معايير أخرى. العيب يكرر عمل NSView/NSControl في بناء آلية التصفية والإدارة.

لذلك في تصميم واجهتك ، تحتاج إلى التفكير فيما إذا كان NSButtonCellNSTextFieldCellS) أفضل حالا في NSControlS في التسلسل الهرمي للعرض الطبيعي ، أو كخلايا فرعية في NSCell الفئة الفرعية. من الأفضل الاستفادة من الوظائف الموجودة بالفعل بالنسبة لك في قاعدة الكود بدلاً من إعادة اختراعها (ومواصلة الحفاظ عليها لاحقًا) دون داع.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top