Create a Second Array by copying items from the Primary Array [duplicate] - arrays

I want a picture to move to the bottom. If I press a button the pic should move down by 1.
I added the picture and a button:
var corX = 0
var corY = 0
var runter: UIButton = UIButton.buttonWithType(UIButtonType.System) as UIButton
var image = UIImage(named: "panzerBlau.jpg");
var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40)); //
override func viewDidLoad() {
super.viewDidLoad()
panzer.image = image; //
self.view.addSubview(panzer); //
runter.frame = CGRectMake(100, 30, 10 , 10)
runter.backgroundColor = UIColor.redColor()
view.addSubview(runter)
runter.addTarget(self, action: "fahren", forControlEvents:UIControlEvents.TouchUpInside)
}
At least I said in function "fahren" to move the picture down by 1.
func fahren(){
corY += 1
panzer.frame = CGRectMake(corX, corY, 30, 40) //
self.view.addSubview(panzer);
}
So my problem is: I get several errors with these corX and corY thing. Without them it works perfectly but than its like a single-use button. The errors are: ViewController.Type does not have a member named corX and ViewController.Type does not have a member names panzer Where I get the errors I made // to show in which lines.
PS: I use Xcode Beta5
Here's the complete code without anything else:
import UIKit
class ViewController: UIViewController {
var corX = 0
var corY = 0
var runter: UIButton = UIButton.buttonWithType(UIButtonType.System) as UIButton
var image = UIImage(named: "panzerBlau.jpg");
var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40));
override func viewDidLoad() {
super.viewDidLoad()
panzer.image = image;
self.view.addSubview(panzer);
runter.frame = CGRectMake(100, 30, 10 , 10)
runter.backgroundColor = UIColor.redColor()
view.addSubview(runter)
runter.addTarget(self, action: "fahren", forControlEvents:UIControlEvents.TouchUpInside)
}
func fahren(){
corY += 100
panzer.frame = CGRectMake(corX, corY, 30, 40)
self.view.addSubview(panzer);
}
}

#MartinR has pointed out the major issue here:
var corX = 0
var corY = 0
var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40))
The problem is that a Swift default initializer cannot refer to the value of another property, because at the time of initialization, the property doesn't exist yet (because the instance itself doesn't exist yet). Basically, in panzer's default initializer you are implicitly referring to self.corX and self.corY - but there is no self because self is exactly what we are in the middle of creating.
One workaround is to make the initializer lazy:
class ViewController: UIViewController {
var corX : CGFloat = 0
var corY : CGFloat = 0
lazy var panzer : UIImageView = UIImageView(frame: CGRectMake(self.corX, self.corY, 30, 40))
// ...
}
That's legal because panzer doesn't get initialized until later, when it is first referred to by your actual code. By that time, self and its properties exist.

Your dependent property needs to be:
lazy
Have an explicit : Type
Use self. to access other properties
Example:
let original = "foo"
// Good:
lazy var depend: String = self.original
// Error:
var noLazy: String = self.original // Error: Value of type '(NSObject) -> () -> URLData' has no member 'original'
lazy var noType = self.original // Error: Value of type '(NSObject) -> () -> URLData' has no member 'original'
lazy var noSelf: String = original // Error: Instance member 'original' cannot be used on type 'YourClass'

I'm addressing the title of the question:
Both lazy and computed properties help you deal with when the initial value for a property is not known until after the object is initialized. But there are some differences. I've highlighted the differences with bold.
If you simply need to initialize a variable after some other variable(s) is initialized then you should use lazy ie if the point is to simply add a delay (so all required properties get initialized before) then using lazy is the right way to go for it.
But if you need to constantly change a variable based on another, then you need a computed property that would work both ways:
if the computed property set then it sets the variables its related stored properties
if the stored properties are set (or are reset again) then it will trigger a change in then computed property.
if you change the lazy property's value it won't affect the storied properties that it was based on. see here
A good example for using a lazy property would be that once you have firstName & lastName then you would lazily instantiate a fullName and likely you would never change the firstName lastName of your object your fullName is a onetime only...
Or perhaps something that can only be done by lazy properties is that up until you don't access the property it won't ever get initialized, therefore this would decrease the initialization load of your class. Loading a heavy calculation.
Additionally using the lazy will signal to other developers: "Hey first go read about the other properties and understand what they are...then come to this lazy property...since the value of this is based on them + this is likely a heavy computation that shouldn't be accessed too early..."
As for computed property a good example would be if you set the temperature to Fahrenheit then you also want your celsius temperature to change its value...and if you set the celsius temperature then again you want to change your Fahrenheit value.
As a result computed property would add extra computation...and if your computation is very simple and isn't called too frequently then it's nothing to worry about but if it get's called too often or is very CPU-consuming then it might be better to think of other options...

