简体   繁体   English

即使在UICollectionView文件中定义了参数,也必须在Swift中使用非nil布局参数初始化UICollectionView

[英]UICollectionView must be initialized with a non-nil layout parameter in Swift, even though the parameter is defined in the UICollectionView file

I have a UIViewController calling a UICollectionView controller. 我有一个UIViewController调用UICollectionView控制器。 I do not get an error when i compile the code, however the following error pops up when i run the simulator. 编译代码时没有出现错误,但是运行模拟器时会弹出以下错误。

错误说明截图

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'UICollectionView must be initialized with a non-nil layout parameter'
*** First throw call stack:
(
    0   CoreFoundation                      0x00000001099806fb __exceptionPreprocess + 331
    1   libobjc.A.dylib                     0x0000000108ae9ac5 objc_exception_throw + 48
    2   CoreFoundation                      0x0000000109980555 +[NSException raise:format:] + 197
    3   UIKitCore                           0x000000010de713d8 -[UICollectionView initWithFrame:collectionViewLayout:] + 81
    4   UIKitCore                           0x000000010df3f090 -[UICollectionViewController _newCollectionViewWithFrame:collectionViewLayout:] + 94
    5   UIKitCore                           0x000000010df3e300 -[UICollectionViewController loadView] + 649
    6   UIKitCore                           0x000000010e000048 -[UIViewController loadViewIfRequired] + 172
    7   UIKitCore                           0x000000010df64004 -[UINavigationController _updateScrollViewFromViewController:toViewController:] + 68
    8   UIKitCore                           0x000000010df642f7 -[UINavigationController _startTransition:fromViewController:toViewController:] + 146
    9   UIKitCore                           0x000000010df653b5 -[UINavigationController _startDeferredTransitionIfNeeded:] + 896
    10  UIKitCore                           0x000000010df666a7 -[UINavigationController __viewWillLayoutSubviews] + 150
    11  UIKitCore                           0x000000010df4738d -[UILayoutContainerView layoutSubviews] + 217
    12  UIKitCore                           0x000000010ead09c1 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1417
    13  QuartzCore                          0x000000011002aeae -[CALayer layoutSublayers] + 173
    14  QuartzCore                          0x000000011002fb88 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 396
    15  QuartzCore                          0x000000011003bee4 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 72
    16  QuartzCore                          0x000000010ffab3aa _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 328
    17  QuartzCore                          0x000000010ffe2584 _ZN2CA11Transaction6commitEv + 608
    18  UIKitCore                           0x000000010e61bccb __34-[UIApplication _firstCommitBlock]_block_invoke_2 + 128
    19  CoreFoundation                      0x00000001098e7aec __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12
    20  CoreFoundation                      0x00000001098e72b0 __CFRunLoopDoBlocks + 336
    21  CoreFoundation                      0x00000001098e1b34 __CFRunLoopRun + 1252
    22  CoreFoundation                      0x00000001098e1302 CFRunLoopRunSpecific + 626
    23  GraphicsServices                    0x0000000111ff72fe GSEventRunModal + 65
    24  UIKitCore                           0x000000010e602ba2 UIApplicationMain + 140
    25  Byte                                0x0000000105cd8c2b main + 75
    26  libdyld.dylib                       0x000000010ba7a541 start + 1
    27  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

I have defined the init parameters in the UICollectionView file, the init properties contains initialization code, followed by UICollectionViewLayout and UICollectionViewDataSource parameters. 我已经在UICollectionView文件中定义了init参数,init属性包含初始化代码,然后是UICollectionViewLayout和UICollectionViewDataSource参数。

import UIKit
import Firebase
import ActiveLabel

private let reuseIdentifier = "Cell"

class FeedVC: UICollectionViewController, UICollectionViewDelegateFlowLayout, FeedCellDelegate {

    // MARK: - Properties

    var posts = [Post]()
    var viewSinglePost = false
    var post: Post?
    var currentKey: String?
    var userProfileController: UserProfileVC?
    var delegate: FeedDelegate?

    var messageNotificationView: MessageNotificationView = {
        let view = MessageNotificationView()
        return view
    }()

    // MARK: - Init

    override func viewDidLoad() {
        super.viewDidLoad()

        collectionView?.backgroundColor = .white

        // register cell classes
        self.collectionView!.register(FeedCell.self, forCellWithReuseIdentifier: reuseIdentifier)

        // configure refresh control
        let refreshControl = UIRefreshControl()
        refreshControl.addTarget(self, action: #selector(handleRefresh), for: .valueChanged)
        collectionView?.refreshControl = refreshControl

        // configure logout button
        configureNavigationBar()

        // fetch posts
        if !viewSinglePost {
            fetchPosts()
        }
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        setUnreadMessageCount()
    }

    // MARK: - UICollectionViewFlowLayout

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

        let width = view.frame.width
        var height = width + 8 + 40 + 8
        height += 50
        height += 60

        return CGSize(width: width, height: height)
    }

    // MARK: - UICollectionViewDataSource

    override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        if posts.count > 4 {
            if indexPath.item == posts.count - 1 {
                fetchPosts()
            }
        }
    }

    override func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        if viewSinglePost {
            return 1
        } else {
            return posts.count
        }
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! FeedCell

        cell.delegate = self

        if viewSinglePost {
            if let post = self.post {
                cell.post = post
            }
        } else {
            cell.post = posts[indexPath.item]
        }

        handleHashtagTapped(forCell: cell)
        handleUsernameLabelTapped(forCell: cell)
        handleMentionTapped(forCell: cell)

        return cell
    }

    // MARK: - FeedCellDelegate

    func handleUsernameTapped(for cell: FeedCell) {
        guard let post = cell.post else { return }
        let userProfileVC = UserProfileVC(collectionViewLayout: UICollectionViewFlowLayout())
        userProfileVC.user = post.user
        navigationController?.pushViewController(userProfileVC, animated: true)
    }

    func handleOptionsTapped(for cell: FeedCell) {
        guard let post = cell.post else { return }

        if post.ownerUid == Auth.auth().currentUser?.uid {
            let alertController = UIAlertController(title: "Options", message: nil, preferredStyle: .actionSheet)

            alertController.addAction(UIAlertAction(title: "Delete Post", style: .destructive, handler: { (_) in
                post.deletePost()

                if !self.viewSinglePost {
                    self.handleRefresh()
                } else {
                    if let userProfileController = self.userProfileController {
                        _ = self.navigationController?.popViewController(animated: true)
                        userProfileController.handleRefresh()
                    }
                }
            }))

            alertController.addAction(UIAlertAction(title: "Edit Post", style: .default, handler: { (_) in

                let uploadPostController = UploadPostVC()
                let navigationController = UINavigationController(rootViewController: uploadPostController)
                uploadPostController.postToEdit = post
                uploadPostController.uploadAction = UploadPostVC.UploadAction(index: 1)
                self.present(navigationController, animated: true, completion: nil)
            }))

            alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
            present(alertController, animated: true, completion: nil)
        }
    }

    func handleLikeTapped(for cell: FeedCell, isDoubleTap: Bool) {
        guard let post = cell.post else { return }

        if post.didLike {
            // handle unlike post
            if !isDoubleTap {
                post.adjustLikes(addLike: false, completion: { (likes) in
                    cell.likesLabel.text = "\(likes) likes"
                    cell.likeButton.setImage(#imageLiteral(resourceName: "LikeOrangeIcon"), for: .normal)
                })
            }
        } else {
            // handle like post
            post.adjustLikes(addLike: true, completion: { (likes) in
                cell.likesLabel.text = "\(likes) likes"
                cell.likeButton.setImage(#imageLiteral(resourceName: "LikeWhiteIcon"), for: .normal)
            })
        }
    }

    func handleShowLikes(for cell: FeedCell) {
        guard let post = cell.post else { return }
        guard let postId = post.postId else { return }

        let followLikeVC = FollowLikeVC()
        followLikeVC.viewingMode = FollowLikeVC.ViewingMode(index: 2)
        followLikeVC.postId = postId
        navigationController?.pushViewController(followLikeVC, animated: true)
    }

    func handleConfigureLikeButton(for cell: FeedCell) {
        guard let post = cell.post else { return }
        guard let postId = post.postId else { return }
        guard let currentUid = Auth.auth().currentUser?.uid else { return }

        USER_LIKES_REF.child(currentUid).observeSingleEvent(of: .value) { (snapshot) in

            // check if post id exists in user-like structure
            if snapshot.hasChild(postId) {
                post.didLike = true
                cell.likeButton.setImage(#imageLiteral(resourceName: "LikeOrangeIcon"), for: .normal)
            } else {
                post.didLike = false
                cell.likeButton.setImage(#imageLiteral(resourceName: "LikeWhiteIcon-1"), for: .normal)
            }
        }
    }

    func configureCommentIndicatorView(for cell: FeedCell) {
        guard let post = cell.post else { return }
        guard let postId = post.postId else { return }

        COMMENT_REF.child(postId).observeSingleEvent(of: .value) { (snapshot) in
            if snapshot.exists() {
                cell.addCommentIndicatorView(toStackView: cell.stackView)
            } else {
                cell.commentIndicatorView.isHidden = true
            }
        }
    }

    func handleCommentTapped(for cell: FeedCell) {
        guard let post = cell.post else { return }
        let commentVC = CommentVC(collectionViewLayout: UICollectionViewFlowLayout())
        commentVC.post = post
        navigationController?.pushViewController(commentVC, animated: true)
    }

    // MARK: - Handlers

    @objc func handleMenuToggle() {
        delegate?.handleMenuToggle(forMenuOption: nil)
    }

    @objc func handleRefresh() {
        posts.removeAll(keepingCapacity: false)
        self.currentKey = nil
        fetchPosts()
        collectionView?.reloadData()
    }

    @objc func handleShowMessages() {
        let messagesController = MessagesController()
        self.messageNotificationView.isHidden = true
        navigationController?.pushViewController(messagesController, animated: true)
    }

    func handleHashtagTapped(forCell cell: FeedCell) {
        cell.captionLabel.handleHashtagTap { (hashtag) in
            let hashtagController = HashtagController(collectionViewLayout: UICollectionViewFlowLayout())
            hashtagController.hashtag = hashtag.lowercased()
            self.navigationController?.pushViewController(hashtagController, animated: true)
        }
    }

    func handleMentionTapped(forCell cell: FeedCell) {
        cell.captionLabel.handleMentionTap { (username) in
            self.getMentionedUser(withUsername: username)
        }
    }

    func handleUsernameLabelTapped(forCell cell: FeedCell) {
        guard let user = cell.post?.user else { return }
        guard let username = user.username else { return }

        let customType = ActiveType.custom(pattern: "^\(username)\\b")

        cell.captionLabel.handleCustomTap(for: customType) { (_) in
            let userProfileController = UserProfileVC(collectionViewLayout: UICollectionViewFlowLayout())
            userProfileController.user = user
            self.navigationController?.pushViewController(userProfileController, animated: true)
        }
    }

    func configureNavigationBar() {

        // top bar to be updated with white logos

        if !viewSinglePost {
//            self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Logout", style: .plain, target: self, action: #selector(handleLogout))
//            self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "send2"), style: .plain, target: self, action: #selector(handleShowMessages))
        }

        self.navigationItem.title = "Stream"
        self.navigationController?.navigationBar.barTintColor = .darkGray
        self.navigationController?.navigationBar.barStyle = .black

        self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "list").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(handleMenuToggle))

    }

    func setUnreadMessageCount() {
        if !viewSinglePost {
            getUnreadMessageCount { (unreadMessageCount) in
                guard unreadMessageCount != 0 else { return }
                self.navigationController?.navigationBar.addSubview(self.messageNotificationView)
                self.messageNotificationView.anchor(top: self.navigationController?.navigationBar.topAnchor, left: nil, bottom: nil, right: self.navigationController?.navigationBar.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 4, width: 20, height: 20)
                self.messageNotificationView.layer.cornerRadius = 20 / 2
                self.messageNotificationView.notificationLabel.text = "\(unreadMessageCount)"
            }
        }
    }

    @objc func handleLogout() {
        let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
        alertController.addAction(UIAlertAction(title: "Log Out", style: .destructive, handler: { (_) in

            do {
                try Auth.auth().signOut()
                let loginVC = LoginVC()
                let navController = UINavigationController(rootViewController: loginVC)
                self.present(navController, animated: true, completion: nil)
            } catch {
                print("Failed to sign out")
            }
        }))

        alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
        present(alertController, animated: true, completion: nil)
    }

    // MARK: - API

    func setUserFCMToken() {
        guard let currentUid = Auth.auth().currentUser?.uid else { return }
        guard let fcmToken = Messaging.messaging().fcmToken else { return }

        let values = ["fcmToken": fcmToken]

        USER_REF.child(currentUid).updateChildValues(values)
    }

    func fetchPosts() {
        guard let currentUid = Auth.auth().currentUser?.uid else { return }

        if currentKey == nil {
            USER_FEED_REF.child(currentUid).queryLimited(toLast: 5).observeSingleEvent(of: .value, with: { (snapshot) in
                self.collectionView?.refreshControl?.endRefreshing()

                guard let first = snapshot.children.allObjects.first as? DataSnapshot else { return }
                guard let allObjects = snapshot.children.allObjects as? [DataSnapshot] else { return }

                allObjects.forEach({ (snapshot) in
                    let postId = snapshot.key
                    self.fetchPost(withPostId: postId)
                })
                self.currentKey = first.key
            })
        } else {

            USER_FEED_REF.child(currentUid).queryOrderedByKey().queryEnding(atValue: self.currentKey).queryLimited(toLast: 6).observeSingleEvent(of: .value, with: { (snapshot) in

                guard let first = snapshot.children.allObjects.first as? DataSnapshot else { return }
                guard let allObjects = snapshot.children.allObjects as? [DataSnapshot] else { return }

                allObjects.forEach({ (snapshot) in
                    let postId = snapshot.key
                    if postId != self.currentKey {
                        self.fetchPost(withPostId: postId)
                    }
                })
                self.currentKey = first.key
            })
        }
    }

    func fetchPost(withPostId postId: String) {
        Database.fetchPost(with: postId) { (post) in
            self.posts.append(post)

            self.posts.sort(by: { (post1, post2) -> Bool in
                return post1.creationDate > post2.creationDate
            })
            self.collectionView?.reloadData()
        }
    }

    func getUnreadMessageCount(withCompletion completion: @escaping(Int) -> ()) {
        guard let currentUid = Auth.auth().currentUser?.uid else { return }
        var unreadCount = 0

        USER_MESSAGES_REF.child(currentUid).observe(.childAdded) { (snapshot) in
            let uid = snapshot.key

            USER_MESSAGES_REF.child(currentUid).child(uid).observe(.childAdded, with: { (snapshot) in
                let messageId = snapshot.key

                MESSAGES_REF.child(messageId).observeSingleEvent(of: .value) { (snapshot) in
                    guard let dictionary = snapshot.value as? Dictionary<String, AnyObject> else { return }

                    let message = Message(dictionary: dictionary)

                    if message.fromId != currentUid {
                        if !message.read  {
                            unreadCount += 1
                        }
                    }
                    completion(unreadCount)
                }
            })
        }
    }
}

Below is the rootViewController from where i am initializing the UICollection file - 下面是我正在初始化UICollection文件的rootViewController-

import UIKit
import Firebase

class ContainerController: UIViewController {

    // MARK: - Properties

    let dot = UIView()
    var isInitialLoad: Bool?

    var menuController: MenuController!
    var centerController: UIViewController!
    var isExpanded = false

    // MARK: - Init

    override func viewDidLoad() {
        super.viewDidLoad()
        configureHomeController()
    }

    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }

    override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
        return .slide
    }

    override var prefersStatusBarHidden: Bool {
        return isExpanded
    }

    // MARK: - Handlers

    func configureHomeController() {
        let homeController = FeedVC()
        homeController.delegate = self
        centerController = UINavigationController(rootViewController: FeedVC())

        view.addSubview(centerController.view)
        addChild(centerController)
        centerController.didMove(toParent: self)
    }

    func configureMenuController() {
        if menuController == nil {
            menuController = MenuController()
            menuController.delegate = self
            view.insertSubview(menuController.view, at: 0)
            addChild(menuController)
            menuController.didMove(toParent: self)
        }
    }

    func animatePanel(shouldExpand: Bool, menuOption: MenuOption?) {

        if shouldExpand {
            // show menu
            UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .curveEaseInOut, animations: {
                self.centerController.view.frame.origin.x = self.centerController.view.frame.width - 80
            }, completion: nil)

        } else {
            // hide menu

            UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .curveEaseInOut, animations: {
                self.centerController.view.frame.origin.x = 0
            }) { (_) in
                guard let menuOption = menuOption else { return }
                self.didSelectMenuOption(menuOption: menuOption)
            }
        }

        animateStatusBar()
    }

    func didSelectMenuOption(menuOption: MenuOption) {
        switch menuOption {
        case .Profile:
            print("Show profile")
            let controller = UserProfileVC()
            present(UINavigationController(rootViewController: controller), animated: true, completion: nil)
        case .Inbox:
            print("Show Inbox")
            let controller = MessagesController()
            present(UINavigationController(rootViewController: controller), animated: true, completion: nil)
        case .Notifications:
            print("Show Notifications")
            let controller = NotificationsVC()
            present(UINavigationController(rootViewController: controller), animated: true, completion: nil)
        case .Settings:
            print("Show settings")
            let controller = UserProfileVC()
            present(UINavigationController(rootViewController: controller), animated: true, completion: nil)
        }
    }

    func animateStatusBar() {
        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .curveEaseInOut, animations: {
            self.setNeedsStatusBarAppearanceUpdate()
        }, completion: nil)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        if isInitialLoad != nil {
            self.followeUserAfterSignUp(uid: "Rdqzc9WWv3OoQkzYrWgFVTuwwUu2")
        }
    }

    // MARK: - API

    func followeUserAfterSignUp(uid: String) {
        if let isInitialLoad = isInitialLoad, isInitialLoad == true {
            Database.fetchUser(with: uid) { (user) in
                user.follow()
//                self.selectedIndex = 1
            }
        }
    }

    func checkIfUserIsLoggedIn() {
        if Auth.auth().currentUser == nil {
            DispatchQueue.main.async {
                let loginVC = LoginVC()
                let navController = UINavigationController(rootViewController: loginVC)
                self.present(navController, animated: true, completion: nil)
            }
            return
        }
    }

    func observeNotifications() {
        guard let currentUid = Auth.auth().currentUser?.uid else { return }

        NOTIFICATIONS_REF.child(currentUid).observeSingleEvent(of: .value) { (snapshot) in
            guard let allObjects = snapshot.children.allObjects as? [DataSnapshot] else { return }

            allObjects.forEach({ (snapshot) in
                let notificationId = snapshot.key

                NOTIFICATIONS_REF.child(currentUid).child(notificationId).child("checked").observeSingleEvent(of: .value, with: { (snapshot) in
                    guard let checked = snapshot.value as? Int else { return }

                    if checked == 0 {
                        self.dot.isHidden = false
                    } else {
                        self.dot.isHidden = true
                    }
                })
            })
        }
    }


}

