UITableViewCell के एक्सेसरी व्यू के लिए एक कस्टम छवि का उपयोग करना और UITableViewDelegate का जवाब देना


139

मैं सेल के लिए उसी सहित एक कस्टम तैयार UITableViewCell का उपयोग कर रहा हूं accessoryView। AccessoryView के लिए मेरा सेटअप कुछ इस तरह से होता है:

UIImage *accessoryImage = [UIImage imageNamed:@"accessoryDisclosure.png"];
UIImageView *accImageView = [[UIImageView alloc] initWithImage:accessoryImage];
accImageView.userInteractionEnabled = YES;
[accImageView setFrame:CGRectMake(0, 0, 28.0, 28.0)];
self.accessoryView = accImageView;
[accImageView release];

इसके अलावा, जब सेल को इनिशियलाइज़ किया जाता है, initWithFrame:reuseIdentifier:तो निम्नलिखित संपत्ति सेट करने के लिए मैंने सुनिश्चित किया:

self.userInteractionEnabled = YES;

दुर्भाग्य से मेरे UITableViewDelegate में, मेरा tableView:accessoryButtonTappedForRowWithIndexPath:तरीका (10 बार दोहराने की कोशिश करें) ट्रिगर नहीं हो रहा है। प्रतिनिधि को निश्चित रूप से ठीक से तार दिया गया है।

संभवतः क्या गायब हो सकता है?

सबको शुक्रीया।

जवाबों:


228

अफसोस की बात है कि जब तक आप किसी पूर्वनिर्धारित प्रकार का उपयोग नहीं करते हैं तब तक आंतरिक बटन प्रकार प्रदान किए जाने तक उस पद्धति को कॉल नहीं किया जाता है। अपना स्वयं का उपयोग करने के लिए, आपको एक बटन या अन्य UIControl उपवर्ग के रूप में अपना एक्सेसरी बनाना होगा (मैं -buttonWithType:UIButtonTypeCustomUIImageView का उपयोग करने के बजाय बटन की छवि का उपयोग करके और सेटिंग करने की सलाह दूंगा)।

यहां कुछ चीजें हैं जो मैं आउटपोस्ट में उपयोग करता हूं, जो मानक विगेट्स (बस थोड़ा सा, हमारे चैती रंग से मेल खाने के लिए) को अनुकूलित करता है जो कि मैं उपयोग करने के लिए अन्य सभी तालिका दृश्यों के लिए उपयोगिता कोड रखने के लिए अपने स्वयं के यूआईटेबल व्यू कॉन्ट्रॉलरी इंटरकॉलेज को करने के लिए घाव करता हूं (वे अब उपवर्ग हैं। OPTableViewController)।

सबसे पहले, यह फ़ंक्शन हमारे कस्टम ग्राफ़िक का उपयोग करके एक नया विवरण प्रकटीकरण बटन लौटाता है:

- (UIButton *) makeDetailDisclosureButton
{
    UIButton * button = [UIButton outpostDetailDisclosureButton];

[button addTarget: self
               action: @selector(accessoryButtonTapped:withEvent:)
     forControlEvents: UIControlEventTouchUpInside];

    return ( button );
}

बटन इस रूटीन को तब करेगा जब यह हो जाएगा, जो तब एक्सेसरी बटन के लिए मानक UITableViewDelegate रूटीन को फीड करता है:

- (void) accessoryButtonTapped: (UIControl *) button withEvent: (UIEvent *) event
{
    NSIndexPath * indexPath = [self.tableView indexPathForRowAtPoint: [[[event touchesForView: button] anyObject] locationInView: self.tableView]];
    if ( indexPath == nil )
        return;

    [self.tableView.delegate tableView: self.tableView accessoryButtonTappedForRowWithIndexPath: indexPath];
}

यह फ़ंक्शन बटन द्वारा प्रदान की गई घटना से एक स्पर्श के तालिका दृश्य में स्थान प्राप्त करके पंक्ति को बताता है और उस बिंदु पर पंक्ति के सूचकांक पथ के लिए तालिका दृश्य पूछ रहा है।


धन्यवाद जिम। यह एक शर्म की बात है कि मैंने 20 से अधिक मिनट बिताए, यह सोचकर कि मैं इसे कस्टम इमेज व्यू के साथ क्यों नहीं कर सकता। मैंने अभी देखा कि कैसे Apple के सैंपल ऐक्सेसरी ऐप पर ऐसा करना है। आपके उत्तर को अच्छी तरह से समझाया और प्रलेखित किया गया है, इसलिए मैं इसे चिह्नित कर रहा हूं और इसे चारों ओर रख रहा हूं। एक बार फिर धन्यवाद। :-)

जिम, शानदार जवाब। एक संभावित मुद्दा (कम से कम मेरे अंत में) - मुझे बटन पर रजिस्टर करने के लिए टच पाने के लिए निम्नलिखित पंक्ति को जोड़ना था: बटन.सुअरी निष्क्रियता = हाँ;
माइक लॉरेंस

