NSScrollView의 높이를 입력 문자 수에 따라 변경

개요



아래와 같이 입력한 문자수에 따라 NSScrollView와 윈도우의 높이가 바뀌는 것과 같은 것을 작성한다



GitHub



NSTextView의 커스텀 클래스 생성 및 설정


  • 아래와 같이 클래스를 할당해 둔다



  • 텍스트가 변경되었을 때의 처리


    // テキストの変更時に呼ばれる
    - (void)didChangeText
    {
        [self updateScrollViewHeight];  // TextViewの含まれるNSSCrollViewのframeのheightを更新する
        [self.delegate ChangeableHeightTextViewDidChange];   // 呼び出し元のWindowControllerでウィンドウの高さを変更する
    }
    
  • NSTextView 메서드 didChangeText override
  • Changing size of NSTextView

  • 할 일은 다음 두 점
  • NSTextView 포함 NSSCrollView의 높이 업데이트(이 클래스에서 처리)
  • ( NSTextView는 자동으로 크기가 바뀌는 것 같습니다.

  • NSWindowController의 delegate 메서드를 호출하여 Window의 크기를 업데이트합니다 (프로토콜을 정의하고 delegate에서 처리)

  • NSSCrollView의 높이 업데이트


    static const int kInitialStringHeight     = 19; // 1行目の文字列の高さ
    static const int kSingleByteStringHeight  = 14; // 非日本語文字列の高さ(FontSiZeが12のとき)
    //static const int kMultiByteStringHeight   = 18; // 日本語文字列の高さ (FontSiZeが12のとき)
    static const int kMaximumLineNum          = 100; // この行数以上では高さを変更しない
    
  • 이후의 계산에 사용하는 매직 넘버를 변수로 정의해 둔다.
  • (원시적… 다른 좋은 방법은 없는 것인가)

  • /**
     @brief NSTextViewの含まれるNSSCrollViewのframeのheightを更新する
     */
    - (void)updateScrollViewHeight {
        // Calculate height
        NSUInteger numberOfLines = [self numberOfLines];
        NSUInteger height = 0;
        if (numberOfLines <= kMaximumLineNum) {
            height = kInitialStringHeight + (numberOfLines - 1) * kSingleByteStringHeight;  // 1行目の高さは固定としている
            if (height < kInitialStringHeight) {
                height = kInitialStringHeight;  // 最低の幅を設定
            }
        }
    
        // Update height
        NSScrollView *scrollView = (NSScrollView *) self.superview.superview;
        NSRect frame      = scrollView.frame;
        frame.size.height = height;
        scrollView.frame  = frame; // 実際のNSScrollViewのframeを更新
    }
    
    // Calculate height
    NSUInteger numberOfLines = [self numberOfLines];
    NSUInteger height = 0;
    if (numberOfLines <= kMaximumLineNum) {
        height = kInitialStringHeight + (numberOfLines - 1) * kSingleByteStringHeight;  // 1行目の高さは固定としている
        if (height < kInitialStringHeight) {
            height = kInitialStringHeight;  // 最低の幅を設定
        }
    }
    
  • 행 수를 계산하는 함수를 만듭니다. (후술)
  • 행수를 알면, 나머지는 원시적으로 계산해 간다.
  • 이번은 영어의 문자열만을 고려하고 있지만, 일본어를 포함하는 경우는 1행당의 높이가 바뀌어 오므로 밸런스를 취할 필요가 있는가.

  • NSString 반각·전각의 판정 당을 참고로 하면 구현은 가능할 것 같다.

  • // Update height
    NSScrollView *scrollView = (NSScrollView *) self.superview.superview;
    NSRect frame      = scrollView.frame;
    frame.size.height = height;
    scrollView.frame  = frame; // 実際のNSScrollViewのframeを更新
    
  • NSTextView 포함 NSSCrollView의 프레임을 여기에서 직접 업데이트
  • 계산 된 height를 사용하여 NSScrollViewframe 높이 업데이트
  • self.superview.superview.frame.size.height = height;
    
  • 처음에는 위와 같이 작성했지만 Expression is not assignable와 오류가 발생했습니다.

  • iOS 개발: “Expression is not assignable.”오류을 참조하여 한 방석을 넣어 쓰고있다.

  • "myLabel.frame"은 속성에 대한 액세스이고 "frame.size"는 구조체 멤버에 대한 액세스입니다. 이것을 혼합하고 있기 때문에 에러가 되고 있다.

    NSTextView 문자열의 행 수 계산


    /**
     @brief NSTextViewの文字列の行数を計算する
     */
    - (NSInteger)numberOfLines {
        NSLayoutManager *layoutManager = [self layoutManager];
        NSUInteger      numberOfLines  = 0;     // 行数のカウント用変数
        NSUInteger      numberOfGlyphs = [layoutManager numberOfGlyphs];
        NSRange         lineRange;  // 現在対象となっている行の、先頭の開始位置と文字数
        for (NSUInteger index = 0; index < numberOfGlyphs; numberOfLines++) {   // index: 現在見ている文字の位置
            (void) [layoutManager lineFragmentRectForGlyphAtIndex:index
                                                   effectiveRange:&lineRange];
            index = NSMaxRange(lineRange);
        }
    
        // 最終行が改行の場合に計算されていないようなので、その補正をする
        NSString *text = self.textStorage.string;
        if (text.length != 0 && [[text substringFromIndex:text.length - 1] isEqualToString:@"\n"]) {
            numberOfLines++;
        }
        return numberOfLines;
    }
    
  • 행 수 취득에 대해서는 Counting Lines of Text (Apple 공식)을 참고로 했다.
  • 코멘트에도 썼지만, 최종행이 개행의 경우에 계산이 맞지 않기 때문에 그 보정을 해 하면 좋다.
  • 【Objective-C】NSString의 전후에 있는 반각 스페이스, 개행, 알파벳, 숫자를 트리밍하는 방법.


  • Protocol 정의


  • Window의 크기를 변경하기 위해 NSWindowController에 대한 프로토콜 정의
  • @protocol ChangeableHeightTextViewDelegate <NSTextViewDelegate>
    - (void)ChangeableHeightTextViewDidChange;  // テキストビューの内容が変更されたときによばれる
    @end
    
  • <NSTextViewDelegate>NSTextViewDelegate를 상속하여 새로운 프로토콜을 만드는 것을 나타냅니다
  • .
    @property (weak, nonatomic) id <ChangeableHeightTextViewDelegate> delegate;
    



    ※상기와 같이 에러가 나오는데, 기존의 Delegate를 계승하는 경우의 delegate 프로퍼티의 작성법이 잘못되어 있는 것일까?

    NSTextView의 크기를 시각화


    - (void)drawRect:(NSRect)dirtyRect {
        [super drawRect:dirtyRect];
    
        // Drawing code here.
        // 見やすいようにNSTextViewの範囲を描画する
        NSRect bounds = [self bounds];
        [[NSColor greenColor] set];
        [NSBezierPath strokeRect:bounds];
    }
    
  • NSTextView의 범위를 알기 쉽게 그리기

  • 창 크기 변경


    // MARK:- ChangeableHeightTextViewDelegate Delegate Methods
    // TextView内のテキストが変更されたときに呼ばれる
    - (void)ChangeableHeightTextViewDidChange {
        NSRect windowFrame = self.window.frame;
        NSRect scrollViewFrame = _myTextView.superview.superview.frame;
        // 20:Windowのヘッダ幅、上下のマージン、
        // 20:(入力していってね)の高さ
        // 6 :固定テキストとNSSCrollViewの間隔
        windowFrame.size.height = (20 + 20 + 6 + 20 + 20) + scrollViewFrame.size.height;
        [self.window setFrame:windowFrame display:YES];
    }
    
  • 창 크기는 ScrollViewの高さ + その他要素の高さ로 계산
  • 좋은 웹페이지 즐겨찾기