Collection cell removes item on each reloadData() call Swift 5 - uicollectionview

Please see below image, that is my correct and initial view.
But after reloadData() is called for collectionView then it is removing the "COMPLETED" status label, and if I again repeat that action it will remove the "PROCESSING" status label. Does anyone know why it is behaving like this?
This is my cellForItemAt code.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DFCollectionViewCellIdentifierConstants.DF_CAPTURED_PANORAMAS_CELL, for: indexPath) as! DFCapturedPanoramasCollectionViewCell
cell.capturedPanoramasImages.layer.masksToBounds = true
cell.capturedPanoramasImages.layer.borderWidth = 1
cell.capturedPanoramasImages.layer.borderColor = UIColor().hexColor(DFColorConstants.INPUT_TEXT_COLOR).cgColor
cell.capturedPanoramasImages.layer.cornerRadius = 4
let url = URL(string:panoramaList[indexPath.item].panoramaThumbnailPath)
if url != nil && self.panoramaList[indexPath.item].panoramaStatus == DFPanoramaStatusConstants.DF_COMPLETED {
self.panoramaStatusLabelFunc(cell: cell, index: indexPath.item, bgColor: UIColor().hexColor(DFColorConstants.PANORAMA_STATUS_COMPLETED_COLOR))
cell.capturedPanoramasImages.contentMode = .scaleAspectFill
if let data = try? Data(contentsOf: url!)
{
let image = UIImage(data: data)
cell.capturedPanoramasImages.image = image
}
cell.capturedPanoramasLabel.text = panoramaList[indexPath.item].name
} else if url == nil && self.panoramaList[indexPath.item].panoramaStatus != DFStatusConstants.COMPLETED {
self.panoramaStatusLabelFunc(cell: cell, index: indexPath.item, bgColor: UIColor().hexColor(DFColorConstants.PANORAMA_STATUS_UPLOADING_AND_PROCESSESING))
cell.capturedPanoramasImages.contentMode = .scaleAspectFit
cell.capturedPanoramasImages.image = self.capturedPanoramasImage[0]
cell.capturedPanoramasLabel.text = panoramaList[indexPath.item].name
} else if self.panoramaList[indexPath.item].name == DFStatusConstants.ADD_PANORAMA {
cell.panoramaStatusLabel.isHidden = true
cell.capturedPanoramasImages.contentMode = .center
cell.capturedPanoramasImages.image = self.capturedPanoramasImage[1]
cell.capturedPanoramasLabel.text = panoramaList[indexPath.item].name
}
return cell
}
And panoramaStatusLabelFunc() function.
func panoramaStatusLabelFunc(cell: DFCapturedPanoramasCollectionViewCell, index: Int, bgColor: UIColor){
cell.capturedPanoramasLabel.isHidden = false
cell.panoramaStatusLabel.layer.backgroundColor = bgColor.cgColor
cell.panoramaStatusLabel.textColor = UIColor().hexColor(DFColorConstants.TEXT_WHITE_COLOR)
cell.panoramaStatusLabel.layer.cornerRadius = 10
cell.panoramaStatusLabel.text = self.panoramaList[index].panoramaStatus
}

Found the issue, it is in below condition with "cell.panoramaStatusLabel.isHidden = true". For some reason it is passing isHidden = true to next cell(I an not sure, but may be beacuse fo Asyn)
else if self.panoramaList[indexPath.item].name == DFStatusConstants.ADD_PANORAMA
{
cell.panoramaStatusLabel.isHidden = true
cell.capturedPanoramasImages.contentMode = .center
cell.capturedPanoramasImages.image = self.capturedPanoramasImage[1]
cell.capturedPanoramasLabel.text = panoramaList[indexPath.item].name
}
To fix I have changed panoramaStatusLabelFunc function as below:
func panoramaStatusLabelFunc(cell: DFCapturedPanoramasCollectionViewCell, index: Int, bgColor: UIColor, isAddHotspot: Bool){
cell.capturedPanoramasLabel.isHidden = false
cell.panoramaStatusLabel.layer.backgroundColor = bgColor.cgColor
cell.panoramaStatusLabel.textColor = UIColor().hexColor(DFColorConstants.TEXT_WHITE_COLOR)
cell.panoramaStatusLabel.layer.cornerRadius = 10
if(isAddHotspot) {
cell.panoramaStatusLabel.text = ""
} else {
cell.panoramaStatusLabel.text = self.panoramaList[index].panoramaStatus
}
}