//
// ViewController.swift
//
// Created by Shivank Agarwal on 19/05/18.
// Copyright © 2018 Shivank Agarwal. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
var corX = 0
var corY = 0
var runter: UIButton = UIButton()
var image = UIImage(named: "panzerBlau.jpg")
var panzer = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
panzer.image = image;
self.view.addSubview(panzer);
panzer.frame = CGRect(x: CGFloat(corX), y: CGFloat(corY), width: 30, height: 40)
runter.backgroundColor = UIColor.red
view.addSubview(runter)
view.addSubview(panzer)
runter.addTarget(self, action: Selector(("fahren")), for:UIControlEvents.touchUpInside)
}
private func fahren(){
corY += 100
}
private func updatePanzerFrame(){
panzer.frame = CGRect(x: CGFloat(corX), y: CGFloat(corY), width: 30, height: 40)
}
}
Note: Do not add panzer imageView every time when user tap only add it on viewDidLoad()

Related

UIButton to append array of UIViews to UIStackView

I have this code, but it is only appending the first uiview. I also want the label in the uiview to display the string from arrayTwo. It's only printing the count right now.
I have the stackView in storyboard set to fill equally. And only top, trailing and leading constraints.
If I don't care about animation, can I append to a container UIView and the same code should just work? Obviously swapping out the stackView for a uiview
class ViewController: UIViewController {
#IBOutlet weak var arrayButton: UIButton!
#IBOutlet weak var stackView: UIStackView!
var arrayView = MyView() /// lives inside a nib only has one uilabel
var arrayOfView: [MyView] = []
var arrayTwo = ["one", "two", "three", "four", "five"]
var arrayIndex = 0
override func viewDidLoad() {
super.viewDidLoad()
stackView.spacing = 4
}
func appendViews() {
for i in 0...arrayTwo.count - 1 {
arrayOfView.append(arrayView)
//arrayOfView[I].myLabel.text = arrayTwo[i]
arrayOfView[i].myLabel.text = arrayTwo.first
stackView.addSubview(arrayOfView[i])
stackView.addArrangedSubview(arrayOfView[i])
}
}
#IBAction func arrayButton Action(_ sender: Any) {
appendViews()
}
}
This line of code repeatedly adds the same object to the array:
arrayOfView.append(arrayView)
I assume that MyView is a class inherited from UIView, so in Swift in has reference type semantics (read more about value and reference types). When this line gets executed, no new instance of MyView gets created, instead the reference to the same existing object gets added to the array. Later, when you add subviews to the stack view, it just tries adding the same view over and over again.
What you need instead is to create a new instance of MyView for every iteration. That said, you'll no longer need the arrayView property.
There are also a few other issues that can be addressed:
The raw for-loop can be replaced with a much safer for-in statement to avoid operating directly with array indices.
You almost never call the addSubview for UIStackView because it will not automatically arrange the views added that way. Calling just addArrangedSubview is what you always want.
The arrayIndex property seems to be unused and can be deleted.
arrayTwo is not a good name because it doesn't tell anything about the contents and the semantics, try making it more descriptive like labelTexts.
After all you get something like this:
for text in labelTexts {
let myView = MyView()
arrayOfView.append(myView)
myView.myLabel.text = text
stackView.addArrangedSubview(myView)
}

Cannot convert value of type '[String?]' to expected argument type 'Video'

