why UIView variables could not create retain cycle problem? - uiview

When I searched code of UIView , I could not understand that why superview and subviews properities does not create retain cycle?
extension UIView {
open var superview: UIView? { get }
open var subviews: [UIView] { get }
open var window: UIWindow? { get }

A computed property is just a pair of a getter and a setter (or, in this case, it's a read-only property which is only a getter). The declaration doesn't tell you how any underlying data that the property uses might be stored or memory-managed.

I could not understand that why superview and subviews properities does not create retain cycle?
It's because the view hierarchy is a directed graph with respect to retains. Views retain their subviews, but subviews don't retain their superviews.

Related

Multiple ViewControllers contained in a UIStackView

I have a UIStackView and I am dynamically adding UIViewControllers contained, here is my code;
[self addChildViewController:driverForm];
[self addChildViewController:marketingView];
[self.stackView insertArrangedSubview:driverForm.view atIndex:0];
[self.stackView insertArrangedSubview:marketingView.view atIndex:1];
[driverForm didMoveToParentViewController:self];
[marketingView didMoveToParentViewController:self];
After reading the documents it states I must call didMoveToParentViewController.
The problem I am facing is, the actions on the final UIViewController are not being called, but the first ViewController does. If I swap these round the first one works and the last one does not.
Simply add the view of your ViewController to your UIStackView like this:
yourStackView.addArrangedSubview(yourViewController.view)
Also, you don't need to be worried about the view being nil as it always returns UIView!
Note that the order is reversed, so the last appears first. To address this, assuming you have an array of view controllers, you can use stride to traverse your array inversely and add view controllers to your stack.
Here is a quick copy/pasta version for Swift 5:
private var childViewController: UIViewController
private var stackView: UIStackView?
// MARK: - UIViewController
override func loadView() {
super.loadView()
// 1. Add the child view controller to the parent.
addChild(childViewController)
// 2 Create and add the stack view containing child view controller's view.
stackView = UIStackView(arrangedSubviews: [childViewController.view])
stackView!.axis = .vertical
self.view.addSubview(stackView!)
// 3. Let the child know that it's been added!
childViewController.didMove(toParent: self)
}
UIStackView is for arranging multiple subviews in the same UIViewController class. how can you use it for different UIViewControllers?
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIStackView_Class_Reference/#//apple_ref/doc/uid/TP40015256-CH1-SW31

Can't get access to custom UIView property

I have UIView subclass
class GraphView: UIView {
var test = false
}
And when I'm trying to get access to the test property via #IBOutlet, I get exc_bad_access error.
What is my problem?
The reason can be, you are trying to access the property of the #IBOutlet before the view has loaded.#IBOutlet is initialised only when view containing the outlet is loaded.
I just had this issue. It turned out the viewController's view was connected to the IBOutlet, instead of my custom view

Subview acessing superview methods

How can a subview access the methods from its superview? I have a button, and when pressed I would like the button to call a method from its superview, but I do not know how.
suppose your super View class name is
MainView.h
sub View Name
SubView.h
So In sub class you can do
MainView *myMainView = (Mainview *)[self superview];
[myMainView someMethod];
Make sure someMethod is public Method.
Other way you could have reference to all the view is set a tag
For example
myMainView.tag = 100; or self.tag = 100;
In the subview you could do
MainView *myMainView = (Mainview *)[self viewWithTag:100];
[myMainView someMethod];
a weird construct but just call the method:
inside a view you have have self.superview
since self.superview is a UIView*, the compiler will claim it is invalid to call method XYZ on it. Cast it to id or to your class name to use it
e.g.
[(id)self.superview myMethod];
or even
id myValue = [(id)self.superview myMethod:param1];
One method is to use delegates.
#protocol ButtonHandlingDelegate <NSObject>
- (void) subviewButtonWasPressed;
#end
In your Subview add this:
#property (nonatomic, weak) id selectionDelegate;
When subview is created, set delegate to superview.
Define Superview as delegate in .h file
#interface SuperView : UIView <ButtonHandlingDelegate>
in Superview .m file
- (void) subviewButtonWasPressed{
// Do somethign about it
}
All of the answers listed are hacky and bad style. You should be using delegation through the subview's ViewController. What you will want to do is create a protocol for your subview with a void method called something like specialButtonPressedOnView:(SUBVIEWCLASS *)view. Then in the subview's view controller you should make yourself the delegate for this protocol and respond to the delegate method from the VC's context.
Using self.superview is a bad idea because you cannot guarantee what your superview is going to be and generally blindly calling method on objects (ESPECIALLY those cast to id) is a really bad idea.
The other answer that suggested having the superview implement the subview's protocol is also not good style because you're creating a dependency between your views. If you were thinking of going down this path your subview should probably just be a subclass of the superview.
One of the core parts of the MVC design pattern is making sure that your views are reusable and totally independent. They are just structures that you can inject your content into, to which they will respond with pretty UI, and they don't need to know anything about what's being passed to them and don't have to ask other views for help. So either using delegation through the subview's ViewController or subclassing is probably the best direction. Both methods preserve the MVC pattern and are much safer than the other suggestions.

Invalidate a timer that belongs to a UIView subclass

I have a repeating timer that belongs to a UIView subclass.
The class has a nib that loads it and I'm using ARC.
I'd like to invalidate the timer when the UIView is either...
Removed from its superview
The ViewController that contains its superView is popped off the stack.
I can't seem to find a method like viewDidDisappear on UIView.
Is there any other way to intercept this?
At the moment, after the ViewController is popped the timer keeps firing and creating NSLog outputs.
For the view controller being popped: just use viewDidDisappear or similar. There's also UINavigationControllerDelegate that may be useful.
For the view itself: have you tried using willMoveToSuperview: method in UIView? I haven't verified this, but in theory the view will move to superview nil when it is removed from its superview.
So try the following in your view:
- (void)willMoveToSuperview:(UIView *)superview {
if (!superview) {
// cancel timers
}
}
There's also a willRemoveSubview: method, but that would get called on the superview rather than the view being removed.
Have you tried invalidating it in the dealloc

How to make IBOutlets out of an array of objects?

I want to make an array with a bunch of UIImageViews I have in Interface Builder. Instead of having 20 or 30
IBOutlet UIImageView *img1;
and linking them all that way, and then putting them into an array, is there a way to declare an array of IBOutlet UIImageViews?
Just so I don't have so many declarations in my header file.
It is possible, it’s called outlet collection. This is the way to define an outlet collection:
#property(retain) IBOutletCollection(UIImageView) NSArray *images;
Now you can stick more than one object into the outlet in the Interface Builder, the array will be created for you when the interface is loaded.
I'm a little late here but it may be easier to set the tag property of each ImageView in IB, then access them like [some_superview viewWithTag:tag] rather than keep a separate handle to each one.
Here is more easier way to do it.
Follow these steps to create an array of outlets an connect it with IB Elements:
Create an array of IBOutlets
Add multiple UIElements (Views) in your Storyboard ViewController interface
Select ViewController (In storyboard) and open connection inspector
There is option 'Outlet Collections' in connection inspector (You will see an array of outlets there)
Connect if with your interface elements
-
class ViewController2: UIViewController {
#IBOutlet var collection:[UIView]!
override func viewDidLoad() {
super.viewDidLoad()
}
}
Swift 3 and above:
#IBOutlet var stuckLabels: [UIImageView]
There's not, unfortunately, but you can keep all of the declarations on a single line:
IBOutlet UIImageView *img1, *img2, *img3, *img4;
The other option (probably best, since you have so many of these) would be to create them programatically and store them in an array, then add them to the view from your view controller class, using, for each,
[self.view addSubview:img];
Also, keep in mind that if the elements are static (like background elements), and you don't actually need to access them, you don't need to declare outlets for each; you can just add them to the nib file and forget about them.
Same goes for UIButton instances. If you don't need to change anything about the button, you can access it from the method that it calls, like so:
-(IBAction) buttonPressed:(id)sender {
UIButton *button = (UIButton *)sender;
// method guts
// stuff with button -- access tag, disable, etc
}

Resources