Related

layout.sectionHeadersPinToVisibleBounds in UICollectionViewDiffableDataSource

I want to keep the header in a UICollectionViewDiffableDataSource, as I understand I can do it with layout.sectionHeadersPinToVisibleBounds, but it's not usable with my collectionView layout
private func createLayout() -> UICollectionViewLayout {
return UICollectionViewCompositionalLayout { section, layoutEnvironment in
var config = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
config.headerMode = section == 0 ? .none : .firstItemInSection
config.backgroundColor = .darkGrayBackground
config.showsSeparators = false
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: layoutEnvironment)
section.interGroupSpacing = 30
self.addHeader(to: section)
return section
}
}
private func addHeader(to section: NSCollectionLayoutSection) {
let headerFooterSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(20)
)
let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: headerFooterSize,
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .top
)
section.boundarySupplementaryItems = [sectionHeader]
}
and
collectionView = UICollectionView(frame: bounds, collectionViewLayout: createLayout())
I even can't access to it via
if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
layout.sectionHeadersPinToVisibleBounds = true // never called
}
Could anyone help me on this? Thank you so much

RxSwift Cannot convert call result type '(_) -> Disposable' to expected type '(_) ->

I'm trying to add an headerView to a collectionView using RxSwift.
I get this error:
Cannot convert call result type '() -> Disposable' to expected type '() ->
at this line:
obsHeader.asObservable().bind(to: collectionView.rx.items(dataSource: dataSource)).disposed(by: disposeBag)
I don't understand how to fix it. Any help?
I post here the rest of the code:
struct SectionItemObject {
let collectionViewRecommendations: UICollectionView
let items: [SFCardViewModelListOfCardsProtocol]
}
struct SectionOfItems {
var items: [Item]
}
extension SectionOfItems: SectionModelType {
typealias Item = SectionItemObject
init(original: SectionOfItems, items: [Item]) {
self = original
self.items = items
}
init(items: [Item]?) {
self.items = items ?? [Item]()
}
}
And this is what I write in the method I call to observe.
let dataSource = RxCollectionViewSectionedReloadDataSource<SectionOfItems>(configureCell: { (datasource, collectionview, indexPath, i) -> UICollectionViewCell in
let cell = collectionview.dequeueReusableCell(withReuseIdentifier: "CardView", for: indexPath) as! CardView
// self.setCell(card:card,cell:cell)
cell.lbTitle.text = "TEST"
return cell
}, configureSupplementaryView: { (datasource, collectionview, kind, indexPath) -> UICollectionReusableView in
let section = collectionview.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "AddNewCardCollectionHeaderView", for: indexPath) as! AddNewCardCollectionHeaderView
section.backgroundColor = UIColor.orange
section.collectionViewRecommendations = self.collectionViewRecommendations
return section
} )
let item = SectionItemObject(collectionViewRecommendations: self.collectionViewRecommendations!, items: viewModelProtocol.searchedCards.value)
let obsHeader = Variable(SectionOfItems(items: [item]))
obsHeader.asObservable().bind(to: collectionView.rx.items(dataSource: dataSource)).disposed(by: disposeBag)
I would bet that your obsHeader needs to be typed as Variable<[SectionOfItems]>
So just make it
let obsHeader = Variable([SectionOfItems(items: [item])])

Stoping a function from happening when a spirit is removed swift 3

