I use SwiftUI and this is UIViewRepresentable. I did CollectionView by this way. When I try to. add NavigationView in the controller, it works, but incorrect. When I scroll, the space between the collectionView and the navigationView is freed up.
#State var data:[Int] = [1,2,3,4,5,6,7,8,9,0,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30]
var didSelectItem: ((_ indexPath: IndexPath)->()) = {_ in }
var didSelectObject: ((_ boject: Recipe)->()) = {_ in }
func makeUIView(context: Context) -> UICollectionView {
let layout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.dataSource = context.coordinator
collectionView.delegate = context.coordinator
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "myCell")
collectionView.backgroundColor = .clear
collectionView.alwaysBounceVertical = true
return collectionView
}
func updateUIView(_ uiView: UICollectionView, context: Context) {
uiView.reloadData()
}
func makeCoordinator() -> Coordinator {
return Coordinator(data: data)
}
class Coordinator: NSObject, UICollectionViewDelegate, UICollectionViewDataSource {
var data: [Int]
init(data: [Int]) {
self.data = data
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "myCell", for: indexPath)
let textLable = UILabel()
textLable.text = String(data[indexPath.row])
cell.addSubview(textLable)
cell.backgroundColor = .red
return cell
}
//something more
private func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
print("User tapped on item \(indexPath.row)")
}
}
this is code represent collection view with NavigationView:
VStack {
NavigationView {
HStack {
MenuController()
}.navigationBarTitle("Menu")
}
}
Basically, it should work good, but what did I do wrong?
You have to call .edgesIgnoringSafeArea(.top) and it will do the trick by removing extra space from your collectionView.
Below is your code looks like after the change.
struct CollectionContainer: View {
var body: some View {
VStack {
NavigationView {
HStack {
MenuController()
}.navigationBarTitle("Menu")
.edgesIgnoringSafeArea(.top) // Trick
}
}
}
}
Hope it will help you.
Related
I'm implementing a UICollectionView inside my SwiftUI View (also with SwiftUI View cells).
So I have a Hosting+Representable Combo.
Now I want to reorder my cells through drag and drop, but nothing happens.
The idea is to use a longpressGesture together with the given functions canMoveItemAt and moveItemAt.
Here is the full code:
import SwiftUI
import UIKit
struct ContentView: View {
var body: some View {
CollectionComponent()
}
}
struct CollectionComponent : UIViewRepresentable {
func makeCoordinator() -> CollectionComponent.Coordinator {
Coordinator(data: [])
}
class Coordinator: NSObject, UICollectionViewDataSource, UICollectionViewDelegate {
var data: [String] = []
init(data: [String]) {
for index in (0...20) {
self.data.append("\(index)")
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! Cell
cell.cellView.rootView = AnyView(CellView(text: data[indexPath.item]))
return cell
}
func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {
return true
}
func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
print("Changing the cell order, moving: \(sourceIndexPath.row) to \(destinationIndexPath.row)")
}
}
func makeUIView(context: Context) -> UICollectionView {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
layout.itemSize = CGSize(width: 150, height: 150)
layout.sectionInset = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.backgroundColor = .white
collectionView.dataSource = context.coordinator
collectionView.delegate = context.coordinator
collectionView.register(Cell.self, forCellWithReuseIdentifier: "cell")
let longPressGesture = UILongPressGestureRecognizer(target: self, action: Selector(("handleLongGesture:")))
collectionView.addGestureRecognizer(longPressGesture)
func handleLongGesture(gesture: UILongPressGestureRecognizer) {
switch(gesture.state) {
case UIGestureRecognizerState.began:
guard let selectedIndexPath = collectionView.indexPathForItem(at: gesture.location(in: collectionView)) else {
break
}
collectionView.beginInteractiveMovementForItem(at: selectedIndexPath)
case UIGestureRecognizerState.changed:
collectionView.updateInteractiveMovementTargetPosition(gesture.location(in: gesture.view!))
case UIGestureRecognizerState.ended:
collectionView.endInteractiveMovement()
default:
collectionView.cancelInteractiveMovement()
}
}
return collectionView
}
func updateUIView(_ uiView: UICollectionView, context: Context) {
}
}
class Cell: UICollectionViewCell {
public var cellView = UIHostingController(rootView: AnyView(EmptyView()))
public override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
private func configure() {
contentView.addSubview(cellView.view)
cellView.view.preservesSuperviewLayoutMargins = false
cellView.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
cellView.view.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor),
cellView.view.rightAnchor.constraint(equalTo: contentView.layoutMarginsGuide.rightAnchor),
cellView.view.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor),
cellView.view.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor),
])
}
}
struct CellView: View {
let text: String
var body: some View {
ZStack {
Text(text)
}
.frame(width: 150, height: 150)
.background(Color.blue)
}
}
Thanks!
Just use UICollectionViewDragDelegate and UICollectionViewDropDelegate to drag and drop cell views inside UICollectionView. It works perfectly. Here is the sample code...
struct ContentView: View {
var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var body: some View {
GeometryReader { proxy in
GridView(self.numbers, proxy: proxy) { number in
Image("image\(number)")
.resizable()
.scaledToFill()
}
}
}
}
struct GridView<CellView: View>: UIViewRepresentable {
let cellView: (Int) -> CellView
let proxy: GeometryProxy
var numbers: [Int]
init(_ numbers: [Int], proxy: GeometryProxy, #ViewBuilder cellView: #escaping (Int) -> CellView) {
self.proxy = proxy
self.cellView = cellView
self.numbers = numbers
}
func makeUIView(context: Context) -> UICollectionView {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
let collectionView = UICollectionView(frame: UIScreen.main.bounds, collectionViewLayout: layout)
collectionView.backgroundColor = .white
collectionView.register(GridCellView.self, forCellWithReuseIdentifier: "CELL")
collectionView.dragDelegate = context.coordinator //to drag cell view
collectionView.dropDelegate = context.coordinator //to drop cell view
collectionView.dragInteractionEnabled = true
collectionView.dataSource = context.coordinator
collectionView.delegate = context.coordinator
collectionView.contentInset = UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4)
return collectionView
}
func updateUIView(_ uiView: UICollectionView, context: Context) { }
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, UICollectionViewDragDelegate, UICollectionViewDropDelegate {
var parent: GridView
init(_ parent: GridView) {
self.parent = parent
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return parent.numbers.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CELL", for: indexPath) as! GridCellView
cell.backgroundColor = .clear
cell.cellView.rootView = AnyView(parent.cellView(parent.numbers[indexPath.row]).fixedSize())
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: ((parent.proxy.frame(in: .global).width - 8) / 3), height: ((parent.proxy.frame(in: .global).width - 8) / 3))
}
//Provides the initial set of items (if any) to drag.
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
let item = self.parent.numbers[indexPath.row]
let itemProvider = NSItemProvider(object: String(item) as NSString)
let dragItem = UIDragItem(itemProvider: itemProvider)
dragItem.localObject = item
return [dragItem]
}
//Tells your delegate that the position of the dragged data over the collection view changed.
func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {
if collectionView.hasActiveDrag {
return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
}
return UICollectionViewDropProposal(operation: .forbidden)
}
//Tells your delegate to incorporate the drop data into the collection view.
func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
var destinationIndexPath: IndexPath
if let indexPath = coordinator.destinationIndexPath {
destinationIndexPath = indexPath
} else {
let row = collectionView.numberOfItems(inSection: 0)
destinationIndexPath = IndexPath(item: row - 1, section: 0)
}
if coordinator.proposal.operation == .move {
self.reorderItems(coordinator: coordinator, destinationIndexPath: destinationIndexPath, collectionView: collectionView)
}
}
private func reorderItems(coordinator: UICollectionViewDropCoordinator, destinationIndexPath: IndexPath, collectionView: UICollectionView) {
if let item = coordinator.items.first, let sourceIndexPath = item.sourceIndexPath {
collectionView.performBatchUpdates({
self.parent.numbers.remove(at: sourceIndexPath.item)
self.parent.numbers.insert(item.dragItem.localObject as! Int, at: destinationIndexPath.item)
collectionView.deleteItems(at: [sourceIndexPath])
collectionView.insertItems(at: [destinationIndexPath])
}, completion: nil)
coordinator.drop(item.dragItem, toItemAt: destinationIndexPath)
}
}
}
}
class GridCellView: UICollectionViewCell {
public var cellView = UIHostingController(rootView: AnyView(EmptyView()))
public override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
private func configure() {
contentView.addSubview(cellView.view)
cellView.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
cellView.view.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 5),
cellView.view.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -5),
cellView.view.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5),
cellView.view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5),
])
cellView.view.layer.masksToBounds = true
}
}
You can see the final result here https://media.giphy.com/media/UPuWLauQepwi5Q77PA/giphy.gif
Thanks. X_X
## How to reload collection view in UIViewRepresentable ##
Working with UIViewRepresentable and collection view, got stuck when its comes to reload() collection view after iterating, how to reload collection view in UIViewRepresentable when performing iterate through data? func updateUIView doesn't do the work.
struct VideoCollectionView: UIViewRepresentable {
var data: VideoViewModel
#Binding var search: String
var dataSearch: [VideoPostModel] {
if search.isEmpty {
return data.postsSearch
}else{
let d = data.postsSearch.filter {$0.artistname.localizedStandardContains(search)}
return d
}
}
var didSelectItem: ((_ indexPath: IndexPath)->()) = {_ in }
var didSelectObject: ((_ boject: VideoPostModel)->()) = {_ in }
func makeUIView(context: Context) -> UICollectionView {
let reuseId = "AlbumPrivateCell"
let collection :UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.sectionHeadersPinToVisibleBounds = true
let collectionV = UICollectionView(frame: .zero, collectionViewLayout: layout)
layout.scrollDirection = .vertical
collectionV.translatesAutoresizingMaskIntoConstraints = false
collectionV.backgroundColor = .clear
collectionV.dataSource = context.coordinator
collectionV.delegate = context.coordinator
collectionV.register(AlbumPrivateCell.self, forCellWithReuseIdentifier: reuseId)
return collectionV
}()
return collection
}
func updateUIView(_ collectionView: UICollectionView, context: UIViewRepresentableContext<VideoCollectionView>) {
print("updateUIView updateUIView")
print(search)
collectionView.reloadData()
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
private var parent: VideoCollectionView
init(_ albumGridView: VideoCollectionView) {
self.parent = albumGridView
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.parent.dataSearch.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AlbumPrivateCell", for: indexPath) as! AlbumPrivateCell
cell.data = self.parent.dataSearch[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let post = self.parent.dataSearch[indexPath.item]
parent.didSelectItem(indexPath)
parent.didSelectObject(post)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.frame.width
let height = collectionView.frame.height/2
return CGSize(width: width, height: height)
}
}
}
Dealing with this right now, only way i was able to make a reloadData, was creating a reference inside my Coordinator class and pass it on creation:
context.coordinator.collectionView = collectionView
From there you can make the call to reloadData(), pretty sure there are more elegant ways to work this though.
EDIT: Answering Request to expand on my approach.
Let's say you have a coordinator like this:
class CoordinatorItemPicturesView: NSObject, UICollectionViewDataSource, UICollectionViewDelegate {
// MARK: - Properties
var collectionView: UICollectionView!
var elements = [String]()
init(elements:[String]) {
self.elements = elements
}
// MARK: UICollectionViewDataSource
func collectionView(_: UICollectionView, numberOfItemsInSection _: Int) -> Int {
return elements.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
return UICollectionViewCell()
}
}
So when you create your UIViewRepresentable, pass the collectionView reference:
struct ItemPicturesView: UIViewRepresentable {
func makeUIView(context: UIViewRepresentableContext<ItemPicturesView>) -> UICollectionView {
/// Set Context, cells, etc
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
collectionView.backgroundColor = .clear
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.dataSource = context.coordinator
collectionView.delegate = context.coordinator
collectionView.isScrollEnabled = true
// --- Right here
context.coordinator.collectionView = collectionView
return collectionView
}
func updateUIView(_ uiView: UICollectionView, context _: Context) {
UIView.animate(withDuration: 1.0) {
uiView.frame = uiView.frame
uiView.reloadData()
}
}
func makeCoordinator() -> CoordinatorItemPicturesView {
CoordinatorItemPicturesView(elements:["Element One", "Element Two"])
}
}
I'm sorry, if the code is not 100% perfect, had to take it from a project with NDA and didn't test with the deleted properties.
UIViewRepresentable is just a wrapper.
you have to make ur model as Observable. then it will automatically update whenever any change in data.
All you need to do is, make your data property in VideoCollectionView as:
#Binding var data: VideoViewModel
Now once you update data, you update view will reload collectionView.
Explanation:
Your data property(value type) should be a Binding property as the Coordinator depends on that. When you init Coordinator with self, you are passing the old instance of data property. Changing it to binding allow it to be mutated.
I am using collection view to load data from API. Here i want to extend size of collection view height instead of scrolling inside collection view. And also need to repeat the background image according to collection view height.
Here is the android layout and i want to develop similar to this.Tap here
import UIKit
import Nuke
import SVProgressHUD
import JSONJoy
class HomeViewController: UIViewController {
#IBOutlet weak var categoryCollection: UICollectionView!
#IBOutlet weak var tabbar: UITabBar!
var sectors:[Sector] = []
var timer = Timer()
var counter = 0
var selectedSector = ""
var selectedSectorName = ""
var webService = ApiService()
let plist = PlistHelper()
override func viewDidLoad() {
super.viewDidLoad()
self.categoryCollection.dataSource = self
self.categoryCollection.delegate = self
for item in tabbar.items ?? []{
item.image = item.image?.withRenderingMode(.alwaysOriginal)
}
UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.white], for: .normal)
UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.black], for: .selected)
listSectors()
self.categoryCollection.backgroundColor = UIColor(patternImage: UIImage(named: "bg")!)
}
override func viewWillAppear(_ animated: Bool) {
listbanners()
}
override func viewWillDisappear(_ animated: Bool) {
self.timer.invalidate()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "sectors"){
let vc = segue.destination as! SectorsViewController
vc.sectorCode = selectedSector
vc.sectorName = selectedSectorName
}
}
func listSectors(){
webService.listSectors({ (sectors, message, status) in
if(status){
if let resData = sectors.arrayObject {
do{
for data in resData{
self.sectors.append(try Sector(JSONLoader(data)))
}
DispatchQueue.main.async {
self.categoryCollection.reloadData()
}
}
catch let error {
print("JSonJoyError:\(error)")
}
}
}
})
}
}
extension HomeViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if(collectionView == bannerCollection){
return banners.count
}
else {
return sectors.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let options = ImageLoadingOptions(placeholder: UIImage(named: "bannerPlaceholder"),transition: .fadeIn(duration: 0.33))
if(collectionView == bannerCollection){
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! DataCollectionViewCell
Nuke.loadImage(with: URL(string: banners[indexPath.row].ImageUrl ?? "")!, options: options, into:cell.img)
return cell
}
else{
let cell = categoryCollection.dequeueReusableCell(withReuseIdentifier: "catCell", for: indexPath) as! catogeryCollectionViewCell
Nuke.loadImage(with: URL(string: sectors[indexPath.row].ImageUrl ?? "")!, options: options, into:cell.photoImageView)
return cell
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if(collectionView == categoryCollection){
selectedSector = sectors[indexPath.row].Code ?? "FOOD"
selectedSectorName = sectors[indexPath.row].Name ?? "FOOD"
self.performSegue(withIdentifier: "sectors", sender: self)
}
}
}
extension HomeViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if(collectionView == bannerCollection){
let size = bannerCollection.frame.size
return CGSize(width: size.width, height: size.height - 10)
}
else{
let size = categoryCollection.frame.size
print("size\(size)")
return CGSize(width: (size.width / 2) - 8, height:120)
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 30
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0.0
}
}
Following steps can help you to increase your collection View height according to data.
Create Heightconstraint outlet.
After loading data in collection view with delay of 0.2 sec in main thread,
Set Height Constraint constant = collection view content size height.
I want to ask. I make a custom menubar from collectionView and I want to link and change my menubar of collection view and the data from another collectionView while swiping. Here a picture what I'm try to make
but while try to swipe to left and right my menu bar is not following, I already make a reference to capture the value. but it's still not work. here is my code
class SegmentedView: UIView {
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
return cv
}()
let knowledge = ["Pengetahuan", "Keterampilan", "Sikap"]
var selectedMenu: CGFloat?
}
extension SegmentedView: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return knowledge.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! SegmentedCell
let item = knowledge[indexPath.item]
cell.nameLabel.text = item
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedMenu = CGFloat(indexPath.item) * frame.width
}
}
// This from my SegmentedViewController
class SegmentedViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var segmentedViews: SegmentedView!
let cellId = "assesmentCell"
let colors: [UIColor] = [UIColor.blue, UIColor.yellow, UIColor.green]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setupCollection()
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print(scrollView.contentOffset.x / 3)
segmentedViews.selectedMenu = scrollView.contentOffset.x / 3
}
func setupCollection() {
if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.scrollDirection = .horizontal
flowLayout.minimumLineSpacing = 0
}
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.isPagingEnabled = true
}
}
thank you.
Your selectedMenu property does not listen for an event with the didSet property observer. So when you set selectedMenu in scrollViewDidScroll, nothing happens.
It may be a solution:
var selectedMenu: CGFloat? {
didSet {
collectionView.selectItem(at: <#T##IndexPath?#>, animated: <#T##Bool#>, scrollPosition: <#T##UICollectionView.ScrollPosition#>)
}
}
But be careful with the collectionView(_:didSelectItemAt:) method of SegmentedView, it can bring some unwanted behaviors. You can use a delegate pattern to trigger the method in another class.
I got a HomeController of type UICollectionViewController which handles some PictureCells (contains a picture and a label).
Now I am sending one PictureCell to another ViewController to edit the label. This all works perfectly. I could send it back to the HVC using a protocol but instead of going back I want to go one step further and Display the edited PictureCell in a new ViewController.
Instead of creating a completely new one, I am subclassing the existing HomeController to reuse his CollectionView. All works fine and the view shows up but the edited PictureCell is not showing up at all, even tho I can show it's layer, the Cell itself with its content doesn't show up.
(Messy) Class-Diagram looks like this:
ClassDiagram
class HomeController: UICollectionViewController, UICollectionViewDelegateFlowLayout, MyProtocol {
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = UIColor.black
collectionView?.register(PictureCell.self, forCellWithReuseIdentifier: Constants.cellId)
}
//MARK: Get value from second VC
var valueSentFromSecondViewController: String?
var cellSentFromSecondViewController: PictureCell?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
//Acting with Protocol here
}
//HERE ARE THE COLLECTIONVIEWFUNCTIONS
}
class PictureEditViewController: UIViewController, UITextFieldDelegate {
var delegate:MyProtocol?
var myCell: PictureCell?
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.white
return cv
}()
init(pictureCellInit: PictureCell?) {
self.myCell = pictureCellInit
super.init(nibName: nil, bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
showThings()
}
#objc func showNextVC() {
let newViewController = PictureShowViewController(withCell: self.myCell)
newViewController.modalPresentationStyle = .overCurrentContext
present(newViewController, animated: true) //with dismiss
}
#objc func showThings() {
view.addSubview(collectionView)
self.collectionView.frame = CGRect(x: x, y: y, width: width, height: height)
self.setupViews()
}
func setupViews() {
//ADDING THE SUBVIEWS
}
func confBounds() {
//LAYOUT
}
#objc func handleDismiss() {
self.dismiss(animated: true, completion: nil)
}
class PictureShowViewController: HomeController {
//MARK: Variable/Constant declaration
var myCellToShow: PictureCell?
init(withCell: PictureCell?) {
let layoutUsing = UICollectionViewFlowLayout()
myCellToShow = withCell
super.init(collectionViewLayout: layoutUsing)
}
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = .white
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
collectionView?.addSubview(myCellToShow!)
self.collectionView?.reloadData()
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = myCellToShow {
cell.layer.borderColor = UIColor.red.cgColor
cell.layer.borderWidth = 2
return cell
}
else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Constants.cellId, for: indexPath) as! PictureCell
return cell
}
}
//Size of Cell
override func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let cellSize = CGFloat(view.frame.width)
return CGSize(width: cellSize, height: cellSize)
}
}
Does anyone have an idea where I went wrong?
It is a bad idea to use PictureCell to move your data around view controllers. UICollectionView reuses instances of cells so the content of the cell is bound to change anytime.
So instead of using your cell, hand over the underlying data and insert the data in to the newly dequeued collection view cell.
In the end,
var cellSentFromSecondViewController: PictureCell?
should be var pictureFromSecondViewController: YourPictureData
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Constants.cellId, for: indexPath) as! PictureCell
//Set your data
cell.picture = pictureFromSecondViewController
return cell
}