mirror of
https://github.com/Instagram/IGListKit
synced 2026-05-23 01:08:27 +00:00
Refactors/macos example
Summary: Issue fixed: #381 - [X] All tests pass. Demo project builds and runs. - [ ] I added tests, an experiment, or detailed why my change isn't tested. - [X] I added an entry to the `CHANGELOG.md` for any breaking changes, enhancements, or bug fixes. - [X] I have reviewed the [contributing guide](https://github.com/Instagram/IGListKit/blob/master/.github/CONTRIBUTING.md) Closes https://github.com/Instagram/IGListKit/pull/915 Differential Revision: D5758006 Pulled By: rnystrom fbshipit-source-id: cbc3f19b6bb9604d8a6c8c15a16414e558e3b70c
This commit is contained in:
parent
65a5e6de05
commit
bb983cf918
5 changed files with 217 additions and 119 deletions
|
|
@ -7,6 +7,8 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
7BF95C4D1F52732200F14EFE /* UserCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7BF95C4C1F52732200F14EFE /* UserCollectionViewCell.xib */; };
|
||||
7BF95C4F1F5273A100F14EFE /* UserCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BF95C4E1F5273A100F14EFE /* UserCollectionViewCell.swift */; };
|
||||
888609091DEF38A00019A4A5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 888609081DEF38A00019A4A5 /* AppDelegate.swift */; };
|
||||
8886090B1DEF38A00019A4A5 /* UsersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8886090A1DEF38A00019A4A5 /* UsersViewController.swift */; };
|
||||
8886090D1DEF38A00019A4A5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8886090C1DEF38A00019A4A5 /* Assets.xcassets */; };
|
||||
|
|
@ -22,6 +24,8 @@
|
|||
/* Begin PBXFileReference section */
|
||||
1CAC2903BAE9D41694D58A7B /* Pods-IGListKitExamples.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-IGListKitExamples.release.xcconfig"; path = "Pods/Target Support Files/Pods-IGListKitExamples/Pods-IGListKitExamples.release.xcconfig"; sourceTree = "<group>"; };
|
||||
63F1F74ED983018C5D607DDC /* Pods_IGListKitExamples.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_IGListKitExamples.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7BF95C4C1F52732200F14EFE /* UserCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = UserCollectionViewCell.xib; sourceTree = "<group>"; };
|
||||
7BF95C4E1F5273A100F14EFE /* UserCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
888609051DEF38A00019A4A5 /* IGListKitExamples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IGListKitExamples.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
888609081DEF38A00019A4A5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
8886090A1DEF38A00019A4A5 /* UsersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsersViewController.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -48,6 +52,15 @@
|
|||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
7BF95C4B1F5272FA00F14EFE /* View */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7BF95C4C1F52732200F14EFE /* UserCollectionViewCell.xib */,
|
||||
7BF95C4E1F5273A100F14EFE /* UserCollectionViewCell.swift */,
|
||||
);
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
888608FC1DEF38A00019A4A5 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -73,6 +86,7 @@
|
|||
DDE3D8551E0311AF00F96BE4 /* Helpers */,
|
||||
DDE3D84D1E030A8000F96BE4 /* Models */,
|
||||
DDE3D84F1E030A9200F96BE4 /* Resources */,
|
||||
7BF95C4B1F5272FA00F14EFE /* View */,
|
||||
DDE3D84E1E030A8400F96BE4 /* ViewControllers */,
|
||||
);
|
||||
path = IGListKitExamples;
|
||||
|
|
@ -204,6 +218,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DDE3D8541E03117600F96BE4 /* users.json in Resources */,
|
||||
7BF95C4D1F52732200F14EFE /* UserCollectionViewCell.xib in Resources */,
|
||||
8886090D1DEF38A00019A4A5 /* Assets.xcassets in Resources */,
|
||||
888609101DEF38A00019A4A5 /* Main.storyboard in Resources */,
|
||||
);
|
||||
|
|
@ -289,6 +304,7 @@
|
|||
DD9018681E0319E40003789D /* IndexSet+Extensions.swift in Sources */,
|
||||
888609091DEF38A00019A4A5 /* AppDelegate.swift in Sources */,
|
||||
DDE3D8511E030AFA00F96BE4 /* User.swift in Sources */,
|
||||
7BF95C4F1F5273A100F14EFE /* UserCollectionViewCell.swift in Sources */,
|
||||
DDE3D8571E0311D000F96BE4 /* UsersProvider.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
@ -407,6 +423,7 @@
|
|||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = IGListKitExamples/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.instagram.IGListKitExamples;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
|
|
@ -421,6 +438,7 @@
|
|||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = IGListKitExamples/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.instagram.IGListKitExamples;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="12120" systemVersion="16E195" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12120"/>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12121"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
|
|
@ -717,7 +718,7 @@ CA
|
|||
</toolbar>
|
||||
</window>
|
||||
<connections>
|
||||
<segue destination="XfG-lQ-9wD" kind="relationship" relationship="window.shadowedContentViewController" id="cq2-FE-JQM"/>
|
||||
<segue destination="YHW-Gd-SIr" kind="relationship" relationship="window.shadowedContentViewController" id="JN0-GW-AMI"/>
|
||||
</connections>
|
||||
</windowController>
|
||||
<customObject id="Oky-zY-oP4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
|
|
@ -725,96 +726,58 @@ CA
|
|||
<point key="canvasLocation" x="75" y="250"/>
|
||||
</scene>
|
||||
<!--Users View Controller-->
|
||||
<scene sceneID="hIz-AP-VOD">
|
||||
<scene sceneID="Tg7-tm-pfb">
|
||||
<objects>
|
||||
<viewController id="XfG-lQ-9wD" customClass="UsersViewController" customModule="IGListKitExamples" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" wantsLayer="YES" id="m2S-Jp-Qdl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="400" height="500"/>
|
||||
<viewController id="YHW-Gd-SIr" customClass="UsersViewController" customModule="IGListKitExamples" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" wantsLayer="YES" id="gY5-sL-9mW">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="50" horizontalPageScroll="10" verticalLineScroll="50" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UBz-hI-v7L">
|
||||
<rect key="frame" x="0.0" y="0.0" width="400" height="500"/>
|
||||
<clipView key="contentView" id="CKR-qX-FYp">
|
||||
<rect key="frame" x="0.0" y="0.0" width="400" height="500"/>
|
||||
<scrollView autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="l6p-IL-yB1">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
|
||||
<clipView key="contentView" drawsBackground="NO" id="Oeu-Bh-ao3">
|
||||
<rect key="frame" x="1" y="1" width="478" height="268"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" alternatingRowBackgroundColors="YES" columnReordering="NO" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="48" viewBased="YES" id="cZM-LD-xWe">
|
||||
<rect key="frame" x="0.0" y="0.0" width="400" height="500"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<size key="intercellSpacing" width="8" height="2"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableViewGridLines key="gridStyleMask" horizontal="YES"/>
|
||||
<color key="gridColor" red="0.97900184037837579" green="0.97900184037837579" blue="0.97900184037837579" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<tableColumns>
|
||||
<tableColumn width="392" minWidth="40" maxWidth="1000" id="4hL-ap-md6">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="zPV-DN-rja">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView identifier="cell" id="RNW-L9-uKs">
|
||||
<rect key="frame" x="4" y="1" width="392" height="48"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField identifier="cell" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="G7m-E1-erw">
|
||||
<rect key="frame" x="0.0" y="16" width="392" height="17"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="5li-Kx-w3N">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="G7m-E1-erw" firstAttribute="leading" secondItem="RNW-L9-uKs" secondAttribute="leading" constant="2" id="2Ty-cM-jDM"/>
|
||||
<constraint firstItem="G7m-E1-erw" firstAttribute="centerY" secondItem="RNW-L9-uKs" secondAttribute="centerY" id="OvZ-D7-X77"/>
|
||||
<constraint firstItem="G7m-E1-erw" firstAttribute="centerX" secondItem="RNW-L9-uKs" secondAttribute="centerX" id="dbG-bh-8jt"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="textField" destination="G7m-E1-erw" id="wk2-cP-5N8"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
</tableColumn>
|
||||
</tableColumns>
|
||||
<collectionView selectable="YES" id="hJf-qq-AxL">
|
||||
<rect key="frame" x="0.0" y="0.0" width="478" height="268"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" id="gXy-w0-ISW">
|
||||
<size key="itemSize" width="50" height="50"/>
|
||||
</collectionViewFlowLayout>
|
||||
<color key="primaryBackgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="secondaryBackgroundColor" name="controlAlternatingRowColor" catalog="System" colorSpace="catalog"/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="XfG-lQ-9wD" id="EAO-x6-iol"/>
|
||||
<outlet property="delegate" destination="XfG-lQ-9wD" id="vDv-jQ-1x9"/>
|
||||
<outlet property="dataSource" destination="YHW-Gd-SIr" id="gbt-g6-ivY"/>
|
||||
<outlet property="delegate" destination="YHW-Gd-SIr" id="jWy-xa-UZS"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</collectionView>
|
||||
</subviews>
|
||||
</clipView>
|
||||
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="Etj-vY-BHi">
|
||||
<rect key="frame" x="0.0" y="254" width="400" height="16"/>
|
||||
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="Xsj-La-QKq">
|
||||
<rect key="frame" x="1" y="144" width="233" height="15"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="WKs-xk-GPd">
|
||||
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="91K-tT-lEx">
|
||||
<rect key="frame" x="234" y="1" width="15" height="143"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="UBz-hI-v7L" secondAttribute="trailing" id="SuS-t1-8hF"/>
|
||||
<constraint firstItem="UBz-hI-v7L" firstAttribute="top" secondItem="m2S-Jp-Qdl" secondAttribute="top" id="Suz-Mg-sXx"/>
|
||||
<constraint firstAttribute="bottom" secondItem="UBz-hI-v7L" secondAttribute="bottom" id="dBT-so-LaR"/>
|
||||
<constraint firstItem="UBz-hI-v7L" firstAttribute="leading" secondItem="m2S-Jp-Qdl" secondAttribute="leading" id="lt2-xn-gAQ"/>
|
||||
<constraint firstItem="l6p-IL-yB1" firstAttribute="top" secondItem="gY5-sL-9mW" secondAttribute="top" id="81q-nn-rn1"/>
|
||||
<constraint firstAttribute="trailing" secondItem="l6p-IL-yB1" secondAttribute="trailing" id="MKh-Ym-9LE"/>
|
||||
<constraint firstItem="l6p-IL-yB1" firstAttribute="leading" secondItem="gY5-sL-9mW" secondAttribute="leading" id="ZHB-t7-kZq"/>
|
||||
<constraint firstAttribute="bottom" secondItem="l6p-IL-yB1" secondAttribute="bottom" id="oar-xD-J5C"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="tableView" destination="cZM-LD-xWe" id="fsK-gP-bU1"/>
|
||||
<outlet property="collectionView" destination="hJf-qq-AxL" id="ZdG-57-x5d"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
<customObject id="zd7-Dq-nvB" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="75" y="923"/>
|
||||
<point key="canvasLocation" x="75" y="806"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// UserCollectionViewCell.swift
|
||||
// IGListKitExamples
|
||||
//
|
||||
// Created by Weyert de Boer on 27/08/2017.
|
||||
// Copyright © 2017 Instagram. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
protocol UserCollectionViewCellDelegate: class {
|
||||
|
||||
func itemDeleted(_ user: User)
|
||||
}
|
||||
|
||||
final class UserCollectionViewCell: NSCollectionViewItem {
|
||||
|
||||
weak var delegate: UserCollectionViewCellDelegate?
|
||||
|
||||
@IBAction func deleteButtonClicked(_ sender: AnyObject) {
|
||||
guard let user = representedObject as? User else { return }
|
||||
delegate?.itemDeleted(user)
|
||||
}
|
||||
|
||||
func bindViewModel(_ user: User) {
|
||||
representedObject = user
|
||||
textField?.stringValue = user.name
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12121"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner"/>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<collectionViewItem id="xP0-v7-EFE" customClass="UserCollectionViewCell" customModule="IGListKitExamples" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="textField" destination="Ypj-SS-m9s" id="BMD-zR-IB8"/>
|
||||
<outlet property="view" destination="NIs-9L-IO1" id="eUJ-SQ-WOE"/>
|
||||
</connections>
|
||||
</collectionViewItem>
|
||||
<customView id="NIs-9L-IO1">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="47"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="zDT-HW-k24" userLabel="Content View">
|
||||
<rect key="frame" x="5" y="5" width="470" height="37"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ypj-SS-m9s">
|
||||
<rect key="frame" x="8" y="10" width="454" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="User Name" id="EMD-h2-FhR">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="OMr-G1-CjN">
|
||||
<rect key="frame" x="381" y="1" width="80" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Delete" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="FAm-Ie-kke">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="deleteButtonClicked:" target="xP0-v7-EFE" id="qoH-AN-ZwB"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="Ypj-SS-m9s" firstAttribute="leading" secondItem="zDT-HW-k24" secondAttribute="leading" constant="10" id="Ihp-O8-Efo"/>
|
||||
<constraint firstAttribute="trailing" secondItem="OMr-G1-CjN" secondAttribute="trailing" constant="15" id="KAP-rQ-1IY"/>
|
||||
<constraint firstItem="OMr-G1-CjN" firstAttribute="centerY" secondItem="zDT-HW-k24" secondAttribute="centerY" id="OpF-yx-eVP"/>
|
||||
<constraint firstItem="Ypj-SS-m9s" firstAttribute="top" secondItem="zDT-HW-k24" secondAttribute="top" constant="10" id="fgx-2W-GAC"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Ypj-SS-m9s" secondAttribute="bottom" constant="10" id="l0W-g6-GHk"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Ypj-SS-m9s" secondAttribute="trailing" constant="10" id="ngw-Cc-XUB"/>
|
||||
</constraints>
|
||||
</customView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="zDT-HW-k24" firstAttribute="leading" secondItem="NIs-9L-IO1" secondAttribute="leading" constant="5" id="Osd-B8-bXz"/>
|
||||
<constraint firstItem="zDT-HW-k24" firstAttribute="top" secondItem="NIs-9L-IO1" secondAttribute="top" constant="5" id="UWq-nv-EsM"/>
|
||||
<constraint firstAttribute="bottom" secondItem="zDT-HW-k24" secondAttribute="bottom" constant="5" id="rt6-4r-5RK"/>
|
||||
<constraint firstAttribute="trailing" secondItem="zDT-HW-k24" secondAttribute="trailing" constant="5" id="tQO-4a-4WN"/>
|
||||
</constraints>
|
||||
</customView>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="NO" id="MA2-Td-T4f">
|
||||
<rect key="frame" x="0.0" y="0.0" width="38" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Label" id="Mhd-Im-KQa">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</objects>
|
||||
</document>
|
||||
|
|
@ -17,7 +17,7 @@ import IGListKit
|
|||
|
||||
final class UsersViewController: NSViewController {
|
||||
|
||||
@IBOutlet weak var tableView: NSTableView!
|
||||
@IBOutlet weak var collectionView: NSCollectionView!
|
||||
|
||||
// MARK: Data
|
||||
|
||||
|
|
@ -51,22 +51,40 @@ final class UsersViewController: NSViewController {
|
|||
// MARK: -
|
||||
// MARK: Diffing
|
||||
|
||||
var isFirstRun = true
|
||||
var filteredUsers = [User]() {
|
||||
didSet {
|
||||
// get the difference between the old array of Users and the new array of Users
|
||||
let diff = ListDiff(oldArray: oldValue, newArray: filteredUsers, option: .equality)
|
||||
|
||||
// this difference is used here to update the table view, but it can be used
|
||||
// to update collection views and other similar interface elements
|
||||
// this code can also be added to an extension of NSTableView ;)
|
||||
tableView.beginUpdates()
|
||||
tableView.insertRows(at: diff.inserts, withAnimation: .slideDown)
|
||||
tableView.removeRows(at: diff.deletes, withAnimation: .slideUp)
|
||||
tableView.reloadData(forRowIndexes: diff.updates, columnIndexes: .zero)
|
||||
diff.moves.forEach { move in
|
||||
self.tableView.moveRow(at: move.from, to: move.to)
|
||||
// A crash occurs when you try to use performBatchUpdates the first time
|
||||
guard !isFirstRun else {
|
||||
collectionView.reloadData()
|
||||
isFirstRun = false
|
||||
return
|
||||
}
|
||||
tableView.endUpdates()
|
||||
|
||||
// get the difference between the old array of Users and the new array of Users
|
||||
let diff = ListDiffPaths(fromSection: 0, toSection: 0, oldArray: oldValue, newArray: filteredUsers, option: .equality)
|
||||
let batchUpdates = diff.forBatchUpdates()
|
||||
let inserts = Set(batchUpdates.inserts)
|
||||
let deletes = Set(batchUpdates.deletes)
|
||||
let updates = Set(batchUpdates.updates)
|
||||
let moves = Set(batchUpdates.moves)
|
||||
|
||||
// this difference is used here to update the collection view, but it can be used
|
||||
// to update collection views and other similar interface elements
|
||||
// this code can also be added to an extension of NSCollectionView ;)
|
||||
|
||||
// Set the animation duration when updating the collection view
|
||||
NSAnimationContext.current().duration = 0.25
|
||||
|
||||
// Perform the updates to the collection view
|
||||
collectionView.animator().performBatchUpdates({
|
||||
collectionView.deleteItems(at: deletes)
|
||||
collectionView.insertItems(at: inserts)
|
||||
collectionView.reloadItems(at: updates)
|
||||
moves.forEach { move in
|
||||
collectionView.moveItem(at: move.from, to: move.to)
|
||||
}
|
||||
}, completionHandler: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -87,6 +105,9 @@ final class UsersViewController: NSViewController {
|
|||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// The view needs to be backed by a CALayer to be able to enable the collections view animations you can
|
||||
// enable this by selecting the view controller's view in the Interface Builder in the Core Animation section
|
||||
// of the View Effects inspector tab, through code you can do by view.wantsLayer = true
|
||||
loadSampleUsers()
|
||||
}
|
||||
|
||||
|
|
@ -103,48 +124,44 @@ final class UsersViewController: NSViewController {
|
|||
@IBAction func search(_ sender: NSSearchField) {
|
||||
searchTerm = sender.stringValue
|
||||
}
|
||||
|
||||
@IBAction func delete(_ sender: Any?) {
|
||||
guard !tableView.selectedRowIndexes.isEmpty else { return }
|
||||
|
||||
tableView.selectedRowIndexes.forEach({ self.delete(user: self.filteredUsers[$0]) })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension UsersViewController: NSTableViewDataSource {
|
||||
|
||||
func numberOfRows(in tableView: NSTableView) -> Int {
|
||||
return filteredUsers.count
|
||||
extension UsersViewController: UserCollectionViewCellDelegate {
|
||||
|
||||
func itemDeleted(_ user: User) {
|
||||
self.delete(user: user)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension UsersViewController: NSTableViewDelegate {
|
||||
extension UsersViewController: NSCollectionViewDelegate {
|
||||
}
|
||||
|
||||
extension UsersViewController: NSCollectionViewDataSource {
|
||||
|
||||
private struct Storyboard {
|
||||
static let cellIdentifier = "cell"
|
||||
static let cellIdentifier = "UserCollectionViewCell"
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||
guard let cell = tableView.make(withIdentifier: Storyboard.cellIdentifier, owner: tableView) as? NSTableCellView else {
|
||||
return nil
|
||||
}
|
||||
|
||||
cell.textField?.stringValue = filteredUsers[row].name
|
||||
|
||||
|
||||
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return self.filteredUsers.count
|
||||
}
|
||||
|
||||
@available(OSX 10.11, *)
|
||||
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
|
||||
let item = collectionView.makeItem(withIdentifier: Storyboard.cellIdentifier, for: indexPath)
|
||||
guard let cell = item as? UserCollectionViewCell else { return item }
|
||||
|
||||
cell.delegate = self
|
||||
cell.bindViewModel(filteredUsers[indexPath.item])
|
||||
return cell
|
||||
}
|
||||
|
||||
@available(OSX 10.11, *)
|
||||
func tableView(_ tableView: NSTableView, rowActionsForRow row: Int, edge: NSTableRowActionEdge) -> [NSTableViewRowAction] {
|
||||
let delete = NSTableViewRowAction(style: .destructive, title: "Delete") { _, row in
|
||||
guard row < self.filteredUsers.count else { return }
|
||||
|
||||
self.delete(user: self.filteredUsers[row])
|
||||
}
|
||||
|
||||
return [delete]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension UsersViewController: NSCollectionViewDelegateFlowLayout {
|
||||
|
||||
func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize {
|
||||
|
||||
let availableWidth = collectionView.bounds.width
|
||||
return CGSize(width: availableWidth, height: 44)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue