I am trying to populate a two dimensional array with a custom class called "card". I run it through two for loops to make a new "card" object and append it to the array. Every time I run the code block, I get differing amounts of objects ranging from 0 to millions.
class arcade: UIViewController {
override func viewDidLoad() {
createBoard()
displayBoard()
}
func createBoard()
{
var repCard = card()
var board = [[card](count: 0, repeatedValue: repCard)]
var indexCounter = 0;
for var xInFor = 0; xInFor < 5; xInFor++ {
for var yInFor = 0; yInFor < 5; yInFor++ {
var newCard = card()
newCard.width = 15.0
newCard.id = indexCounter
++indexCounter
var xPos = 50
var yPos = 50
newCard.x = xPos * xInFor
newCard.y = yPos * yInFor
board[xInFor].append(newCard)
}
board.append([card](count: 0, repeatedValue: repCard))
}
}
Problem solved - I needed to declare my "board" array in the class not in the method
Related
I am currently creating a view controller for an app that helps visualize the Bubble Sort algorithm. However, when the user presses the sort button, instead of the swaps occurring one by one (giving the user some time to see how the algorithm works), the numbers sort all at once and they all go to their respective places all at once. I'm looking to get the UIAnimation to iterate over a for loop and have the elements in swap one by one, instead of all at once.
I have already tried passing a delay as a parameter to the UIView.animate method. All it does is pause the specified amount of time before sorting them all at once (which I do not want).
//IBOutlets for the images (containing numbers) and the sort button
#IBOutlet weak var sortButton: UIButton!
#IBOutlet weak var firstImage: UIImageView!
#IBOutlet weak var secondImage: UIImageView!
#IBOutlet weak var thirdImage: UIImageView!
#IBOutlet weak var fourthImage: UIImageView!
#IBOutlet weak var fifthImage: UIImageView!
#IBOutlet weak var sixthImage: UIImageView!
#IBOutlet weak var seventhImage: UIImageView!
#IBOutlet weak var eighthImage: UIImageView!
#IBOutlet weak var ninthImage: UIImageView!
#IBOutlet weak var tenthImage: UIImageView!
//Create an array of UIImageViews
var elements : [UIImageView] = [UIImageView]()
var numberArray : [Int] = [Int]()
//Function when the view loads
override func viewDidLoad() {
super.viewDidLoad()
//Add the UIImageViews to the array
elements += [firstImage, secondImage, thirdImage, fourthImage, fifthImage, sixthImage, seventhImage, eighthImage, ninthImage, tenthImage]
//Create a variable that will indicate whether there is a duplicate number in the array
var duplicate = true;
//Assign each element in the array a random number between 1 and 10
for index in elements {
//Repeat-while loop to avoid duplicate numbers in the array
repeat {
//Create a random number
let randNumber = Int.random(in: 1 ... 10)
//Display the appropriate numbered image
index.image = UIImage(named: "\(randNumber)")
//Check to see if the number is already a duplicate
if numberArray.contains(randNumber) {
duplicate = true
} else {
duplicate = false
//Add the number to the list of used numbers in the array
numberArray.append(randNumber)
}
} while duplicate == true
}
} //End of viewDidLoad
#IBAction func sortButtonPressed(_ sender: Any) {
//Bubble Sort algorithm
for i in (0..<elements.count) {
for j in (1..<elements.count) {
if numberArray[j]<numberArray[j-1] {
let temp = numberArray[j-1]
numberArray[j-1] = numberArray[j]
numberArray[j] = temp
let tempElement = elements[j-1]
elements[j-1] = elements[j]
elements[j] = tempElement
//Swap the corresponding UIImageViews on screen
let tempView : CGRect = elements[j-1].frame
UIView.animate(withDuration: 0.75, delay: 0, animations: {
self.elements[j-1].frame = CGRect(x: self.elements[j].frame.minX, y: self.elements[j].frame.minY, width: tempView.width, height: tempView.height)
self.elements[j].frame = CGRect(x: tempView.minX, y: tempView.minY, width: self.elements[j].frame.width, height: self.elements[j].frame.width)
self.view.layoutIfNeeded()
})
}
}
}
}
Does anyone know how to make the image swaps (animations) happen one by one (for each iteration) instead of all at once? (Area of focus is towards the bottom of the sortButtonPressed function...) Any help would be greatly appreciated. Thanks for the help in advance!
To do something like this manually you will need to start a new animation when current one finishes. There are few ways of doing that but one of them is to pack all data you need for your animation in a class and then create an array of such classes. You may then create a single method which is called recursively. Something like this:
- (void)performAllAnimations:(NSArray *)allAnimations
{
NSMutableArray *duplicate = [allAnimations mutableCopy];
AnimationDescription *currentAnimation = duplicate.firstObject;
[duplicate removeObjectAtIndex:0];
[UIView animateWithDuration:currentAnimation.duration animations:^{
// Use currentAnimation to do the animation
} completion:^(BOOL finished) {
if(duplicate.count > 0) [self performAllAnimations:duplicate];
}];
}
You do not always need AnimationDescription as a custom class. In your case it might be enough to simply use an array of indices. For instance you would then do:
- (void)performAllAnimations:(NSArray *)allAnimations
{
NSMutableArray *duplicate = [allAnimations mutableCopy];
int currentAnimationIndex = [duplicate.firstObject integerValue];
[duplicate removeObjectAtIndex:0];
[UIView animateWithDuration:0.3 animations:^{
// Use currentAnimation to do the animation
} completion:^(BOOL finished) {
if(duplicate.count > 0) [self performAllAnimations:duplicate];
}];
}
You could then do something like:
NSMutableArray *indexArray = [[NSMutableArray alloc] init];
for(int i=0; i<10; i++) {
[indexArray addObject:#(i)];
}
[self performAllAnimations: indexArray];
Using your own data naturally.
Another approach is using a delay. For some cases it could be better as you have more control over when the next animation starts. See the following example:
- (void)performAllAnimations:(NSArray *)allAnimations withAnimationExecution:(void (^)(id animationObject, int index))execution
{
NSTimeInterval duration = 0.3;
for(int i=0; i<allAnimations.count; i++) {
[UIView animateWithDuration:duration delay:(duration*0.5)*i options:UIViewAnimationOptionCurveEaseInOut animations:^{
execution(allAnimations[i], i);
} completion:^(BOOL finished) {
}];
}
}
- (void)testAnimation
{
NSMutableArray *views = [[NSMutableArray alloc] init];
for(int i=0; i<10; i++) {
UIView *aView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 50.0, 100.0, 100.0)];
[self.view addSubview:aView];
aView.backgroundColor = [UIColor blackColor];
aView.layer.borderWidth = 2.0;
aView.layer.borderColor = [UIColor yellowColor].CGColor;
[views addObject:aView];
}
[self performAllAnimations:views withAnimationExecution:^(id animationObject, int index) {
UIView *view = animationObject;
view.frame = CGRectMake(75.0*index, 100.0, 100.0, 100.0);
}];
}
I have a random integer that needs to update every second, but my issue is that the random int cannot be defined outside of a function because it is an random int with the range of 0 to the screen size
var randomX = UInt32(self.size.width)
I cannot use self.size.width outside of a function. At first I thought maybe its not updating because its 1. Declared in the function didMoveToView() and 2. I declared it using "let" instead of "var". If I declare it in my function that updates every second, the variable cannot be used outside of that function which is a huge problem.
You can declare your var as an instance variable of your class (outside a method), update it in a method, and use it anywhere else in an instance of that class.
class RTest {
var randomX: UInt32
init() {
self.randomX = 2;
}
func rx5()->UInt32 {
return self.randomX * 5
}
}
Create a global class variable so you could access it anywhere in the class
class MyClass: UIViewController {
var screenWidth: CGFloat = 0.0; //Global variable for this class
func getRand() -> UInt32 {
//generate your random number
return UInt32(self.screenWidth)
}
func useRandom() {
//call getRand anytime you want to generatea a new random num
var myRandNum = getRand()
}
func override viewWillAppear(animated: Bool) {
self.screenWidth = self.view.frame.size.width
}
}
Code not tested but should give you an idea on how to go about it.
hope that helps.
I am trying to use core plot with swift and I feel very close except for one error. My code so far is :
class ViewController: UIViewController, CPTPlotDataSource {
#IBOutlet weak var graphView: CPTGraphHostingView!
override func viewDidLoad() {
super.viewDidLoad()
// create graph
var graph = CPTXYGraph(frame: CGRectZero)
graph.title = "Hello Graph"
graph.paddingLeft = 0
graph.paddingTop = 0
graph.paddingRight = 0
graph.paddingBottom = 0
// hide the axes
var axes = graph.axisSet as CPTXYAxisSet
var lineStyle = CPTMutableLineStyle()
lineStyle.lineWidth = 0
axes.xAxis.axisLineStyle = lineStyle
axes.yAxis.axisLineStyle = lineStyle
// add a pie plot
var pie = CPTPieChart()
pie.dataSource = self
pie.pieRadius = (self.view.frame.size.width * 0.9)/2
graph.addPlot(pie)
self.graphView.hostedGraph = graph
}
func numberOfRecordsForPlot(plot: CPTPlot!) -> UInt {
return 4
}
func numberForPlot(plot: CPTPlot!, field fieldEnum: UInt, recordIndex idx: UInt) -> NSNumber! {
return idx+1
}
}
The error i am getting is from the line : var pie = CPTPieChart() My error is :
Core Plot with swift getting error : "Missing Argument for Parameter ‘frame’ in call"
I am following a tutorial and it is possible that swift has changed, however I am new to the language and can't seem to solve this one. Thanks in advance.
I experienced similar problems while developing another app. To my experience, CGRectZero is causing problems with Swift. Try with an actual CGRect or call
var pie = CPTPieChart(frame: CGRectMake(x,y,width,height))
I have a scrollView that shows the first item in my Array, but I cannot get the second item to show when I scroll to the next page. Also, the text from first item in the Array shows when I am on the second page of the scrollView.
Could someone please help?
func scrollViewDidScroll(scrollView: UIScrollView)
{
var pageSize: Float = Float(scrollView.bounds.size.width)
var page: Float = floorf((Float(scrollView.contentOffset.x) - (pageSize/2.0)) / pageSize) + 1
//Bind Page limits
if (page >= Float(numpages)){
page = Float(numpages) - 1
} else if (page < 0) {
page = 0
}
pageControl.currentPage = Int(page)
question.text = featureQuestions[pageControl.currentPage]
println(pageControl.currentPage)
}
override func viewWillAppear(animated: Bool)
{
for var q = 0; q < numpages; q++
{
let mainFrame : CGRect = CGRectMake(scrollView.frame.size.width * CGFloat(q), 0, scrollView.frame.size.width, scrollView.frame.size.height)
var question : UILabel = UILabel(frame: scrollView.frame)
question.clipsToBounds = true
scrollView.addSubview(question)
}
}
You need to set the frame for question to be mainFrame not scrollView.frame.
How can I add my starsSqArray to a for loop in my update function that grabs all the SKSpriteNodesfor _starsSq1 so that all of the stars move together and not separately?
Right now my Swift class keeps returning an error saying that _starsSqArray doesn't have a position (my code is bugged out). My goal is to grab the plotted stars and move them downward all at once.
import SpriteKit
class Stars:SKNode {
//Images
var _starsSq1:SKSpriteNode?
//Variables
var starSqArray = Array<SKSpriteNode>()
var _starSpeed1:Float = 5;
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init() {
super.init()
println("stars plotted")
createStarPlot()
}
/*-------------------------------------
## MARK: Update
-------------------------------------*/
func update() {
for (var i = 0; i < starSqArray.count; i++) {
_starsSq1!.position = CGPoint(x: self.position.x , y: self.position.y + CGFloat(_starSpeed1))
}
}
/*-------------------------------------
## MARK: Create Star Plot
-------------------------------------*/
func createStarPlot() {
let screenSize: CGRect = UIScreen.mainScreen().bounds
let screenWidth = screenSize.width
let screenHeight = screenSize.height
for (var i = 0; i < 150 ; i++) {
_starsSq1 = SKSpriteNode(imageNamed: "starSq1")
addChild(_starsSq1!)
//starSqArray.addChild(_starsSq1)
var x = arc4random_uniform(UInt32(screenSize.width + 400))
var y = arc4random_uniform(UInt32(screenSize.height + 400))
_starsSq1!.position = CGPoint(x: CGFloat(x), y: CGFloat(y))
}
}
}
A couple of suggestions by a design point of view:
You already have all your stars (I guess so) grouped togheter inside a common parent node (that you correctly named Stars). Then you just need to move your node of type Stars and all its child node will move automatically.
Manually changing the coordinates of a node inside an update method does work but (imho) it is not the best way to move it. You should use SKAction instead.
So, if you want to move the stars forever with a common speed and direction you can add this method to Stars
func moveForever() {
let distance = 500 // change this value as you prefer
let seconds : NSTimeInterval = 5 // change this value as you prefer
let direction = CGVector(dx: 0, dy: distance)
let move = SKAction.moveBy(direction, duration: seconds)
let moveForever = SKAction.repeatActionForever(move)
self.runAction(moveForever)
}
Now you can remove the code inside the update method and call moveForever when you want the stars to start moving.
Finally, at some point the stars will leave the screen. I don't know the effect you want to achieve but you will need to deal with this.
for (SKSpriteNode *spriteNode in starSqArray) {
spriteNode.position = CGPoint(x: spriteNode.position.x , y: spriteNode.position.y + GFloat(_starSpeed1))
}
Use the above code in the update() function.
Hope this helps...