I have gameCenterButton in VC1. Its purpose is to take the user to Game Center's Leaderboards where they can see High Scores. If the user decides to authenticate with Game Center, then I want to change gameCenterButton's state (un-grey and enable). In my GameKitHelper class I have these:
func authenticateLocalPlayer() {
GKLocalPlayer.local.authenticateHandler =
{ (viewController, error) in
self.gameCenterEnabled = false
if viewController != nil {
self.authenticationViewController = viewController
NotificationCenter.default.post(name: NSNotification.Name(
GameKitHelper.PresentAuthenticationViewController),
object: self)
} else if GKLocalPlayer.local.isAuthenticated {
self.gameCenterEnabled = true
}
}
}
extension GameKitHelper: GKGameCenterControllerDelegate {
func gameCenterViewControllerDidFinish(_ gameCenterViewController: GKGameCenterViewController) {
gameCenterViewController.dismiss(animated: true, completion: nil)
}
}
In VC1 I have this:
#IBOutlet weak var gameCenterButton: UIButton!
#IBAction func gameCenter(_ sender: UIButton) {
GameKitHelper.sharedInstance.showGKGameCenterViewController(viewController: self)
}
I'm thinking that inside of extension GameKitHelper I can do ...
if gameCenterEnabled == true {
gameCenterButton.isEnabled = true // How do I allow for this?
gameCenterButton.alpha = 1 // How do I allow for this?
How do I allow gameCenterButton state to change outside of it's class. Is there something I need to do in AppDelegate?
Put var gameCenterEnabled = false outside (above) of your GameKitHelper class, thus making it "global". You will likely be prompted to remove the self. in self.gameCenterEnabled = false and in self.gameCenterEnabled = true. Do so.
Now, you can reference gameCenterEnabled in VC1's class and change gameCenterButton's state like this:
// code to determine gameCenterButton's state based on gameCenterEnabled's status
if gameCenterEnabled == false {
self.gameCenterButton.isEnabled = false
self.gameCenterButton.alpha = 0.37
} else {
self.gameCenterButton.isEnabled = true
self.gameCenterButton.alpha = 1
}
Related
I'm have a hard time creating a user setting options. I would like the user to customize the frequency of the timer to receive the local notifications. I'm using a switch on the SystemSettingsVC to for the user to select and set the user default and I'm using the user default setting in my MainVC for the TimerInterval. My app runs but the time doesnt change. I know that the switch is working because I'm also testing the background color change.
Here is my code for my SystemSettingsVC:
...
import UIKit
import SwiftUI
import CoreData
class SettingsViewController: UIViewController {
#IBOutlet weak var timeSelection: UISegmentedControl!
let userDefaults = UserDefaults.standard
let TIME_KEY = "TIME_KEY"
let ONE_HOUR_KEY = 60.0
let THREE_HOUR_KEY = 120.0
let SIX_HOUR_KEY = 300.0
override func viewDidLoad() {
super.viewDidLoad()
updateTime()
}
func updateTime() {
let time = userDefaults.object(forKey: "TIME_KEY")
if(time as? Double == ONE_HOUR_KEY) {
timeSelection.selectedSegmentIndex = 0
let userDefaults = UserDefaults.standard
userDefaults.set(60.0, forKey: "TIME_KEY")
view.backgroundColor = UIColor.white
save()
}
else if(time as? Double == THREE_HOUR_KEY) {
timeSelection.selectedSegmentIndex = 1
let userDefaults = UserDefaults.standard
userDefaults.set(120.0, forKey: "TIME_KEY")
view.backgroundColor = UIColor.gray
save()
}
else if(time as? Double == SIX_HOUR_KEY) {
timeSelection.selectedSegmentIndex = 2
let userDefaults = UserDefaults.standard
userDefaults.set(300.0, forKey: "TIME_KEY")
view.backgroundColor = UIColor.darkGray
save()
}
}
func save() {
if let savedData = try? NSKeyedArchiver.archivedData(withRootObject: clock, requiringSecureCoding: false){
let defaults = UserDefaults.standard
defaults.set(savedData, forKey: "TIME_KEY")
}
}
#IBAction func selectTimeOfQuotes(_ sender: Any) {
switch timeSelection.selectedSegmentIndex
{
case 0:
userDefaults.set(60.0, forKey: "TIME_KEY")
save()
case 1:
userDefaults.set(120.0, forKey: "TIME_KEY")
save()
case 2:
userDefaults.set(300.0, forKey: "TIME_KEY")
save()
default:
userDefaults.set(60.0, forKey: "TIME_KEY")
save()
}
updateTime()
}
}
...
Here is the code for my view controller to where I call the user defaults, I placed let userDefaults = UserDefaults.standard in my ViewDidLoad :
'''Code''' ```
func configureAlerts() {
let center = UNUserNotificationCenter.current()
center.removeAllDeliveredNotifications()
center.removeAllPendingNotificationRequests()
let listQuotes = quotes
let i = 1
let content = UNMutableNotificationContent()
content.title = “Inspire”
content.body = listQuotes[i].shareMessage
content.sound = UNNotificationSound.default
let alertDate = Date().byAdding(days: i)
var alertComponents = Calendar.current.dateComponents([.day, .month, .year], from: alertDate)
alertComponents.hour = 8
let userDefaults = UserDefaults.standard
typealias NSTimeInterval = Double
let thisTime:TimeInterval = userDefaults.double(forKey: "TIME_KEY")
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: thisTime, repeats: true)
let uuidString = UUID().uuidString
let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: trigger)
center.add(request) { error in
if let error = error {
print(error.localizedDescription)
}
}
You are not showing how your Models are connected so we can't tell where the miscommunication is happening or maybe that is the issue. They are not connected.
But at a simple glance you are not rescheduling the notifications. selectTimeOfQuotes, save or updateTime do not call configureAlerts.
Something to note you have a lot of repeating code and hardcoded values that could be the source of the confusion.
BTW 120 is 2 hours not 3 idk if that is on purpose but it highlights my next point.
When you change a value you only want to do it in 1 place; if possible; so centralizing the models will help you avoid having to change things in multiple places.
For the options for your picker an enum can hold everything.
enum NotificationInterval: Double, CaseIterable, Codable{
case ONE_HOUR_KEY = 3660 //TimeInterval == seconds
case THREE_HOUR_KEY = 10800 //TimeInterval == seconds
case SIX_HOUR_KEY = 21600 //TimeInterval == seconds
func label() -> String{
var result = ""
switch self {
case .ONE_HOUR_KEY:
result = "1 hour"
case .THREE_HOUR_KEY:
result = "3 hours"
case .SIX_HOUR_KEY:
result = "6 hours"
}
return result
}
func color() -> UIColor{
var result = UIColor.label
switch self {
case .ONE_HOUR_KEY:
result = UIColor.white
case .THREE_HOUR_KEY:
result = UIColor.gray
case .SIX_HOUR_KEY:
result = UIColor.darkGray
}
return result
}
///Key for storage of user selected interval
static var userDefaultKey: String{
"TIME_KEY"
}
///Saves value to store using the `userDefaultKey`
func saveToStore(){
var mgr = UserDefaultManager()
mgr.intervalTime = self
}
///Gets value from store using the `userDefaultKey`
static func getFromStore() -> NotificationInterval{
let raw = UserDefaultManager().intervalTime
return raw
}
///Gets the index for the object in the `allCases` array
func getAllCasesIndex() -> Int?{
NotificationInterval.allCases.firstIndex(where: {
self == $0
})
}
///Gets the index for the `userDefaultKey` stored object in the `allCases` array
static func getStoredIndex() -> Int?{
NotificationInterval.getFromStore().getAllCasesIndex()
}
}
Then since you have at least 2 unrelated classes that use the value store in user defaults you can centralize that work too
///This stores and retreives userdefaults to a predetermined store
struct UserDefaultManager{
//Having a single location for this will simplify UserDefault storage
//A use case would be switching to an App Group store when you decide to support watch in the future or if you want to add Widgets
private let store = UserDefaults.standard
///User selected interval for the notifications
var intervalTime: NotificationInterval{
get{
getObject(forKey: NotificationInterval.userDefaultKey, type: NotificationInterval.self) ?? NotificationInterval.ONE_HOUR_KEY
}
set{
save(newValue, forKey: NotificationInterval.userDefaultKey)
}
}
///Saves any Codable to UserDefaults
func save<T: Codable>(_ object: T, forKey: String){
let encoder = JSONEncoder()
do{
let encoded = try encoder.encode(object)
store.set(encoded, forKey: forKey)
}catch{
print(error)
}
}
//Gets any Codable from UserDefaults
func getObject<T: Codable>(forKey: String, type: T.Type) -> T?{
guard let saved = store.object(forKey: forKey) as? Data else {
return nil
}
let decoder = JSONDecoder()
do{
let loaded = try decoder.decode(T.self, from: saved)
return loaded
}catch{
print(error)
return nil
}
}
}
Then your SettingsViewController will look like this
class SettingsViewController: UIViewController {
///Programatic use of IBOutlet
var timeSelection: UISegmentedControl!
private let quoteManager = QuoteManager()
override func viewDidLoad() {
super.viewDidLoad()
//Create control PS I dont have a storyboard setup but you can replace this with your IBOutlet and IBAction
timeSelection = UISegmentedControl(items: NotificationInterval.allCases.map({
$0.label()
}))
timeSelection.addTarget(self, action: #selector(selectTimeOfQuotes), for: .allEvents)
//Set the initial value from storage
timeSelection.selectedSegmentIndex = NotificationInterval.getStoredIndex() ?? 0
self.view.addSubview(timeSelection)
timeSelection.translatesAutoresizingMaskIntoConstraints = false
timeSelection.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
timeSelection.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
//End of programatic setup
//Set the color from storage
view.backgroundColor = NotificationInterval.getFromStore().color()
}
///Programatic use of IBAction
#objc
func selectTimeOfQuotes() {
//Identify the selected interval
let interval = NotificationInterval.allCases[timeSelection.selectedSegmentIndex]
//Save it
interval.saveToStore()
//Change the color
view.backgroundColor = interval.color()
//Change the notification
quoteManager.rescheduleQuotes()
}
}
As the last line of code shows once all the work is done you should reschedule the quotes.
I created a mini-QuoteManager since you do not show this connection. This manager can be used by any View Controller to get the quotes and maybe even reschedule when the quotes change by calling the provided method.
//Adapt this to your use case this is just a sample
///Liason for quote Storege
struct QuoteManager{
var listQuotes = ["one", "two", "three"]
private let notificationManager = NotificationManager.shared
private let userDefaultsManager = UserDefaultManager()
///Reschedules quotes
func rescheduleQuotes(count: Int = 10){
let title = "Inspire"
notificationManager.deleteNotifications()
print(#function)
for n in 1..<count+1{
print(n)
let newDate = userDefaultsManager.intervalTime.rawValue*Double(n)
//Idenfier must be unique so I added the n
notificationManager.scheduleUNTimeIntervalNotificationTrigger(title: title, body: listQuotes.randomElement()!, timeInterval: newDate, identifier: "com.yourCompany.AppName.\(title)_\(n.description)")
}
}
}
The QuoteManager calls the NotificationManager. I created a small version below.
class NotificationManager: NSObject, UNUserNotificationCenterDelegate{
//Singleton is requierd because of delegate
static let shared: NotificationManager = NotificationManager()
let notificationCenter = UNUserNotificationCenter.current()
private override init(){
super.init()
//This assigns the delegate
notificationCenter.delegate = self
requestAuthorization()
}
func scheduleUNTimeIntervalNotificationTrigger(title: String, body: String, timeInterval: TimeInterval, identifier: String, repeats: Bool = false){
print(#function)
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = .default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval, repeats: repeats)
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
notificationCenter.add(request) { (error) in
if error != nil {
print(error!)
}
self.printNotifications()
}
}
func requestAuthorization() {
print(#function)
notificationCenter.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
if granted {
print("Access Granted!")
} else {
print("Access Not Granted")
}
}
}
func deleteNotifications(){
print(#function)
notificationCenter.removeAllPendingNotificationRequests()
notificationCenter.removeAllDeliveredNotifications()
}
///Prints to console schduled notifications
func printNotifications(){
print(#function)
notificationCenter.getPendingNotificationRequests { request in
print("UNTimeIntervalNotificationTrigger Pending Notification")
for req in request{
if req.trigger is UNTimeIntervalNotificationTrigger{
print((req.trigger as! UNTimeIntervalNotificationTrigger).nextTriggerDate()?.description ?? "invalid next trigger date")
print(req.content.body)
}
}
print("UNCalendarNotificationTrigger Pending Notification")
for req in request{
if req.trigger is UNCalendarNotificationTrigger{
print((req.trigger as! UNCalendarNotificationTrigger).nextTriggerDate()?.description ?? "invalid next trigger date")
}
}
}
}
//MARK: UNUserNotificationCenterDelegate
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler(.banner)
}
}
It might seem like a lot but if you focus on the SettingsViewController you will see how much simpler the whole thing becomes.
All this is working code. Just copy and paste into a .swift file.
You might have to change the UISegmentedControl since I created it programmatically but if you put the SettingsViewController in a blank storyboard it should work as is.
In my app I have a function where I want it to print something depending on the current view controller that is loaded. I do this by setting a global variable (Bool) and then toggling the flags in the view controller classes. From my main class I have something like this:
var FirstViewControllerisVisible: Bool = false
var SecondViewControllerisVisible: Bool = false
var ThirdViewControllerisVisible: Bool = false
#objc func PlayAgainfunc(_ sender: Any) {
if counter % 15 == 0 {
if FirstViewControllerisVisible == true {
print("First View Controller is visible")
} else if SecondViewControllerisVisible == true {
print("Second View Controller is visible")
} else if ThirdViewControllerisVisible == true {
print("Third View Controller is visible")
}
}
counter += 1
}
Then, in those classes I can set the flags like this:
override func viewDidAppear(_ animated: Bool) {
FirstViewControllerisVisible = true
}
override func viewDidDisappear(_ animated: Bool) {
FirstViewControllerisVisible = false
}
This worked great when it was exclusively Swift, but the problem with global variables is that they can't be accessed by Objective-C. I can't find a way to set flags in my ObjC classes and then check if true or false. For instance, If I tried:
- (void)viewDidAppear:(BOOL)animated; {
[(FirstViewControllerisVisible) == true];
}
- (void)viewDidDisappear:(BOOL)animated; {
[(FirstViewControllerisVisible) == false];
}
I would get the error 'use of undeclared identifier' because my global vars declared in Swift are Swift only.
After the view controller has loaded, you can
if let viewController = UIApplication.shared.keyWindow?.rootViewController {
print(type(of: viewController))
}
add below mentioned extension. you will get the desired result.
extension UIApplication {
/// will return currently showing view controller
static var topMostViewController: UIViewController? {
return UIApplication.shared.keyWindow?.rootViewController?.visibleViewController
}
}
extension UIViewController {
/// The visible view controller from a given view controller
var visibleViewController: UIViewController? {
if let navigationController = self as? UINavigationController {
return navigationController.topViewController?.visibleViewController
} else if let tabBarController = self as? UITabBarController {
return tabBarController.selectedViewController?.visibleViewController
} else if let presentedViewController = presentedViewController {
return presentedViewController.visibleViewController
} else {
return self
}
}
}
and access it like:
let viewController = UIApplication.topMostViewController
Happy Coding
Consider the following simplified code example. It's presented here in Swift, but the same behavior occurs in objective-c.
import Foundation
import Cocoa
class MainWindow : NSWindow {
#IBAction func onClick_openFile(sender : AnyObject?) {
let path = runOpenPanel(false);
NSLog(path as String)
}
#IBAction func onClick_crashyByeBye(sender : AnyObject?) {
let path = runOpenPanel(true);
NSLog(path as String)
}
private func runOpenPanel(useCrashyDelegate : Bool) -> NSString {
let openPanel = NSOpenPanel.init()
openPanel.canChooseDirectories = false
openPanel.canChooseFiles = true
openPanel.allowsMultipleSelection = false
let safeDelegate = MyOpenPanelDelegate.init() //same scope as openPanel.runModal()--works fine
if (useCrashyDelegate) {
let crashyDelegate = MyOpenPanelDelegate.init() //falls out of scope before openPanel.runModal() and crashes
openPanel.delegate = crashyDelegate
} else {
openPanel.delegate = safeDelegate
}
if (openPanel.runModal() == NSFileHandlingPanelOKButton && openPanel.URLs.count == 1) {
return openPanel.URLs[0].path!
}
return ""
}
}
class MyOpenPanelDelegate : NSObject, NSOpenSavePanelDelegate {
func panel(sender: AnyObject, shouldEnableURL url: NSURL) -> Bool {
var isDir : ObjCBool = false
if (NSFileManager.defaultManager().fileExistsAtPath(url.path!, isDirectory: &isDir)) {
return isDir || (url.path! as NSString).lastPathComponent.lowercaseString == "foo.txt"
}
return false
}
}
When the useCrashyDelegate argument to runOpenPanel is true, crashyDelegate is instantiated in a nested scope and falls out of scope before the call to openPanel.runModal(). Since the open panel assigns crashyDelegate as its delegate, I would expect crashyDelegate's reference count to be incremented. However, the application crashes with an EXC_BAD_ACCESS when useCrashyDelegate is true. If useCrashyDelegate is false, safeDelegate, which is instantiated in the same scope as the call to openPanel.runModal(), is assigned to the open panel and there is no EXC_BAD_ACCESS.
This is leading me to believe that NSOpenPanel is not incrementing its delegate's reference count. Is this the expected behavior, or might this be a bug?
This is leading me to believe that NSOpenPanel is not incrementing its delegate's reference count. Is this the expected behavior, or might this be a bug?
It is expected. Check the type of the property and you will see it is assign (Objective-C) or unsafe (Swift), a strong reference is not kept. This is a common design pattern for Cocoa delegates.
HTH
I have a program that has a NSTableView populated with files to be uploaded. Once the file is sent, the Text Cell with the file's name gets a hyperlink placed into it (the array data is given an NSMutableString with an NSLinkAttributeName attribute). How do I allow users to click this link to open the webpage in their default browser?
After much searching and trying multiple methods, this is what I came up with as a solution.
Creating a custom class that extends NSTableViewCell:
class TableViewCellCursor: NSTableCellView {
internal var active = false
//MARK: - View Life Cycle
override func awakeFromNib() {
superview?.awakeFromNib()
self.createTrackingArea()
}
//MARK: - IBActions
override func mouseEntered(theEvent: NSEvent) {
if (NSCursor.currentCursor() == NSCursor.arrowCursor() && active) {
NSCursor.pointingHandCursor().set()
}
}
override func mouseExited(theEvent: NSEvent) {
if (NSCursor.currentCursor() == NSCursor.pointingHandCursor() && active) {
NSCursor.arrowCursor().set()
}
}
//Informs the receiver that the mouse cursor has moved into a cursor rectangle.
override func cursorUpdate(event: NSEvent) {
if (active) {
NSCursor.pointingHandCursor().set()
}
}
//MARK: - Util
func createTrackingArea() {
var focusTrackingAreaOptions:NSTrackingAreaOptions = NSTrackingAreaOptions.ActiveInActiveApp
focusTrackingAreaOptions |= NSTrackingAreaOptions.MouseEnteredAndExited
focusTrackingAreaOptions |= NSTrackingAreaOptions.AssumeInside
focusTrackingAreaOptions |= NSTrackingAreaOptions.InVisibleRect
var focusTrackingArea:NSTrackingArea = NSTrackingArea(rect: NSZeroRect,
options: focusTrackingAreaOptions,
owner: self, userInfo: nil)
self.addTrackingArea(focusTrackingArea)
}
}
Checking first responder status when the NSTableView selection changes. This is necessary because the table's selection can be changed, even when it is not the firstResponder:
func tableViewSelectionDidChange(aNotification: NSNotification) {
if (self.firstResponder == filesToTransferTable) {
changeSelectedRowTextColorTo(NSColor.whiteColor(), unselectedColor: NSColor.blueColor())
} else {
changeSelectedRowTextColorTo(NSColor.blackColor(), unselectedColor: NSColor.blueColor())
}
}
func changeSelectedRowTextColorTo(selectedColor: NSColor, unselectedColor: NSColor) {
let selectedRows = filesToTransferTable.selectedRowIndexes
for (index, tableEntry) in enumerate (tableData) {
if tableData[index]["FileName"] is NSMutableAttributedString {
var name = tableData[index]["FileName"] as! NSMutableAttributedString
var range = NSMakeRange(0, NSString(string:name.string).length)
name.beginEditing()
name.removeAttribute(NSForegroundColorAttributeName, range: range)
if (selectedRows.containsIndex(index)) {
name.addAttribute(NSForegroundColorAttributeName, value:selectedColor, range:range)
} else {
name.addAttribute(NSForegroundColorAttributeName, value:unselectedColor, range:range)
}
name.endEditing()
tableData[index]["FileName"] = name
}
filesToTransferTable.reloadDataForRowIndexes(NSIndexSet(index: index), columnIndexes: NSIndexSet(index:0))
}
}
Adding KVO for checking when FirstResponder changes:
//This is somewhere in your code where you initialize things
//KVO for first responder behavior regarding tableView and updating attributedStrings' colors
self.addObserver(self, forKeyPath: "firstResponder", options: NSKeyValueObservingOptions.Old | NSKeyValueObservingOptions.New, context: nil)
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
if (change[NSKeyValueChangeNewKey] is NSTableView) {
changeSelectedRowTextColorTo(NSColor.whiteColor(), unselectedColor: NSColor.blueColor())
} else if (change[NSKeyValueChangeOldKey] is NSTableView) {
changeSelectedRowTextColorTo(NSColor.blackColor(), unselectedColor: NSColor.blueColor())
}
}
Finally, checking if the main window (the app itself) is in focus (if this is not done, then the colors won't change appropriately when the window loses focus):
//Put these in the same place as the KVO code
NSNotificationCenter.defaultCenter().addObserver(self, selector: "windowDidBecomeKey:",
name: NSWindowDidBecomeKeyNotification , object: self)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "windowDidResignKey:",
name: NSWindowDidResignKeyNotification , object: self)
func windowDidBecomeKey(notification: NSNotification) {
if (self.firstResponder == filesToTransferTable) {
changeSelectedRowTextColorTo(NSColor.whiteColor(), unselectedColor: NSColor.blueColor())
} else {
changeSelectedRowTextColorTo(NSColor.blackColor(), unselectedColor: NSColor.blueColor())
}
}
func windowDidResignKey(notification: NSNotification) {
if (self.firstResponder == filesToTransferTable) {
changeSelectedRowTextColorTo(NSColor.blackColor(), unselectedColor: NSColor.blueColor())
}
}
Text fields automatically support clicking on embedded links, but only if they are at least selectable (if not editable). So, set your text field to be selectable.
I know that theoretically it's possible to create multiple instances of the same class with a property that would have a different value for each instance.
The thing is, I can't make it happen.
Each time I'm creating a new instance, it gets the property's value of the other instances, and when I'm changing one value for an instance, it changes the other's too.
So my guess is that I'm doing something wrong (obviously), like accessing the class property value instead of the instance property value... Here's the code.
class CustomUIImageView: UIImageView {
var someParameter: Bool = false // This is the property I want to be different in each version of the instance.
}
class ClassSiege: UIViewController, UIGestureRecognizerDelegate {
var myView: CustomUIImageView! //the instance declaration.
// I use this gesture recognizer to find out the value of the instance I'm tapping on.
func handleTap (sender: UITapGestureRecognizer) {
print("value of someParameter \(self.myView.someParameter)")
}
func handlePan(recognizer: UIPanGestureRecognizer) {
let iv: UIView! = recognizer.view
let translation = recognizer.translationInView(self.view)
iv.center.x += translation.x
iv.center.y += translation.y
recognizer.setTranslation(CGPointZero, inView: self.view)
var centerBoardX = BlackBoard.center.x // 'Blackboard' is a fixed image on the screen.
var centerBoardY = BlackBoard.center.y
var centerRondX = iv.center.x
var centerRondY = iv.center.y
if centerRondY - centerBoardY < 100 {
self.myView.someParameter = true // If the distance between myView and the blackboard is under 100 I want the instance's property to become true.
} else {
self.myView.someParameter = false // On the other hand, if the distance is greater than 100, I want it to be false.
}
}
// When the user pushes a button, it triggers this method that creates a new instance of myView and add it to the screen.
#IBAction func showContent(sender: AnyObject) {
// some code...
// Here I'm creating the instance of the view and I give it the gesture recognizer parameters. I don't think that relevant to the issue, so I'm not adding the code.
}
}
So clearly that's not the good way to do it, but what's wrong, and how can it be solved?
Basing my answer on your related question.
If what you want to achieve is initializing a property with a value that you provide, just add a new parameter to the initializer. If for instance you are using the initializer with a CGRect passed in, then you can implement an initializer like this:
class CustomUIImageView : UIImageView {
let someParameter : Bool
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init(frame: CGRect, someParameter: Bool) {
self.someParameter = someParameter
super.init(frame: frame)
}
}
I hope that this is what you are looking for - let me know otherwise.
I've found the solution, and if you've been facing the same issu, here's how to deal with it.
The secret is to downcast the recognizer.view to take the parameter of the subclass CustomUIImageView.
here's how :
func handleTap (sender: UITapGestureRecognizer) {
println("value of someParameter \(self.myView.someParameter)") //I use this gesture recognizer to find out the value of the instance I'm tapping on.
}
func handlePan(recognizer:UIPanGestureRecognizer) {
let iv : UIView! = recognizer.view
let translation = recognizer.translationInView(self.view)
iv.center.x += translation.x
iv.center.y += translation.y
recognizer.setTranslation(CGPointZero, inView: self.view)
var centerBoardX = BlackBoard.center.x //blackboard is a fixed image on the screen.
var centerBoardY = BlackBoard.center.y
var centerRondX = iv.center.x
var centerRondY = iv.center.y
var myParameter = recognizer.view as CustomUIImageView //<- this is the key point. Downcasting let you access the custom subclass parameters of the object that is currently moved
if centerRondY - centerBoardY < 100 {
myParameter.someParameter = true //so now I'm really changing the parameter's value inside the object rather than changing a global var like I did before.
} else {
myParameter.someParameter = false
}
}
//when user pushes a button, it triggers this func that creates a new instance of myView and add it to the screen.
#IBAction func showContent(sender: AnyObject) {
some code...
//here I'm creating the instance of the view and I give it the gesture recognizer parameters. I don't think that relevant to the issue, so I'm not adding the code.
}