how to pass data between UICollectionViewLayout and UIViewController [duplicate] - objective-c

This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 7 years ago.
I need to create a custom layout based on http call to an api. Based on result from api in loop i will populate an array from CollectionView
In CustomLayout I need to use this array in order to draw correctly details in each row.
In collectionView I use reload data after the data was added to array
Thank YOu
code:
class epgViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource{
var events = [Event]()
#IBOutlet weak var collectionView: UICollectionView!
//another initializations
. . .
//====== load Events ===============
func get_events() {
//http api call
//as result in loop i append data to Events()
self.events.append(newEvent)
//reload collection view data
self.collectionView.reloadData()
}
}
//////////
/// LAyout
////////
class customCollectionViewLayout: UICollectionViewLayout {
//prepare layout
override func prepareLayout() {
for section in 0...collectionView!.numberOfSections()-1 {
how to access array from collectionView
Events[i] ????
}
}
}

I'm not entirely sure I understand your situation, but I'll provide two methods for communication within your application.
Using NSNotificationCenter
Add an observer In the viewDidLoad of the controller that needs an event fired:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "remoteRefresh:", name:"remoteRefreshID", object: nil)
In that same controller add an observer with a name the same as our selector, in the above example "remoteRefresh".
func remoteRefresh(notification: NSNotification) {
// do stuff
}
Then in your other controller can post messages and your remoteRefresh action will run:
NSNotificationCenter.defaultCenter().postNotificationName("remoteRefreshID", object: nil)
for more details I have a two part tutorial on NSNotificationCenter
Protocol / Delegate pattern
The second is the Protocol / Delegate pattern which is a bit more involved but I have a written tutorial here:
protocol / delegate pattern
Another Option
Here's an option if you're still having issues. You could declare a global struct to hold your data, paste this into a playground to understand.
struct SessionData {
static var events = [String]()
}
class classOne {
func addValues() {
SessionData.events.append("a message")
SessionData.events.append("another message")
}
}
class classTwo {
func getValues() -> [String] {
return SessionData.events
}
}
classOne().addValues()
classTwo().getValues()

Related

Is it possible to prevent property changes of an object from triggering didSet{} in Swift/Obj-c

I have an object bound to a controller as shown below:
// ViewController
#objc dynamic var objectX: SomeClass {
didSet {
print("something")
}
}
override func viewDidLoad() {
self.bind(NSBindingName(rawValue: "objectX"),
to: controller,
withKeyPath: "selectedObject",
options: nil)
}
An I expect didSet{} to be called if the controller has a different object selected. However if one of the properties of objectX gets changed then didSet{} is also getting called.
Surely this shouldn't be occurring - and if this is expected then is there any way to prevent property changes on the object itself from triggering the didSet{} or is it necessary to check in the didSet{} whether the object is a different one to the old one?
This is a bit odd because when I test in a Playground changing a property on the object does not trigger the didSet{} callback.

Unable to cast element in ForEach Loop to NSManagedObject Subclass SwiftUI

I got a ListView in SwiftUI and want to generate RowViews depending on a given property.
Therefore I want to check the property of the element in the ForEach loop.
Xcode does not recognize the type of the element, thats why i want to cast the element to the correct Class which is an NSManagedObject Subclass.
I created the NSManagedObject Subclass from the xcdatamodeld (Editor -> Create NSManagedObject Subclass...).
The NSManagedObject Subclass is created in objective-c and I added the .h file to the bridging header.
I have no problem working with the NSManagedObject Subclass in the rest of the project.
I can even create an property of that Type in the same Struct like so:
struct MyListView: View {
var test : MyNSManagedObjectSubclass //Xcode does recognize it here
For some reason Xcode wont recognize the NSManagedObject Subclass inside the ForEach Loop.
code looks like:
struct MyListView: View {
var test : MyNSManagedObjectSubclass //Xcode does recognize it here
#EnvironmentObject private var fetchResultsContainer : FetchedResultsContainer
var body: some View {
NavigationView{
VStack{
ForEach(fetchResultsContainer.results , id: \.identifier) { result in
if let castedResult = result as! MyNSManagedObjectSubclass { //Xcode does not recognize it here
if castedResult.property{
ResultRowView(input: result)
}
}
}
}
}
}
}
FetchedResultsContainer:
#objc class FetchedResultsContainer : NSObject, ObservableObject{
#objc #Published var results: [MyNSManagedObjectSubclass]()
#objc static let sharedInstance: FetchedResultsContainer = {
let instance = FetchedResultsContainer()
return instance
}()
}
I feel like im missing something obvious, as im still quite new to Swift and SwiftUI.
Appreciate your help.
Ok after taking a step back and simply coding it again from scratch it simply worked an no casting was needed...
List(fetchResultsContainer.results, id: \.identifier) { result in
if (result.property == 0) {
//do something
}
}
My assumption that the casting was not working was not correct.
I probably checked result.property == false, with property being of type NSNumber and it gave out some strange compile errors that led me on the wrong path.

SwiftUI - ObservableObject created multiple times

