XCode 7: Copy Layoutattributes - swift2

I'm getting the warning "This is likely occurring because the flow layout subclass MyApp.MenuFlowLayout is modifying attributes returned by UICollectionViewFlowLayout without copying them". How can I copy this in Swift?
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
if let array = super.layoutAttributesForElementsInRect(rect) as [UICollectionViewLayoutAttributes]? {
var visibleRect = CGRectZero
visibleRect.origin = (self.collectionView?.contentOffset)!
visibleRect.size = (self.collectionView?.bounds.size)!
let activeDisatance : CGFloat = visibleRect.size.width / 2
let zoomFactor : CGFloat = 0.15
for attributes in array{
if CGRectIntersectsRect(attributes.frame, visibleRect){
let distance = CGRectGetMidX(visibleRect) - attributes.center.x
let normalizedDistance = distance / activeDisatance
if abs(distance) < activeDisatance{
let zoom = 1 + zoomFactor * (1 - abs(normalizedDistance))
attributes.transform3D = CATransform3DMakeScale(zoom, zoom, 1.0)
attributes.zIndex = 1
}
}
}
return array
}
return super.layoutAttributesForElementsInRect(rect)
}

Collections in Swift are value types, that's the reason why they don't have a copy() method. So when you are calling
array = super.layoutAttributesForElementsInRect(rect) as [UICollectionViewLayoutAttributes]?
it already creates a copy of the super layout attributes array and stores it in array
The problem is not that the array has not been copied, the problem is, that you are changing the UICollectionViewLayoutAttributes inside the array without copying them first.
To avoid this error message you can do something like this:
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attributes = super.layoutAttributesForElementsInRect(rect)
var attributesCopy = [UICollectionViewLayoutAttributes]()
for itemAttributes in attributes! {
let itemAttributesCopy = itemAttributes.copy() as! UICollectionViewLayoutAttributes
// add the changes to the itemAttributesCopy
attributesCopy.append(itemAttributesCopy)
}
return attributesCopy
}

Related

Saved Parse Database into an array called recipes and am now trying to access content within that array