I'm having an issue when my Spirit dies or is removed by removeFromParent()
command. The Spirit is removed successful but I can still shoot missiles even when the Spirit isn't there. My code for the GameScene.swft is as follows:
import SpriteKit
import GameplayKit
import CoreMotion
class GameScene: SKScene, SKPhysicsContactDelegate {
var starfield:SKEmitterNode!
var player = SKSpriteNode()
var scoreLabel:SKLabelNode!
var score:Int = 0 {
didSet {
scoreLabel.text = "Score: \(score)"
}
}
var gameTimer:Timer!
var possibleAliens = ["alien", "alien2", "alien3"]
let alienCategory:UInt32 = 0x1 << 1
let photonTorpedoCategory:UInt32 = 0x1 << 0
let photonshuttleCategory:UInt32 = 0x1 << 0
let photonalienCategory: UInt32 = 0x1 << 0
let shuttleCategory:UInt32 = 0x1 << 1
let motionManger = CMMotionManager()
var xAcceleration:CGFloat = 0
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
starfield = SKEmitterNode(fileNamed: "Starfield")
starfield.position = CGPoint(x: 0, y: 1472)
starfield.advanceSimulationTime(10)
self.addChild(starfield)
func restart(){
let mainStoryboard = UIStoryboard(name: "Start", bundle: nil)
let vc = mainStoryboard.instantiateViewController(withIdentifier: "Start")
self.view?.window?.rootViewController?.present(vc, animated: true, completion: nil)
}
starfield.zPosition = -1
player = SKSpriteNode(imageNamed: "shuttle")
player.position = CGPoint(x: self.frame.size.width / 2, y: player.size.height / 2 + 20)
var Ghost = player
Ghost.physicsBody = SKPhysicsBody(circleOfRadius: (Ghost.frame.height) / 2)
Ghost.physicsBody?.categoryBitMask = photonshuttleCategory
Ghost.physicsBody?.collisionBitMask = 0
Ghost.physicsBody?.contactTestBitMask = alienCategory
Ghost.physicsBody?.isDynamic = true
Ghost.physicsBody?.usesPreciseCollisionDetection = true
self.addChild(player)
self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
self.physicsWorld.contactDelegate = self
scoreLabel = SKLabelNode(text: "Score: 0")
scoreLabel.position = CGPoint(x: 100, y: self.frame.size.height - 60)
scoreLabel.fontName = "AmericanTypewriter-Bold"
scoreLabel.fontSize = 36
scoreLabel.fontColor = UIColor.white
score = 0
if score >= 100{
player = SKSpriteNode(imageNamed: "Spaceship")
}
self.addChild(scoreLabel)
gameTimer = Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(addAlien), userInfo: nil, repeats: true)
if score >= 100{
gameTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(addAlien), userInfo: nil, repeats: true)
}
motionManger.accelerometerUpdateInterval = 0.1
motionManger.startAccelerometerUpdates(to: OperationQueue.current!) { (data:CMAccelerometerData?, error:Error?) in
if let accelerometerData = data {
let acceleration = accelerometerData.acceleration
self.xAcceleration = CGFloat(acceleration.x) * 0.75 + self.xAcceleration * 0.25
}
}
}
func addAlien () {
possibleAliens = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: possibleAliens) as! [String]
let alien = SKSpriteNode(imageNamed: possibleAliens[0])
let randomAlienPosition = GKRandomDistribution(lowestValue: 0, highestValue: 414)
let position = CGFloat(randomAlienPosition.nextInt())
alien.position = CGPoint(x: position, y: self.frame.size.height + alien.size.height)
alien.physicsBody = SKPhysicsBody(rectangleOf: alien.size)
alien.physicsBody?.isDynamic = true
alien.physicsBody?.categoryBitMask = alienCategory
alien.physicsBody?.contactTestBitMask = photonTorpedoCategory
alien.physicsBody?.collisionBitMask = 0
self.addChild(alien)
let animationDuration:TimeInterval = 6
var actionArray = [SKAction]()
actionArray.append(SKAction.move(to: CGPoint(x: position, y: -alien.size.height), duration: animationDuration))
actionArray.append(SKAction.removeFromParent())
alien.run(SKAction.sequence(actionArray))
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let delay = SKAction.wait(forDuration: 2.6)
self.run(delay)
fireTorpedo()
}
func fireTorpedo() {
self.run(SKAction.playSoundFileNamed("torpedo.mp3", waitForCompletion: false))
let torpedoNode = SKSpriteNode(imageNamed: "torpedo")
torpedoNode.position = player.position
torpedoNode.position.y += 60
torpedoNode.physicsBody = SKPhysicsBody(circleOfRadius: torpedoNode.size.width / 2)
torpedoNode.physicsBody?.isDynamic = true
torpedoNode.physicsBody?.categoryBitMask = photonTorpedoCategory
torpedoNode.physicsBody?.contactTestBitMask = alienCategory
torpedoNode.physicsBody?.collisionBitMask = 0
torpedoNode.physicsBody?.usesPreciseCollisionDetection = true
self.addChild(torpedoNode)
let animationDuration:TimeInterval = 0.3
var actionArray = [SKAction]()
actionArray.append(SKAction.move(to: CGPoint(x: player.position.x, y: self.frame.size.height + 10), duration: animationDuration))
actionArray.append(SKAction.removeFromParent())
torpedoNode.run(SKAction.sequence(actionArray))
}
func didBegin(_ contact: SKPhysicsContact) {
var firstBody:SKPhysicsBody
var secondBody:SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
}else{
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if (firstBody.categoryBitMask & photonTorpedoCategory) != 0 && (secondBody.categoryBitMask & alienCategory) != 0 {
torpedoDidCollideWithAlien(torpedoNode: firstBody.node as! SKSpriteNode, alienNode: secondBody.node as! SKSpriteNode)
}
}
func torpedoDidCollideWithAlien (torpedoNode:SKSpriteNode, alienNode:SKSpriteNode) {
let explosion = SKEmitterNode(fileNamed: "Explosion")!
explosion.position = alienNode.position
self.addChild(explosion)
self.run(SKAction.playSoundFileNamed("explosion.mp3", waitForCompletion: false))
torpedoNode.removeFromParent()
alienNode.removeFromParent()
self.run(SKAction.wait(forDuration: 2)) {
explosion.removeFromParent()
}
score += 5
}
func hitdidBegin(_ contact: SKPhysicsContact) {
var firstBody:SKPhysicsBody
var secondBody:SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
}else{
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if (firstBody.categoryBitMask & photonshuttleCategory) != 0 && (secondBody.categoryBitMask & alienCategory) != 0 {
spaceshipDidCollideWithNode(alienNode: secondBody.node as! SKSpriteNode, spaceShip: firstBody.node as! SKSpriteNode)
}
}
func spaceshipDidCollideWithNode (alienNode:SKSpriteNode, spaceShip:SKSpriteNode){
let explosion = SKEmitterNode(fileNamed: "Explosion")
explosion?.position = alienNode.position
self.addChild(explosion!)
self.run(SKAction.playSoundFileNamed("explosion.mp3", waitForCompletion: false))
alienNode.removeFromParent()
spaceShip.removeFromParent()
self.run(SKAction.wait(forDuration: 2)){
explosion?.removeFromParent()
}
score = 0
}
override func didSimulatePhysics() {
player.position.x += xAcceleration * 50
if player.position.x < -20 {
player.position = CGPoint(x: self.size.width + 20, y: player.position.y)
}else if player.position.x > self.size.width + 20 {
player.position = CGPoint(x: -20, y: player.position.y)
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
What I want to happen is that the fireTorpedo function will not be called when the Ghost/shuttle/player is removed.
Thank you.
There is a few ways to achieve what you want
1) You check if the players parent property is nil and not fire the torpedo if it is (I am not sure this is the best way)
func fireTorpedo() {
guard player.parent != nil else { return }
...
}
2) You could make your player an optional property
class GameScene: SKScene {
var player: SKSpriteNode?
override func didMove(to view: SKView) {
player = SKSpriteNode()
...
}
}
and when you remove it set it to nil
player = nil
and than not fire the torpedo if the player is nil
func fireTorpedo() {
guard player != nil else { return }
...
}
3) You can create another property in your GameScene class
var isPlayerRemoved = false
Than when you remove your player just set it to true
isPlayerRemoved = true
and than adjust your torpedo function to this
func fireTorpedo() {
guard !isPlayerRemoved else { return }
...
}
Hope this helps

