Last active
June 25, 2023 14:12
-
-
Save dobster/3d19901adf4e55eeaeaa74a321da3269 to your computer and use it in GitHub Desktop.
Expanding Table View Cell to reveal UITextView
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ExpandoTableViewCell.swift | |
// TestExpandoCells | |
// | |
import UIKit | |
protocol ExpandoTableViewCellDelegate: class { | |
func expandoTableViewCellDidTapCell(_ cell: ExpandoTableViewCell) | |
func expandoTableViewCell(_ cell: ExpandoTableViewCell, didChangeNotes notes: String) | |
} | |
class ExpandoTableViewCell: UITableViewCell, UITextViewDelegate { | |
@IBOutlet var button: UIButton! | |
@IBOutlet var smallLayoutConstraint: NSLayoutConstraint! | |
@IBOutlet var largeLayoutConstraint: NSLayoutConstraint! | |
@IBOutlet var textView: UITextView! | |
override func awakeFromNib() { | |
super.awakeFromNib() | |
textView.delegate = self | |
} | |
weak var delegate: ExpandoTableViewCellDelegate? | |
var notes: String = "" { | |
didSet { | |
textView.text = notes | |
} | |
} | |
var isExpanded: Bool = true { | |
didSet { | |
if isExpanded { | |
smallLayoutConstraint.priority = .defaultLow | |
largeLayoutConstraint.priority = .defaultHigh | |
textView.textColor = UIColor.gray | |
textView.isHidden = false | |
} else { | |
textView.isHidden = true | |
largeLayoutConstraint.priority = .defaultLow | |
smallLayoutConstraint.priority = .defaultHigh | |
} | |
} | |
} | |
@IBAction func didTapCell(_ sender: Any) { | |
delegate?.expandoTableViewCellDidTapCell(self) | |
} | |
func textViewDidBeginEditing(_ textView: UITextView) { | |
textView.textColor = UIColor.black | |
} | |
func textViewDidEndEditing(_ textView: UITextView) { | |
textView.textColor = UIColor.gray | |
delegate?.expandoTableViewCell(self, didChangeNotes: textView.text ?? "") | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> | |
<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> | |
<objects> | |
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> | |
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> | |
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="135" id="KGk-i7-Jjw" customClass="ExpandoTableViewCell" customModule="TestExpandoCells" customModuleProvider="target"> | |
<rect key="frame" x="0.0" y="0.0" width="330" height="135"/> | |
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> | |
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM"> | |
<rect key="frame" x="0.0" y="0.0" width="330" height="134.5"/> | |
<autoresizingMask key="autoresizingMask"/> | |
<subviews> | |
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="750" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="y7a-Vx-IPl"> | |
<rect key="frame" x="16" y="11" width="162" height="20.5"/> | |
<fontDescription key="fontDescription" type="system" pointSize="17"/> | |
<nil key="textColor"/> | |
<nil key="highlightedColor"/> | |
</label> | |
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="pb4-od-cH6"> | |
<rect key="frame" x="16" y="39.5" width="298" height="84"/> | |
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | |
<constraints> | |
<constraint firstAttribute="height" constant="84" id="e8f-0Y-vV4"/> | |
</constraints> | |
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. odioque civiuda.</string> | |
<fontDescription key="fontDescription" type="system" pointSize="14"/> | |
<textInputTraits key="textInputTraits" autocapitalizationType="sentences" enablesReturnKeyAutomatically="YES"/> | |
</textView> | |
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Shh-q3-7at"> | |
<rect key="frame" x="268" y="6" width="46" height="30"/> | |
<state key="normal" title="Button"/> | |
<connections> | |
<action selector="didTapCell:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="ghg-1u-4CM"/> | |
</connections> | |
</button> | |
</subviews> | |
<constraints> | |
<constraint firstAttribute="bottom" secondItem="y7a-Vx-IPl" secondAttribute="bottom" priority="250" constant="8" id="8CB-LD-RlP"/> | |
<constraint firstItem="Shh-q3-7at" firstAttribute="leading" secondItem="y7a-Vx-IPl" secondAttribute="trailing" constant="90" id="JgC-mw-hdE"/> | |
<constraint firstAttribute="trailing" secondItem="Shh-q3-7at" secondAttribute="trailing" constant="16" id="Zqf-AA-tIZ"/> | |
<constraint firstItem="y7a-Vx-IPl" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" id="b15-bz-CXd"/> | |
<constraint firstItem="pb4-od-cH6" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" id="cPl-CD-TE5"/> | |
<constraint firstItem="pb4-od-cH6" firstAttribute="top" secondItem="y7a-Vx-IPl" secondAttribute="bottom" priority="750" constant="8" id="feQ-F8-WZJ"/> | |
<constraint firstItem="pb4-od-cH6" firstAttribute="trailing" secondItem="H2p-sc-9uM" secondAttribute="trailingMargin" id="hBK-nv-gta"/> | |
<constraint firstItem="Shh-q3-7at" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="6" id="m1g-jv-yOE"/> | |
<constraint firstAttribute="bottom" secondItem="y7a-Vx-IPl" secondAttribute="bottom" priority="250" constant="108" id="obx-Hh-SdV"/> | |
<constraint firstItem="y7a-Vx-IPl" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="topMargin" id="wDZ-4B-RFo"/> | |
</constraints> | |
</tableViewCellContentView> | |
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/> | |
<connections> | |
<outlet property="button" destination="Shh-q3-7at" id="ZMV-rR-DmC"/> | |
<outlet property="largeLayoutConstraint" destination="obx-Hh-SdV" id="9zq-tt-lCS"/> | |
<outlet property="smallLayoutConstraint" destination="8CB-LD-RlP" id="JqL-2O-zlw"/> | |
<outlet property="textView" destination="pb4-od-cH6" id="c6N-ae-wcE"/> | |
</connections> | |
<point key="canvasLocation" x="145.59999999999999" y="194.75262368815595"/> | |
</tableViewCell> | |
</objects> | |
</document> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ViewController.swift | |
// TestExpandoCells | |
// | |
import UIKit | |
struct Model { | |
let notes: String | |
var isExpanded: Bool { return notes.isNotEmpty } | |
static let `default` = Model(notes: "") | |
} | |
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, ExpandoTableViewCellDelegate { | |
@IBOutlet var tableView: UITableView! | |
var model: [Model] = [Model.default, Model.default, Model.default, Model.default, Model.default, Model.default, Model.default, Model.default, Model.default] | |
var editingIndexPath: IndexPath? | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
tableView.delegate = self | |
tableView.dataSource = self | |
tableView.tableFooterView = UIView(frame: tableView.bounds) | |
tableView.register(UINib(nibName: "ExpandoTableViewCell", bundle: nil), forCellReuseIdentifier: "Expando Cell") | |
} | |
func numberOfSections(in tableView: UITableView) -> Int { | |
return 1 | |
} | |
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { | |
return model.count | |
} | |
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | |
let cell = tableView.dequeueReusableCell(withIdentifier: "Expando Cell", for: indexPath) as! ExpandoTableViewCell | |
let thisModel = model[indexPath.row] | |
cell.isExpanded = thisModel.isExpanded || indexPath == editingIndexPath | |
cell.notes = thisModel.notes | |
cell.delegate = self | |
return cell | |
} | |
func expandoTableViewCellDidTapCell(_ cell: ExpandoTableViewCell) { | |
guard let indexPath = tableView.indexPath(for: cell) else { return } | |
editingIndexPath = indexPath | |
tableView.reloadRows(at: [indexPath], with: .automatic) | |
guard let newCell = tableView.cellForRow(at: indexPath) as? ExpandoTableViewCell else { return } | |
newCell.textView.becomeFirstResponder() | |
} | |
func expandoTableViewCell(_ cell: ExpandoTableViewCell, didChangeNotes notes: String) { | |
guard let indexPath = tableView.indexPath(for: cell) else { return } | |
let trimmedNotes = notes.trimmingCharacters(in: .whitespaces) | |
let newModel = Model(notes: trimmedNotes) | |
model[indexPath.row] = newModel | |
editingIndexPath = nil | |
tableView.reloadRows(at: [indexPath], with: .automatic) | |
} | |
} | |
extension Collection { | |
var isNotEmpty: Bool { return !isEmpty } | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment