gpt4 book ai didi

ios - UICollectionView 必须在 Swift 中使用非 nil 布局参数进行初始化,即使该参数是在 UICollectionView 文件中定义的

转载 作者:行者123 更新时间:2023-11-29 05:38:29 25 4
gpt4 key购买 nike

我有一个 UIViewController 调用 UICollectionView Controller 。我编译代码时没有收到错误,但是当我运行模拟器时会弹出以下错误。

Error Description Screenshot

*** 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

我在 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)
}
})
}
}
}

下面是我初始化 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)
}
}

我无法理解我在这方面缺少什么?有人可以帮助我理解这个问题。非常感谢。

最佳答案

在 FeedVC() 的 init 中,您必须将 CollectionViewFlowLayout 添加到 init

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)
}

关于ios - UICollectionView 必须在 Swift 中使用非 nil 布局参数进行初始化,即使该参数是在 UICollectionView 文件中定义的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56795575/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com