How can I animate a text change in swift or objective-c?

I tried a test-project. It worked but the method I used, put programmatically created labels over old existing labels. When the length of a string array is lower than the old one, it shows unnecessary old labels due to addSubview method.
How can I handle this problem?
import UIKit
extension UIView {
func pushTransition(duration:CFTimeInterval) {
let animation:CATransition = CATransition()
animation.timingFunction = CAMediaTimingFunction(name:
kCAMediaTimingFunctionEaseInEaseOut)
animation.type = kCATransitionPush
animation.subtype = kCATransitionFromBottom
animation.duration = duration
self.layer.addAnimation(animation, forKey: kCATransitionPush)
}
}
class ViewController: UIViewController {
var current:Float = 125.24
var discount:Float = 1.212342748
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func doAction(sender: AnyObject) {
let currentstr = String(format: "₺%.2f", self.current)
let length = currentstr.characters.count
var strarray = Array(currentstr.characters)
strarray[length-3] = ","
print(strarray)
self.current = self.current-self.discount
let newcurrent = String(format: "₺%.2f", self.current)
let newcurrentlength = newcurrent.characters.count
var newcurrentarray = Array(newcurrent.characters)
newcurrentarray[newcurrentlength-3] = ","
print(newcurrentarray)
var labels = [UILabel]()
print(labels)
if (length == newcurrentlength) {
for i in 1 ..< length+1 {
labels.append(UILabel(frame: CGRectMake((15*CGFloat(i)), 100, 20, 20)))
if (strarray[i-1] == newcurrentarray[i-1]){
print("SAME")
labels[i-1].text = String(newcurrentarray[i-1])
labels[i-1].textAlignment = NSTextAlignment.Center
labels[i-1].backgroundColor = UIColor.redColor()
self.view.addSubview(labels[i-1])
} else {
print("CHANGED")
labels[i-1].pushTransition(0.4)
labels[i-1].text = String(newcurrentarray[i-1])
labels[i-1].textAlignment = NSTextAlignment.Center
labels[i-1].backgroundColor = UIColor.redColor()
self.view.addSubview(labels[i-1])
}
}
} else {
for i in 1..<newcurrentlength+1 {
labels.append(UILabel(frame: CGRectMake((15*CGFloat(i)), 100, 20, 20)))
if (strarray[i-1] == newcurrentarray[i-1]){
print("SAME")
labels[i-1].text = String(newcurrentarray[i-1])
labels[i-1].textAlignment = NSTextAlignment.Center
labels[i-1].backgroundColor = UIColor.redColor()
self.view.addSubview(labels[i-1])
} else {
print("CHANGED")
labels[i-1].pushTransition(0.4)
labels[i-1].text = String(newcurrentarray[i-1])
labels[i-1].textAlignment = NSTextAlignment.Center
labels[i-1].backgroundColor = UIColor.redColor()
self.view.addSubview(labels[i-1])
}
}
}
}
}
EDIT
I put label array out of action section and it's fixed.
var labels = [UILabel]()
However, the transition between 100,00 and 99,99 there is a problem with the last child of label array. It still shows last digit like 99,990