I have created an ObservableObject in a View.
#ObservedObject var selectionModel = FilterSelectionModel()
I put a breakpoint inside the FilterSelectionModel's init function and it is called multiple times. Because this View is part of a NavigationLink, I understand that it gets created then and along with it, the selectionModel. When I navigate to the View, the selectionModel is created again.
In this same View I have a "sub View" where I pass the selectionModel as an EnvironmentObject so the sub-view can change it.
AddFilterScreen().environmentObject(self.selectionModel)
When the sub view is dismissed, the selectionModel is once more created and the changes made to it have disappeared.
Interesting Note: At the very top level is a NavigationView. IF I add
.navigationViewStyle(StackNavigationViewStyle())
to this NavigationView, my selectionModel's changes disappear. BUT if I do not add the navigationStyle, the selectionModel's changes made in the sub view remain!! (But I don't want a split nav view, I want a stacked nav view)
In both cases - with or without the navigationStyle, the selectionModel is created multiple times. I can't wrap my head around how any of this is supposed to work reliably.
Latest SwiftUI updates have brought solution to this problem. (iOS 14 onwards)
#StateObject is what we should use instead of #ObservedObject, but only where that object is created and not everywhere in the sub-views where we are passing the same object.
For eg-
class User: ObservableObject {
var name = "mohit"
}
struct ContentView: View {
#StateObject var user = User()
var body: some View {
VStack {
Text("name: \(user.name)")
NameCount(user: self.user)
}
}
}
struct NameCount: View {
#ObservedObject var user
var body: some View {
Text("count: \(user.name.count)")
}
}
In the above example, only the view responsible (ContentView) for creating that object is annotating the User object with #StateObject and all other views (NameCount) that share the object is using #ObservedObject.
By this approach whenever your parent view(ContentView) is re-created, the User object will not be re-created and it will persist its #State, while your child views just observing to the same User object doesn't have to care about its re-creation.
You can instantiate the observable object in the init method, in this way you will be able to hold its value or the value won't disappear.
Instantiate this way in the view file.
#ObservedObject var selectionModel : FilterSelectionModel
init() {
selectionModel = FilterSelectionModel(value : "value to be saved from disappearing")
}
Instantiate this way in the viewModel file.
class FilterSelectionModel : ObservableObject {
#Published var value : String
init(value : String) {
self.value = value
}
}
This is a workaround that I found, but still, the init method is called multiple times and I didn't get any success with this issue.
In order to stop multiple initializing of the ViewModels as the view is declared in the Navigation View and SwiftUI uses struct which is a value type, so eventually these are initialized before the view is presented, therefore you can convert that view into a LazyView, so that it will only be initialized once the view is about to be presented or shown.
// Use this to delay instantiation when using `NavigationLink`, etc...
struct LazyView<Content: View>: View {
var content: () -> Content
var body: some View {
self.content()
}
}
You can call it like this...
NavigationLink(destination: LazyView { ViewTobePresented() })

How to use pointer from other classes in swift

I'm immigrating my old ObjectiveC code to swift. In ObjcC had a separate class to handle my Admob activity.
In this class I've created a pointer in the init func, and when changing scene, I could use this pointer to change the location of the ads banner.
#implementation MyAdsSupport
+(id)ShowAds:(My_Ads_Position)posIndex
{
if (_adsBannerPointer == nil)
{
_adsBannerPointer = [[KTFAdsSupport alloc]initAds:posIndex];
}
else
{
[_adsBannerPointer setAdsPosition:posIndex];
}
return _adsBannerPointer;
}
In Swift I created the Admob class, and managed to present ads on screen but when I try to call the pointer from another class it returns always nil.
Here is my Swift Code:
var adsPointer: My_Ads_Support!
func initAds(myView: UIViewController, atPos: My_Ads_Position) -> KTF_Ads_Support {
if adsPointer == nil {
adsPointer = self
adsPointer.ShowAds(myView: myView, atPos: atPos)
}
else
{
print("adsPointer ALIVE")
adsPointer.setAdsPos( atPos: atPos)
}
return self.adsPointer!
}
How can I set a pointer in Swift to be able to reach the ads banner from any scene?
In your Objective-C code you have three methods, the instance methods initAds: and setAdsPosition:, and the class method ShowAds:. The latter uses a variable, presumably declared static, called _adsBannerPointer.
Your Swift code is not the same. It has two methods, the instance methods initAds and setAdsPos, and one variable, the instance variable adsPointer.
In Swift class methods are termed type methods (as they can belong to classes, structs and enumerations) and are indicated by the use of the keyword static, type (class) variables are also indicated with static. So to follow your Objective-C model you need something along the lines of:
static var adsPointer: My_Ads_Support!
// instance init
init(startingPos : My_Ads_Position) { ... }
// instance set position
fun setAdsPos(atPos : My_Ads_Position) { ... }
static func showAds(myView: UIViewController, atPos: My_Ads_Position) -> KTF_Ads_Support { ... }
HTH

JSON from Objective-C in Swift

Connected Objective-C classes to the project Swift, as it is written here:
https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/buildingcocoaapps/MixandMatch.html
There's this code of the test project:
import UIKit
class ViewController: UITableViewController {
var locations:NSArray=[]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var jsonLoader:JSONLoader
var url = NSURL(fileURLWithPath: "http://mechnikova.info/api/pic2.php?task=1")
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0), {
self.locations = jsonLoader.locationsFromJSONFile(url)
})
}
JSONLoader - Objective-C class. This class is connected normally, without errors.
Get an error indicating that the character "{" in the function dispatch_async:
Variable 'jsonLoader' used before initialized
Something is wrong in declaration variables.... Help please!
You can use former methods such as objectForKey() or access a specific element in an array using square brackets. Examples can be found at this link. Hope this will help you parse your json!
I fixed it:
var jsonLoader:JSONLoader = JSONLoadeer()
:-)