2011-11-24 23 views
6

मैं एक NSTextView और एक बटन, TextView के लिए एक NSTextViewDelegate प्रदान की के साथ एक सरल डेमो एप्लिकेशन बनाया है और एक कार्रवाई कहा: अगर मैं हाथ से पाठ बदलने के लिए, समस्याओं के बिनाटेक्स्ट के प्रोग्रामेटिक परिवर्तन के साथ पूर्ववत/दोबारा कैसे कार्यान्वित करें NSTextView के वॉल्यूम?

- (IBAction)actionButtonClicked:(id)sender { 
    NSString *oldText = [[[self.textView textStorage] string] copy]; 
    NSString *newText = @"And... ACTION!"; 

    [[self.textView undoManager] registerUndoWithTarget:self.textView 
               selector:@selector(setString:) 
               object:oldText]; 
    [[self.textView undoManager] setActionName:@"ACTION"]; 

    [self.textView setString:newText]; 
} 

पूर्ववत करें/फिर काम करता है। लेकिन अगर मैं क्रिया विधि के साथ पाठ को पूर्ववत करता हूं, तो अपेक्षित कार्यों को पूर्ववत करें, लेकिन फिर से काम नहीं करता है (कुछ भी नहीं होता है) और पूर्ववत प्रबंधक को स्कैम्बल किया जाता है ...

ठीक - NSTextView के साथ समस्याओं से बचने के लिए मैंने बनाया एक मॉडल वर्ग, एनएसटीएक्स्टव्यू को बाध्य करता है और मॉडल को पूर्ववत/फिर से ले जाता है, लेकिन यह पहले जैसा ही व्यवहार दिखाता है - मैं क्या गलत कर रहा हूं - यह आसान होना चाहिए, है ना?

#import "GFTextStore.h" 

@implementation GFTextStore 

@synthesize textVal = textVal_; 

-(void)changeText{ 
    if (!undoMan_) { 
     undoMan_ = [[[NSApplication sharedApplication] mainWindow] undoManager]; 
    } 

    NSAttributedString *oldText = [self.textVal copy]; 
    NSString *tempStr = [[oldText string] stringByAppendingFormat:@"\n%@",[[NSCalendarDate date]description]]; 
    NSAttributedString *newText = [[NSAttributedString alloc] initWithString:tempStr]; 

    [self setTextVal:newText]; 


    [undoMan_ registerUndoWithTarget:self 
          selector:@selector(setTextVal:) 
           object:oldText]; 

    [undoMan_ setActionName:@"ACTION"]; 
} 
@end 

उत्तर

5

-setString:NSText से इनहेरिट तरीका है। NSTextView तरीकों केवल इतना पूर्ववत कि नियंत्रित किया जाता है का उपयोग कर इस संभाल के लिए, बस ऐसा करते हैं:

[self.textView setSelectedRange:NSMakeRange(0, [[self.textView textStorage] length])]; 
[self.textView insertText:@"And… ACTION!"]; 

पाठ बदलने इस तरह से सब पर पूर्ववत प्रबंधक के साथ के बारे में mucking टाल बनाना।

+0

धन्यवाद रॉब, लेकिन यह मेरी समस्या का समाधान नहीं करता है। NSTextView के साथ समस्याओं से बचने के लिए मैंने कुछ बदलाव किए और मॉडल को पूर्ववत/फिर से ले जाया - संपादित प्रश्न देखें ... – user808667

7

यहां NSUndoManager जोड़ने की आवश्यकता नहीं है, बस NSTextView नौकरी करें।

[self.textView insertText:newString]; 

:

तुम बस सुनिश्चित करें कि आप NSTextView का केवलडालने ... के साथ शुरुआत उच्च स्तर तरीकों बुला रहे हैं और TextView या textStorage सीधे में पाठ/स्ट्रिंग की स्थापना नहीं करने की जरूरत है यदि आपको पूरी तरह से सेटस्ट्रिंग या अन्य निचले स्तर के तरीकों का उपयोग करने की आवश्यकता है, तो आपको टेक्स्टडिड चेंज प्रतिनिधिमंडल को संभालने के लिए आवश्यक विधियों को जोड़ने की आवश्यकता है: -ShouldChangeTextInRange: replacementString और -didChangeText (जो द्वारा किया जाता है सम्मिलित ... तरीकों Btw):

if([self.textView shouldChangeTextInRange:editedRange replacementString:editedString]) { 
    // do some fancy stuff here… 
    [self.textView.textStorage replaceCharactersInRange:editedRange 
          withAttributedString:myFancyNewString]; 
    // … and finish the editing with 
    [self.textView didChangeText]; 
} 

यह स्वचालित रूप से NSTextView की undoManager में लात मुझे लगता है कि undoManager shouldChangeTextInRange में एक undoGrouping तैयारी कर रहा है की सुविधा देता है:। और didChangeText में पूर्ववत लागू :.

1

मान लीजिए कि जब आप एंटर कुंजी (ऐप्पल पेज व्यवहार) हिट करते हैं तो आप अपना एनएसटीक्स्ट व्यू एक नया पूर्ववत समूह बनाना चाहते हैं। फिर आप इस कोड को अपने NSTextView उपclass में टाइप कर सकते हैं:

override func shouldChangeTextInRange(affectedCharRange: NSRange, replacementString: String?) -> Bool { 
    super.shouldChangeTextInRange(affectedCharRange, replacementString: replacementString) 
    guard replacementString != nil else { return true } 

    let newLineSet = NSCharacterSet.newlineCharacterSet() 
    if let newLineRange = replacementString!.rangeOfCharacterFromSet(newLineSet) { 

     // check whether it's a single character (user hit Return key) 
     let singleCharRange = (replacementString!.startIndex)! ..< (replacementString!.startIndex.successor())! 
     if newLineRange == singleCharRange { 
      self.breakUndoCoalescing() 
     } 
    } 

    return true 
}