Rotation & Pinch(scale) gesture messes parent UIView and his subviews frame

I'm trying to implement Pinch & Rotate gestures on a UIView who contains several subviews(UIButton,UITextView).
Code organized below to your convenience
I'm guessing i'm missing something. No idea what tho. Thank you!
Result :
Code
Scale :
var scaleAnchorPoint = CGPoint()
func handleSizeIncreasing(sender:UIPinchGestureRecognizer) {
if sender.state == UIGestureRecognizerState.Began
{
print("Began")
scaleAnchorPoint = self.center
}
else if sender.state == UIGestureRecognizerState.Changed
{
txtView.transform = CGAffineTransformScale(txtView.transform, sender.scale, sender.scale)
sender.scale = 1.0
self.frame.size = CGSizeMake(txtView.frame.width + buttonSize, txtView.frame.height + buttonSize)
self.center = scaleAnchorPoint
updateViews(scaleAnchorPoint)
print("Changed")
}
}
Rotate :
var rotateAnchorPoint = CGPoint()
func handleRotate(sender : UIRotationGestureRecognizer) {
if sender.state == UIGestureRecognizerState.Began
{
print("Began")
rotateAnchorPoint = self.center
}
else if sender.state == UIGestureRecognizerState.Changed
{
sender.view!.transform = CGAffineTransformRotate(sender.view!.transform, sender.rotation)
sender.rotation = 0
updateViews(rotateAnchorPoint)
print("Changed")
}
}
Side:
func updateViews(aroundPoint : CGPoint )
{
self.center = aroundPoint
txtView.frame.origin = CGPointMake(buttonSize / 2, buttonSize / 2)
txtView.contentSize = txtView.frame.size
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
Figured it out. Simply, when the scale/frame changes, instead of accessing the Frame like i did here : self.frame.size = CGSizeMake(txtView.frame.width + buttonSize, txtView.frame.height + buttonSize)
Access self.bounds , like this -
self.bounds.size = CGSizeMake(txtView.frame.width + buttonSize, txtView.frame.height + buttonSize)
Enjoy!