This question already has answers here:
Shorthand setter declaration for a subscript of an array in Swift
(2 answers)
Closed 8 years ago.
To keep it short, what I want to achieve is for example:
var actions: [String]{
get{
if (_actions==nil){
_actions = []
}
return _actions!
}
set{
_actions = newValue
}
subscript(index:Int) -> String{
set {
assert(index<_actions.count && index>=0, "Index out of range")
_actions[index] = newValue
}
}
}
I know subscript isn't an accessor for array, but then what is the most convinient alternative to do just that?
I truly appreciate for succinct answers if possible! Thank you very much!
Edit:
To extend my explanation for #jrturton,
What I am trying to achieve is whenever actions[i] is set to a newValue, I would like to do some extra computations, such as repositioning actions[i]'s respective subview.
But if i say actions[3] = "randomMethod", the computed setter for the entire array will get called. Right? So I'd like to find a way so that when actions[3] is set to a newValue, a function repositionView(3) can get called, for example.
I know other ways to do it, but my question simply askes if there is a more convinient way, like the example above: a computed setter, to do what I want?
Edit 2:
To show #Vatsal Manot what I truly mean, I removed getter for subscript, and here is a complete example.swift(which wont run due to error):
import UIKit
import Foundation
class DWActionsSubmenu: UIView{
var actions: [DWAction]{
get{
if (_actions==nil){
_actions = []
}
return _actions!
}
set{
_actions = newValue
}
subscript(index:Int) -> DWAction{
set {
assert(index<_actions.count && index>=0, "Index out of range")
_actions[index] = newValue
a()
}
}
}
var _actions: [DWAction]?
init(actions:[DWAction]?){
super.init()
_actions = actions
}
required init(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
}
func a(){
}
}
I'd wrap your actions list in a custom class that you can then access via subscripting. You can then add a block to be run whenever a subscripted member is set:
class ActionList {
private var actions = [String]()
var actionDidChange : ((Int) -> ())?
subscript(actionIndex:Int) -> String {
get {
return actions[actionIndex]
}
set {
actions[actionIndex] = newValue
if let actionDidChange = actionDidChange {
actionDidChange(actionIndex)
}
}
}
func addAction(action: String) {
actions.append(action)
}
func addActions(newActions:[String]) {
actions += newActions
}
}
Usage (in a playground):
let actionList = ActionList()
actionList.actionDidChange = {
actionIndex in
println("Action \(actionIndex) did change")
}
actionList.addActions(["One", "Two", "Three"])
actionList[2] = "New"
// Prints "Action 2 did change"
The following should work:
var actions: [String] = []
subscript(index:Int) -> String
{
get
{
assert(index < actions.count && index >= 0, "Index out of range")
return actions[index]
}
set(newValue)
{
assert(index < actions.count && index >= 0, "Index out of range")
actions[index] = newValue
}
}
Related
How can I create a predicate so that when the user selects "Full Body" it returns the entire list with no predicate? Right now, it is returning "part" which corresponds to the muscle groups I have set (Abs, Legs, Push, Pull). I want to return all of the options when "Full Body" is selected. How could I write an If statement so that the predicate is not used?
import SwiftUI
var parts = ["Abs", "Legs", "Push", "Pull", "Full Body"]
struct ExerciseList: View {
#State private var selectedPart = " "
var body: some View {
NavigationView {
VStack (alignment: .leading) {
NavigationLink(destination: AddExerciseView()){
Text("Add Exercise")
.fontWeight(.bold)
}
Picker("Body Part", selection: $selectedPart) {
ForEach(parts, id:\.self) { part in
Text(part)
}
}.pickerStyle(.segmented)
ListView(part:selectedPart)
}
}
}
}
import SwiftUI
struct ListView: View {
var part: String
#FetchRequest var exercises: FetchedResults<Exercise>
init(part: String) {
self.part = part
self._exercises = FetchRequest(
entity: Exercise.entity(),
sortDescriptors: [],
predicate: NSPredicate(format: "musclegroup == %#", part as any CVarArg)
)
}
var body: some View {
List(exercises) { e in
Text(e.exercisename)
}
}
}
It's not a good idea to init objects inside View structs because the heap allocation slows things down. You could either have all the predicates created before hand or create one when the picker value changes, e.g. something like this:
// all the Picker samples in the docs tend to use enums.
enum Part: String, Identifiable, CaseIterable {
case abs
case legs
case push
case pull
case fullBody
var id: Self { self }
// Apple sometimes does it like this
// var localizedName: LocalizedStringKey {
// switch self {
// case .abs: return "Abs"
// case .legs: return "Legs"
// case .push: return "Push"
// case .pull: return "Pull"
// case .fullBody: return "Full Body"
// }
// }
}
struct ExerciseListConfig {
var selectedPart: Part = .fullBody {
didSet {
if selectedPart == .fullBody {
predicate = nil
}
else {
// note this will use the lower case string
predicate = NSPredicate(format: "musclegroup == %#", selectedPart.rawValue)
}
}
}
var predicate: NSPredicate?
}
struct ExerciseList: View {
#State private var config = ExerciseListConfig()
var body: some View {
NavigationView {
VStack (alignment: .leading) {
Picker("Body Part", selection: $config.selectedPart) {
//ForEach(Part.allCases) // Apple sometimes does this but means you can't easily change the display order.
Text("Abs").tag(Part.abs)
Text("Legs").tag(Part.legs)
Text("Push").tag(Part.push)
Text("Pull").tag(Part.pull)
Text("Full Body").tag(Part.fullBody)
}.pickerStyle(.segmented)
ExerciseListView(predicate:config.predicate)
}
}
}
}
struct ExerciseListView: View {
// var part: String
let predicate: NSPredicate?
// #FetchRequest var exercises: FetchedResults<Exercise>
init(predicate: NSPredicate?) {
self.predicate = predicate
// self._exercises = FetchRequest(
// entity: Exercise.entity(),
// sortDescriptors: [],
//
// predicate: NSPredicate(format: "musclegroup == %#", part as any CVarArg)
// )
}
var body: some View {
Text(predicate?.description ?? "")
// List(exercises) { e in
// Text(e.exercisename)
// }
}
}
Since you are using Core Data you might want to use an Int enum in the entity for less storage and faster queries.
you could try this simple approach in ListView:
init(part: String) {
self.part = part
self._exercises = FetchRequest(
entity: Exercise.entity(),
sortDescriptors: [],
predicate: (part == "Full Body")
? nil
: NSPredicate(format: "musclegroup == %#", part as any CVarArg)
)
}
(first post)
usually im able to find answers here or elsewhere but no luck this time =(
Question: in Swift, how do you filter an array that is of a protocol type by an implementing type supplied as a function parameter?
protocol Aprotocol {
var number:Int { get set }
}
class Aclass: Aprotocol {
var number = 1
}
class AnotherClass: Aprotocol {
var number = 1
}
var array:[Aprotocol] = [ Aclass(), AnotherClass(), Aclass() ]
func foo (parameter:Aprotocol) -> Int {
return array.filter({ /* p in p.self == parameter.self */ }).count
}
var bar:Aprotocol = // Aclass() or AnotherClass()
var result:Int = foo(bar) // should return 2 or 1, depending on bar type
maybe this is not the right approach at all?
thanks!
Here is what I think you want:
return array.filter { (element: Aprotocol) -> Bool in
element.dynamicType == parameter.dynamicType
}.count
But I recommend this, which does the same, but without the useless instance of Aclass() which is passed in the answer on the top. Also this way is faster:
func foo <T: Aprotocol>(type: T.Type) -> Int {
return array.filter { (element: Aprotocol) -> Bool in
element.dynamicType == type
}.count
}
var result:Int = foo(Aclass)
The dynamicType will return the Type of an instance
Very easy:
return array.filter({ parameter.number == $0.number }).count
Kametrixoms solution works (if you use "is T" instead of "== type") but in my case, since i didnt know which implementing class was going to call it, had to go with this solution:
protocol Aprotocol: AnyObject {
var number:Int { get set }
}
class func foo(parameter: AnyObject) -> Int {
return array.filter ({ (element: Aprotocol) -> Bool in
object_getClassName(element) == object_getClassName(parameter)
}).count
}
I have the following Playground code:
// Playground - noun: a place where people can play
import Cocoa
extension Array {
func toHexString<CUnsignedChar>() -> String {
var returnString = NSMutableString(capacity: self.count * 2)
for i in self {
let val = i as Int // if someone would like to answer why casting this as a CUnsignedChar throws an error, I'd appreciate it -- but that's a separate question
returnString.appendFormat("%02X", val)
}
return returnString
}
}
var hashedString: String? {
get {
let x: CUnsignedChar[] = [0xA, 0xB, 0xC]
return x.toHexString()
}
}
println(hashedString)
This causes the error, "NSString is not a subtype of 'String'"
However, if I rewrite this code to be:
var hashedString: String? {
get {
let x: CUnsignedChar[] = [0xA, 0xB, 0xC]
var returnString = NSMutableString(capacity: x.count * 2)
for i in x {
returnString.appendFormat("%02X", i)
}
return returnString
}
}
println(hashedString)
I get no error.
Couple things I would recommend.
First, use UInt8 instead of CUnsignedChar for an array of bytes. Also, I would stay away from NSMutableString and use standard string concatenation and interpolation via Swift. I have not had much success trying to use CVarArgs inside Swift.
Here is my implementation:
extension Array
{
func toHexString() -> String
{
var hexString = ""
for value in self
{
if let integerValue = value as? UInt8
{
let stringValue = String(integerValue, radix: 16)
if integerValue < 0x10
{ hexString += "0\(stringValue)" }
else
{ hexString += stringValue }
}
}
return hexString;
}
}
let arrayOfBytes : Array<UInt8> = [ 0x0A, 0x13, 0x02, 0x2F, 0x22, 0x7A, 0xF1 ]
let hash = arrayOfBytes.toHexString()
let hashUppercase = hash.uppercaseString
It is unfortunate that you cannot create an extension solely for Array<UInt8> and must extend all Arrays, even if your method is only valid for one type.
I think it has to do with the fact that your extension does not return an optional, but your hashedString does.
However, the playground crashes like crazy when I try to mess around with the above code. =)
I begin my project with a split view controller as initial view controller and start it automatically from storyboard.
Generally, an app with this UI have one and only one split view controller as root, so I create a static variable in the subclass and set it when initialisation was done.
So I want try this behaviour with swift.
I read the Swift programming language guide book on iBook about Type properties (with static and class keyword) and trying a piece of code to the job:
import UIKit
class SplitViewController: UISplitViewController {
class func sharedInstance() -> SplitViewController {
return SplitViewController.instance
}
class let instance: SplitViewController = nil
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
self.initialization()
}
init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder);
self.initialization()
}
func initialization() {
SplitViewController.instance = self;
}
}
but I figured out when Xcode say the class keyword for type properties wasn't supported yet.
Did you have a solution to do this ?
Embedding a struct can work just fine as a workaround:
class SomeClass
{
// class var classVariable: Int = 0
// "Class variables not yet supported." Weird.
// Workaround:
private struct SubStruct { static var staticVariable: Int = 0 }
class var workaroundClassVariable: Int
{
get { return SubStruct.staticVariable }
set { SubStruct.staticVariable = newValue }
}
}
The SomeClass.workaroundClassVariable computed type property can then be used as if it were a stored type property.
Swift now has support for static variables in classes. This is not exactly the same as a class variable (because they aren't inherited by subclasses), but it gets you pretty close:
class X {
static let y: Int = 4
static var x: Int = 4
}
println(X.x)
println(X.y)
X.x = 5
println(X.x)
It seems to be possible to declare variables with static storage duration in file scope (as in C):
var sharedInstance: SplitViewController? = nil
class SplitViewController: UISplitViewController {
....
func initialization() {
sharedInstance = self
}
}
My preferred method is to just use a private file scope var outside of the class and then implement class/static getters and setters:
private var _classVar: Int = 0;
class SomeClass
{
public class var classVar: Int
{
get { return _classVar }
set { _classVar = newValue }
}
}
As of Swift 1.2 (available with Xcode 6.3b1 and onwards), static class properties and methods are supported.
class SomeClass
{
static var someVariable: Int = 0
}
Using a dispatch_once singleton model in Swift
Seems to be the best answer so far, avoiding the use of a global variable.
A solution enough similar than var in file scope but more customisable and near singleton is to use a struct which support static var as property of class
struct PersonSharedData {
static var backstore = ""
var data: String {
get { return PersonSharedData.backstore }
set { PersonSharedData.backstore = newValue }
}
}
class Person {
var shared=PersonSharedData() //<< pseudo class var
var family: String {
get { return shared.data }
set { shared.data=newValue }
}
var firstname = ""
var lastname = ""
var sexe: Sexe = .Unknown
}
Ok, with the solution of Nikolai that do the work. I post my changes in this thread for information
var instance: SplitViewController? = nil
class SplitViewController: UISplitViewController {
class func sharedInstance() -> SplitViewController? {
return instance;
}
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
self.initialization()
}
init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder);
self.initialization()
}
func initialization() {
instance = self
}
}
and for example, in my appDelegate, I can access this static method like this
SplitViewController.sharedInstance()!.presentsWithGesture = false
The wording in the error heavily implies this will be a language feature in the future.
You may want to resort temporarily to declaring a property variable in the Application Delegate and retrieve it from there. Not ideal, definitely an anti-pattern, but would give you a central place to retrieve the UISplitViewController when needed.
You have to wrap the class variables inside an inner struct variable
class Store{
var name:String
var address:String
var lat:Int
var long:Int
init(name:String, address:String, lat:Int, long:Int){
self.name = name
self.address = address
self.lat = lat
self.long=long
}
private struct FACTORY_INITIALIZED_FLAG { static var initialized: Bool = false
static var myStoreList:[Store]?
static func getMyStoreList()->[Store]{
if !initialized{
println("INITIALIZING")
myStoreList = [
Store(name: "Walmart", address: "abcd", lat: 10, long: 20),
Store(name: "JCPenny", address: "kjfnv", lat: 23, long: 34)
]
initialized = true
}
return myStoreList!
}
}
}
var a = Store.FACTORY_INITIALIZED_FLAG.getMyStoreList()
var b = Store.FACTORY_INITIALIZED_FLAG.getMyStoreList()
// only prints INITIALIZING once
Try this:
class var instance: SplitViewController {
return nil
}
It is called Type Property in Swift.
You define type properties with the static keyword. For computed type properties for class types, you can use the class keyword instead to allow subclasses to override the superclass’s implementation. The example below shows the syntax for stored and computed type properties:
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 1
}
}
enum SomeEnumeration {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 6
}
}
class SomeClass {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 27
}
class var overrideableComputedTypeProperty: Int {
return 107
}
}
Read more at link below,
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html#//apple_ref/doc/uid/TP40014097-CH14-ID254
I'm trying to convert some code that works in Objective-C to Swift. The problem I'm running into is that needsDisplayForKey/actionForKey aren't getting called the same way. As far as I can tell, the custom key values aren't getting passed in correctly. Here is what I get when I debug it:
default value:
(String!) event = {
core = {
_baseAddress = Builtin.RawPointer = 0x00feee51 "onOrderIn"
_countAndFlags = 1073741833
_owner = Some {
Some = (instance_type = Builtin.RawPointer = 0x01026348 #"onOrderIn")
}
}
}
custom value (empty string passed in):
(String!) event = {
core = {
_baseAddress = Builtin.RawPointer = 0x0b418f79
_countAndFlags = 1073741833
_owner = Some {
Some = (instance_type = Builtin.RawPointer = 0x0b418f70 -> 0x006e38f0 (void *)0x006e38c8: __NSCFString)
}
}
}
I'm not sure what the relevant code might be. I'll just ask - has anyone else was able to define a custom implicit animation in Swift? Is there anything I need to keep in mind when moving over from Objective C?
override class func needsDisplayForKey(key: String!) -> Bool{
if key == "angleFrom" || key == "angleTo" {
return true;
}
return super.needsDisplayForKey(key)
}
override func actionForKey(event: String!) -> CAAction!{
if event == "angleFrom" || event == "angleTo" {
return self.makeAnimationForKey(event)
}
return super.actionForKey(event)
}
I got this working in Swift by using #NSManaged attribute in front of the variable declaration (where you would use the the #dynamic attribute in Objective-C)