I was confused by immutable copy and mutable copy in Objective-C - ios

Oddly, there is no command for removing all of a view’s subviews at once. However, a
view’s subviews array is an immutable copy of the internal list of subviews, so it is legal
to cycle through it and remove each subview one at a time:
for v in myView.subviews as [UIView] {
v.removeFromSuperview()
}
This content is in the Programming IOS 8,if the copy is immutable copy ,why it can change?

I don't think you are a really asking about immutable arrays here as you aren't invoking any methods on the array itself, so it's mutability cannot be an issue. The immutable attribute of the subviews array is how the view has decided to present the list to you. It's got nothing to do with how sub-views interact with parent views.
You appear to be confused about why a subview can remove itself from the parent view and you cannot; this is simply because the subview is a UIView-subclass and the parent view is a UIView-subclass and therefore the subview has access to all of the internal variables of the parent and can do whatever it likes to the parent. You cannot. This is deliberate as you don't know the intricacies of the view hierarchy (and don't want to), where as the UIView obviously does.
Another interesting aspect of the code you posted is that often getting an element in an array to remove itself from the array while you are enumerating it, will cause an exception. In this particular case, however the subviews array you receive from the view is a copy of the original (an immutable copy) and therefore getting the subview to remove itself from the parent view will not affect this array and the enumeration will not falter. Thanks to Christopher Kevin Howell for pointing this out, as I missed it completely, first time round.

The subviews array is immutable, so you can't change it. For example, you can't remove an element of the array yourself, or overwrite it with a completely new array.
However, there's nothing stopping the internal implementation of the class from changing it by overwriting it internally.
In this case though, the subviews array that is returned is a copy of the actual subviews array.

Related

Swift - Store the stacking order of views

I used view.addSubview(imageView) to add two UIimageview on a UIview. How do I store the stacking order of them and reproduce them with the exact order? Any suggestions? I wish to store the stacking order information on firebase.
Here is how you can do it with the approach mentioned in the other answers (using subviews of the parent),
if let indexOfImageView = view.subviews.firstIndex(where: {$0 === imageView})) {
// Save index of image view
}
From Apple Doc,
You can use this property to retrieve the subviews associated with your custom view hierarchies. The order of the subviews in the array reflects their visible order on the screen, with the view at index 0 being the back-most view.
For complex views declared in UIKit and other system frameworks, any subviews of the view are generally considered private and subject to change at any time. Therefore, you should not attempt to retrieve or modify subviews for these types of system-supplied views. If you do, your code may break during a future system update.
Ref : https://developer.apple.com/documentation/uikit/uiview/1622614-subviews
When you add a subview, it is stored in an array. To retrieve the array you have to write down just
view.subviews

Model NSArray mutable or immutable?