extension ContainerController: FeedDelegate {
    func handleMenuToggle(forMenuOption menuOption: MenuOption?) {
        if !isExpanded {
            configureMenuController()
        }

        isExpanded = !isExpanded
        animatePanel(shouldExpand: isExpanded, menuOption: menuOption)
    }
}

I am unable to understand what i am missing in this? 我无法理解我在其中缺少什么? Would someone be able to help me understand the problem. 有人能够帮助我理解问题。 Many thanks. 非常感谢。

On the init of FeedVC() you gotta add a CollectionViewFlowLayout to the init 在FeedVC()的初始化中,您必须将CollectionViewFlowLayout添加到初始化中

func configureHomeController() {
        let layout = UICollectionViewFlowLayout()
        let homeController = FeedVC(frame: .zero, collectionViewLayout: layout)
        homeController.delegate = self
        centerController = UINavigationController(rootViewController: FeedVC())

        view.addSubview(centerController.view)
        addChild(centerController)
        centerController.didMove(toParent: self)
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 &#39;UICollectionView必须使用非nil布局参数初始化&#39;-Swift 4 - 'UICollectionView must be initialized with a non-nil layout parameter' - Swift 4 UiCollectionView必须使用非nil布局参数初始化Swift 3 - UiCollectionView must be initialized with a non-nil layout parameter Swift 3 必须使用非nil布局参数初始化UICollectionView。 迅速与UICollectionView错误 - UICollectionView must be initialized with a non-nil layout parameter. error in swift with UICollectionView “ UICollectionView必须使用非null布局参数初始化” - 'UICollectionView must be initialized with a non-nil layout parameter' 错误:UICollectionView必须使用非nil布局参数初始化 - Error: UICollectionView must be initialized with a non-nil layout parameter UICollectionView:必须使用非 nil 布局参数初始化 - UICollectionView: must be initialized with a non-nil layout parameter “必须使用非零布局参数初始化 UICollectionView”错误 - 'UICollectionView must be initialized with a non-nil layout parameter' bug 由于未捕获的异常&#39;NSInvalidArgumentException&#39;而终止应用程序,原因:&#39;UICollectionView必须使用非nil布局参数初始化 - Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'UICollectionView must be initialized with a non-nil layout parameter 使用JSQMessagesCollectionView时,“ UICollectionView必须使用非nil布局参数初始化” - 'UICollectionView must be initialized with a non-nil layout parameter' while using JSQMessagesCollectionView Swift 3.0 中的 UICollectionViewController 错误:必须使用非 nil 布局参数进行初始化 - UICollectionViewController Error in Swift 3.0: must be initialized with a non-nil layout parameter
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM