I have a UIPageViewController that works as expected. I can scroll left and right and the delegate method didFinishAnimating is called when I scroll each direction. However, if I scroll too quickly I end up on a page where didFinishAnimating is not called, though it is called for all previous pages. Does anyone know why this might be happening?
I would think that didFinishAnimating would be called on every page transition regardless (e.g., even if the turn was aborted).
This bug is still here in 2017.. I tried many alternative ways.. now I gave up the hope that this will be fixed by Apple and I think the best way is to use protocol to bound UIPageViewController with content ViewController, so we can notify UIPageViewController in viewDidAppear()
I've got the same issue. I tried to use the UIScrollView delegate instead of UIPageViewController to solve the issue. This is a tricky method, not recommend to use.
1.get the UIScrollView in UIPageViewController to set delegate:
for (UIView *view in self.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
UIScrollView *scroll = (UIScrollView *) view;
scroll.delegate = self;
}
}
2.override scrollViewWillBeginDragging:(UIScrollView *)scrollView
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
[[NSNotificationCenter defaultCenter] postNotificationName:PageSwitchingBegan
object:nil];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
[[NSNotificationCenter defaultCenter] postNotificationName:PageSwitchingEnded
object:nil];
}
You may also try override - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView instead of scrollViewDidEndDragging to achieve better result.
Related
I'm currently posting a NSNotification that the cell is listening for if the tableview starts scrolling and when it stops scrolling. Is this the right/most efficient approach?
* UPDATE*
Apparently this wasn't clear enough. I'm not trying to inform the tableview that it's scrolling.
I'm using this:
- (void )scrollViewDidScroll: (UIScrollView *)scrollView {
// How should I tell all of my tableview cells that we scrolled.
[[NSNotificationCenter defaultCenter] postNotificationName:#"scrolled" object:self];
}
And then when the cell is created -
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(pauseVideo:) name:#"scrolled" object:nil];
And then posting a notification that each allocated cell is listening for.
Is this the best approach?
A UITableView inherits from UIScrollView. So, there are delegate methods on the UIScrollView which is contained inside the TableView. You should adapt those methods inside your controller.
UPDATED:
Try adapting this:
tableView:didEndDisplayingCell:forRowAtIndexPath:
I think for scrolling performance it is better to use scrollViewWillBeginDragging, because it is called once. And in this handler you can just iterate through visible cells and stop player in each of them.
I've tried subclassing UICollectionView and overriding touchesBegan:withEvent: and hitTest:WithEvent:, and both of those methods trigger when I touch a cell. However, if I touch the space between the cells, nothing happens at all. Here's what I've created:
#interface WSImageGalleryCollectionView : UICollectionView
#end
..and..
#implementation WSImageGalleryCollectionView
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Touches began");
[super touchesBegan:touches withEvent:event];
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
NSLog(#"Hit test reached");
return [super hitTest:point withEvent:event];
}
#end
Note: gesture recognizers seem to have the exact same issue, which is why I tried going lower-level with touchesBegan.
You just need to set a view as background view that you can then add a gesture recognizer to like:
collectionView.backgroundView = [[UIView alloc] init];
[collectionView.backgroundView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapOnBackgroundRecognized)]];
You must have got something else that is preventing touchesBegan from firing.
Subclassing UICollectionView and using that subclass inside of a UICollectionViewController will allow you to detect in-between cells taps in touchesBegan.
I have just tried it out by modifying Apple's CollectionView-simple sample.
In my case, the problem was that you can click on the background whenever it is "clear" colored. Otherwise simply setting a background color makes the background clickable. I don't quite know why this is, but the simple fix was to just give it a background color.
Edit: Actually, this may have had something to do with setting the "opaque" flag.
I have a UIControl inside a UIScrollView. In my UIControl's init, I rig up some touch event handlers, e.g.
[self addTarget:_delegate
action:#selector(touchedDown) forControlEvents:UIControlEventTouchDown];
iOS6 and iOS7 behave differently when I do the following:
Swipe the UIScrollView to start scrolling
Tap the UIScrollView to stop scrolling
In iOS6, my app continues to behave as intended: the tap at step #2 does not call touchedDown -- the UIScrollView swallows the touch event as it immediately stops scrolling.
But in iOS7, the UIScrollView stops scrolling as expected, while touchedDown is still called.
Was there a documented API change? I'd like my app to behave identically to iOS6 in iOS7.
workaround for iOS 7
#interface UIScrollViewFixed : UIScrollView
#end
#implementation UIScrollViewFixed
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (self.isDragging || self.isDecelerating) {
return self;
}
return [super hitTest:point withEvent:event];
}
#end
Just replace the event type
UIControlEventTouchDown must be UIControlEventTouchUpInside
Not very elegant, but in the absence of any better ideas, here's what's working for me now:
On the UIScrollView, set canCancelContentTouches to YES and delaysContentTouches to NO.
In the UIScrollViewDelegate, toggle the UIScrollView's subview's userInteractionEnabled property when the UIScrollView scrolls:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
[_contentView setUserInteractionEnabled:NO];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (!decelerate) {
[_contentView setUserInteractionEnabled:YES];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[_contentView setUserInteractionEnabled:YES];
}
Subclass the UIScrollView and implement:
- (BOOL)touchesShouldCancelInContentView:(UIView *)view
{
return YES;
}
Subclass the UIControl and implement touchesCancelled:withEvent to reverse whatever the UIControlEventTouchDown handler does:
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
//custom logic
}
Same here with UIButtons on a UIScrollView. This is my solution for now.
Instead of using the contents UIControlEventTouchDown event:
[button addTarget:_delegate
action:#selector(touchedDown) forControlEvents:UIControlEventTouchDown];
I implemented the UIResponder touchesEnded method in my content UIViewController:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
// my scroll content touch logic
}
If the user touches the content and starts dragging the touchesEnded handler will not be called. The UIResponder touchesCanceled method will.
If the user does not drag the UIscrollview the touchesEnded handler is fired, which can be used for touch logic.
I am setting up a NOTIFICATION to get KEYBOARD information to adjust the view so that the selected text field is not covered by the KEYBOARD.
#property CGSize keyboard;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:#"UIKeyboardWillShowNotification"
object:nil];
...
}
- (void) keyboardWillShow:(NSNotification *)note
{
NSDictionary *userInfo = [note userInfo];
self.keyboard = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
}
I have set up the UITextFieldDelegate so I can use the KEYBOARD information in
- (void)textFieldDidBeginEditing:(UITextField *)textField
Everything works perfectly ...
except for the very first time.
The NOTIFICATION that sets up the KEYBOARD property is called after textFieldDidBeginEditing so the very first time the KEYBOARD property has not been set up so it displays incorrectly.
After that things are fine since the KEYBOARD property has already been set up and the values don't change.
How can I get the KEYBOARD information before the first execution of textFieldDidBeginEditing?
EDIT:
Did find something of a solution, and it does seem to work, but it feels a little hackish to me and I'm still wondering if there is a way to get the keyboard information.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.txtField becomeFirstResponder];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.txtField resignFirstResponder];
}
Does give a brief flash at the very bottom of the screen as it displays and hides the keyboard, which I don't like, but it's probably not very obvious or noticeable to someone that doesn't know to look for it.
I have to break up the becomeFirstResponder and resignFirstResponder into different methods because if they get called from the same method then keyboardWillShow does not get called.
Also, can not place becomeFirstResponder in viewDidLoad because keyboardWillShow is not called in that situation either.
If anyone has an improvement, or a better way, I'd love to hear it.
One way you can do this is that instead of setting the text field's frame in the begin editing, you can iterate through your text fields checking the isFirstResponderproperty, and move the frame of the one that is first responder. You can do all that in your method where you get the keyboards frame.
When the keyboardWillHide, I scroll the tableView to a specified point. The code is below. This works well.
Now, I implement NSFetchedResultsControllerDelegate. I set it "on" by setting fetchedResultsController.delegate = self; The scroll animation is interrupted. The NSFetchedResultsControllerDelegate is calling [tableView beginUpdates], which I think is causing the interruption on the tableView animation.
How can I prevent the scroll animation from being interrupted and still implement NSFetchedResultsControllerDelegate?
- (void)keyboardWillHide:(NSNotification *)notification {
[screen setHidden:YES];
[suggestView setHidden:YES];
[_tableView setContentOffset:origin animated:YES];
}
I found out that you can't do animations in keyboardWillHide. You shouldn't do any animation in any "WILL" event.