Add a custom view to the UIViewController from the custom view class - ios

I have a custom view. I named AnimatingView. I have customized the view in the AnimatingView.swift. In the ViewController, I am initiating the view with
let customAnimatingView = AnimatingView(overView: self.view)
and then I add the subView with -
self.view.addSubview(customAnimatingView)
However, I don't want the user add it as a subView like above, rather. I want to call a func from AnimatingView and the custom view should add it the controller like -
customAnimatingView.preset()
Can anyone give me some hint on how can I achieve such behavior.

Inside the custom view do
// add a property that refers to the the vc container of the view
// init it when you create an instance of the view
weak var parentController:UIViewController?
func present() {
parentController?.view.addSubview(self)
}
OR if you need to send the main view instead
var parentView:UIView?
func present() {
parentView?.addSubview(self)
}

Related

Same button across all view controllers in ios app

I am developing an app with 10 view controllers, including 2 webview controllers. Every view controller contains a 'send feedback' button on top right with exactly same style and functionality.
Right now, I am writing exact same code in each view controller for the buttons.
I was wondering if there is a way to write the method only at one place and use it for all the buttons, since the function is same.
I am using swift 3.
Create new subclass of view controller:
class SendFeedbackViewController: UIViewController {
#IBOutlet weak var sendFeedbackButton: UIButton!
#IBAction func sendFeedback() {
/* do whatever you need */
}
}
Then subclass all your view controllers from this new view controller:
class YourViewController: SendFeedbackViewController {
/* your entire logic */
}
In storyboard set class type:
Now you can connect your send feedback button outlet and action:
Use extension of UIViewController.
extension UIViewController {
func sendFeedBack() {
//write your code here
}
}
And call from any ViewController.

Subclassing UIView

I see this topic is discussed elsewhere but I don't see an answer to my questions.
I subclassed UIView to create a custom view. Currently I'm using interface builder to create a UIView and then setting the custom class option to my subclass.
First question. When I do that, how to I reference that instance from my code? I have a public function I would like to call that updates my view but I don't know how to call it from my view controller
Second question. I created an instance of the class from within my view controller just playing around and I noticed the public function I created isn't available with that instance. Can I create public functions when I inherit from UIView?
It is easy to do:
1)subclass UIView to create CustomView, add your public function,in your project:
import UIKit
class CunstomView: UIView {
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
public func printHello() {
print("hello")
}
}
2)In your storyboard, drag a UIView into your vc, and set the class to CunstomView, you can see that in my red frame:
3)click the Show the Assistant Editor, and ctrl-drag the view to the vc, set the name custom:
4)then in your vc's viewDidload function, you call the public function:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var custom: CunstomView!
override func viewDidLoad() {
super.viewDidLoad()
custom.printHello()
}
}
5)the result:
First question. When I do that, how to I reference that instance from
my code? I have a public function I would like to call that updates my
view but I don't know how to call it from my view controller
A: A view cannot exist by itself in app. You need viewcontroller to handle the custom view. Then in that VC, you can refer the view as IBOutlet.
Second question. I created an instance of the class from within my
view controller just playing around and I noticed the public function
I created isn't available with that instance. Can I create public
functions when I inherit from UIView?
A: You can create public function of custom view, just declare them in the header file. Then import the header in your VC. Then u can access it.
You can try to update your view from IB with the mothod below.
Objective-C:
-(void)awakeFromNib {
[super awakeFromNib];
//do something
}
Swift
override func awakeFromNib() {
super.awakeFromNib()
}
Second question
Do you mean the custom view don't answer the function you create within the view controller?

Changing view from custom class

I have a custom class. It loads when the app starts. I have to change the view inside this class method.
Class:
import Foundation
class ChatManager {
class var sharedInstance: ChatManager {
struct Singleton { static let instance = ChatManager() }
return Singleton.instance
}
override init() {
super.init()
}
function changeView() {
//I need to change view here.
}
}
I can change the view inside a view controller but this class is not an UIViewController
What should I do ?
My suggestion would be to do it in UIViewController. Your class should not be responsible for the View part.
But that's not your question. What you could do is send YourView as a parameter to the function do some logic there and return it. If you have to resize YourView on several different location, then create method for that inside YourView class. If you use same logic for several different UIViews create BaseView and implement that method there, and then inherit in your views BaseView.
I have to mention it again, this is not the place to do any UI-related stuff.
This is how you pass your UIView through. I agree with Nick however, you should be doing View logic in a custom View class or the UIViewController class.
function changeView(yourView: UIView) {
//I need to change view here.
}
I agree with Nick. The answer is, don't. You should treat a view controller's views as private. Mucking around with a view controller's views violates the OOP principle of encapsulation.
If you insist on having an outside class make changes to your views, create an instance method in your class that takes a view as a parameter and then applies changes to that view. Then you can call the method from your view controller class to make changes to your view.
Here is a solution that requires a navigation controller. If you are unfamiliar with navigation controllers, I strongly recommend looking at a tutorial. They make life much easier for iOS developers. Sorry if that code is running off your screen.
// Switches to MyViewController, a class I have implemented somewhere else
func moveToMyView() {
if let navController = getNavigationController() {
// The Identifier was set in the Identity Inspector tab on the storyboard (The field is called "Storyboard ID")
if let myController = navController.storyboard?.instantiateViewControllerWithIdentifier( "myClassID" ) as? MyViewController {
// Switch to the newController
navController.pushViewController( exerciseController, animated: true )
}
}
}
// Returns the navigation controller if it exists
func getNavigationController() -> UINavigationController? {
if let navigationController = UIApplication.sharedApplication().keyWindow?.rootViewController {
return navigationController as? UINavigationController
}
return nil
}

