programing

입력하는 동안 UITextField 크기 조정 (자동 레이아웃 사용)

projobs 2021. 1. 15. 07:28
반응형

입력하는 동안 UITextField 크기 조정 (자동 레이아웃 사용)


두 개의 UITextFields (테두리 없음)가있는 UITableViewCell이 있습니다. 수평 레이아웃을 설정하는 데 다음 제약 조건이 사용됩니다.

@"|-10-[leftTextField(>=80)]-(>=10)-[rightTextField(>=40)]-10-|"

화려하지 않고 예상대로 작동합니다.

여기에 이미지 설명 입력

보시다시피 상단 textField의 크기가 정확합니다. 아래쪽 textField는 빈 텍스트로 시작했으며 빈 텍스트로 인해 너비가 80 포인트입니다. 편집 텍스트 필드에 텍스트를 입력하면 텍스트가 왼쪽으로 스크롤되지만 너비는 변경되지 않습니다.

마음에 들지 않습니다. 사용자가 해당 textField에 입력하는 동안 textField의 너비가 조정되어야합니다.

내 생각에 그것은 상자에서 작동해야합니다. IBActionfor UIControlEventEditingChanged이벤트 를 구현함으로써 타이핑이 실제로 intrinsicContentSizeUITextField를 변경하는지 확인할 수 있습니다 .

그러나 너비는 textField가 더 이상 첫 번째 응답자가 될 때까지 변경되지 않습니다. 커서를 다른 textField에 넣으면 편집 된 textField의 너비가 설정됩니다. 내가 원하는 것에 조금 늦었습니다.

이 주석 처리 된 줄은 성공하지 못한 채 시도한 것입니다.

- (IBAction)textFieldDidChange:(UITextField *)textField {
    [UIView animateWithDuration:0.1 animations:^{
//        [self.contentView removeConstraints:horizontalConstraints];
//        [self.contentView addConstraints:horizontalConstraints];
//        [self.contentView layoutIfNeeded];

//        [self.contentView setNeedsLayout];

//        [self.contentView setNeedsUpdateConstraints];
    }];
    NSLog(@"%@", NSStringFromCGSize(textField.intrinsicContentSize));
}

내가 뭘 놓치고 있는지 아는 사람 있나요? 이 작업을 수행하기 위해 무엇을 시도 할 수 있습니까?


이것이 나를 위해 작동하는 것입니다.

- (IBAction) textFieldDidChange: (UITextField*) textField
{
    [UIView animateWithDuration:0.1 animations:^{
        [textField invalidateIntrinsicContentSize];
    }];
}

흥미로운 점은 문서의 다음 텍스트를 감안할 때 상자 밖으로 작동해야하는 것처럼 보인다는 것입니다 .

또한 뷰의 속성이 변경되고 해당 변경 사항이 기본 콘텐츠 크기에 영향을 미치는 경우 레이아웃 시스템이 변경 사항을 인식하고 다시 레이아웃 할 수 있도록 뷰에서 invalidateIntrinsicContentSize를 호출해야합니다. 뷰 클래스를 구현할 때 고유 크기가 의존하는 속성의 값이 변경되면 invalidateIntrinsicContentSize를 호출해야합니다. 예를 들어 텍스트 필드는 문자열 값이 변경되면 invalidateIntrinsicContentSize를 호출합니다.

내 생각에 텍스트 필드는 편집이 끝나는 동안이 아니라 invalidateIntrinsicContentSize 만 호출한다는 것입니다.

편집 : "이것은 나를 위해 작동하지 않습니다"의 무리. 여기서 혼란은 아마도 textFieldDidChange:핸들러에 연결된 트리거링 이벤트라고 생각 합니다. 이벤트는 UIControlEventEditingChanged 여야합니다. IB를 사용하는 경우 올바른 이벤트를 처리하고 있는지 다시 확인하십시오.

UITextField또한 크기에 제약을받을 수 없습니다. 제약 조건을 사용하여 위치에 고정 할 수 있지만 너비 제약 조건 또는 왼쪽 + 오른쪽 위치 제약 집합으로 인해 고유 콘텐츠 크기로 크기가 조정되지 않습니다.


