it-swarm.com.ru

Центрировать текст по вертикали в UITextView

Я хочу центрировать текст по вертикали внутри большой UITextView, которая заполняет весь экран - так что, когда текста мало, скажем пару слов, он центрируется по высоте. Дело не в центрировании текста (свойство, которое можно найти в IB), а в том, чтобы поместить текст вертикально прямо в середину UITextView если текст короткий, поэтому есть нет пробелов в UITextView. Можно ли это сделать? Заранее спасибо!

57
Sergey Grischyov

Сначала добавьте наблюдателя для значения ключа contentSize для UITextView при загрузке представления:

- (void) viewDidLoad {
     [textField addObserver:self forKeyPath:@"contentSize" options:(NSKeyValueObservingOptionNew) context:NULL];
     [super viewDidLoad];
}

Затем добавьте этот метод для настройки contentOffset при каждом изменении значения contentSize:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
     UITextView *tv = object;
     CGFloat topCorrect = ([tv bounds].size.height - [tv contentSize].height * [tv zoomScale])/2.0;
     topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
     tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
}
59
Carlos Solana Martínez

Поскольку UIKit не совместим с KVO , я решил реализовать это как подкласс UITextView, который обновляется всякий раз, когда изменяется contentSize

Это слегка измененная версия ответ Карлоса который устанавливает contentInset вместо contentOffset. В дополнение к совместимости с iOS 9 , он также кажется менее глючным на iOS 8.4.

class VerticallyCenteredTextView: UITextView {
    override var contentSize: CGSize {
        didSet {
            var topCorrection = (bounds.size.height - contentSize.height * zoomScale) / 2.0
            topCorrection = max(0, topCorrection)
            contentInset = UIEdgeInsets(top: topCorrection, left: 0, bottom: 0, right: 0)
        }
    }
}
44
Senseful

Если вы не хотите использовать KVO, вы также можете вручную отрегулировать смещение, экспортировав этот код в такую ​​функцию:

-(void)adjustContentSize:(UITextView*)tv{
    CGFloat deadSpace = ([tv bounds].size.height - [tv contentSize].height);
    CGFloat inset = MAX(0, deadSpace/2.0);
    tv.contentInset = UIEdgeInsetsMake(inset, tv.contentInset.left, inset, tv.contentInset.right);
}  

и зовет это в 

-(void)textViewDidChange:(UITextView *)textView{
    [self adjustContentSize:textView];
}

и каждый раз, когда вы редактируете текст в коде. Не забудьте установить контроллер в качестве делегата

Swift 3 версия:  

func adjustContentSize(tv: UITextView){
    let deadSpace = tv.bounds.size.height - tv.contentSize.height
    let inset = max(0, deadSpace/2.0)
    tv.contentInset = UIEdgeInsetsMake(inset, tv.contentInset.left, inset, tv.contentInset.right)
}

func textViewDidChange(_ textView: UITextView) {
    self.adjustContentSize(tv: textView)
}
21
Beninho85

Для iOS 9.0.2. вместо этого нам нужно установить contentInset. Если мы используем KVO для contentOffset, iOS 9.0.2 устанавливает его в 0 в последний момент, отменяя изменения в contentOffset.

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    UITextView *tv = object;
    CGFloat topCorrect = ([tv bounds].size.height - [tv     contentSize].height * [tv zoomScale])/2.0;
    topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
    [tv setContentInset:UIEdgeInsetsMake(topCorrect,0,0,0)];
}

- (void) viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:NO];
    [questionTextView addObserver:self forKeyPath:@"contentSize" options:(NSKeyValueObservingOptionNew) context:NULL];
}

Я использовал 0,0 и 0 для левой, нижней и правой вставок Edge соответственно. Обязательно рассчитайте их и для вашего варианта использования. 

20
s.ka

Вот расширение UITextView, которое центрирует контент по вертикали:

extension UITextView {

    func centerVertically() {
        let fittingSize = CGSize(width: bounds.width, height: CGFloat.max)
        let size = sizeThatFits(fittingSize)
        let topOffset = (bounds.size.height - size.height * zoomScale) / 2
        let positiveTopOffset = max(0, topOffset)
        contentOffset.y = -positiveTopOffset
    }

}
6
Rudolf Adamkovič

Swift 3:

override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        textField.frame = self.view.bounds

        var topCorrect : CGFloat = (self.view.frame.height / 2) - (textField.contentSize.height / 2)
        topCorrect = topCorrect < 0.0 ? 0.0 : topCorrect
        textField.contentInset = UIEdgeInsetsMake(topCorrect,0,0,0)

    }
