I want to load HTML String in UICollectionview. Each collectionviewcell has WKWebview. When we try load more uicollectionviewcell apps get crashed without any error displayed. I further looking in memory tab and in memory page other process memory get growing every wkwebview loadhtmlstring is fired. How to remove WKWebview memory in swift4... Or other than WKWebview is there any control to show and edit HTML content in iOS? Please help me on this. Thanks in advance..
Note:
I already tried this method suggested in stack overflow
WKWebView causes my view controller to leak
Please give any other suggestion on this.
You can cache the response of the HTML page and after the cell gets disappear or not in view you can remove those cells from view and again when you view that cell you can pick cell info from cache, so that you can save memory.d
I got the same work for do. you can use this below mentioned code.
First you just need to "import WebKit" framework.
class SpecificLessonDetailCVC: UICollectionViewCell {
//MARK:- IBOUTLET & PROPERTIES
#IBOutlet weak var containerView: UIView!
let webView = WKWebView()
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collVwLesson.dequeueReusableCell(withReuseIdentifier: "SpecificLessonDetailCVC", for: indexPath) as? SpecificLessonDetailCVC else { return UICollectionViewCell()}
cell.webView.scrollView.showsHorizontalScrollIndicator = false
cell.webView.scrollView.showsVerticalScrollIndicator = false
cell.webView.scrollView.indicatorStyle = .white
cell.webView.scrollView.delegate = self
cell.webView.navigationDelegate = self
cell.webView.loadHTMLStringWithMagic(content: objLessonDetailsVM.responseModel?.data.data[indexPath.row].dataDescription ?? "", baseURL: nil)
cell.webView.frame = CGRect(x: 0, y: 0, width: cell.vwLesson.frame.width, height: cell.vwLesson.frame.height)
cell.vwLesson.addSubview(cell.webView)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width, height: collectionView.frame.height)
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if let cell = cell as? SpecificLessonDetailCVC {
cell.webView.becomeFirstResponder()
}
}
Related
I'm new to swift and building iOS Application from the scratch (using swift 4) and want to do something like below.
1. Implement Multiple cell selections in UICollectionView,
2. Pass selected cells data to Server.
Please anyone can help me, how to do that? Tell me the process and supporting articles to do that.
Below is reference Image. Thanks in Advance.
Well, the best way to handle multiple selections in UICollectionView
Enable Multiple Selection
myCollectionView.allowsMultipleSelection = true
put this code in your cell awakeFromNib
override func awakeFromNib() {
super.awakeFromNib()
let view = UIView(frame: bounds)
self.backgroundView = view
let coloredView = UIView(frame: bounds)
coloredView.backgroundColor = UIColor.red
self.selectedBackgroundView = coloredView
}
you can get the selected indexPath items
let items = myCollectionView.indexPathsForSelectedItems
This basic example. You can change as per your data.
When you select any cell then you need to check that selected cell is already selected before or not.
If not then add selected cell indexPath in indexArray and selected cell value in valueArray.
If current selected cell is previously selected then remove indexPath from indexArray and also remove selected cell value from valueArray
on continue button press pass arrSelectedData to server or next screen.
Define below 3 array.
var arrData = [String]() // This is your data array
var arrSelectedIndex = [IndexPath]() // This is selected cell Index array
var arrSelectedData = [String]() // This is selected cell data array
//UICollectionView Delegate & DataSource
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.arrData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell : CollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
if arrSelectedIndex.contains(indexPath) { // You need to check wether selected index array contain current index if yes then change the color
cell.vw.backgroundColor = UIColor.red
}
else {
cell.vw.backgroundColor = UIColor.lightGray
}
cell.layoutSubviews()
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 100, height: 100)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.item)!")
let strData = arrData[indexPath.item]
if arrSelectedIndex.contains(indexPath) {
arrSelectedIndex = arrSelectedIndex.filter { $0 != indexPath}
arrSelectedData = arrSelectedData.filter { $0 != strData}
}
else {
arrSelectedIndex.append(indexPath)
arrSelectedData.append(strData)
}
collectionView.reloadData()
}
}
You can write the code like this to Enable Multiple Selection :-
yourCollectionViewName.allowsMultipleSelection = true
then you can Do it like this to see the cell Selected -
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
var cell = collectionView.cellForItemAtIndexPath(indexPath)
if cell?.selected == true {
cell?.backgroundColor = UIColor.orangeColor()
}
}
To Deselect You can do something Like this -
func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
var cell = collectionView.cellForItemAtIndexPath(indexPath)
cell?.backgroundColor = UIColor.clearColor()
}
Enable Multiple Selection
collectionView.allowsMultipleSelection = true
Overrider isSelected property of collectionViewCell.
override var isSelected: Bool {
didSet {
if self.isSelected {
//You can change this method according to your need.
setSelected()
}
else {
//You can change this method according to your need.
setUnselected()
}
}
}
func setSelected(){
bgView.layer.borderWidth = 4
bgView.layer.borderColor = UIColor.Palette.darkBlue.cgColor
bgView.backgroundColor = .blue.withAlphaComponent(0.2)
}
func setUnselected(){
bgView.layer.borderWidth = 0
bgView.backgroundColor = .white
}
You can print selected cell's indexPath
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(collectionView.indexPathsForSelectedItems)
}
Well, to achieve a thing like that, you need to mainly perform the following tasks
Whenever user clicks on a particular cell, you need to change the background colour for that item in the didSelectItemAt delegate method of UICollectionView
Now to send that data to server, you need an array to store all the selected cells and then send that array to server . You can perform the same in didSelectItemAt method as well
I can show you a prototype of what the function will look like:
Let's assume you have an array named arrayForPopulating for populating data inside Collection View and we have array named finalSelections which consist of names of all the selections that user made
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
// Change the background colour of the cell here
cell.contentView.backgroundColor = UIColor.red
// Add the selected cell's data to the array
finalSelections.append(arrayForPopulating[indexPath.row])
}
Now you can send you finalSelections array to the server !
I am using didSelectItemAt and didDeselectItemAt for multiple selection of collectionViewCell. I want to select the cell and make the border blue color if it is selected and also unselect the 'selected' cell and make the border default. But my problem is that didDeselectItemAt is getting called alternately. when once i tap on any cell then didSelectItemAt is called and if i tap on any other cell then didDeselectItemAt is called. This should not happen i guess. didDeselectItemAt should be called only if i am tapping on already selected cell. Please correct me if i am going wrong. I have refered this UICollectionView - didDeselectItemAtIndexPath not called if cell is selected1 but dint work for me :(
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
let cell = collectionView.cellForItem(at: indexPath) as! MomentDetailCell
let moment = self.arrOfMoments[indexPath.row] as! MomentModel
cell.toggleSelection(moment: moment)
self.arrOfDeletingImgs.append(moment)
}
public func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath)
{
let cell : MomentDetailCell = self.collectionViewImages.cellForItem(at: indexPath) as! MomentDetailCell
let moment = self.arrOfMoments[indexPath.row] as! MomentModel
cell.toggleSelection(moment: moment)
self.arrOfDeletingImgs.remove(at: (find(objecToFind: moment))!)
}
// Also this is the code i am using in the class. I have also made allowsMultipleSelection true in viewdidload
extension MomentDetailViewController : UICollectionViewDelegateFlowLayout
{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
return CGSize(width: 75, height: 75)
}
}
// This is my customCell code
func toggleSelection( moment : MomentModel)
{
if (isSelected)
{
moment.isSelected = true
self.layer.borderWidth = 3
self.layer.borderColor = Constant.APP_BLUE_COLOR.cgColor
}
else
{
moment.isSelected = false
self.layer.borderWidth = 1
self.layer.borderColor = UIColor.red.cgColor
}
}
Try this :
This solution for multiple selection
1- make deSelect as below
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
let cell = collectionView.cellForItem(at: indexPath) as! MomentDetailCell
let moment = self.arrOfMoments[indexPath.row] as! MomentModel
cell.toggleSelection(moment: moment)
self.arrOfDeletingImgs.append(moment)
}
2 - Comment/Remove Deselect method
3 change in this method
func toggleSelection( moment : MomentModel)
{
moment.isSelected = !moment.isSelected
self.layer.borderWidth = moment.isSelected ? 3 : 1
self.layer.borderColor = moment.isSelected ? Constant.APP_BLUE_COLOR.cgColor : UIColor.red.cgColor
}
After long time i troubleshooted the problem and this is what worked for me...
I was doing following thing in cellForItemAtIndexPath: which is WRONG
cell.isSelected = false
collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .left)
I was doing this as didDeselect was getting called alternately without considering the selection of cell. I just uncommeted this code and it worked for me. Now didSelect is calling on first click and if i click on same cell again then only didDeselect is called as per expected flow.
Also make sure allowsMultipleSelection is true and also allowsSelection is true
I guess didselect method is called when you tap on second cell. Might be you forgot:
_collectionView.allowsMultipleSelection = YES
In my case i had a separate delegate class, and if i set collectionView.delegate = MyDelegate(), it didnt work, if i stored a var myDelegate = MyDelegate()in my viewcontroller and then set collectionView.delegate = myDelegate it somehow worked.
my qustion is simple.I have searched a lot and i'm familiar with UICollectionView but never saw this behavior of UICollectionView. please don't answer quickly and please read all my question.
I'm working on a project that uses collectionView (and auto layout). my collectionView is paging enabled and is working fine. but when I scroll it too fast (I mean very too fast) it scrolls two pages and shows the contents for next next row (previous previous row). also this code :
decelerationRate = UIScrollViewDecelerationRateFast
didn't work.
I created a temp project that have 1 controller and a simple collectionView (that is paging enabled and the cells's size are same as the viewController size). and there is just a label in the cell. the lable number is same as the collectionView row. also set this:
decelerationRate = UIScrollViewDecelerationRateFast
when I scroll it very fast from 1 to 2, it stops on 3 or vice versa.
this is my temp project code (that is auto layout):
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let collectionViewLayout = UICollectionViewFlowLayout()
collectionViewLayout.sectionInset = UIEdgeInsets.zero
collectionViewLayout.minimumLineSpacing = 0
collectionViewLayout.minimumInteritemSpacing = 0
collectionViewLayout.scrollDirection = UICollectionViewScrollDirection.horizontal
self.collView.collectionViewLayout = collectionViewLayout
self.collView.decelerationRate = UIScrollViewDecelerationRateFast
}
func numberOfSections(in collectionView: UICollectionView) -> Int
{
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollCell
print("cell For Item At Row \(indexPath.row)")
cell.lblNumber.text = "\(indexPath.row)"
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
return CGSize(width: collectionView.frame.size.width, height: collectionView.frame.size.height)
}
anybody knows what should I do? is it a normal behavior for UICollectionView?
I have a weird issue with UICollectionView and UITabBarController. Inside the UITabBarController i have references to two different ViewControllers. If i put View Controller that has UICollectionView as a first page of UITabBarController then my list with cells (UICollectionView) is loading normally like this:
But if i put it as a second View Controller when i open the tab it is like UIImageView and UILabel are disappearing from the cells:
I have checked collectionView method that is getting the cells and there is always data printed for the Label and Image. Here is a code of the methods for DataSource and Delagate
// tell the collection view how many cells to make
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.items.count
}
// make a cell for each cell index path
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
// get a reference to our storyboard cell
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! AdsCollectionViewCell
// Use the outlet in our custom class to get a reference to the UILabel in the cell
cell.myLabel.text = self.items[indexPath.item]
cell.backgroundColor = UIColor.whiteColor() // make cell more visible in our
cell.layer.borderColor = UIColor.grayColor().CGColor
cell.layer.borderWidth = 1
cell.layer.cornerRadius = 8
cell.layoutIfNeeded()
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let width = collectionView.frame.width - 22;
return CGSize(width: width/2, height: width/2);
}
// MARK: - UICollectionViewDelegate protocol
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
// handle tap events
print("You selected cell #\(indexPath.item)!")
}
Is there someone that had problem like this? How can it be solved?
I've set the width of a cell(UICollectionViewCell) to be equal to the width of the UICollectionView and I'm trying to do exactly the same thing with the UILabel that is included inside that cell. I think the code below explains exactly what I'm trying to achieve. So i've read some question here in SO and also a couple of tutorials but I'm still not sure how I can achieve this.
In a couple of questions it was saying about using collectionViewLayout but I'm really struggling on how to use it within my code. Any ideas? Thank you!
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("myCell", forIndexPath: indexPath) as LocationViewCell
cell.locationLabel.text = "Hello there!"
// Set cell & label width to 100%
let collectionViewWidth = self.collectionView.bounds.size.width
cell.frame.size.width = collectionViewWidth // Works
cell.locationLabel.frame.size.width = collectionViewWidth // Does NOT
Update 1
So I added the following:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
// Set cell width to 100%
let collectionViewWidth = self.collectionView.bounds.size.width
return CGSize(width: collectionViewWidth, height: 35)
}
What happens is that when the view is loaded the UILabel's width is still small. If I go to another view and then return back then it's 100%. So I have to do something in the viewDidLoad() right? I'm already using self.collectionView.reloadData() but I guess that's only for data.
Update 2
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("locationCell", forIndexPath: indexPath) as LocationViewCell
cell.locationLabel.text = "Hello UILabel"
// Set cell width to 100%
let collectionViewWidth = self.collectionView.bounds.size.width
cell.frame.size.width = collectionViewWidth
cell.locationLabel.frame.size.width = collectionViewWidth
return cell
}
It doesn't work because by the time this method is called, the collection view already knows how big the cell should be because it has got it from the flow delegate method:
optional func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
This is where you should be setting the size of your cells.