Trigger a method in UIViewController from its View
You can do something like this
CustomView.h
#import <UIKit/UIKit.h>
@protocol CustomViewDelegate <NSObject>
-(void)didButtonPressed;
@end
@interface CustomView : UIView
@property (assign) id<CustomViewDelegate> delegate;
@end
CustomView.m
#import "CustomView.h"
@implementation CustomView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.backgroundColor = [UIColor whiteColor];
//[self addSubview:titleLbl];
UIButton *button= [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(100, 100, 100, 50);
[button addTarget:self.delegate action:@selector(didButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[button setTitle:@"pressMe" forState:UIControlStateNormal];
[self addSubview:button];
}
return self;
}
in your ViewController.m
-(void)loadView
{
[super loadView];
CustomView *view = [[CustomView alloc]initWithFrame:self.view.bounds];
view.delegate = self;
[self.view addSubview:view];
}
How to call a method/an action inside UIViewController from custom UIView?
Use protocol/delegate:
protocol TapHandler {
func tapped()
}
class MyView: UIVieww {
weak var tapHandler: TapHandler?
/// button tap handler
@obj func yourTapFunctionInsideView() {
tapHandler?.tap()
}
}
class MyViewController: UIViewController, TapHandler {
var view: MyView?
override func viewDidLoad() {
super.viewDidLoad()
view?.tapHandler = self
}
func tapped() {
/// tapped
}
}
How Do I call a method from a view contained in a view controller ?
I think you can set up a protocol in your Subview, which can be implemented by your ViewController
Your SubView.h
@class SubView;
@protocol SubViewDelegate <NSObject>
- (void)actionToPromoteToViewController: (NSString *)exampleString isSelected:(BOOL)exampleBool;
@end
Then, in your ViewController.h:
@interface MainViewController : UIViewController <SubViewDelegate>
and your ViewController.m, implement the method.
- (void)actionToPromoteToViewController: (NSString *)exampleString isSelected:(BOOL)exampleBool{
// Method Implementation
}
Call instance method in ViewController from View
View does not have default method to access its view-controller object.
You need to pass the view-controller object into the view object yourself.
Typical way to do this is making a property.
@class ViewController;
@interface DraggableView : NSObject
@property (readwrite,nonatomic,assign) ViewController* theOwnerViewController;
@end
@implementation DraggableView
- (void)testCallingOwnerViewControllerMethod
{
[self.theOwnerViewController test];
}
@end
You need to set the theOwnerViewController
after you created the DraggableView
object.
- (void)loadView
{
draggableView = [[DraggableView alloc]initWithFrame:CGRectMake(20, 190, 280, 280)];
draggableView.theOwnerViewController = self;
//...extra view setup.
}
Use assign
to avoid retain-cycle on the property.
Delegate pattern
You can do this by above pattern, but as you noticed, you need to know and forward-declare the owner view-controller class' name from its view (which is sub-node of the VC). Usually This is bad design choice because it's easily makes circular dependency (or backward dependency), which usually creates tight-coupling.
Instead, you can use delegate pattern to avoid circular dependency issue.
@protocol TestDelegate
- (void)test;
@end
@interface DraggableView : NSObject
@property(readwrite,nonatomic,assign) id<TestDelegate> testDelegate;
@end
@implementation DraggableView
- (void)test
{
[self.testDelegate test];
}
@end
You need to set the testDelegate
after you created the DraggableView
object.
@interface ViewController<TestDelegate>
@end
@implementation
- (void)test
{
// do something.
}
- (void)loadView
{
draggableView = [[DraggableView alloc]initWithFrame:CGRectMake(20, 190, 280, 280)];
draggableView.testDelegate = self;
//...extra view setup.
}
@end
In this case, you don't have to know the class name of the view object before you create. Any class which conforms TestDelegate
protocol can be used, and now the view and VC are loosely-coupled via the protocol.
Call UIView method from ViewController
Create a new variable chart
in your ViewController and add this chart as subView
to the view
outlet
connected from xib
Your ViewController class would look like below:
class DetailDashboardViewController: UIViewController {
@IBOutlet weak var chartView: UIView!
var chart: UIView!
override func viewDidLoad() {
super.viewDidLoad()
if index != 3{
chart = NSBundle.mainBundle().loadNibNamed("BarChartDashboard", owner: self, options: nil)[0] as? BarChartDashboard
}
else{
chart = NSBundle.mainBundle().loadNibNamed("LineChartDashboard", owner: self, options: nil)[0] as? LineChartDashboard
}
self.chartView.addSubView(chart)
self.chart.frame = self.chartView.bounds
runQueries(2)
}
private extension DetailDashboardViewController {
func runQueries(period: Int) {
self.query(false, periodIndex: period){ (success) -> Void in
if success {
// do second task if success
dispatch_async(dispatch_get_main_queue(), {
if self.index != 3{
(self.chart as! BarChartDashboard).setChart(self.yAxis, values: self.xAxis)
}
else{
(self.chart as! LineChartDashboard).setChartData(self.yAxis, yAxisMax: self.xAxisMax, yAxisMin : self.xAxisMin)
}
})
}
}
}
How to call from a Popover a method of the main view controller?
You are trying to call the method from the new instance of your ViewController. Instead, you need to declare a delegate property in the Popover VC and pass the original parent VC within prepareForSegue:sender:
method as a delegate for your Popover VC.
Call method in another View Controller, but not segue to it?
Using Swift 3
Before I provide a solution I'll point you to @Paulw11's comment, he's 100% correct. The proper convention is to pull your connectivity logic out of your view controllers. The only code you should have in your view controllers is the code necessary to display your view. Connecting to a server and managing the result has nothing to do with actually presenting the resulting data in the view.
If you really felt compelled to call a function directly from another view, you shouldn't do it by creating a new instance of your view controller. Rather, you should just get the existing instance like this:
let app = UIApplication.shared.delegate! as! AppDelegate
if let viewControllers = app.window?.rootViewController?.childViewControllers {
viewControllers.forEach { vc in
if let view = vc as? AnotherViewController {
view.downloadIt()
}
}
}
Personally, I'd recommend pulling this logic out of your view controller either way. If you're trying to keep things minimal, or if this is the only web service call you are making, you could simply do something like this:
Add a new Swift file with a protocol and an extension to that protocol:
protocol Downloadable : class { }
extension Downloadable {
func downloadStuff() -> String {
// Add your download logic here
return "downloading"
}
}
Then, in any view you want to be able to use this download function just add the protocol to your View Controller like this:
class ViewController: UIViewController, Downloadable {}
Then you can very easily get your downloaded data like this:
@IBAction func handleDownloadTapped(_ sender: UIButton) {
let result = downloadStuff()
print(result)
}
Again, you shouldn't get in the habit of doing this as it can get messy if you are working with a few different api calls.
The best practice is to create a class that contains your web service stuff, I personally like the objc.io approach. You can find a tutorial here.
https://talk.objc.io/episodes/S01E01-networking
Basically you'll create a service like this
public typealias JSONDict = [String: AnyObject]
struct Resource<A> {
let url: URL
let parse: (NSData) -> A?
}
let url = URL(string: "https://url")!
let myResource = Resource<[JSONDict]>(url: url) { data in
let json = try? JSONSerialization.jsonObject(with: data as Data, options: [])
return json as? [JSONDict]
}
final class Connect {
class func load<A>(resource: Resource<A>, completion: @escaping (A?) -> Void) {
URLSession.shared.dataTask(with: resource.url) { (data, _, _ ) in
if let data = data {
completion(resource.parse(data as NSData))
} else {
completion(nil)
}
}.resume()
}
}
And you can call it wherever you need to like this:
Connect.load(resource: myResource) { result in
// do stuff with result
}
I hope this was helpful to someone
Call method of presenting viewcontroller from modal
You need to create a delegate of ContentViewController
and in that delegate you need to add this method goToContent
.
Now when you are logged in successfully, you need to call this delegate method. It will work.
Related Topics
How to Find the Kind of Errors a Method May Throw and Catch Them in Swift
How to Prevent a Remote Notification from Being Displayed
iOS Color to Transparent in Uiimage
Pinch to Zoom Effect on Uiimageview Inside Scrollview
How to Start an Ibeacon Transmitter in the Background
Restore Button for iOS Non-Consumable In-App Purchase
Dismiss a Parent Modal in Swiftui from a Navigationview
Building Ffmpeg iOS Libraries for Armv7, Armv7S, Arm64, I386 and Universal
How to Make a Mkannotationview Touch Sensitive
Gssendevent - Inject Touch Event iOS
Conditionally Hide Code from the Compiler
Swift: Gradient Along a Bezier Path (Using Calayers)
Pull Notification Locally on Jailbroken Device
Uitapgesturerecognizer on Uiimageview Within Uitablevlewcell Not Getting Called
Will Fork() in iOS App Likely Be Rejected by Apple's Vetting Process
Why Is Audio Coming Up Garbled When Using Avassetreader with Audio Queue
Expected Declaration Error Creating Array in Viewcontroller, Can't Work Out Why