I have been trying to understand the problem below. Since I am new to Swift whatever I am coding may not make any sense at all and therefore there's no way to explain. I understand. The codes are copied from some online tutorials and I am trying to incorporate data persistence into it using Core Data. This is where I got stuck half way through.
Hope someone can help.
enter code here
Core Data model attached to the following view controller :
entity : Video
attribute : image - String
attribute : title - String
import UIKit
import CoreData
class VideoListScreenVC: UIViewController {
#IBOutlet weak var tableView: UITableView!
var videos : [Video] = [ ]
override func viewDidLoad() {
super.viewDidLoad()
videos = createArray()
}
func createArray() -> [Video] {
var tempVideos : [Video] = []
if let context = (UIApplication.shared.delegate as AppDelegate)?.persistentContainer.viewContext{
let videoX = Video(context: context)
let v1 = videoX.image
let t1 = videoX.title
let video1 = [v1,t1]
let video2 = [v1,t1]
let video3 = [v1,t1]
let video4 = [v1,t1]
let video5 = [v1,t1]
tempVideos.append(video1) // error : Cannot convert value of type '[String?]' to expected argument type 'Video'
// tempVideos.append(video2)
// tempVideos.append(video3)
// tempVideos.append(video4)
// tempVideos.append(video5)
return tempVideos
}
}
}
Let's consider the typing of each line:
This line:
var tempVideos : [Video]
means tempVideos is an array containing items of type Video
These lines:
let v1 = videoX.image
let t1 = videoX.title
means v1 is a String (I guess it's the URL of the image, which is a String), and t1 is also a String (the title of the video).
This line:
let video1 = [v1,t1]
means you are building an array with 2 items (v1 and t1).
v1 and t1 are both String items, so video1 is an array containing items of type String
Let's recap:
tempVideos is an array containing items of type Video
video1 is an array containing items of type String
So:
tempVideos.append(video1)
means I want to put an array of String (which is [String?]) inside an array that can only contain Video items.
Thus, the error:
error : Cannot convert value of type '[String?]' to expected argument type 'Video'
The solution is to create video1 as a Video object (I can't tell you how, that's your code, not mine), then you can do tempVideos.append(video1)

godot invalid get index '-1'(on base 'array') during A.P.I

I'm making an A.P.I. that displays dialogue, choices, sprites, audio etc. etc., i was able to do it after looking up this video on youtube but i keep getting this error invalid get '-1' (on base 'array') here's my code so you can see
extends Control
onready var bg_rect = get_node("Media_Sys/BG_Rect")
onready var char_sprite = get_node("Media_Sys/Sprite")
onready var text_box = get_node("Dialog_Sys/Dialog_Text")
onready var text_nam = get_node("Dialog_Sys/Label/Name")
onready var click = get_node("ClickManager")
var player = []
var lu = "Lu"
var ciel = "Ciel"
func _ready():
click.request_ready()
func write(char_name_str, text_str=null):
if text_str == null:
text_str = char_name_str
char_name_str = ""
player.push_front({"type": "dialogue","name": char_name_str, "text": text_str})
func write_component():
text_nam.clear()
text_box.clear()
text_nam.add_text(player[player.size() - 1]["name"])#the debugger says the problem is here
text_box.add_text(player[player.size() - 1]["text"])#as well as here if you remove the first line.
player.pop_back()
func _input(event):
if event is InputEventMouseButton:
write_component()
I have some people say that it's because i didn't define the size of the array, i have some people say that it's really weird that my code somehow set the player as an integer. I've check the array docs page for this and came up with nothing, I've changed some minor things in the code but it still churns out the same invalid error.
extends Node
onready var g = get_parent()
var lu = "Lu"
var ciel = "Ciel"
func _ready():
g.write(lu, "hello")
One thing to note is that it does display the dialogue and name, i just get the error immediantly afterwards.
You initialize the player variable to be an empty list. Then, in write_component you attempt to access this list with the calculated index [player.size() - 1]. The issue is that if player is empty, this ends up as [0-1] or [-1] as an index, which is not valid in GDScript. Your code assumes Write will be called first (and it may, some of the time) so that player is not empty by the time write_component is called.
The most immediate solution is to check if player.size() == 0 before attempting to access the last element.
func write_component():
text_nam.clear()
text_box.clear()
if player.size() > 0:
text_nam.add_text(player[player.size() - 1]["name"])
text_box.add_text(player[player.size() - 1]["text"])
player.pop_back()

variable of object type as an Array misbehaving

environment XCode 6.4 Swift 2.something
I have two simple Custom Classes
class Stock {
var ageArray = [3,6,9,12,15,18,24,30,36,42,48,54,60,66,72] // in months
var beastArray = ["Angus","Braford","Charolais","Droughtmaster","Euro","Hereford"]
var breedArray = ["Calves","Vealers","Weaners","Heifers","Steers","Cows","Bulls"]
var priceArray = [600.00,650.00,700.00,750.00,800.00,850.00,900.00,950.00,1000.00,]
}
and
class Sale {
var age : Int = 0
var beast : String = " "
var breed : String = " "
var price : Double = 0.00
}
Now to my view controller
I've instantiated a report from Sale Class. The ViewController holds an array of all those reports as I'm taught in nuremous places, declaring it first and instatiating it in viewDidLoad
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var picker: UIPickerView!
var report = Sale()
var sheets = Stock()
var saleArray : [Sale] = [] // declaration
override func viewDidLoad() {
super.viewDidLoad()
saleArray = [Sale]() // instantiating
}
the other delegate functions of the picker work sweetly, no need to show them all here, but this is where the two class Objects meet to pass values
func pickerView(picker: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
switch(component) {
case 0:
report.breed = sheets.breedArray[row]
break
case 1:
report.beast = sheets.beastArray[row]
break
case 2:
report.age = sheets.ageArray[row]
break
default:
report.price = sheets.priceArray[row]
break
}
}
and an onscreeen button loads them into an array
#IBAction func AcceptValues(sender:UIButton) {
saleArray.append(report)
}
that's all there is to it.
This is the result as seen in the debugger with a breakpoint set as below after 3 times around with [0] supposed to have Angus Calves and [1] having Droughtmaster Heifers
so the question is why would the saleArray be filling up with n instances of whatever was last entered? I know i'm not looking at the last entry n times. But curiously the address for report is all the same at 7A19C000.
I thought it might be the compiler can't infer the array type after the instatiaton in viewDidLoad. Examining various tutorials and the code supplied with them treats the matter more or less as I have above. Which is the reason for the title, I'm sure Swift can hold an array of objects other than the usuals.
I've even gone to the expense at purchasing not one, but two of Matt Neuberg's books just to check on how to set up an Object Array, but like all the material available they just discuss arrays of Int and Double and String and no more. Sometimes the most infuriating bugs are the most staring-at-you-in-your-face, and I've been chasing this for close on a week.
There's little else to it the code.
EDIT:
thanks to the two contributors below, the comments show what i did to make it work. But I have a feeling its not elegant:
I also made Stock a struct.
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var picker: UIPickerView!
#IBOutlet weak var textPanel: UITextView!
var sheets = Stock()
var saleArray : [Sale] = []
var localBreed = " " // introduced some handover values
var localBeast = " " // because report is out of scope
var localAge = 0 // in the picker functions
var localPrice = 0.00
.
.
.
#IBAction func AcceptValues(sender:UIButton) {
var report = Sale() // new reports instantiated here
report.breed = localBreed // and locals handover values to
report.beast = localBeast // report instance before adding
report.age = localAge // to array
report.price = localPrice
saleArray.append(report)
}
.
.
.
func pickerView(picker: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
switch(component) {
case 0:
localBreed = sheets.breedArray[row]
break
case 1:
localBeast = sheets.beastArray[row]
break
case 2:
localAge = sheets.ageArray[row]
break
default:
localPrice = sheets.priceArray[row]
break
}
}
I believe that it is because report is a single instance of Sale. Therefore each time you select a new combination - you are not creating a new Sale item, rather you are changing the values associated with that single instance of Sale called report. When you press the button it adds another reference to that single report instance.
These class objects are passed by reference - i.e. when you add report to the saleArray it is not creating a copy of the values - rather you are passing a reference to the actual instance. Each time you press the button you are adding a reference to the same instance of Sale. And the values associated with that instance are then getting changed when you use the picker. It's the same instance over and over.
You would need to change your code so that you create a new instance of Sale for each selection. As a quick hack, to test this, you could add code to your button which creates a new Sale instance based on the selected values - and then adds that to saleArray.
ETA - one other thing I noticed: You are instantiating saleArray twice - both at //declaration and in viewDidLoad(). Although that is not causing the issue you have described, it could cause an issue under potential circumstances.