I never realised this dichotomy before so I need to ask you.
Let's say we have some kind of model manager class that will expose an NSArray of objects via public property. This is our model.
Now I also have a view controller that shows members of this array in tableview cells.
I set this NSArray as a datasource for tableview. But what if I want my model change data in time?
I have two options.
1) Make the array mutable.
2) Replace the instance NSArray with a different instance containing new data.
The issue with option 1 is anybody can change array's content which seems wrong.
The issue with option 2 is the tableViewController will happily keep pointing to the original array instance and ignore that the manager class is now pointing to a new instance (since it replaced it's property array instance with one having updated data.).
To sum it up, I want an array instance that can only be mutated from the model manager, but would be immutable to outside world. Which is impossible right?
Any ideas how to solve this problem?
Either the object managing the array should also be the table view's data source or the table view's data source should always make sure to get a fresh copy of the array from the object managing the array just before the table view reloads its data.
Either way, the array that the table view's datasource is working with should, in the end, be an immutable array, and any time this array is changed, a call to reloadData should immediately be made.
This will prevent the data in the array being modified in the middle of the table view displaying data. It's very problematic if the contents of the array change after numberOfRowsInSection: has been called, for example.

Custom ViewController objects don't persist through View changes

I have no idea if I'm using the right terminology here, but I'm hoping someone can help - I'm pretty new to iOS Dev and have run into a problem:
I have created a custom class that is used to store an object. It has a bunch of properties and functions, just as an object should. This object is being declared in my ViewController's .h file, and initialised and used throughout the ViewController. The object holds a bunch of information about a test, and takes some measurements in various threads.
The problem I'm facing is that when I load another view (using ECSlidingViewController for the menu) and then return to the view which had the object...it seems to have forgotten the object. The tasks running in the threads are all still running, but that instance of the object seems to be gone.
Is there a way to preserve an instance of the object when changing views so that when I return to the appropriate view, the object is still there and I can still use it?
Thanks!
I'd recommend you to put the objects reference in a transversal class, something like a manager or any other class which you consider appropriate according to your design and most important it needs to be a class that you're completely sure won't be release nor re-created as view controllers are usually when you change from one view controller to the other.
A singleton instance that manages your main logic could be a good option.
Turns out that my menuViewController that was implementing the ECSlidingViewController was recreating the view that contained the object each time I selected the item from the menu. Thanks #rdelmar for pointing this out!
The object was persisting, but in a viewController that was being replaced each time it was selected from the menu and hence the object was unreachable.
I simply implemented this and references to the viewControllers are now being stored in a mutable dictionary and simply recalled when the view is needed, rather than recreating the view. This means that the viewController is being reused and the object is persisting with it.

Using view space of a hidden UIView

I have three UIViews one below another in my application.The UIView's data is filled by API responses. I want to hide the UIView when it's corresponding API response is nil. I am checking the API response and if it is nil, I am setting [myview setHidden:YES] which hides the view but here is my problem.
Say, if the second view doesn't have any data, it is hidden but the view's coordinate is still occupied and I get big blank space between my first and third view.
To solve this problem, what I did was I used UIView animation method to move the third view in to the second view's coordinate space if second view data is nil.
This works fine for me but this kind of approach adds complexity to my code if I add fourth or fifth view. I am sure there are better solutions than my approach and would be helpful if you can guide me to a better approach than mine.
Thank you
Among the solutions provided I went with the solution provided by Jonah.at.GoDaddy which suited my app. Thank other too for providing great inputs.
Why not use a UITableView with 0-3 cells depending on on how many api responses you have?
What you could do is check the APIs and only do
UIView *APIView1=[[UIView alloc] init];
[self.view addSubview:APIView1];
when you know the API is available.
This could be a good approach for you:
1. Define coordinates for each place you would like to use for placing view and store those coordinates in array A.
2. Each time you add/remove a view to/from superview, add/remove reference to that view to/from array B.
3. After adding/removing references run a loop and assign coordinates from array A to view's frames from array B. Assign them through array ids.
4. Put that loop in UIView animation block.
Now you can use any number of views and remove/add them in which place you want.

Navigation arc memory not released

I have two table table controllers A and B.
A has a list of items and when an item is clicked, it pushes to B.
The problem is that when B was shown, the instrutment indicated a live memory increase, but it did not decreased when I clicked on back button.
The dealloc method in B was executed but the memory seemed changed slightly.
I checked that B was not retained anywhere else so what may the reasons that can cause this problem?
In B, there are many textfields, labels which are nonatomic and strong. I draw them in codes rather than using xibs. If I commented the codes for initializing them and adding them to the table header view, then the problem is gone.
I use Arc and simulator 6.1 by the way.
It seems to me that some of the textfields/labels inside B might have some strong reference to B itself making it impossible for arc to release them cause B has a strong pointer to and element inside it and this element has a strong pointer to B.
As far as I know thats not the default behaviour of textfields/labels, but maybe you've subclassed them...
Hard to try anything other than guessing with that little amount of information you gave
Do you need the views to be accessible in the view controller context? Can you not assign a tag to them and retrieve them from the tableview header when you need to change them?

Resources