11
इस उत्तर को देखने वाले अन्य लोगों के लिए, आप केवल उस बटन पर एक टैग लगा सकते हैं, जो पंक्ति से मेल खाता हो (यदि आपके पास कई खंड हैं, तो आपको कुछ गणित करने की आवश्यकता होगी) और फिर बटन से टैग को बाहर खींचें कार्यक्रम। मुझे लगता है कि यह स्पर्श की गणना करने की तुलना में थोड़ा तेज़ हो सकता है।
रयानजाम

3
इस कड़ी मेहनत से कोड करने की आवश्यकता है self.tableView। यदि आपको नहीं पता कि किस तालिका में पंक्ति है?
23:10 पर user102008

4
@RyanJM मुझे लगता था कि हिटटेस्ट करना ओवरकिल है और टैग पर्याप्त होगा। मैंने वास्तव में अपने कुछ कोड में टैग आइडिया का इस्तेमाल किया है। लेकिन आज मुझे एक समस्या का सामना करना पड़ा जहां उपयोगकर्ता नई पंक्तियों को जोड़ सकता है। यह टैग का उपयोग करके हैक को मारता है। जिम डोवी द्वारा सुझाए गए समाधान (और जैसा कि एप्पल के नमूना कोड में देखा गया है) एक सामान्य समाधान है और सभी स्थितियों में काम करता है
बजे

77

मुझे यह वेबसाइट बहुत मददगार लगी: iPhone में आपके यूटिटेव के लिए कस्टम एक्सेसरी व्यू

संक्षेप में, इस में उपयोग करें cellForRowAtIndexPath::

UIImage *image = (checked) ? [UIImage imageNamed:@"checked.png"] : [UIImage imageNamed:@"unchecked.png"];

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
CGRect frame = CGRectMake(0.0, 0.0, image.size.width, image.size.height);
button.frame = frame;
[button setBackgroundImage:image forState:UIControlStateNormal];

[button addTarget:self action:@selector(checkButtonTapped:event:)  forControlEvents:UIControlEventTouchUpInside];
button.backgroundColor = [UIColor clearColor];
cell.accessoryView = button;

फिर, इस विधि को लागू करें:

- (void)checkButtonTapped:(id)sender event:(id)event
{
    NSSet *touches = [event allTouches];
    UITouch *touch = [touches anyObject];
    CGPoint currentTouchPosition = [touch locationInView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint: currentTouchPosition];

    if (indexPath != nil)
    {
        [self tableView: self.tableView accessoryButtonTappedForRowWithIndexPath: indexPath];
    }
}

4
मैं इसके लिए +1 कहता हूं क्योंकि यह Apple अपने डॉक्स में अपने सैंपल कोड में क्या करने की सिफारिश करता है: developer.apple.com/library/ios/#samplecode/Accessory/Listings/…
चतुरबाहु

फ्रेम सेट करना मेरे लिए गायब होने वाला टुकड़ा था। जब तक आप भी कोई पाठ नहीं चाहते हैं तब तक आप केवल पृष्ठभूमि (पृष्ठभूमि के बजाय) सेट कर सकते हैं।
जेरेमी हिक्स

1
@ ऋचादास के उत्तर में लिंक टूट गया है। नया लिंक: developer.apple.com/library/prerelease/ios/samplecode/Accessory/…
delavega66

7

मेरा दृष्टिकोण एक UITableViewCellउपवर्ग का निर्माण करना है और उस तर्क को एनकैप्सुलेट करना है जो इसके UITableViewDelegateभीतर की सामान्य विधि को बुलाएगा ।

// CustomTableViewCell.h
@interface CustomTableViewCell : UITableViewCell

- (id)initForIdentifier:(NSString *)reuseIdentifier;

@end

// CustomTableViewCell.m
@implementation CustomTableViewCell

- (id)initForIdentifier:(NSString *)reuseIdentifier;
{
    // the subclass specifies style itself
    self = [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier];
    if (self) {
        // get the button elsewhere
        UIButton *accBtn = [ViewFactory createTableViewCellDisclosureButton];
        [accBtn addTarget: self
                   action: @selector(accessoryButtonTapped:withEvent:)
         forControlEvents: UIControlEventTouchUpInside];
        self.accessoryView = accBtn;
    }
    return self;
}

#pragma mark - private

- (void)accessoryButtonTapped:(UIControl *)button withEvent:(UIEvent *)event
{
    UITableViewCell *cell = (UITableViewCell*)button.superview;
    UITableView *tableView = (UITableView*)cell.superview;
    NSIndexPath *indexPath = [tableView indexPathForCell:cell];
    [tableView.delegate tableView:tableView accessoryButtonTappedForRowWithIndexPath:indexPath];
}

@end

यह सबसे अच्छा जवाब है। लेकिन button.superview, cell.superviewऔर [tableView.delegate tableView:...]पर्याप्त सुरक्षित नहीं हैं।
श्री मिंग

3

ऊपर जिम डोवी के जवाब के लिए एक विस्तार:

