I'm working on an iPhone app in StoryBoard. It has a UIScrollView in it. Inside of this, it has a SliderView, which is a custom subclass that I wrote that derives from UIView.
Inside SliderView I'm executing this method:
// Inside SliderView.m
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// whenever someone touches inside of SliderView, this method fires
}
Here's my problem. In my StoryBoard, I would like to disable the bouncy vertical scrolling of the UIScrollView.
Normally, that would be easy. Assuming I had connected the UIScrollView as an IBOutlet in the app's main ViewController, I could paste this into that file:
// Inside ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
self.scrollView.bounces = NO;
}
Unfortunately, this isn't so easy because I want this disabling to occur only when touchesBegan is fired.
So here's where I'm completely stumped: how can I get the SliderView.m file to communicate with the ViewController.m file?
Use delegate for this:
SliderView.h
// SliderView.h
#protocol SliderViewDelegate <NSObject>
#optional
- (void) isScrollViewToBounce:(BOOL)isBounce;
#end
#interface SliderView : UIView
#property (nonatomic, weak) id <SliderViewDelegate> sliderViewDelegate;
#end
SliderView.m
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.sliderViewDelegate isScrollViewToBounce:NO];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self.sliderViewDelegate isScrollViewToBounce:YES];
}
ViewController.m
#import "SliderView.h"
#interface ViewController () <SliderViewDelegate>
#end
viewDidLoad() method
- (void)viewDidLoad {
[super viewDidLoad];
self.sliderView setSlideViewDelegate:self];
}
Create Delegate method in your ViewController:
- (void) isScrollViewToBounce:(BOOL)isBounce {
self.scrollView.bounces = isBounce;
}
Also do not forget to remove delegation
#pragma mark Memory management methods
//---------------------------------------------------------------
- (void)dealloc {
// Release resources here.
[self.scrollView setSliderViewDelegate:nil]
}
Related
I got a textField declared as an outlet, it is correctly connected via the storyboard:
#property (weak, nonatomic) IBOutlet UITextField *addBillEventName;
I also have set the delegate of the UITextfield via code and via the storyboard, code:
-(void) viewDidAppear:(BOOL)animated {
[self.addBillEventName setDelegate:self];
}
So you would expect that methods like this:
-(void) viewDidAppear:(BOOL)animated {
[self.addBillEventName setDelegate:self];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"Touch end controller");
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"Touch end controller");
}
Would trigger, but they don't get triggered. Weird is that charactersInRange does work.
Anyone familiar with this problem?
I create many subviews inside a UIScrollView. Let me call each subview a Cell. Each cell has a UILabel in it.
Then I implement touchEnd method on the Cell class, hoping that my app will respond to my touch whenever I tap on any of the cells. However I am not able to get response from all Cells. I can only get responses from the Cells that are above the bottom of my phone (iPhone 5 is 568 something) in the ScrollView whereas the contentsize of my ScrollView is 2300 * 1000, which means those cells below y = 568 don't respond to my touches. How can I fix this? Here is my touchEnd method in Cell class
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (![self.unit.text isEqualToString:#"?"]) {
[self.soundManager playSoundForUnit:self.unit];
}
}
You have to make CustomScrollView :
.h :
#protocol CustomScrollViewDelegate <NSObject>
#optional
// optional becuase we always don't want to interact with ScrollView
- (void) customScrollViewTouchesEnded :(NSSet *)touches withEvent:(UIEvent *)event;
#end
#interface CustomScrollView : UIScrollView
#property (weak, nonatomic) id<CustomScrollViewDelegate> touchDelegate;
// delegate was giving error becuase name is already there in UIScrollView
#end
.m :
#import "CustomScrollView.h"
#implementation CustomScrollView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.dragging) {
//NSLog(#"touchesEnded in custom scroll view");
if (_touchDelegate != nil) {
if ([_touchDelegate respondsToSelector:#selector(customScrollViewTouchesEnded:withEvent:)]) {
[_touchDelegate customScrollViewTouchesEnded:touches withEvent:event];
}
}
}
else {
// it wouldn't be called becuase at the time of dragging touches responding stops.
[super touchesEnded:touches withEvent:event];
}
}
#end
I have a UIView class that is loaded in to a UIViewController as a subview, to capture touches and draw them to the screen (a simple drawing app). I want to hide some buttons in the UIViewController when drawing begins. In the UIViewController (DrawingController.m) viewDidLoad method:
//init the draw screen
drawScreen=[[MyLineDrawingView alloc]initWithFrame:self.view.bounds];
[drawScreen setBackgroundColor:[UIColor clearColor]];
[self.view addSubview:drawScreen];
I have a method in the UIViewController (DrawingController.m) that is hiding my colour picker, brush size buttons etc that are present within the UIViewController. The hide method works fine when called from the same class (DrawingController.m). Basically when the -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event method is called within the UIView class (MyLineDrawingView.m) I am hoping to hide the buttons:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
DrawingController *drawController = [[DrawingController alloc] init];
[drawController hideDrawIcons];
}
I've added some logging to the hideDrawIcons method within the UIViewController and it is getting called, but all of my hiding code is not working:
self.undoBtn.hidden = YES;
I suspect this is because I am making a new instance of the UIViewController class with DrawingController *drawController = [[DrawingController alloc] init]; - but I'm not sure how to do things differently?
I have of course exposed the correct method in the DrawingController.h header file, to be able to call it from the UIView class (MyLineDrawingView.m):
-(void)hideDrawIcons;
Hopefully all of that makes sense, thanks in advance!
You are right. You do not need to make create new instance of DrawingController.
All you need yo have pointer of your DrawingController in your View class. If you want to know more about this technology you can read about Delegate pattern. If not, here is simple steps.
Add protocol in your MyLineDrawingView.h file ( or you can create separated file for it )
#protocol MyLineDrawingProtocol <NSObject>
-(void)hideDrawIcons;
#end
In your DrawingController.h
#interface DrawingController <MyLineDrawingProtocol>
{
// members ....
}
#end
In your MyLineDrawingView.h
#interface MyLineDrawingView
{
// members ....
}
#property (nonatomic,weak) id <MyLineDrawingProtocol> delegate;
#end
Set delegate for your view
//init the draw screen
drawScreen=[[MyLineDrawingView alloc]initWithFrame:self.view.bounds];
drawScreen.delegate = self;// self is a pointer of DrawingController
[drawScreen setBackgroundColor:[UIColor clearColor]];
[self.view addSubview:drawScreen];
And last change. Call hideDrawIcons method for delegate ( DrawingController )
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if ( [self.delegate respondsToSelector:#selector(hideDrawIcons)] )
[self.delegate hideDrawIcons];
}
Yes, you're creating a new copy of the view controller in your view, which is wrong. What you want is to have a reference to the actual view controller. Here's how you can do it:
In MyLineDrawingView, declare a property:
#property (nonatomic, weak) DrawingController *drawingController;
Note that it is declared as weak, which prevents a retain cycle.
When instantiating the view:
drawScreen=[[MyLineDrawingView alloc]initWithFrame:self.view.bounds];
drawScreen.drawingController = self; // pass the actual view controller instance to the drawScreen object.
Finally, in MyLineDrawingView:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.drawingController hideDrawIcons];
}
I am unable to call touchesBegan from a text view.
Its working fine for me in a dummy project where i have just a View and inside it a TextView.
When I try to do it in real proj , having hierarchy like: (i feel here is some problem)
view (parent)
scrollview(subview)
view (grandSubview
textview.
the delegate is not getting called.
.h file
#interface ActionViewController : UIViewController<UITextViewDelegate>
#property (weak, nonatomic) IBOutlet UITextView *actionTakenTextView;
.m file
- (void)viewDidLoad
{
[super viewDidLoad];
actionTakenTextView.delegate=self; //actiontakentextview is the text view i am using.
actionTakenTextView.layer.borderWidth=1;
actionTakenTextView.layer.borderColor=[[UIColor grayColor] CGColor];
// Below property is to set rounded corner of text view.
actionTakenTextView.layer.cornerRadius=5.0;
actionTakenTextView.clipsToBounds=YES;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"touchesBegan:withEvent:");
[self.view endEditing:YES];
[super touchesBegan:touches withEvent:event];
}
I feel i am missing something very small here..please help.
thanks in adv.
I recently had a problem regarding not being able to register touches events (touchesMoved, etc) from within a UIScrollView. After talking with lots of people and reading tons of posts, it turns out UIScrollView does't accept touches itself, and instead the touches need to be passed to a UIScrollView subclass.
I am very new to objective-c, and am having a terrible time wrapping my head around how subclasses are defined (in separate .h/.m files or somehow in #interface or something), as well as how iOS deals with responder chains.
Please let me describe my exact situation, and anyone willing to explain what I need to do in noob-speak will be really appreciated.
I have a single page in my app, and I changed it's default UIView to UIScrollView, so if I inspect it in InterfaceBuidler all I see is my SampleViewController and UIScrollView. I linked my UIScrollView to the #Interface of SampleViewControler.h like this:
#import <UIKit/UIKit.h>
#interface SampleViewController : UIViewController
{
}
#property (strong, nonatomic) IBOutlet UIScrollView *mainScroller;
#end
And I referenced my UIScrollView in SampleViewController.m like this:
#import "SampleViewController.h"
#interface SampleViewController ()
#end
#implementation SampleViewController
#synthesize mainScroller = _mainScroller;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Movement!");
}
-(void)viewWillAppear:(BOOL)animated
{
[_mainScroller setContentOffset:CGPointMake(0,0) animated:YES];
}
If I change the class of UIScrollView to UIView, the touches are detected and I fire off a few NSLog messages. However, once UIView is set back to UIScrollView, touches are ignored.
From what I understand I need to setup a subclass of UIScrollView with the code:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Movement!");
}
...and then change my SampleViewController.m to have the code:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[[self.nextResponder nextResponder] touchesMoved:touches withEvent:event];
}
However, I really don't understand 1) how to setup such a subclass, and 2) why this would work, since I don't see why [[self.nextResponder nextResponder] touchesMoved:touches withEvent:event]; would ever be called in the first place from within SampleViewController.m, since my similar NSLog(#"Movement!"); statement is never executed.
I have tried every tutorial I could find, and am turning toward to the expertise of SE! Thanks.
You're thinking about this wrong. If you want to know when a scrollView is moving, there's no need to subclass it. iOS has set up all the methods you need inside of the UIScrollViewDelegate. IF however you want to say, pick up touchEvents for items based inside the scrollView, then that is perfectly okay. You need to then just subclass the items inside the scrollView and pick up the touchEvents inside the respective classes though. But for just standard scrollView methods you should set it up as so:
.H
#import <UIKit/UIKit.h>
#interface SampleViewController : UIViewController<UIScrollViewDelegate>
{
}
#property (strong, nonatomic) IBOutlet UIScrollView *mainScroller;
#end
.M
#import "SampleViewController.h"
#interface SampleViewController ()
#end
#implementation SampleViewController
#synthesize mainScroller = _mainScroller;
-(void)viewDidLoad
{
self.mainScroller.delegate=self;//Adopt the delegate for the scrollView..this is the important line
}
Then just implement the UIScrollViewDelegateMethods to grab movement etc. Undefined behavior will occur when trying to pick up touchEvents inside a scrollView/tableView, since the scrollView itself already implements those touchEvents behind the scenes
Look at all the methods on the Developer site and implement them in your .M file. Since you've adopted the delegate in viewDidLoad, these will be your scrollView call backs. I've listed a few of them before but go ahead and look on the UIScrollViewDelegate Protocol site
– scrollViewDidScroll:
– scrollViewWillBeginDragging:
– scrollViewWillEndDragging:withVelocity:targetContentOffset:
– scrollViewDidEndDragging:willDecelerate:
– scrollViewShouldScrollToTop:
– scrollViewDidScrollToTop:
– scrollViewWillBeginDecelerating:
– scrollViewDidEndDecelerating:
I posted an answer to a similar question a few days ago that might help you as well. See this question and my answer.
It looks like you're close to getting it working, but you've gotten a bit confused about where to put the touchesMoved methods.
In your subclass of UIScrollView, you should have:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[[self.nextResponder nextResponder] touchesMoved:touches withEvent:event];
}
And in your SampleViewController.m file:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Movement!");
}
(You have these two around the other way in your question.)