첫 번째 응답자 상태를 사임 할 UITextField때만 업데이트하는 이유를 모르겠습니다 intrinsicContentSize. 이것은 iOS 7 및 iOS 8 모두에서 발생합니다.

임시 해결책으로, 나는 무시 intrinsicContentSize하고 사용하여 크기를 결정합니다 typingAttributes. 이 차지하는 leftViewrightView뿐만 아니라

// This method is target-action for UIControlEventEditingChanged
func textFieldEditingChanged(textField: UITextField) {
  textField.invalidateIntrinsicContentSize()
}


override var intrinsicContentSize: CGSize {
    if isEditing {
      let string = (text ?? "") as NSString
      let size = string.size(attributes: typingAttributes)
      return CGSize(width: size.width + (rightView?.bounds.size.width ?? 0) + (leftView?.bounds.size.width ?? 0) + 2,
                    height: size.height)
    }

    return super.intrinsicContentSize
  }

여기서는 캐럿을 설명하기 위해 더 넓게 만듭니다.


Swift의 답은 다음과 같습니다. 보너스로이 스 니펫은 자리 표시자가있는 경우 텍스트 필드를 축소하지 않습니다.

// UITextField subclass

override func awakeFromNib() {
    super.awakeFromNib()
    self.addTarget(self, action: #selector(textFieldTextDidChange), forControlEvents: .EditingChanged)
}

func textFieldTextDidChange(textField: UITextField) {
    textField.invalidateIntrinsicContentSize()
}

override func intrinsicContentSize() -> CGSize {
    if self.editing {
        let textSize: CGSize = NSString(string: ((text ?? "" == "") ? self.placeholder : self.text) ?? "").sizeWithAttributes(self.typingAttributes)
        return CGSize(width: textSize.width + (self.leftView?.bounds.size.width ?? 0) + (self.rightView?.bounds.size.width ?? 0) + 2, height: textSize.height)
    } else {
        return super.intrinsicContentSize()
    }
}

가장 먼저 할 일은 Swift 4에서 UITextField의 intrinsicContentSize는 메서드가 아니라 계산 된 변수입니다.

아래는 특수한 사용 사례가있는 Swift4 솔루션입니다. 오른쪽 정렬 텍스트 필드는 크기를 조정하고 특정 픽셀 경계까지 왼쪽으로 확장 할 수 있습니다. 텍스트 필드는 UITextField 에서 상속되는 CurrencyTextField 클래스에 할당 됩니다 . 그런 다음 CurrencyTextField가 확장되어 계산 된 변수 인 객체의 intrinsicContentSize 를 반환하기위한 사용자 지정 동작을 제공 합니다. . CurrencyTextField 인스턴스
의 " editing changed "이벤트 는 호스트 UIViewController 클래스 repositionAndResize IBAction 메서드매핑됩니다 . 이 메서드는 단순히 CurrencyTextField 인스턴스의 현재 내장 콘텐츠 크기를 무효화합니다. CurrencyTextField의 invalidateIntrinsicContentSize 호출 메서드 (상속 된 형식 UITextField)는 객체의 고유 콘텐츠 크기를 가져 오려는 시도를 트리거합니다.

CurrencyTextField의 intrinsicContentSize getter 메서드의 사용자 지정 논리는 CurrentTextField의 텍스트 속성을 NSString으로 변환하여 크기를 확인해야합니다. CurrencyTextField의 x 좌표가 규정 된 픽셀 경계보다 크면 intrinsicContentSize로 반환됩니다.

class CurrencyTextField:UITextField {

}

extension CurrencyTextField {


     override open var intrinsicContentSize: CGSize {

        if isEditing {
            let string = (text ?? "") as NSString
            let stringSize:CGSize = string.size(withAttributes: 
             [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 
             19.0)])
             if self.frame.minX > 71.0 {
                return stringSize
             }
       }

       return super.intrinsicContentSize
    }


}

class TestController: UIViewController {

    @IBAction func repositionAndResize(_ sender: CurrencyTextField) {
        sender.invalidateIntrinsicContentSize()
    }


    override func viewDidLoad() {
        super.viewDidLoad()

    }

}

나는 a와 비슷한 요구 사항이 UITextView있었습니다 (높이와 다른 것들을 동적으로 늘려야했습니다).