जब आप अपने UITableView के साथ UISearchBarController का उपयोग करते हैं तो सावधान रहें। उस मामले में आप के बजाय के लिए जाँच self.searchDisplayController.activeऔर उपयोग करना चाहते self.searchDisplayController.searchResultsTableViewहैंself.tableView । अन्यथा आपको अप्रत्याशित परिणाम मिलेंगे जब सर्चडिसप्लेकंट्रोलर सक्रिय है, खासकर जब खोज परिणाम स्क्रॉल किए जाते हैं।

उदाहरण के लिए:

- (void) accessoryButtonTapped:(UIControl *)button withEvent:(UIEvent *)event
{
    UITableView* tableView = self.tableView;
    if(self.searchDisplayController.active)
        tableView = self.searchDisplayController.searchResultsTableView;

    NSIndexPath * indexPath = [tableView indexPathForRowAtPoint:[[[event touchesForView:button] anyObject] locationInView:tableView]];
    if(indexPath)
       [tableView.delegate tableView:tableView accessoryButtonTappedForRowWithIndexPath:indexPath];
}

2
  1. बटन के टैग के लिए एक मैक्रो को परिभाषित करें:

    #define AccessoryViewTagSinceValue 100000 // (AccessoryViewTagSinceValue * sections + rows) must be LE NSIntegerMax
  2. बटन बनाएँ और सेल बनाते समय cell.accessoryView सेट करें

    UIButton *accessoryButton = [UIButton buttonWithType:UIButtonTypeContactAdd];
    accessoryButton.frame = CGRectMake(0, 0, 30, 30);
    [accessoryButton addTarget:self action:@selector(accessoryButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
    cell.accessoryView = accessoryButton;
  3. UITableViewDataSource विधि -tableView में indexPath द्वारा cell.accessoryView.tag सेट करें: cellForRowAtIndexPath:

    cell.accessoryView.tag = indexPath.section * AccessoryViewTagSinceValue + indexPath.row;
  4. बटन के लिए घटना हैंडलर

    - (void) accessoryButtonTapped:(UIButton *)button {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:button.tag % AccessoryViewTagSinceValue
                                                    inSection:button.tag / AccessoryViewTagSinceValue];
    
        [self.tableView.delegate tableView:self.tableView accessoryButtonTappedForRowWithIndexPath:indexPath];
    }
  5. UITableViewDelegate पद्धति को लागू करें

    - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
        // do sth.
    }

1
किसी को भी उपयोग नहीं करना चाहिए tagजब तक कि पूर्ण आवश्यक न हो, अन्य समाधान देखें।
16

2

जब बटन टैप किया जाता है, तो आप इसे UITableViewCell उपवर्ग के अंदर निम्नलिखित विधि कह सकते हैं

 -(void)buttonTapped{
     // perform an UI updates for cell

     // grab the table view and notify it using the delegate
     UITableView *tableView = (UITableView *)self.superview;
     [tableView.delegate tableView:tableView accessoryButtonTappedForRowWithIndexPath:[tableView indexPathForCell:self]];

 }

1

Yanchenko दृष्टिकोण के साथ मुझे जोड़ना था: [accBtn setFrame:CGRectMake(0, 0, 20, 20)];

यदि आप अपनी तालिका को अनुकूलित करने के लिए xib फ़ाइल का उपयोग कर रहे हैं तो initWithStyle: reuseIdentifier: अभ्यस्त कहलाएँ।

इसके बजाय ओवरराइड करें:

-(void)awakeFromNib
{
//Put your code here 

[super awakeFromNib];

}

1

आपको सरल के बजाय UIControlईवेंट प्रेषण (उदाहरण के लिए UIButton) को ठीक से प्राप्त करने के लिए उपयोग करना चाहिए UIView/UIImageView


1

स्विफ्ट 5

यह दृष्टिकोण UIButton.tagमूल बिट-शिफ्टिंग का उपयोग करते हुए इंडेक्सपाथ को स्टोर करने के लिए उपयोग करता है । जब तक आपके पास 65535 से अधिक अनुभाग या पंक्तियाँ नहीं होंगी, तब तक यह दृष्टिकोण 32 और 64 बिट सिस्टम पर काम करेगा।

public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: "cellId")
    let accessoryButton = UIButton(type: .custom)
    accessoryButton.setImage(UIImage(named: "imageName"), for: .normal)
    accessoryButton.sizeToFit()
    accessoryButton.addTarget(self, action: #selector(handleAccessoryButton(sender:)), for: .touchUpInside)

    let tag = (indexPath.section << 16) | indexPath.row
    accessoryButton.tag = tag
    cell?.accessoryView = accessoryButton

}

@objc func handleAccessoryButton(sender: UIButton) {
    let section = sender.tag >> 16
    let row = sender.tag & 0xFFFF
    // Do Stuff
}

0

IOS 3.2 के रूप में, आप उन बटनों से बच सकते हैं, जो यहां अन्य लोग सुझा रहे हैं और इसके बजाय एक टैप जेस्चर पहचानकर्ता के साथ अपने UIImageView का उपयोग करें। UIImageViews में डिफ़ॉल्ट रूप से बंद होने वाले उपयोगकर्ता इंटरैक्शन को सक्षम करना सुनिश्चित करें।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.