-
-
Save Azhrei/265ce5c12be9e3303239ebdad3a36de0 to your computer and use it in GitHub Desktop.
<?xml version="1.0" encoding="UTF-8"?> | |
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="gUO-Uk-rVh"> | |
<device id="retina4_7" orientation="portrait"> | |
<adaptation id="fullscreen"/> | |
</device> | |
<dependencies> | |
<deployment identifier="iOS"/> | |
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/> | |
<capability name="Safe area layout guides" minToolsVersion="9.0"/> | |
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> | |
</dependencies> | |
<scenes> | |
<!--Item 2--> | |
<scene sceneID="B6D-zg-AL2"> | |
<objects> | |
<viewController id="3ub-SL-PrQ" sceneMemberID="viewController"> | |
<view key="view" contentMode="scaleToFill" id="orx-mp-Y7e"> | |
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> | |
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | |
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | |
<viewLayoutGuide key="safeArea" id="mo3-06-6kI"/> | |
</view> | |
<tabBarItem key="tabBarItem" title="Item 2" id="Lfa-0g-MH8"/> | |
</viewController> | |
<placeholder placeholderIdentifier="IBFirstResponder" id="lC5-np-aH3" userLabel="First Responder" sceneMemberID="firstResponder"/> | |
</objects> | |
<point key="canvasLocation" x="1030" y="455"/> | |
</scene> | |
<!--My SubclassedTVC--> | |
<scene sceneID="1Px-hQ-8Z2"> | |
<objects> | |
<tableViewController id="aYp-Ls-x3G" customClass="MySubclassedTVC" customModule="TestProject" customModuleProvider="target" sceneMemberID="viewController"> | |
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="wcH-hv-H3C"> | |
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> | |
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | |
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | |
<sections> | |
<tableViewSection headerTitle="MySubclassedTVC" id="Wq8-eO-PWV"> | |
<cells> | |
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="row1" textLabel="nCW-ld-pIh" style="IBUITableViewCellStyleDefault" id="vbN-Iv-5QF"> | |
<rect key="frame" x="0.0" y="28" width="375" height="44"/> | |
<autoresizingMask key="autoresizingMask"/> | |
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="vbN-Iv-5QF" id="XCh-y4-Y1a"> | |
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/> | |
<autoresizingMask key="autoresizingMask"/> | |
<subviews> | |
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Row 1 (initiate segue)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="nCW-ld-pIh"> | |
<rect key="frame" x="16" y="0.0" width="343" height="43.5"/> | |
<autoresizingMask key="autoresizingMask"/> | |
<fontDescription key="fontDescription" type="system" pointSize="17"/> | |
<nil key="textColor"/> | |
<nil key="highlightedColor"/> | |
</label> | |
</subviews> | |
</tableViewCellContentView> | |
<connections> | |
<segue destination="So6-9U-ErM" kind="show" id="z0W-ti-r34"/> | |
</connections> | |
</tableViewCell> | |
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="row2" textLabel="3mJ-aS-ejI" style="IBUITableViewCellStyleDefault" id="MSe-Gh-vGZ"> | |
<rect key="frame" x="0.0" y="72" width="375" height="44"/> | |
<autoresizingMask key="autoresizingMask"/> | |
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="MSe-Gh-vGZ" id="L8Q-kb-DQE"> | |
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/> | |
<autoresizingMask key="autoresizingMask"/> | |
<subviews> | |
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Row 2" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="3mJ-aS-ejI"> | |
<rect key="frame" x="16" y="0.0" width="343" height="43.5"/> | |
<autoresizingMask key="autoresizingMask"/> | |
<fontDescription key="fontDescription" type="system" pointSize="17"/> | |
<nil key="textColor"/> | |
<nil key="highlightedColor"/> | |
</label> | |
</subviews> | |
</tableViewCellContentView> | |
</tableViewCell> | |
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="row3" textLabel="8X7-0k-gW2" style="IBUITableViewCellStyleDefault" id="1hF-90-LNx"> | |
<rect key="frame" x="0.0" y="116" width="375" height="44"/> | |
<autoresizingMask key="autoresizingMask"/> | |
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="1hF-90-LNx" id="aZt-ZZ-AdL"> | |
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/> | |
<autoresizingMask key="autoresizingMask"/> | |
<subviews> | |
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Row 3" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="8X7-0k-gW2"> | |
<rect key="frame" x="16" y="0.0" width="343" height="43.5"/> | |
<autoresizingMask key="autoresizingMask"/> | |
<fontDescription key="fontDescription" type="system" pointSize="17"/> | |
<nil key="textColor"/> | |
<nil key="highlightedColor"/> | |
</label> | |
</subviews> | |
</tableViewCellContentView> | |
</tableViewCell> | |
</cells> | |
</tableViewSection> | |
</sections> | |
<connections> | |
<outlet property="dataSource" destination="aYp-Ls-x3G" id="0Tf-3x-ZFe"/> | |
<outlet property="delegate" destination="aYp-Ls-x3G" id="USF-qY-dKN"/> | |
</connections> | |
</tableView> | |
<navigationItem key="navigationItem" id="osl-fH-wOu"/> | |
</tableViewController> | |
<placeholder placeholderIdentifier="IBFirstResponder" id="onQ-Z1-gxz" userLabel="First Responder" sceneMemberID="firstResponder"/> | |
</objects> | |
<point key="canvasLocation" x="1959" y="-210"/> | |
</scene> | |
<!--My Sub SubclassedTVC--> | |
<scene sceneID="npu-qz-94D"> | |
<objects> | |
<tableViewController id="So6-9U-ErM" customClass="MySubSubclassedTVC" customModule="TestProject" customModuleProvider="target" sceneMemberID="viewController"> | |
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="V7k-LQ-pkk"> | |
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> | |
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | |
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | |
<sections> | |
<tableViewSection headerTitle="MySubSubclassedTVC" id="DC5-O3-Y4i"> | |
<cells> | |
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="Kss-5J-a4B"> | |
<rect key="frame" x="0.0" y="28" width="375" height="44"/> | |
<autoresizingMask key="autoresizingMask"/> | |
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Kss-5J-a4B" id="Epw-KJ-VU7"> | |
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/> | |
<autoresizingMask key="autoresizingMask"/> | |
</tableViewCellContentView> | |
</tableViewCell> | |
</cells> | |
</tableViewSection> | |
</sections> | |
<connections> | |
<outlet property="dataSource" destination="So6-9U-ErM" id="0vY-8g-c6A"/> | |
<outlet property="delegate" destination="So6-9U-ErM" id="15t-qo-URt"/> | |
</connections> | |
</tableView> | |
</tableViewController> | |
<placeholder placeholderIdentifier="IBFirstResponder" id="Fi9-A8-mP0" userLabel="First Responder" sceneMemberID="firstResponder"/> | |
</objects> | |
<point key="canvasLocation" x="2942" y="-210"/> | |
</scene> | |
<!--My Tab BarVC--> | |
<scene sceneID="Ei6-s2-GoC"> | |
<objects> | |
<tabBarController id="gUO-Uk-rVh" customClass="MyTabBarVC" customModule="TestProject" customModuleProvider="target" sceneMemberID="viewController"> | |
<tabBar key="tabBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="NjD-wU-fg3"> | |
<rect key="frame" x="0.0" y="0.0" width="375" height="49"/> | |
<autoresizingMask key="autoresizingMask"/> | |
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | |
</tabBar> | |
<connections> | |
<segue destination="6Lg-yF-lMB" kind="relationship" relationship="viewControllers" id="oCO-J8-zSr"/> | |
<segue destination="3ub-SL-PrQ" kind="relationship" relationship="viewControllers" id="Lgv-aG-XL7"/> | |
</connections> | |
</tabBarController> | |
<placeholder placeholderIdentifier="IBFirstResponder" id="UEB-ST-kDp" userLabel="First Responder" sceneMemberID="firstResponder"/> | |
</objects> | |
<point key="canvasLocation" x="-41" y="121"/> | |
</scene> | |
<!--Item--> | |
<scene sceneID="2Iq-t0-rlN"> | |
<objects> | |
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="6Lg-yF-lMB" sceneMemberID="viewController"> | |
<tabBarItem key="tabBarItem" title="Item" id="vKq-bx-53o"/> | |
<toolbarItems/> | |
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="N9C-Do-5iD"> | |
<rect key="frame" x="0.0" y="20" width="375" height="44"/> | |
<autoresizingMask key="autoresizingMask"/> | |
</navigationBar> | |
<nil name="viewControllers"/> | |
<connections> | |
<segue destination="aYp-Ls-x3G" kind="relationship" relationship="rootViewController" id="Ebo-G6-ePb"/> | |
</connections> | |
</navigationController> | |
<placeholder placeholderIdentifier="IBFirstResponder" id="bek-vp-oL7" userLabel="First Responder" sceneMemberID="firstResponder"/> | |
</objects> | |
<point key="canvasLocation" x="1030" y="-210"/> | |
</scene> | |
</scenes> | |
</document> |
// | |
// MyApp.swift | |
// TestProject | |
// | |
// Created by Frank J. Edwards on 9/8/18. | |
// Copyright © 2018 Frank J. Edwards. All rights reserved. | |
// | |
/* | |
* From this thread: https://forums.developer.apple.com/message/329975 | |
* | |
* Execute this code and you'll get a tab bar with two items, the first (and default) | |
* is a table view. Click on the first row to initiate a segue to a second table view. | |
* (This represents a user wanting to edit the data represented by the row.) When the | |
* user is done and clicks _Done_ in the navigation bar, the `viewWillDisappear()` | |
* method invokes a callback to pass data back to the parent. The parent should change | |
* the model and register an undo operation. | |
* | |
* You'll notice in the output that all pieces of code can see the undoManager object | |
* except the closure in the parent. (Look for "NOT found" in the output.) | |
* | |
* Why? Shouldn't the undoManager that was accessible before still be accessible? | |
* If this code is wrong, what is the right way to pass data back to the parent and | |
* have it register the undo operation? | |
*/ | |
import UIKit | |
class MyTabBarVC: UITabBarController { | |
private var _undoManager = UndoManager() | |
override var undoManager: UndoManager { return _undoManager } | |
override var canBecomeFirstResponder: Bool { return true } | |
} | |
protocol DelegateForCallback { | |
func callbackFunction(_ msg: String) | |
} | |
class MyTableViewVC: UITableViewController, DelegateForCallback { | |
override func viewDidAppear(_ animated: Bool) { | |
super.viewDidAppear(animated) | |
let typ = "MyTableViewVC" | |
if let _ = self.undoManager { | |
print("\(typ).viewDidAppear: Found in 'self'") | |
} else { | |
print("\(typ).viewDidAppear: NOT found in 'self'") | |
} | |
if let _ = view.undoManager { | |
print("\(typ).viewDidAppear: Found in 'view'") | |
} else { | |
print("\(typ).viewDidAppear: NOT found in 'view'") | |
} | |
} | |
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { | |
guard let vc = segue.destination as? MySubSubclassedTVC else { | |
fatalError() | |
} | |
vc.callbackClosure = { | |
self.callbackFunction("via closure") | |
} | |
vc.delegateForCallback = self | |
} | |
func callbackFunction(_ msg: String) -> Void { | |
let typ = "MyTableViewVC" | |
if let _ = self.undoManager { | |
print("\(typ).callbackFunction: Found in 'self' \(msg)") | |
} else { | |
print("\(typ).callbackFunction: NOT found in 'self' \(msg)") | |
} | |
if let _ = self.view.undoManager { | |
print("\(typ).callbackFunction: Found in 'self.view' \(msg)") | |
} else { | |
print("\(typ).callbackFunction: NOT found in 'self.view' \(msg)") | |
} | |
} | |
} | |
class MySubclassedTVC: MyTableViewVC { | |
override func viewDidAppear(_ animated: Bool) { | |
super.viewDidAppear(animated) | |
let typ = "MySubclassedTVC" | |
if let _ = self.undoManager { | |
print("\(typ).viewDidAppear: Found in 'self'") | |
} else { | |
print("\(typ).viewDidAppear: NOT found in 'self'") | |
} | |
if let _ = view.undoManager { | |
print("\(typ).viewDidAppear: Found in 'view'") | |
} else { | |
print("\(typ).viewDidAppear: NOT found in 'view'") | |
} | |
} | |
} | |
class MySubSubclassedTVC: MySubclassedTVC { | |
var callbackClosure: (() -> Void)? = nil | |
var delegateForCallback: DelegateForCallback? | |
override func viewDidAppear(_ animated: Bool) { | |
super.viewDidAppear(animated) | |
let typ = "MySubSubclassedTVC" | |
if let _ = self.undoManager { | |
print("\(typ).viewDidAppear: Found in 'self'") | |
} else { | |
print("\(typ).viewDidAppear: NOT found in 'self'") | |
} | |
if let _ = view.undoManager { | |
print("\(typ).viewDidAppear: Found in 'view'") | |
} else { | |
print("\(typ).viewDidAppear: NOT found in 'view'") | |
} | |
} | |
override func viewWillDisappear(_ animated: Bool) { | |
super.viewWillDisappear(animated) | |
let typ = "MySubSubclassedTVC" | |
if let _ = self.undoManager { | |
print("\(typ).viewWillDisappear: Found in 'self'") | |
} else { | |
print("\(typ).viewWillDisappear: NOT found in 'self'") | |
} | |
if let _ = view.undoManager { | |
print("\(typ).viewWillDisappear: Found in 'view'") | |
} else { | |
print("\(typ).viewWillDisappear: NOT found in 'view'") | |
} | |
callbackClosure?() | |
delegateForCallback?.callbackFunction("via delegate") | |
} | |
} |
Run the above code in an Xcode project (I'm using Xcode 9.4.1 and targeting iOS 11.4 with Swift 4.1). I'm targeting an iPhone 8 Plus, but it doesn't matter which device is used.
When the main window appears, you'll see a table with three entries and four lines of text in the debug window. The text indicates whether undoManager
has a valid value or whether it contains nil. So far, it has been non-nil in the places shown:
MyTableViewVC.viewDidAppear: Found in 'self'
MyTableViewVC.viewDidAppear: Found in 'view'
MySubclassedTVC.viewDidAppear: Found in 'self'
MySubclassedTVC.viewDidAppear: Found in 'view'
Click on the first row and the view replaces itself with a view pushed onto the nav controller's stack. The following appear in the debug window. So far, everything is good and undoManager
is found in all cases:
MyTableViewVC.viewDidAppear: Found in 'self'
MyTableViewVC.viewDidAppear: Found in 'view'
MySubclassedTVC.viewDidAppear: Found in 'self'
MySubclassedTVC.viewDidAppear: Found in 'view'
MySubSubclassedTVC.viewDidAppear: Found in 'self'
MySubSubclassedTVC.viewDidAppear: Found in 'view'
Now click the <Back button. The detail view's viewWillDisappear()
is invoking a closure provided by the parent view's prepare(0
method. This closure attempts to access the undoManager
and it fails (returns nil). See the text below:
MySubSubclassedTVC.viewWillDisappear: Found in 'self'
MySubSubclassedTVC.viewWillDisappear: Found in 'view'
MyTableViewVC.callbackFunction: NOT found in 'self' via closure
MyTableViewVC.callbackFunction: NOT found in 'self.view' via closure
MyTableViewVC.callbackFunction: NOT found in 'self' via delegate
MyTableViewVC.callbackFunction: NOT found in 'self.view' via delegate
MyTableViewVC.viewDidAppear: Found in 'self'
MyTableViewVC.viewDidAppear: Found in 'view'
MySubclassedTVC.viewDidAppear: Found in 'self'
MySubclassedTVC.viewDidAppear: Found in 'view'
I don't understand why. The documentation for UIResponder
indicates that the undoManager
property does a dynamic lookup, walking up the hierarchy of views until it finds one. So why does the closure not have a valid undoManager
property when the same class previous did have a valid one (see the MyTableViewVC.viewDidAppear()
messages)?
Obviously, the
Main-storyboard.xml
has to be renamed toMain.storyboard
, but GitHub wouldn't let me attach a file with extension of.storyboard
Images: Storyboard and Class Diagram