Why does initializing a view controller with nibName not set the new instance as file owner?

I am trying to display one view or another view inside the detail view of a master/detail based on a conditional.
These views will contain outlets and elements, so I would like to have view controllers for each that I can play with.
So I created a new UIViewController called AddPhotoViewController. This is how I add AddPhotoViewController.xib inside DetailViewController:
let photoVC = AddPhotoViewController(nibName: "AddPhotoViewController", bundle: nil)
let photoView = photoVC.view
photoVC.delegate = self
photoView.autoresizingMask = UIViewAutoresizing.FlexibleWidth
photoView.frame = area.bounds
area.addSubview(photoView)
The view loads properly in the detail view and looks like this:
AddPhotoViewController.xib's owner class has been set as well here:
When I tap the button, though the action is set properly in AddPhotoViewController to print a message, Xcode crashes.
Am I doing this correctly? Is there a more common practice for loading view X or view Y inside a view controller depending on user data?
Button action:
#IBAction func ButtonPressed(sender: AnyObject) {
println("worked!")
}
Button connection:
Console output:
I think you need to add the viewController:
addChildViewController(PhotoVC)
//and then
PhotoVC.didMoveToParentViewController(self)

Present subclassed view controller from another view controller in Swift

I have some problems to use subclasses in Swift, hope someone can help me.
What I have
Two view controllers:
VC1 with just some UIButtons
EffectVC that do some animation depending on the button pressed on VC1
import UIKit
protocol viewAnimation {
func initialStateSet()
func finalStateSet()
}
class EffectVC: UIViewController {
#IBOutlet weak var mainImage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
self.initialStateSet()
}
override func viewDidAppear(animated: Bool) {
self.finalStateSet()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func initialStateSet() {
}
func finalStateSet() {
}
}
class GrowingEffect : EffectVC, viewAnimation {
override func initialStateSet() {
// some stuff
}
override func finalStateSet() {
// other stuff
}
}
The problem
Maybe a simple question but I can't do what I want in Swift: I need to set a subclass according to the button that is pressed.
In other words I need to present subclassed view controller from my VC1 according to which button is pressed on VC1.
If I press the first button for example I want to show the VC 2 with the class GrowingEffect for use some custom stuff (this stuff must change according to the selected button).
What I tried
use IBAction for create my subclassed VC2 and show it
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destinationViewController : UIViewController = storyboard.instantiateViewControllerWithIdentifier("EffectVC") as! GrowingEffect
self.presentViewController(destinationViewController, animated: true, completion: nil)
but I got
Could not cast value of type 'ViewAnimationDemo.EffectVC'
(0x109948570) to 'ViewAnimationDemo.GrowingEffect' (0x109948650).
use PrepareForSegue
but I can't set any subclass
What I really want to do
I know there are some other solution, like not using storyboard, but now I describe exactly what I want to do, hoping this is possibile:
have only one view controller in IB (EffectVC) associate with the class EffectVC. The class EffectVC has some subclasses like GrowingEffect.
In my code I want to instantiate the view controller EffectVC with the subclass that I need: for example instantiate the view controller in IB EffectVC with the class GrowingEffect.
I know that if I have one view controller for every subclass of EffectVC I can do what I want but I don't want so many view controller in IB because they are equal, the only things that I want to change are 2 methods.
I think there are some things mixed up in your setup. You should have 2 view controllers, each set up in its file, and each present in the storyboard with its identifier. It is ok if GrowingEffect inherits from EffectVC.
What you currently do with as! GrowingEffect is actually trying to cast the UIViewController instance you get from calling instantiateViewControllerWithIdentifier("EffectVC") to GrowingEffect. This will not work, because it is of type EffectVC.
Rather, you need to call instantiateViewControllerWithIdentifier("EffectVC") if button X is pressed, and instantiateViewControllerWithIdentifier("GrowingEffect") if button Y is pressed.
EDIT
If you use storyboard, you have to instantiate view controllers using instantiateViewControllerWithIdentifier. But you can only get an instance of GrowingEffect, if it is present on the storyboard.
It is not possible to "cast" an instance of EffectVC to GrowingEffect once created.
So, you have two possibilities here:
Use storyboard and put both view controllers on it. Use instantiateViewControllerWithIdentifier to instantiate the view controller you need, depending on the button pressed.
Do not use storyboard. Then you can create the needed view controller manually and use your UINavigationController's pushViewController method to present it.
You can't cast from parent class to child class, parent class just doesn't have the capacity to know what the child is doing. You can however cast from a child to parent, so you would want to set your view controller as GrowingEffect, then cast it to Effect, but again there is no strong suit to doing this either unless some method needs the parent class and you are using the child class. It looks like you need a redesign of how you want your view controllers laid out. Now I am assuming you have 2 children, lets call GrowingEffect and ShrinkingEffect. In your designer, you set your 1 to GrowingEffect and the other to ShrinkingEffect and make sure they have unique identifiers. Then you can use your view to present an Effect, and pass in either of those objects.

Resources