Found nil while unwrapping an Optional value (Music Library)

var songList = NSMutableArray()
var player = AVPlayer()
var isSelected = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var songs = MPMediaQuery() // also tried var songs = MPMediaQuery.songsQuery()
var localSongs: NSArray = songs.items
songList = NSMutableArray(array: localSongs)
tableView.reloadData()
var song: MPMediaItem = songList[0] as MPMediaItem // <<- Error here
var currentItem = AVPlayerItem(URL: song.valueForProperty(MPMediaItemPropertyAssetURL) as NSURL)
player.replaceCurrentItemWithPlayerItem(currentItem)
player.play()
var songTitle: NSString = song.valueForProperty(MPMediaItemPropertyTitle) as NSString
songName.text = songTitle
var time = Float(CMTimeGetSeconds(player.currentItem.duration))
sliderOutlet.value = time
}
I'm building a music player, but the app crasher when i run in the simulator and gives this error: index 0 beyond bounds for empty array, and says this when i try run on iPhone : {
MediaLibrary} Database validation succeeded
fatal error: unexpectedly found nil while unwrapping an Optional value
EDIT: Seems like it's not accessing the local music library on my iPhone and adding songs to the array.
You're accessing an element without testing its size. So songList[0] is out of bounds, hence the crash.
If your media query returns no items, then localSongs and songlist will both be arrays of size 0. When you then try to reference a song with songList[0] you will be accessing index 0 in an empty array. Hence the crash.
Seems more like a problem with expecting the simulator to have music in it. Since it doesn't, songList.count == 0, which means songList[0] should generate an exception. Seems like the expected and desired behavior to me.
Put a check on count using if condition. You are accessing index which can be nil. If accessed it will through runtime error.
Preferred approach would it be wrapped explicitly with ?

Resources