0

I'm trying to align text in UITextView vertically in middle. After search I found code and it seems to work until we start to type and hit enter when cursor jumps. When we hit enter key while typing inside text view the cursor jumps to adjust its position which I'm assuming due to the way it's called/implemented.

Is there a better and more elegant solution to this?

I've implemented it as UITextView extension. This code is taken from one of the solutions suggested on Center text vertically in a UITextView

My question is to fix the jumping issue. It's not a duplicate of above question.

extension UITextView {
    func alignTextVerticallyInContainer() {
        let fittingSize = CGSize(width: bounds.width, height: CGFloat.greatestFiniteMagnitude)
        let size = sizeThatFits(fittingSize)
        let topOffset = ((bounds.size.height - size.height * zoomScale) / 2)
        let positiveTopOffset = max(1, topOffset)
        contentOffset.y = -positiveTopOffset + 1
    }
}

And calling it from

override func viewDidLayoutSubviews() {
    textView.alignTextVerticallyInContainer()
}
DaveMS
  • 157
  • 2
  • 13
  • Possible duplicate of [Center text vertically in a UITextView](https://stackoverflow.com/questions/12591192/center-text-vertically-in-a-uitextview) – Faisal Rahman Avash May 29 '19 at 02:38

2 Answers2

0

You can accomplish this with auto-layout only - no code needed.

Start by adding a UIScrollView - I've constrained it to 80-pts from the Top, 20-pts on each side, and a Height constraint of 240 (I set the background to orange-is, to make it easy to see):

enter image description here

Next, add a standard UIView as a subview of the scrollView (I set the background to cyan to make it easy to see). Constrain it 8-pts on each side and on top and bottom (for a little padding). Constrain its Width equal to the scrollView Width, with a constant of -16 (for the side padding), and constrain its Height equal to the scrollView Height, also with a constant of -16 (for the top/bottom padding).

Key point: set the Priority of the Height constraint to Low (250). That will allow it to grow when needed:

enter image description here

Now add a UITextView as a subview of the cyan view. Start with only one line of text. Constrain it 8-pts from each side (for padding); constrain it >= 8-pts on the Top and Bottom; and constrain it Centered Vertically in the span view:

enter image description here

When you run the app, it should function like this (again, no code needed):

enter image description here

enter image description here

enter image description here

Here is the source of the Storyboard:

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="LZe-dy-wRt">
    <device id="retina4_7" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="k3M-Zu-xX0">
            <objects>
                <viewController id="LZe-dy-wRt" sceneMemberID="viewController">
                    <view key="view" contentMode="scaleToFill" id="fZP-6s-NIY">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="guO-HM-qxH">
                                <rect key="frame" x="20" y="100" width="335" height="240"/>
                                <subviews>
                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="zW5-KI-gna">
                                        <rect key="frame" x="8" y="8" width="319" height="224"/>
                                        <subviews>
                                            <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" text="Start with one line." textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="S6g-U0-TeD">
                                                <rect key="frame" x="8" y="95.5" width="303" height="33"/>
                                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                                <fontDescription key="fontDescription" type="system" pointSize="14"/>
                                                <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
                                            </textView>
                                        </subviews>
                                        <color key="backgroundColor" red="0.45138680930000002" green="0.99309605359999997" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <constraints>
                                            <constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="S6g-U0-TeD" secondAttribute="bottom" constant="8" id="Ewc-yq-KHO"/>
                                            <constraint firstItem="S6g-U0-TeD" firstAttribute="leading" secondItem="zW5-KI-gna" secondAttribute="leading" constant="8" id="RIE-gD-bX2"/>
                                            <constraint firstAttribute="trailing" secondItem="S6g-U0-TeD" secondAttribute="trailing" constant="8" id="SfV-e8-TXV"/>
                                            <constraint firstItem="S6g-U0-TeD" firstAttribute="top" relation="greaterThanOrEqual" secondItem="zW5-KI-gna" secondAttribute="top" constant="8" id="byT-nx-8K1"/>
                                            <constraint firstItem="S6g-U0-TeD" firstAttribute="centerY" secondItem="zW5-KI-gna" secondAttribute="centerY" id="qGs-hb-AdH"/>
                                        </constraints>
                                    </view>
                                </subviews>
                                <color key="backgroundColor" red="1" green="0.83234566450000003" blue="0.47320586440000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                <constraints>
                                    <constraint firstAttribute="bottom" secondItem="zW5-KI-gna" secondAttribute="bottom" constant="8" id="1xU-wS-Qcb"/>
                                    <constraint firstItem="zW5-KI-gna" firstAttribute="top" secondItem="guO-HM-qxH" secondAttribute="top" constant="8" id="CgB-kT-vjH"/>
                                    <constraint firstItem="zW5-KI-gna" firstAttribute="leading" secondItem="guO-HM-qxH" secondAttribute="leading" constant="8" id="O4I-aF-PEV"/>
                                    <constraint firstAttribute="height" constant="240" id="OZD-dD-yht"/>
                                    <constraint firstAttribute="trailing" secondItem="zW5-KI-gna" secondAttribute="trailing" constant="8" id="QXI-Lh-37A"/>
                                    <constraint firstItem="zW5-KI-gna" firstAttribute="height" secondItem="guO-HM-qxH" secondAttribute="height" priority="250" constant="-16" id="dMe-rS-ZOO"/>
                                    <constraint firstItem="zW5-KI-gna" firstAttribute="width" secondItem="guO-HM-qxH" secondAttribute="width" constant="-16" id="pGF-wL-V4q"/>
                                </constraints>
                            </scrollView>
                        </subviews>
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <constraints>
                            <constraint firstItem="XpO-5d-W48" firstAttribute="trailing" secondItem="guO-HM-qxH" secondAttribute="trailing" constant="20" id="Mvu-Ug-NEY"/>
                            <constraint firstItem="guO-HM-qxH" firstAttribute="leading" secondItem="XpO-5d-W48" secondAttribute="leading" constant="20" id="Rfe-Ev-3xj"/>
                            <constraint firstItem="guO-HM-qxH" firstAttribute="top" secondItem="XpO-5d-W48" secondAttribute="top" constant="80" id="YhH-mc-af6"/>
                        </constraints>
                        <viewLayoutGuide key="safeArea" id="XpO-5d-W48"/>
                    </view>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="p5Q-GK-BKG" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="-10" y="775"/>
        </scene>
    </scenes>
</document>
DonMag
  • 69,424
  • 5
  • 50
  • 86
  • Thank you for detailed reply. This may work in example scenario but my scene is too complex. I already have scroll view etc. My constraints are also similar to what you have suggested for UITextView. It still doesn't work for me. It may be because of attributed text I'm using in UITextView – DaveMS Jun 02 '19 at 17:13
-1

Please check this link for center text alignment.

 textView.textAlignment = .center
Mitul.Patel
  • 252
  • 2
  • 8