Here is one way to approach this...
For your labels, give the top (the message) label:
Content Hugging Priority
Horizontal: 750
Vertical: 1000
Content Compression Resistance Priority
Horizontal: 1000
Vertical: 750
and give the bottom (the time) label:
Content Hugging Priority
Horizontal: 251
Vertical: 1000
Content Compression Resistance Priority
Horizontal: 1000
Vertical: 750
There are two examples here... the top one will constrain the "bubble view" to the cell's Content View
margins. The bottom one will constrain the width of the "bubble view" to 80% of the width of the cell.
Setup your constraints like this - ALL with Priority: 1000
:
define @IBOutlet
vars for the leading and trailing constraints:
@IBOutlet var sentConstraint: NSLayoutConstraint!
@IBOutlet var receivedConstraint: NSLayoutConstraint!
connect sentConstraint
to the Bubble View.trailing = trailingMargin
constraint, and connect receivedConstraint
to the Bubble View.leading = leadingMargin
constraint.
It will now look like this:
When you set the data for the cell, activate / deactivate the corresponding constraint, for example:
// activate / deactivate Trailing / Leading constraints
// based on sent or received
sentConstraint.isActive = msg.type == .sent
receivedConstraint.isActive = msg.type == .received
The >=
Leading and Trailing constraints will prevent the bubble view from extending outside the content view frame.
Here's how it could look:
It's common in messaging apps to leave a bit of "padding" on the side. By limiting the width of the bubble view to 80%, it could look like this:
Here's the code I used to produce this layout:
enum MessageType {
case sent, received
}
struct MessageStruct {
var imageName: String = ""
var message: String = ""
var time: String = ""
var type: MessageType = .sent
}
class BubbleCell: UITableViewCell {
@IBOutlet var bubbleView: UIView!
@IBOutlet var myImageView: UIImageView!
@IBOutlet var messageLabel: UILabel!
@IBOutlet var timeLabel: UILabel!
@IBOutlet var sentConstraint: NSLayoutConstraint!
@IBOutlet var receivedConstraint: NSLayoutConstraint!
func fillData(_ msg: MessageStruct) -> Void {
// I don't know how you're getting / setting your possible image,
// so I'll just set it to hidden for now
myImageView.isHidden = true
// set message label text
messageLabel.text = msg.message
// set time label text, append either sent or rec'd
// and set alignment
timeLabel.text = msg.time + (msg.type == .sent ? " - sent" : " - rec'd")
timeLabel.textAlignment = msg.type == .sent ? .right : .left
// set colors based on sent or received
bubbleView.backgroundColor = msg.type == .sent ? .systemGreen : .white
messageLabel.textColor = msg.type == .sent ? .white : .black
timeLabel.textColor = msg.type == .sent ? .white : .black
// activate / deactivate Trailing / Leading constraints
// based on sent or received
sentConstraint.isActive = msg.type == .sent
receivedConstraint.isActive = msg.type == .received
// set corner radii based on sent or received
bubbleView.layer.cornerRadius = 16
bubbleView.layer.maskedCorners = msg.type == .sent
? [.layerMaxXMinYCorner, .layerMinXMinYCorner, .layerMinXMaxYCorner]
: [.layerMaxXMaxYCorner, .layerMaxXMinYCorner, .layerMinXMinYCorner]
}
}
class MessageTableViewController: UITableViewController {
var myData: [MessageStruct] = []
override func viewDidLoad() {
super.viewDidLoad()
var msg: MessageStruct!
msg = MessageStruct(imageName: "",
message: "Short message.",
time: "09:15",
type: .sent)
myData.append(msg)
msg = MessageStruct(imageName: "",
message: "Short message.",
time: "09:15",
type: .received)
myData.append(msg)
msg = MessageStruct(imageName: "",
message: "This is a longer message.",
time: "09:20",
type: .sent)
myData.append(msg)
msg = MessageStruct(imageName: "",
message: "This is a longer message.",
time: "09:20",
type: .received)
myData.append(msg)
msg = MessageStruct(imageName: "",
message: "This message should be long enough that it needs to wrap in the cell.",
time: "09:25",
type: .sent)
myData.append(msg)
msg = MessageStruct(imageName: "",
message: "Another Short message.",
time: "09:30",
type: .sent)
myData.append(msg)
msg = MessageStruct(imageName: "",
message: "Another message, long enough that it will need to wrap in the cell.",
time: "09:35",
type: .sent)
myData.append(msg)
msg = MessageStruct(imageName: "",
message: "Another message, long enough that it will need to wrap in the cell.",
time: "09:35",
type: .received)
myData.append(msg)
tableView.separatorStyle = .none
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// use "bubbleCell" identifier for margin-width limit
// use "bubble80Cell" identifier for 80% width limit
let c = tableView.dequeueReusableCell(withIdentifier: "bubbleCell", for: indexPath) as! BubbleCell
let d = myData[indexPath.row]
c.fillData(d)
return c
}
}
and here's the Storyboard source so you can inspect it:
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="t48-nh-14V">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Message Table View Controller-->
<scene sceneID="mMr-vR-h6X">
<objects>
<tableViewController id="t48-nh-14V" customClass="MessageTableViewController" customModule="QuickTest" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="BrW-mC-6e6">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="bubbleCell" rowHeight="260" id="Oi2-T7-oyW" customClass="BubbleCell" customModule="QuickTest" customModuleProvider="target">
<rect key="frame" x="0.0" y="28" width="375" height="260"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Oi2-T7-oyW" id="gQY-hx-B2I">
<rect key="frame" x="0.0" y="0.0" width="375" height="260"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="h2Z-Bt-MGu">
<rect key="frame" x="16" y="11" width="343" height="238"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="6" translatesAutoresizingMaskIntoConstraints="NO" id="wrN-dr-6PW">
<rect key="frame" x="8" y="12" width="327" height="214"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Aio-Ve-8b1">
<rect key="frame" x="0.0" y="0.0" width="327" height="165.5"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="1000" horizontalCompressionResistancePriority="1000" text="Label" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="83w-nH-MFV">
<rect key="frame" x="0.0" y="171.5" width="327" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="1000" horizontalCompressionResistancePriority="1000" text="13:50 - sent" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3MO-It-xQw">
<rect key="frame" x="0.0" y="198" width="327" height="16"/>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
</subviews>
<color key="backgroundColor" systemColor="systemGreenColor"/>
<constraints>
<constraint firstItem="wrN-dr-6PW" firstAttribute="top" secondItem="h2Z-Bt-MGu" secondAttribute="top" constant="12" id="61F-T7-VXd"/>
<constraint firstAttribute="trailing" secondItem="wrN-dr-6PW" secondAttribute="trailing" constant="8" id="Aso-zu-ZPc"/>
<constraint firstItem="wrN-dr-6PW" firstAttribute="leading" secondItem="h2Z-Bt-MGu" secondAttribute="leading" constant="8" id="bsG-sY-260"/>
<constraint firstAttribute="bottom" secondItem="wrN-dr-6PW" secondAttribute="bottom" constant="12" id="oAO-Jt-UX3"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.897361517" green="0.8974906802" blue="0.89733326440000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailingMargin" relation="greaterThanOrEqual" secondItem="h2Z-Bt-MGu" secondAttribute="trailing" id="6hf-N2-14b"/>
<constraint firstAttribute="bottomMargin" secondItem="h2Z-Bt-MGu" secondAttribute="bottom" id="FxF-PE-6iW"/>
<constraint firstItem="h2Z-Bt-MGu" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="gQY-hx-B2I" secondAttribute="leadingMargin" id="Wsa-eO-InO"/>
<constraint firstItem="h2Z-Bt-MGu" firstAttribute="top" secondItem="gQY-hx-B2I" secondAttribute="topMargin" id="ZYU-hS-2Pl"/>
<constraint firstItem="h2Z-Bt-MGu" firstAttribute="trailing" secondItem="gQY-hx-B2I" secondAttribute="trailingMargin" id="edX-9l-Ye2"/>
<constraint firstItem="h2Z-Bt-MGu" firstAttribute="leading" secondItem="gQY-hx-B2I" secondAttribute="leadingMargin" id="s0J-rx-Rzx"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="bubbleView" destination="h2Z-Bt-MGu" id="uJ0-t9-ZQ9"/>
<outlet property="messageLabel" destination="83w-nH-MFV" id="pEd-Gg-49x"/>
<outlet property="myImageView" destination="Aio-Ve-8b1" id="qJQ-G9-Qc6"/>
<outlet property="receivedConstraint" destination="s0J-rx-Rzx" id="fCI-9h-2V1"/>
<outlet property="sentConstraint" destination="edX-9l-Ye2" id="KKm-FT-Zcq"/>
<outlet property="timeLabel" destination="3MO-It-xQw" id="bxt-vZ-c2v"/>
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="bubble80Cell" rowHeight="260" id="DAc-Zm-a8s" customClass="BubbleCell" customModule="QuickTest" customModuleProvider="target">
<rect key="frame" x="0.0" y="288" width="375" height="260"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="DAc-Zm-a8s" id="te5-Rn-aLa">
<rect key="frame" x="0.0" y="0.0" width="375" height="260"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="lFi-s6-a3X">
<rect key="frame" x="16" y="11" width="300" height="238"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="6" translatesAutoresizingMaskIntoConstraints="NO" id="HX0-dn-sRz">
<rect key="frame" x="8" y="12" width="284" height="214"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Xsw-i1-9mU">
<rect key="frame" x="0.0" y="0.0" width="284" height="165.5"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="1000" horizontalCompressionResistancePriority="1000" text="Label" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6Zs-Af-RNQ">
<rect key="frame" x="0.0" y="171.5" width="284" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="1000" horizontalCompressionResistancePriority="1000" text="13:50 - sent" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7nV-rU-i4W">
<rect key="frame" x="0.0" y="198" width="284" height="16"/>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
</subviews>
<color key="backgroundColor" systemColor="systemGreenColor"/>
<constraints>
<constraint firstItem="HX0-dn-sRz" firstAttribute="leading" secondItem="lFi-s6-a3X" secondAttribute="leading" constant="8" id="6Kk-f4-tiB"/>
<constraint firstItem="HX0-dn-sRz" firstAttribute="top" secondItem="lFi-s6-a3X" secondAttribute="top" constant="12" id="7H5-6V-Ag0"/>
<constraint firstAttribute="trailing" secondItem="HX0-dn-sRz" secondAttribute="trailing" constant="8" id="Czf-wx-QBQ"/>
<constraint firstAttribute="bottom" secondItem="HX0-dn-sRz" secondAttribute="bottom" constant="12" id="edZ-aP-M1N"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.897361517" green="0.8974906802" blue="0.89733326440000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="lFi-s6-a3X" firstAttribute="trailing" secondItem="te5-Rn-aLa" secondAttribute="trailingMargin" id="Ua5-F4-YR5"/>
<constraint firstAttribute="bottomMargin" secondItem="lFi-s6-a3X" secondAttribute="bottom" id="WKr-87-LJK"/>
<constraint firstItem="lFi-s6-a3X" firstAttribute="leading" secondItem="te5-Rn-aLa" secondAttribute="leadingMargin" id="h4U-ie-xYq"/>
<constraint firstItem="lFi-s6-a3X" firstAttribute="width" relation="lessThanOrEqual" secondItem="te5-Rn-aLa" secondAttribute="width" multiplier="0.8" id="sKr-Xc-5bn"/>
<constraint firstItem="lFi-s6-a3X" firstAttribute="top" secondItem="te5-Rn-aLa" secondAttribute="topMargin" id="ueL-NI-qoX"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="bubbleView" destination="lFi-s6-a3X" id="5qj-C0-Uf3"/>
<outlet property="messageLabel" destination="6Zs-Af-RNQ" id="YTU-M9-s7P"/>
<outlet property="myImageView" destination="Xsw-i1-9mU" id="Unr-HS-Iv1"/>
<outlet property="receivedConstraint" destination="h4U-ie-xYq" id="tCy-cc-uHa"/>
<outlet property="sentConstraint" destination="Ua5-F4-YR5" id="Kgg-gY-JCv"/>
<outlet property="timeLabel" destination="7nV-rU-i4W" id="GUX-T1-4Nc"/>
</connections>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="t48-nh-14V" id="3oa-OS-rO8"/>
<outlet property="delegate" destination="t48-nh-14V" id="7Ua-sV-mwS"/>
</connections>
</tableView>
<navigationItem key="navigationItem" id="fpm-tv-1pb"/>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="2On-8d-2Kp" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-1447.2" y="-749.7751124437782"/>
</scene>
</scenes>
<resources>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="systemGreenColor">
<color red="0.20392156862745098" green="0.7803921568627451" blue="0.34901960784313724" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>
Edit
Worth noting -- if we replace these two lines in fillData()
:
sentConstraint.isActive = msg.type == .sent
receivedConstraint.isActive = msg.type == .received
with these:
sentConstraint.priority = msg.type == .sent ? .defaultHigh : .defaultLow
receivedConstraint.priority = msg.type == .received ? .defaultHigh : .defaultLow
we'll accomplish the same goal.
Without seeing your actual layout (difficult to decipher the "description of all constraints" you posted), and without seeing the code you are using, it's tough to say why that wasn't working for you.