51092


MacOS用のデスクトップアプリの開発をしております。  
その中でVerticalなNSStackViewに対して、プログラムで動的にNSTextFieldやNSTextViewを追加する処理を行なっています。

このとき、NSTextView について、他の項目で使われる領域を除く全ての領域を使用しようとしてしまいます。

ちょっとわかりにくいかと思いますので、はじめに事象のスクリーンショットを添付いたします。

[![スクリーンショット](https://i.stack.imgur.com/V0oAV.png)](https://i.stack.imgur.com/V0oAV.png)

簡単化のためにNSStackViewに対して、  
1 NSTextField  
2 NSTextView  
3 NSTextField  
という３つのコントロールを配置したものですが、  
2 NSTextView と 3 NSTextField の間のスペース（グレーの部分）が大きく開いてしまっている状態です。

最後に、環境とソースコードを提示させていただきます。

開発環境、言語  
XCode 10.1  
Objective-C  
Project Format:XCode 10.0-compatible

〜TestView.h〜

[code]

    #import <Cocoa/Cocoa.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface TestView : NSStackView<NSStackViewDelegate>
    
    @end
    
    NS_ASSUME_NONNULL_END
    
[/code]

〜TestView.m〜

[code]

    #import "TestView.h"
    
    @implementation TestView
    
    - (void)drawRect:(NSRect)dirtyRect {
        [super drawRect:dirtyRect];
    
        NSColor *bgcolor = [NSColor grayColor];
        [bgcolor set];
        NSRectFill(dirtyRect);
    
        // 1.NSTextField
        NSTextField *ctrl = [[NSTextField alloc]init];
        [ctrl setStringValue:@"1"];
        [self insertView:ctrl atIndex:self.subviews.count inGravity:NSStackViewGravityLeading];
    
        // 2.NSTextView
        NSRect rect = NSMakeRect(0, 0, self.frame.size.width, 60);
        NSTextView *ctrl2 = [[NSTextView alloc]initWithFrame:rect];
        [ctrl2 insertText:@"2"];
        [self insertView:ctrl2 atIndex:self.subviews.count inGravity:NSStackViewGravityLeading];
    
        // 3.NSTextField
        NSTextField *ctrl3 = [[NSTextField alloc]init];
        [ctrl3 setStringValue:@"3"];
        [self insertView:ctrl3 atIndex:self.subviews.count inGravity:NSStackViewGravityLeading];
    
    }
    
    @end
    
[/code]

NSTextField、NSTextViewに関わらず、上から順番に表示されるようにするにはどうすればよいでしょうか。  
アドバイスいただけましたら幸いです。  
※不足情報ございましたらご指摘ください。


CC BY-SA 4.0
2018-12-10T11:30:55.327
51089
2019-01-17T04:57:48.993
2019-01-17T04:57:48.993
76
31272
question
objective-c macos
NSStackViewにNSTextViewを追加した場合の高さの制御方法について


`Autolayout`を使うのはいかがでしょうか？  
`Autolayout`は、コードから`VisualFormat`という方法と`constraintWithItem`というクラスメソッドを使って、4辺（必要なら更に高さ、幅等）のレイアウト制約を作り、`[NSLayoutConstraint
activateConstraints:@[有効にしたいレイアウト制約の配列]];`で、作成した`NSLayoutConstraint`のうち、有効にしたいルールを纏めて有効にします。  
@condorさんの行いたいことは多分、  
* 上下端の`view`は`superView`(=`StackView`)と距離`n`の`constraint`を3つ（左端、右端、上端または下端）作成する  
* 中間の`view`の、左右端を`superView`と距離`n`の`constraint`を2つそれぞれ作成する  
* 中間の`view`上又は下端を隣り合う`view`と距離`n`の`constraint`を作成していく  
* 一つ足りない`constraint`があるはずなので、隣り合う下又は上端の`view`と距離`n`の`constraint`を作成する  
* 高さが固定の`view`に高さを指定する`constraint`を作成する  
* これらを配列（例えば`NSArray<NSLayoutConstraint *> *stackViewConstraints`）に追加する  
* `[NSLayoutConstraint activateConstraints:stackViewConstraints];`で、制約を有効にする

事で、高さを指定していない`view`は隣り合う`view`と`n`ピクセルの距離を残して高さ／幅が自動で伸び縮みします。

上記ソースに対する制約(`constraint`)を行うサンプルコードは以下の様になります。(`drawRect`の中で続けて行います）

[code]

    CGFloat clearance = 0;
    
        // constraints for ctrl and stack view
    NSLayoutConstraint *ctrlTop = [NSLayoutConstraint constraintWithItem:ctrl attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:clearance];
    NSLayoutConstraint *ctrlLeft = [NSLayoutConstraint constraintWithItem:ctrl attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0 constant:clearance];
    NSLayoutConstraint *ctrlRight = [NSLayoutConstraint constraintWithItem:ctrl attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1.0 constant:clearance];
        // constraints for ctrl2 and stack view
    NSLayoutConstraint *ctrl2Left = [NSLayoutConstraint constraintWithItem:ctrl2 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0 constant:clearance];
    NSLayoutConstraint *ctrl2Right = [NSLayoutConstraint constraintWithItem:ctrl2 attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1.0 constant:clearance];
        // constraints for ctrl3 and stack view
    NSLayoutConstraint *ctrl3Left = [NSLayoutConstraint constraintWithItem:ctrl3 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0 constant:clearance];
    NSLayoutConstraint *ctrl3Right = [NSLayoutConstraint constraintWithItem:ctrl3 attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1.0 constant:clearance];
    NSLayoutConstraint *ctrl3Bottom = [NSLayoutConstraint constraintWithItem:ctrl3 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0 constant:clearance];
        // constraints for ctrl to ctrl2
    NSLayoutConstraint *ctrlToCtrl2 = [NSLayoutConstraint constraintWithItem:ctrl attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:ctrl2 attribute:NSLayoutAttributeTop multiplier:1.0 constant:clearance];
        // constraints for ctrl2 to ctrl3
    NSLayoutConstraint *ctrl2ToCtrl3 = [NSLayoutConstraint constraintWithItem:ctrl2 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:ctrl3 attribute:NSLayoutAttributeTop multiplier:1.0 constant:clearance];
    
    NSArray<NSLayoutConstraint *> *stackViewConstraints = @[ctrlTop, ctrlLeft, ctrlRight, ctrl2Left, ctrl2Right, ctrl3Left, ctrl3Right, ctrl3Bottom, ctrlToCtrl2, ctrl2ToCtrl3];
    
        // set height if need
    CGFloat ctrlsHeight = 20;
    NSLayoutConstraint *ctrlHeight = [NSLayoutConstraint constraintWithItem:ctrl attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeHeight multiplier:1.0 constant:ctrlsHeight];
    
        // activate constraints
    [NSLayoutConstraint activateConstraints:stackViewConstraints];
    [NSLayoutConstraint activateConstraints:@[ctrlHeight]];
    
[/code]


CC BY-SA 4.0
2018-12-10T13:00:57.983
51092
2018-12-10T13:00:57.983
14745
51089
answer


@Чайкаさん、ご回答ありがとうございます。

いただいたアドバイスをもとに希望通りのレイアウト制御を行うことができました。  
なお、今回必要だったのはNSTextView部分の高さ調整のみでしたので、以下のように最低限の制御だけを入れてみました。  
大変助かりました。最終的なレイアウト結果とコードを記載しておきます。

[![改善したレイアウト](https://i.stack.imgur.com/S1g9X.png)](https://i.stack.imgur.com/S1g9X.png)

[code]

    - (void)drawRect:(NSRect)dirtyRect {
        [super drawRect:dirtyRect];
    
        NSColor *bgcolor = [NSColor grayColor];
        [bgcolor set];
        NSRectFill(dirtyRect);
    
        // 1.NSTextField
        NSTextField *ctrl = [[NSTextField alloc]init];
        [ctrl setStringValue:@"1"];
        [self insertView:ctrl atIndex:self.subviews.count inGravity:NSStackViewGravityTop];
    
        // 2.NSTextView
        NSRect rect = NSMakeRect(0, 0, self.frame.size.width, 60);
        NSTextView *ctrl2 = [[NSTextView alloc]initWithFrame:rect];
        [ctrl2 insertText:@"2"];
        [self insertView:ctrl2 atIndex:self.subviews.count inGravity:NSStackViewGravityTop];
    
        // 3.NSTextField
        NSTextField *ctrl3 = [[NSTextField alloc]init];
        [ctrl3 setStringValue:@"3"];
        [self insertView:ctrl3 atIndex:self.subviews.count inGravity:NSStackViewGravityTop];
    
    
        CGFloat ctrlsHeight = 60;
        NSLayoutConstraint *ctrlHeight = [NSLayoutConstraint constraintWithItem:ctrl2 attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeHeight multiplier:1.0 constant:ctrlsHeight];
    
        NSArray<NSLayoutConstraint *> *stackViewConstraints = @[ctrlHeight];
       [NSLayoutConstraint activateConstraints:stackViewConstraints];
    
    
    }
    
[/code]


CC BY-SA 4.0
2018-12-11T03:35:05.957
51106
2018-12-11T03:35:05.957
31272
51089
answer