I have a database in parse that i have pulled into a swift array. The custom parse object is called UserRecipe. The array is called recipes and is located in the viewDidLoad method. I am trying to set the imageview i have called recipeImage, to always access the image of the first element in the array. I do this in the updateImage function but am not sure if I have the correct syntax. Also the array seems to be stored only with the viewDidLoad method and is not accessible to my updateImage function. I'm wondering how to make it global so all functions can access it. Thanks in advance for any help.
The database looks like this:
import UIKit
import Parse
//import ParseFacebookUtilsV4
import FBSDKCoreKit
import FBSDKLoginKit
class ViewController: UIViewController {
let recipes = [PFObject]?.self
#IBOutlet var recipeImage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
//load in all data from Parse custom Object UserRecipe and store it in variable recipes
var query = PFQuery(className:"UserRecipe")
query.findObjectsInBackgroundWithBlock {
(recipes: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let recipes = recipes {
for recipe in recipes {
print(recipe["recipeName"])
}
}
} else {
print("Error: \(error!) \(error!.userInfo)")
}
}
let gesture = UIPanGestureRecognizer(target: self, action: Selector("wasDragged:"))
recipeImage.addGestureRecognizer(gesture)
//let tapping = UITapGestureRecognizer(target: self, action: Selector("wasTapped:"))
//recipeImage.addGestureRecognizer(tapping)
recipeImage.userInteractionEnabled = true
//updateImage()
//getUserInfo()
}
func wasDragged(gesture: UIPanGestureRecognizer) {
//Dragging Animation
let translation = gesture.translationInView(self.view)
let imageDrag = gesture.view!
imageDrag.center = CGPoint(x: self.view.bounds.width / 2 + translation.x, y: self.view.bounds.height / 2 + translation.y - 153)
let xFromCenter = imageDrag.center.x - self.view.bounds.width / 2 + translation.x
let scale = min(100 / abs(xFromCenter), 1)
var rotation = CGAffineTransformMakeRotation(xFromCenter / 200)
var stretch = CGAffineTransformScale(rotation, scale, scale)
imageDrag.transform = stretch
//determines whether current user has accepted or rejected certain recipes
if gesture.state == UIGestureRecognizerState.Ended {
var acceptedOrRejected = ""
if imageDrag.center.x < 100 {
acceptedOrRejected = "rejected"
print("not chosen")
//print("not chosen" + object["recipeName"])
} else if imageDrag.center.x > self.view.bounds.width - 100 {
acceptedOrRejected = "accepted"
print("Chosen")
}
/*if acceptedOrRejected != "" {
PFUser.currentUser()?.addUniqueObjectsFromArray([displayedUserId], forKey: acceptedOrRejected)
PFUser.currentUser()?.saveInBackgroundWithBlock({
(succeeded: Bool, error: NSError?) -> Void in
if succeeded {
} else {
print(error)
}
})
}*/
//Resets image position after it has been let go of
rotation = CGAffineTransformMakeRotation(0)
stretch = CGAffineTransformScale(rotation, 1, 1)
imageDrag.transform = stretch
imageDrag.center = CGPoint(x: self.view.bounds.width / 2, y: self.view.bounds.height / 2 - 153)
updateImage()
}
}
func updateImage() {
recipeImage.image = recipes["image"][0]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I am not across Parse but if I were you these are things I would try,
Set a breakpoint in
func updateImage() {
// set breakpoint here and check whether recipes contains any data.
recipeImage.image = recipes["image"][0]
}
Replace the code as shown below,
//replace
recipes["image"][0]
//to
recipes[0]["image"]

updating score variable from a different class

I'm trying to get my score to carry over to a new scene after the game is over and when this happens the score is coming out at 0. The score is set to 0 in my PlayScene as a default but I need it to read what the score is in ScoreScene when the PlayScene is over. Help Please!!
import SpriteKit
import AVFoundation
class PlayScene: SKScene, SKPhysicsContactDelegate {
let scoreText = SKLabelNode(fontNamed: "System-Bold")
var score = 0
override func didMoveToView(view: SKView) {
self.scoreText.text = "0"
self.scoreText.fontSize = 42
self.scoreText.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMaxY(self.frame) / 1.075)
func didBeginContact(contact:SKPhysicsContact) {
var scene = ScoreScene(size: self.size)
let skView = self.view as SKView!
skView.ignoresSiblingOrder = true
scene.scaleMode = .ResizeFill
scene.size = skView.bounds.size
skView.presentScene(scene)
func blockRunner() {
for(block, blockStatus) in self.blockStatuses {
var thisBlock = self.childNodeWithName(block)
if blockStatus.shouldRunBlock() {
blockStatus.timeGapForNextRun = random()
blockStatus.currentInterval = 0
blockStatus.isRunning = true
}
if blockStatus.isRunning {
if thisBlock!.position.x > blockMaxX {
thisBlock!.position.x -= CGFloat(self.groundSpeed)
}else {
thisBlock!.position.x = self.origBlockPositionX
blockStatus.isRunning = false
self.score++
if ((self.score % 10) == 0) {
self.groundSpeed++
}
self.scoreText.text = String(self.score)
}
}else {
blockStatus.currentInterval++
}
}
}
and Now when the game is over it switches to Score scene which is below
class ScoreScene: SKScene {
var playScene = PlayScene()
let scoreText = SKLabelNode(fontNamed: "System-Bold")
override func didMoveToView(view: SKView) {
self.scoreText.text = "score: \(playScene.score)"
self.scoreText.fontSize = 42
self.scoreText.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMaxY(self.frame) / 1.075)
You need to create a separate model to hold your data so you can access it from different parts of the app, I normally use a singleton approach so I can create the class only once and access its values anytime I need. What I would do:
create a new class player that keep the details about the player like
score make this class a singleton use this class to populate the
score value through all the scenes of the game
More details about design patterns in swift here (including singleton)
You will find a lot of code demos for singleton in here

sometimes sprites will not bounce off the physics boundary

I'm beginning a new game and have a weird problem right off the bat. In my didMoveToView function I have the following to put a bounds on my sprites within the frame(whole screen)
self.physicsBody=SKPhysicsBody(edgeLoopFromRect: self.frame)
The following code adds an SKSpriteNode at the touch point and adds a rotatebyangle action with a repeat forever
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(balloonWorld)
let nodeAtPoint = self.nodeAtPoint(location)
if(nodeAtPoint.name == nil) {
let location = touch.locationInNode(self)
let randNum:Int = Int.random(min: 0, max: 7)
var stringColor:String = (balloonColors.objectAtIndex(randNum) as String)
stringColor = stringColor.stringByReplacingOccurrencesOfString("face",
withString :"")
let sprite = Balloon(theColor:stringColor)
//let spriteFileName:String = balloonColors.objectAtIndex(randNum) as String
//let sprite = SKSpriteNode(imageNamed:spriteFileName)
sprite.xScale = 0.5
sprite.yScale = 0.5
sprite.position = location
sprite.zPosition = SceneLevel.hero.rawValue
balloonWorld!.addChild(sprite)
let action = SKAction.rotateByAngle(CGFloat(-M_PI), duration:1)
sprite.runAction(SKAction.repeatActionForever(action))
} else {
nodeAtPoint.removeFromParent()
println(nodeAtPoint.name)
}
}
}
I've setup balloonWorld as follows:
balloonWorld = SKNode()
self.addChild(balloonWorld!)
My problem is that sometimes the balloon sprites will not bounce off the edge, but just keep going thru the edge never to be seen again.
Any suggestions?
Thanks,
Ken
per request here's the code for setting up the physics bodies
balloon:
class Balloon: SKNode {
// properties
var myColor:String?
var objectSprite:SKSpriteNode?
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init(theColor:String){
super.init()
self.myColor=theColor
let imageFileName = "\(theColor)face"
let objectSprite:SKSpriteNode = SKSpriteNode(imageNamed:imageFileName)
objectSprite.physicsBody = SKPhysicsBody(circleOfRadius: objectSprite.size.height / 2.0)
objectSprite.physicsBody?.affectedByGravity = true
objectSprite.name = theColor + " balloon"
addChild(objectSprite)
}
}
I didn't set up a physics body on the boundary as I though all I needed was the line self.physicsBody = SKPhysicsBody(edgeLoopFrom Rect : self:frame) since the boundary has the same frame.
I tried adding the following, but that did not change the behavior:
let borderShape=SKShapeNode(rect: CGRectMake(self.frame.origin.x+2, self.frame.origin.y+2, self.frame.size.width-4, self.frame.size.height-4))
borderShape.fillColor=SKColor.clearColor()
borderShape.strokeColor=SKColor.blackColor()
borderShape.lineWidth=1
borderShape.physicsBody?.categoryBitMask=BodyType.boundary.rawValue
borderShape.zPosition=SceneLevel.border.rawValue
borderShape.physicsBody=SKPhysicsBody(edgeLoopFromRect: borderShape.frame)
balloonWorld!.addChild(borderShape)
Create an enum to differentiate the two types of sprites:
enum ColliderType {
case sprite1 = 1
case sprite2 = 2
}
You need to add a contact and collision bit mask like so:
borderShape.physicsBody?.contactTestBitMask = ColliderType.sprite2.rawValue
Also add a collisionBitMask:
borderShape.physicsBody?.collisionBitMask = ColliderType.sprite2.rawValue
Do the same for sprite2 and set it's collisionBitMask and it's contactTestBitMask to be the rawValue of sprite1. Make sure you set this class to be the contactDelegate so you receive a warning though a function that will notify you when two objects have collided:
self.physicsWorld?.contactDelegate = self
func didBeginContact(contact: SKPhysicsContact) {
[handle the contact however you would like to here]
}
I hope this helps!
LearnCocos2D(Steffen Itterheim) pointed me in the right direction in his comment. I was using moveTo/moveBy and the physics engine would not properly handle that. Once I simply made the moves based on impulses everything worked fine.
Thanks Steffen

errors while trying to compare a string to element in array

let verbList: [String] = ["hacer", "ser", "estar"]
let POVList: [String] = ["él / usted","ella / usted","ellas / ustedes","ellos / ustedes","tú","yo","nosotros",]
let correctConjugation: [[String]] = [["hace","hace","hacen","hacen","haces","hago","hacemos"], ["es","es","son","son","eres","soy","somos"], ["está","está","estan","estan","estas","estoy","estamos"]]
func randomVerb() -> Int { //creates and returns a random number for the prefix arrray
var randomVerb = Int(arc4random_uniform(3))
return randomVerb
}
func randomPrefix() -> Int { //creates and returns a random number for the verb array
var randomPrefix = Int(arc4random_uniform(7))
return randomPrefix
}
#IBAction func changeVerb(sender: AnyObject) {
Verb.text = verbList[randomVerb()]
POV.text = POVList[randomPrefix()]
userResponse.backgroundColor = UIColor.whiteColor()
userResponse.text = ""
}
#IBAction func checkResponse(sender: AnyObject) {
var userResponseA: String
userResponseA = userResponse.text
if (userResponseA == correctConjugation[randomVerb()[randomPrefix()]]){
userResponse.backgroundColor = UIColor.greenColor()
} else {
userResponse.backgroundColor = UIColor.redColor()
}
}
So I get two errors here (in the if statement in checkResponse): first, "int does not have a member named 'subscript'" and if I just take out the call for the function in the if statement I get: "'String' is not convertible to 'Mirror Disposition'"
I really have no idea why this is not working. Bear with me, as I am an Xcode noob just trying to get a better grade in spanish.
Very close - just need to have your subscripts separated:
if (userResponseA == correctConjugation[randomVerb()][randomPrefix()]) {
// ...
}
When working with an array of arrays (in this case correctConjugation), each subscript takes you one level down.
For the other issue, you want a couple variables to hold the current verb and prefix indexes:
class VC: UIViewController {
// list declarations here
var verbIndex = 0
var povIndex = 0
#IBAction func changeVerb(sender: AnyObject) {
verbIndex = randomVerb()
povIndex = randomPrefix()
Verb.text = verbList[verbIndex]
POV.text = POVList[povIndex]
userResponse.backgroundColor = UIColor.whiteColor()
userResponse.text = ""
}
#IBAction func checkResponse(sender: AnyObject) {
var userResponseA = userResponse.text
if (userResponseA == correctConjugation[verbIndex][povIndex]){
userResponse.backgroundColor = UIColor.greenColor()
} else {
userResponse.backgroundColor = UIColor.redColor()
}
}
}