5
user5195185
func alignTextVerticalInTextView(textView :UITextView) {

    let size = textView.sizeThatFits(CGSizeMake(CGRectGetWidth(textView.bounds), CGFloat(MAXFLOAT)))

    var topoffset = (textView.bounds.size.height - size.height * textView.zoomScale) / 2.0
    topoffset = topoffset < 0.0 ? 0.0 : topoffset

    textView.contentOffset = CGPointMake(0, -topoffset)
}
5
Ievgen

Вы можете установить его напрямую, используя только ограничения:

Есть 3 ограничения, которые я добавил, чтобы выровнять текст по горизонтали и вертикали в ограничениях, как показано ниже:

 enter image description here

  1. Сделайте высоту 0 и добавьте ограничения больше
  2. Добавить выравнивание по вертикали к родительским ограничениям
  3. Добавить выравнивание по горизонтали к родительским ограничениям

 enter image description here

5
KOTIOS

У меня есть текстовое представление, которое я использую с autolayout и с установкой lineFragmentPadding и textContainerInset в ноль. Ни одно из приведенных выше решений не сработало в моей ситуации. Тем не менее, это работает для меня. Протестировано с iOS 9

@interface VerticallyCenteredTextView : UITextView
@end

@implementation VerticallyCenteredTextView

-(void)layoutSubviews{
    [self recenter];
}

-(void)recenter{
    // using self.contentSize doesn't work correctly, have to calculate content size
    CGSize contentSize = [self sizeThatFits:CGSizeMake(self.bounds.size.width, CGFLOAT_MAX)];
    CGFloat topCorrection = (self.bounds.size.height - contentSize.height * self.zoomScale) / 2.0;
    self.contentOffset = CGPointMake(0, -topCorrection);
}

@end
5
user1687195

У меня также есть эта проблема, и я решил ее с помощью UITableViewCell с UITextView. Я создал метод в пользовательском подклассе UITableViewCell, свойство statusTextView:

- (void)centerTextInTextView
{
    CGFloat topCorrect = ([self.statusTextView bounds].size.height - [self.statusTextView contentSize].height * [self.statusTextView zoomScale])/2.0;
    topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
    self.statusTextView.contentOffset = (CGPoint){ .x = 0, .y = -topCorrect };

И вызвать этот метод в методах:

- (void)textViewDidBeginEditing:(UITextView *)textView
- (void)textViewDidEndEditing:(UITextView *)textView
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

Это решение работало для меня без проблем, вы можете попробовать его.

4
Miro Durkovic

Я только что создал пользовательский текст по вертикали в Swift 3:

class VerticallyCenteredTextView: UITextView {
    override var contentSize: CGSize {
        didSet {
            var topCorrection = (bounds.size.height - contentSize.height * zoomScale) / 2.0
            topCorrection = max(0, topCorrection)
            contentInset = UIEdgeInsets(top: topCorrection, left: 0, bottom: 0, right: 0)
        }
    }
}

ссылка: http://geek-is-stupid.github.io/blog/2017/03/14/how-to-center-text-vertically-in-a-uitextview/

3
Tai Le

Добавьте к ответу Карлоса, на тот случай, если у вас текст в телевизоре больше, чем размер телевизора, вам не нужно вводить текст заново, поэтому измените этот код:

tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};

к этому:

if ([tv contentSize].height < [tv bounds].size.height) {
     tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
}
1
user3777977

Решение для автоматической разметки:

  1. Создайте UIView, который действует как контейнер для UITextView.
  2. Добавьте следующие ограничения:
    • TextView: выравнивание начального пространства по: контейнеру
    • TextView: выравнивание конечного пространства по: контейнеру
    • TextView: выравнивание по центру Y: контейнер
    • TextView: равная высота: контейнер, отношение: ≤
1
Etan

Вы можете попробовать код ниже, наблюдатель не обязательно. иногда наблюдатель выдает ошибку, когда view освобождает . Вы можете сохранить этот код в viewDidLoad, viewWillAppear или в viewDidAppear в любом месте. 

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_async(dispatch_get_main_queue(), ^(void) {
        UITextView *tv = txtviewDesc;
        CGFloat topCorrect = ([tv bounds].size.height - [tv contentSize].height * [tv zoomScale])/2.0;
        topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
        tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
    });
});
0
Kiran Jasvanee

Я сделал это так: во-первых, я встроил UITextView в UIView (это должно работать и для Mac OS). Затем я прикрепил все четыре стороны внешнего UIView к сторонам его контейнера, придав ему форму и размер, аналогичные или равные UITextView. Таким образом, у меня был правильный контейнер для UITextView. Затем я прикрепил левую и правую границы UITextView к сторонам UIView, и дал UITextView высоту. Наконец, я центрировал UITextView вертикально в UIView. Бинго :) теперь UITextView вертикально центрирован в UIView, следовательно, текст внутри UITextView также центрирован вертикально.

0
user2626974