从子类访问 IBOutlet,插座不能连接到重复内容子类

Accessing IBOutlet from subclass, outlets cannot be connected to repeating content subclass

我在 collectionviewcell 中嵌入了一个按钮,并研究了为什么“插座无法连接到重复的内容子类”。我继续创建了一个具有 UICollectionViewCell 超类的子类。但是,我想通过 viewcontroller 的 viewdidload 设置按钮的 属性。我该怎么做?这是我的代码:

import UIKit

class Classic: UIViewController {

    override func viewDidLoad()
         {
             
        
         }
         
}
class Subclass: UICollectionViewCell{

    @IBOutlet weak var buttonOne: UIButton!
   
}

在我的图片中,左下方“collectionviewcell”中的按钮就是我要操作的按钮

您可以这样做,而不是在 viewDidLoad() 中获取按钮访问权限。在您的视图控制器中创建一个 collectionView 的 @IBOutlet class。

之后注册你自定义的 UICollectionViewCell subclass,在你的例子中是“Subclass”

之后使用 collectionView 的 dataSource 方法加载单元格并访问按钮。

class Classic: UIViewController, UICollectionViewDataSource {

   @IBOutlet var collectionView: UICollectionView!
   override func viewDidLoad()
     {
        // Register your cell
        let nib = UINib(nibName: "Subclass", bundle: nil)
        collectionView?.registerNib(nib, forCellWithReuseIdentifier: "myCell")
     }     
    
    // Collection view data source method 
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
     
    // get your cell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Subclass

      // here you will have access to your button
      cell.buttonOne.setTitle("Button Title", for: .normal)
       
     // To handle tap on button
     cell.buttonOne.tag = indexPath.row 
     cell.buttonOne.addTarget(self, action: #selector(didTapButton(sender:)), for: .touchUpInside)   

    return cell
}

    @objc func didTapButton(sender: UIButton) {
        print("button tapped at index:-", sender.tag)
    }
     
}

根据需要使用其他数据源和集合视图委托。希望这个解决方案对你有用。

您在 cellForItemAt 中设置了按钮属性(例如标题)。

旁注:使用 Subclass 作为您的 class 的名称会很混乱。

这是一个简单的例子:

class MyButtonCell: UICollectionViewCell{
    @IBOutlet weak var buttonOne: UIButton!
    
    var callback: (() -> ())?
    
    @IBAction func buttonTapped(_ sender: UIButton) {
        callback?()
    }
}

class TestCollectionViewController: UICollectionViewController {

    let buttonTitles: [String] = [
        "First", "Second", "Third", "etc..."
    ]

    override func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return buttonTitles.count
    }
    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "myCellID", for: indexPath) as! MyButtonCell
        cell.buttonOne.setTitle(buttonTitles[indexPath.item], for: [])
        cell.callback = {
            print("Button was tapped at \(indexPath)")
            // do what you want when the button is tapped
        }
        return cell
    }
}

请注意,我还在 单元格子 class 内为按钮 添加了一个 @IBAction。我还添加了这个 var / 属性:

var callback: (() -> ())?

这使得在您的控制器代码中设置 closure 变得容易 - 同样,在 cellForItemAt 中 - 允许您的控制器在单元格中的按钮被点击时处理和操作。


编辑

这是一个完整的实现:

class MyButtonCell: UICollectionViewCell{
    @IBOutlet weak var buttonOne: UIButton!
    
    var callback: (() -> ())?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        contentView.layer.borderWidth = 1
        contentView.layer.borderColor = UIColor.black.cgColor
    }
    
    @IBAction func buttonTapped(_ sender: UIButton) {
        callback?()
    }
}

class StevenViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {

    let buttonTitles: [String] = [
        "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"
    ]
    
    @IBOutlet var collectionView: UICollectionView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.delegate = self
        collectionView.dataSource = self
    }
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return buttonTitles.count
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "myCellID", for: indexPath) as! MyButtonCell

        // set the button title (and any other properties)
        cell.buttonOne.setTitle(buttonTitles[indexPath.item], for: [])

        // set the cell's callback closure
        cell.callback = {
            print("Button was tapped at \(indexPath)")
            // do what you want when the button is tapped
        }

        return cell
    }
}