set and create paragraph style in swift

I am trying to port an objective C method to draw text in a PDF context to Swift.
I could convert most of the code, but the following lines are giving me a problem.
Some help in the conversion would be welcome.
Here the Objective C code:
-(void)drawText:(NSString *)textToDraw context:(CGContextRef)myPDFContext textcolor:(NSColor *)textcolor textfont:(NSFont*)textfont textbox:(CGRect)boxsize pagesize:(CGSize)pageSize {
// .........
// create paragraph style and assign text alignment to it
CTTextAlignment alignment = kCTJustifiedTextAlignment;
CTParagraphStyleSetting _settings[] = { {kCTParagraphStyleSpecifierAlignment, sizeof(alignment), &alignment} };
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(_settings, sizeof(_settings) / sizeof(_settings[0]));
// set paragraph style attribute
CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTParagraphStyleAttributeName, paragraphStyle);
// .........
}
// The following lines are my try in Swift, but this gives errors:
func DrawText(textToDraw:String, myPDFContext:CGContextRef, textcolor:NSColor, textfont:NSFont, boxsize:CGRect, pagesize:CGSize) {
var alignment = CTTextAlignment.TextAlignmentLeft
let alignmentSetting = CTParagraphStyleSetting(spec: CTParagraphStyleSpecifier.Alignment, valueSize: sizeof(alignment), value: &alignment)
let paragraphStyle = CTParagraphStyleCreate(alignmentSetting, 1)
//.......
If this is solved I could post the complete method in Swift.
I think this does it:
var alignment = CTTextAlignment.TextAlignmentLeft
let alignmentSetting = [CTParagraphStyleSetting(spec: .Alignment, valueSize: UInt(sizeofValue(alignment)), value: &alignment)]
let paragraphStyle = CTParagraphStyleCreate(alignmentSetting, 1)
CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTParagraphStyleAttributeName, paragraphStyle)
I made the following modifications:
I changed sizeof(alignment) to UInt(sizeofValue(alignment)) because sizeof in Swift only takes a type (such as sizeof(Int)). The UInt() is a constructor that turns the Int returned by sizeofValue into the UInt needed by this call.
I make alignmentSetting into an array so that it could be passed to an UnsafePointer of that type. This also better matches the original version.
I changed CTParagraphStyleSpecifier.Alignment to just .Alignment since the first part isn't needed since spec is of type CTParagraphStyleSpecifier.
The hardcoded 1 is OK in this case because you are passing just one value, but the more general solution would be to do:
let paragraphStyle = CTParagraphStyleCreate(alignmentSetting, UInt(alignmentSetting.count))
Here the full working method based on the corrections and help.
func drawText(textToDraw:String, myPDFContext:CGContextRef, textcolor:NSColor, textfont:NSFont, boxsize:CGRect, pagesize:CGSize) {
let _font = NSFont(name:textfont.fontName, size:textfont.pointSize )
if (_font == nil) {
println("Font [\(textfont)] does not exist")
return
}
let myBoxWidth = boxsize.size.width
let myBoxHeight = boxsize.size.height
let myBoxxpos = boxsize.origin.x
let myBoxypos = boxsize.origin.y
let frameRect = CGRectMake(myBoxxpos, myBoxypos,myBoxWidth,myBoxHeight);
let framePath = CGPathCreateMutable()
CGPathAddRect(framePath, nil, frameRect);
// create attributed string
let attrStr = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
CFAttributedStringReplaceString (attrStr, CFRangeMake(0, 0), textToDraw);
// create font
let font = CTFontCreateWithName(textfont.fontName, textfont.pointSize, nil);
var alignment = CTTextAlignment.TextAlignmentLeft
let alignmentSetting = [CTParagraphStyleSetting(spec: .Alignment, valueSize: UInt(sizeofValue(alignment)), value: &alignment)]
//let paragraphStyle = CTParagraphStyleCreate(alignmentSetting, 1)
let paragraphStyle = CTParagraphStyleCreate(alignmentSetting, UInt(alignmentSetting.count))
CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTParagraphStyleAttributeName, paragraphStyle)
// set font attribute
CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTFontAttributeName, font);
// set color attribute
CFAttributedStringSetAttribute(attrStr,CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTForegroundColorAttributeName,textcolor.CGColor);
// Prepare the text using a Core Text Framesetter.
let framesetter = CTFramesetterCreateWithAttributedString(attrStr);
// Get the frame that will do the rendering.
let currentRange = CFRangeMake(0, 0);
let frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, nil);
// Put the text matrix into a known state. This ensures
// that no old scaling factors are left in place.
CGContextSetTextMatrix(myPDFContext, CGAffineTransformIdentity);
// Draw the frame.
CTFrameDraw(frameRef, myPDFContext);
}

Resources