내가 한 일은 다음과 비슷했습니다.

고려 self.contentView는 IS superviewtextField

- (IBAction)textFieldDidChange:(UITextField *)textField {
    //You can also use "textField.superview" instead of "self.contentView"
    [self.contentView setNeedsUpdateConstraints];

    //Since you wish to animate that expansion right away...
    [UIView animateWithDuration:0.1 animations:^{
        [self.contentView updateConstraintsIfNeeded];
    }];
    NSLog(@"%@", NSStringFromCGSize(textField.intrinsicContentSize));
}

또한, 내가 가진 요구 사항을 고려, 나는 무시했다 updateConstraints나의 방법 UITextView'들 superview.

해당 솔루션을 선택하는 경우 (아마 더 미세 조정 된 접근 방식) 다음을 수행 할 수 있습니다.

- (void)updateConstraints {
    [super updateConstraints];

    //Firstly, remove the width constraint from the textField.
    [self.myTextField removeConstraint:self.textFieldWidthConstraint];
    self.textFieldWidthConstraint = nil;

    CGSize contentSize = self.myTextField.intrinsicContentSize;
    self.textFieldWidthConstraint = [NSLayoutConstraint constraintWithItem:self.myTextField attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:contentSize.width];

    [self.myTextField addConstraint:self.textFieldWidthConstraint];
}

앞서 언급했듯이 재정의 옵션은 더 미세 조정 된 접근 방식이 필요했기 때문에 사용되었습니다.

또한 항상 그렇듯이 직면 할 수 있다고 생각되는 가장자리 사례 (크기 조정 값 측면)를 확인해야합니다.

도움이 되었기를 바랍니다!

건배!


어떤 이유로 전화 invalidateIntrinsicContentSize가 나에게도 효과가 없었지만 여백과 편집 컨트롤로 인해 다른 솔루션에도 문제가 발생했습니다.

This solution isn't always needed; if invalidateIntrinsicContentSize works, then all that needs to be done is to add a call to that when the text is changed, as in the other answers. If that isn't working, then here's a solution (adapted from here) I found which also works with a clear control:

class AutoResizingUITextField: UITextField {
    var lastTextBeforeEditing: String?

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupTextChangeNotification()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupTextChangeNotification()
    }                

    func setupTextChangeNotification() {
        NotificationCenter.default.addObserver(
            forName: Notification.Name.UITextFieldTextDidChange,
            object: self,
            queue: OperationQueue.main) { (notification) in                   
                self.invalidateIntrinsicContentSize()
        }
        NotificationCenter.default.addObserver(
            forName: Notification.Name.UITextFieldTextDidBeginEditing,
            object: self,
            queue: OperationQueue.main) { (notification) in
                self.lastTextBeforeEditing = self.text
        }
    }                

    override var intrinsicContentSize: CGSize {
        var size = super.intrinsicContentSize

        if isEditing, let text = text, let lastTextBeforeEditing = lastTextBeforeEditing {
            let string = text as NSString
            let stringSize = string.size(attributes: typingAttributes)
            let origSize = (lastTextBeforeEditing as NSString).size(attributes: typingAttributes)
            size.width = size.width + (stringSize.width - origSize.width)
        }

        return size
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

If invalidateIntrinsicContentSize is working by itself, this will end up double-counting the change so that needs to be checked first.


Another solution to this is to set leading/trailing constraints to the textField and use isActive to turn them on/off.

When editing start, turn them on, if the text is centered, the effect will be the same (text will seems to be auto resizing as typing). When editing stops, turn constraints off, which will wrap the frame around the text.


나를 위해 나는 또한 텍스트 필드에 대상을 추가했습니다 (UIControlEventEditingChanged)

- (void)textFieldChanged:(UITextField *)textField {
    [textField sizeToFit];
}

배경색이 있으면 대리자 메서드에서 업데이트하십시오.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {

    textField.backgroundColor = [UIColor grayColor];
    return YES;
}

viewDidLoad에이 코드를 추가하여 문제를 해결할 수 있습니다.

if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)])
{
    self.edgesForExtendedLayout = UIRectEdgeNone;
}

참조 URL : https://stackoverflow.com/questions/18236661/resize-a-uitextfield-while-typing-by-using-autolayout

반응형