这是 Storyboard 源 - 如果您以前没有这样做,请创建一个新的 Storyboard,select Open As -> Source Code,删除其中的内容,复制并粘贴以下内容...然后您可以 select Open As -> Interface Builder -Storyboard:

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="81J-PL-nYH">
    <device id="retina4_7" orientation="portrait" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="collection view cell content view" minToolsVersion="11.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--Steven View Controller-->
        <scene sceneID="UOc-kB-D4u">
            <objects>
                <viewController id="81J-PL-nYH" customClass="StevenViewController" customModule="FirstNewMini" customModuleProvider="target" sceneMemberID="viewController">
                    <view key="view" contentMode="scaleToFill" id="5pE-V8-AOY">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="eKk-rE-Fyn">
                                <rect key="frame" x="20" y="567" width="335" height="60"/>
                                <color key="backgroundColor" red="0.99942404029999998" green="0.98555368190000003" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                <constraints>
                                    <constraint firstAttribute="height" constant="60" id="t8R-1V-ELi"/>
                                </constraints>
                                <collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" automaticEstimatedItemSize="YES" minimumLineSpacing="10" minimumInteritemSpacing="10" id="34u-td-lg8">
                                    <size key="itemSize" width="60" height="60"/>
                                    <size key="headerReferenceSize" width="0.0" height="0.0"/>
                                    <size key="footerReferenceSize" width="0.0" height="0.0"/>
                                    <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
                                </collectionViewFlowLayout>
                                <cells>
                                    <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="myCellID" id="slK-WV-d6z" customClass="MyButtonCell" customModule="FirstNewMini" customModuleProvider="target">
                                        <rect key="frame" x="0.0" y="0.0" width="60" height="60"/>
                                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                        <collectionViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="inr-uV-Mkf">
                                            <rect key="frame" x="0.0" y="0.0" width="60" height="60"/>
                                            <autoresizingMask key="autoresizingMask"/>
                                            <subviews>
                                                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="jO2-43-Y97">
                                                    <rect key="frame" x="7" y="15" width="46" height="30"/>
                                                    <color key="backgroundColor" red="0.92143100499999997" green="0.92145264149999995" blue="0.92144101860000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                                    <state key="normal" title="Button"/>
                                                    <connections>
                                                        <action selector="buttonTapped:" destination="slK-WV-d6z" eventType="touchUpInside" id="ILI-g9-h3v"/>
                                                    </connections>
                                                </button>
                                            </subviews>
                                            <constraints>
                                                <constraint firstItem="jO2-43-Y97" firstAttribute="centerY" secondItem="inr-uV-Mkf" secondAttribute="centerY" id="K8s-Jl-sfY"/>
                                                <constraint firstItem="jO2-43-Y97" firstAttribute="centerX" secondItem="inr-uV-Mkf" secondAttribute="centerX" id="kF4-R0-H31"/>
                                            </constraints>
                                        </collectionViewCellContentView>
                                        <connections>
                                            <outlet property="buttonOne" destination="jO2-43-Y97" id="EkG-Dz-2DC"/>
                                        </connections>
                                    </collectionViewCell>
                                </cells>
                            </collectionView>
                        </subviews>
                        <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
                        <constraints>
                            <constraint firstItem="OxZ-Gp-ekf" firstAttribute="trailing" secondItem="eKk-rE-Fyn" secondAttribute="trailing" constant="20" id="Gua-Zi-IFQ"/>
                            <constraint firstItem="OxZ-Gp-ekf" firstAttribute="bottom" secondItem="eKk-rE-Fyn" secondAttribute="bottom" constant="40" id="I6q-K5-nN3"/>
                            <constraint firstItem="eKk-rE-Fyn" firstAttribute="leading" secondItem="OxZ-Gp-ekf" secondAttribute="leading" constant="20" id="sCP-Nn-RqH"/>
                        </constraints>
                        <viewLayoutGuide key="safeArea" id="OxZ-Gp-ekf"/>
                    </view>
                    <connections>
                        <outlet property="collectionView" destination="eKk-rE-Fyn" id="rxw-DZ-Kpi"/>
                    </connections>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="Ap0-Qs-M9q" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="199.19999999999999" y="155.17241379310346"/>
        </scene>
    </scenes>
</document>

当你 运行 时的结果应该是这样的(我给 collection 视图一个黄色背景以便于看到框架):

  • 从情节提要中添加 CollectionView outlet
  • cellForItemAtIndexPath
  • 中添加 Button Tapped 块
import UIKit

class Classic: UIViewController {

    @IBoutlet weak var collectionView : UICollectionView

    override func viewDidLoad() {
             
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.register(SubClass.self, forCellWithReuseIdentifier: "cell")
    }
         
}

extension Classic : UICollectionViewDelegate {

}

extension Classic : UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)

        cell.buttonTapped = {
            print("Button Tapped")
        }

        return cell
    }
}

class Subclass: UICollectionViewCell{

    @IBOutlet weak var buttonOne: UIButton!

    var buttonTapped : () -> () = { }

    @IBAction func buttonTapped(_ sender: UIButton) {
        self.buttonTapped()
     }
   
}