I have a ScrollView that contains a ContainerView. The ContainerView contains another View that the user is supposed to be able to pan around in.
The scrollView scrolls vertical only, the "view inside the containerView" is panable in all directions.
Here is what I have
self.scrollView.contentSize = CGSizeMake(1024,1440);
self.modelController = [self.storyboard instantiateViewControllerWithIdentifier:#"LCProduct3DViewController"];
self.modelController.meshIdentifier = self.meshIdentifier;
[self addChildViewController:self.modelController];
self.modelController.view.frame = self.threeDView.bounds;
[self.threeDView addSubview:self.modelController.view];
What happens is that the touch events inside the modelController's view and the ones outside the modelControler's view but inside the scrollview bounds seems to be getting the the way of each other.
I played around with
self.scrollView.canCancelContentTouches = NO;
self.scrollView.delaysContentTouches = NO;
but havent found a working solution yet.
Any Ideas ?
Thanks in advance
as can be found in other places:
The trick is to subclass the scrollView and override the hitTest method like this:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView* result = [super hitTest:point withEvent:event];
if ([result isKindOfClass:[GLKView class]]) {
self.scrollEnabled = NO;
} else {
self.scrollEnabled = YES;
}
return result;
}
this way - if the hittest for the innver view is positive, scrolling for the scrollview is disabled and only the innver view will get the event.
Related
I've got an app with an NSScrollView nested inside another NSScrollView. I'd like the user to be able to scroll the inner scrollview using two-finger swipe, and to scroll the outer scrollview using three fingers.
I imagine I'll need to somehow configure each scrollview to reject touches with the wrong numbers of fingers, but I'm not sure how to do this.
I figured it out! The trick is to subclass the inner ScrollView and force it to reject gestures that have a certain number of touches, forwarding them to the parent scrollview:
- (void)scrollWheel:(NSEvent *)event {
if (_forwardScrollToParent) {
// [self.enclosingScrollView scrollWheel:event];
} else {
[super scrollWheel:event];
[self recordInteractionWithThisTab];
}
}
- (void)touchesBeganWithEvent:(NSEvent *)event {
[super touchesBeganWithEvent:event];
NSInteger nTouches = [event touchesMatchingPhase:NSTouchPhaseTouching inView:self].count;
if (nTouches == 3) {
_forwardScrollToParent = YES;
} else {
_forwardScrollToParent = NO;
}
}
I have an app which I'm updating to auto layout and size classes and I'm getting some weird behaviour with the label on a button.
The button should be a circle and have a label in the centre. I'm implementing my own subclass so I can reuse it.
Here's the storyboard:
and the code for the class which extends UIButton:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self setBackgroundImage:[UIImage imageNamed:#"selected-green"] forState:UIControlStateHighlighted ];
self.layer.borderColor = [UIColor tlbWhiteColor].CGColor;
self.layer.borderWidth = 10;
}
return self;
}
-(void) layoutSubviews {
self.layer.cornerRadius = self.frame.size.width / 2;
}
With this the appears as expected but there is no label. On debugging I see that the frame of the label has 0 height and width. So I extended layoutSubviews:
-(void) layoutSubviews {
self.layer.cornerRadius = self.frame.size.width / 2;
if (self.titleLabel.frame.size.width == 0) {
[self.titleLabel sizeToFit];
[self setNeedsLayout];
[self layoutIfNeeded];
}
}
The label then appears, but it's in the wrong place:
The only extra info I can offer is that in Reveal the button has weird height and width constraints added:
The titleInsets are all at 0.
Help hugely appreciated.
I don't think you need the layoutSubviews override. I think it's a hack and it's hiding your real issue.
What are the constraints on the 'Go' label? How are you centering it in the UIButton? Also what is the height constraint on the label?
My suggestion would be to put both the UIButton and the Go label inside a container view and centre the label inside the container view. The container view should have the same height and width as the UIButton inside it.
Also are you changing the frames of the views programmatically somewhere in the app? Those weird constraints you talk about in reveal point to that.
As per your requirements:
- (void)viewDidLoad {
[super viewDidLoad];
view_1.layer.cornerRadius = 10; //view_1 is white color
view_1.layer.masksToBounds = YES;
view_2.layer.cornerRadius = _view_2.frame.size.width/2;//view_2 is green color
view_2.layer.masksToBounds = YES;
}
output
Ok, this was a real school boy error.
When the docs said 'The implementation of this method is empty before iOS 6' I for some reason took this to mean there's no need to call super on layoutSubviews.
So the fix was:
-(void) layoutSubviews {
[super layoutSubviews]; // <<<<< THIS WAS MISSING
self.layer.cornerRadius = self.frame.size.width / 2;
self.layer.masksToBounds = YES;
}
I have the following code to dismiss the keyboard if the user taps the background. It works fine if the scrollview is in the PointZero position, but if the user scrolls the view and then selects the textview, it doesn't call the "dismissKeyboard' method until the 2nd background tap.
On the first tap (for some reason) moves the scrollview offset to align with the scrollview frame to the screen bottom. The second tap will dismiss the keyboard and run the code below. I know it has to do with the scrollview. Any help would be appreciated.
Thanks
- (void)viewDidLoad {
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dismissKeyboard)];
tapGesture.cancelsTouchesInView = NO;
[_scrollView addGestureRecognizer:tapGesture];
}
-(void)dismissKeyboard {
[self.view endEditing:YES];
}
- (void)keyboardWasShown:(NSNotification *)notification {
scrollViewRect = _scrollView.contentOffset.y;
NSDictionary* info = [notification userInfo];
CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
keyboardSize.height += 10;
CGFloat viewBottom = CGRectGetMaxY(self.scrollView.frame);
if ([_itemNotes isFirstResponder]) {
CGFloat notesBottom = CGRectGetMaxY(_itemNotes.frame);
viewBottom -= notesBottom;
if (viewBottom < keyboardSize.height) {
keyboardSize.height -= viewBottom;
CGPoint scrollPoint = CGPointMake(0.0, keyboardSize.height);
[self.scrollView setContentOffset:scrollPoint animated:YES];
}
else {
[self.scrollView setContentOffset:CGPointZero animated:YES];
}
}
else {
[self.scrollView setContentOffset:CGPointZero animated:YES];
}
}
- (void)keyboardWillBeHidden:(NSNotification *)notification {
CGPoint scrollPoint = CGPointMake(0.0, scrollViewRect);
[self.scrollView setContentOffset:scrollPoint animated:YES];
}
EDIT:
So I figured out a solution but it seems like there must be a better way to handle this. The problem was because I was setting the contentOffset of the scrollView so that the contentSize was beyond the screen boundaries. Thus the first tap was moving the scrollView contentOffset back within the screen boundaries and the second was performing the tap gesture. I will post my solution below hoping that someone has a better answer.
I would recommend setting
_scrollView.layer.borderColor = [UIColor redColor].CGColor;
_scrollView.layer.borderWidth = 1;
This will show you exactly where your scrollview boundaries are, which may not be where you think they are, or may be covered by something else. Also, when I open the keyboard, I generally set the scrollview frame bottom to the top of the keyboard. Otherwise, you may have content below the keyboard you can't get to. Not sure if this is exactly related to your issues.
I am assuming there must be a better solution to this but I was able to solve the issue by extending the contentSize when the keyboard is displayed and then shrinking it back down when the keyboard is hidden.
Set a float (scrollViewHeight) to hold the original content size for the reset.
//add this right before setting the content offset
scrollViewHeight = _scrollView.contentSize.height;
_scrollView.contentSize = CGSizeMake(_scrollView.frame.size.width , scrollViewHeight + keyboardSize.height);
//add this right before reseting the content offset
_scrollView.contentSize = CGSizeMake(_scrollView.frame.size.width , scrollViewHeight);
It really seems like there must be a better way that I'm not aware of. I will have to go back through the documentation to see if there is another way.
I am developing my first MAC application, i downloaded one Example of PxListView
and i have to added one button and background image on cell xib and bind them with controller
and, when on button click i was set height of that cell is much bigger then other. that is done,
and work fine.
but now i want to develop like after is witch cell has open in that cell i want to add some extra contain (Controller) on it, so how it will possible using given example?
pls help me to give some suggest how it will be done.
for Ex like before click on button
after chick on button i want to develop like
You write
i have to added one button and background image on cell xib and bind them with controller
It sounds like you've subclassed PXListViewCell--for convenience, let's call your subclass TemplateListViewCell--and added a xib from which instances of TemplateListViewCell will be loaded in
+[PXListViewCell cellLoadedFromNibNamed:bundle:reusableIdentifier:]
In addition, there is a[t least one] button in TemplateListViewCell.xib.
You write
when on button click i was set height of that cell is much bigger then other. that is done, and work fine
It sounds like this button has as its action a method on TemplateListViewCell such as
- (IBAction)toggleDetail:(id)sender
{
//Code to grow or shrink the height of [self frame].
//...
}
In my approach to implementing -toggleDetail, two modifications to the PXListView files were necessary:
1. Adding a protocol method
- (void)listView:(PXListView *)aListView setHeight:(CGFloat)height ofRow:(NSUInteger)row;
to the PXListViewDelegate protocol.
2. Adding a property
#property (nonatomic, assign) BOOL expanded;
to PXListViewCell.
My implementation of -toggleDetail looks something like this:
- (IBAction)toggleDetail:(id)sender
{
BOOL wasExpanded = [self expanded];
NSRect oldFrame = [self frame];
CGFloat oldHeight = oldFrame.size.height;
CGFloat newHeight = oldHeight;
CGFloat heightIncrement = 0.0f;
if (wasExpanded) {
heightIncrement = -80.0f; //use whatever value is appropriate
} else {
heightIncrement = 80.0f; //use whatever value is appropriate
}
newHeight += heightIncrement;
[[[self listView] delegate] listView:[self listView] setHeight:newHeight ofRow:[self row]];
[[self listView] reloadData];
BOOL isExpanded = !wasExpanded;
[self setExpanded:isExpanded];
}
It might seem better to use [[self listView] reloadRowAtIndex:[self row]]; in place of [[self listView] reloadData], but unfortunately, this doesn't work: if the user hides the detail--shrinks the cell vertically--new cells which should appear on the screen do not.
You write
that is done, and work fine.
It sounds like you were able to implement successfully a method analogous to -[TemplateListViewCell toggleDetail:].
You write
but now i want to develop like after is witch cell has open in that cell i want to add some extra contain (Controller) on it, so how it will possible using given example? pls help me to give some suggest how it will be done.
It sounds like you want instances of TemplateListViewCell to contain extra views if they are expanded.
It might seem tempting to put this code into -[TemplateListViewCell toggleDetail], but this will not work out as we might hope. The trouble is, we need to handle cases where expanded cells have been scrolled out of view and scrolled back into view.
To get this right, we need to have a notion of expanded which persists beyond the usage of a PXListViewCell subclass instance: we either need to keep track of expansion in the PXListView itself or in its delegate.
The better--but less expedient--design seems to be to keep track of this information in the PXListView itself. For the sake of this question, however, I'll demonstrate how to keep track of cell expansion in the delegate. To do this, I'm expanding the PXListViewDelegate protocol and making other changes to the PXListView files:
1. Adding the methods
- (void)listView:(PXListView *)aListView setExpanded:(BOOL)expanded atRow:(NSUInteger)row;
- (BOOL)listView:(PXListView *)aListView expandedAtRow:(NSUInteger)row;
to PXListViewDelegate.
2. Adding the method
- (void)setCell:(PXListViewCell *)cell expandedAtRow:(NSUInteger)row
{
if ([[self delegate] respondsToSelector:#selector(listView:expandedAtRow:)]) {
[cell setExpanded:[[self delegate] listView:self expandedAtRow:row]];
}
}
to PXListView.
3. Calling -[PXListView setCell:expandedAtRow:] from -[PXListView layoutCells]
- (void)layoutCells
{
//Set the frames of the cells
for(id cell in _visibleCells)
{
NSInteger row = [cell row];
[cell setFrame:[self rectOfRow:row]];
[self setCell:cell expandedAtRow:row];
[cell layoutSubviews];
}
NSRect bounds = [self bounds];
CGFloat documentHeight = _totalHeight>NSHeight(bounds)?_totalHeight:(NSHeight(bounds) -2);
//Set the new height of the document view
[[self documentView] setFrame:NSMakeRect(0.0f, 0.0f, NSWidth([self contentViewRect]), documentHeight)];
}
and from -[PXListView layoutCell:atRow:]:
- (void)layoutCell:(PXListViewCell*)cell atRow:(NSUInteger)row
{
[[self documentView] addSubview:cell];
[cell setFrame:[self rectOfRow:row]];
[cell setListView:self];
[cell setRow:row];
[cell setHidden:NO];
[self setCell:cell expandedAtRow:row];
}
4. Setting _expanded to NO in -[PXListViewCell prepareForReuse]:
- (void)prepareForReuse
{
_dropHighlight = PXListViewDropNowhere;
_expanded = NO;
}
Note: In the sample PXListViewCell subclass, MyListViewCell, distributed with PXListView, the implementation of -[MyListViewCell prepareForReuse] fails to call [super prepareForReuse]. Make sure that this call is made in [TemplateListViewCell prepareForReuse]:
- (void)prepareForReuse
{
//...
[super prepareForReuse];
}
One change needs to be made to -[TemplateListViewCell toggleDetail:]. The line
[self setExpanded:isExpanded];
needs to be replaced by
[[[self listView] delegate] listView:[self listView] setExpanded:isExpanded atRow:[self row]];
Once you've set up your PXListView's delegate to properly handle the new delegate methods, you're ready to override [PXListViewCell setExpanded:] in your subclass TemplateListViewCell:
- (void)setExpanded:(BOOL)expanded
{
if (expanded) {
//add detail subviews
} else {
//remove detail subviews
}
[super setExpanded:expanded];
}
Replace //add detail subviews with your own code which programmatically adds the detail subviews that you want and replace //remove detail subviews with code to remove the detail subviews that you want, checking to see that they are present first.
You write
i want to add some extra contain (Controller) on it
It sounds like you want to add view controllers rather than views to your TemplateListViewCell. To do this, use an NSBox and set the box's contentView to your view controller's view. (For details on this, see this answer.)
If you plan on just showing a single view controller's view in an NSBox on the expanded TemplateListViewCell, you can just (1) add a property to TemplateListViewCell referencing your view controller and (2) add an NSBox to TemplateListViewCell xib and set its contentView to the appropriate view controller's view on [cell setExpanded:YES] and set its contentView to nil on [cell setExpanded:NO].
I have a webview which I would like to remove the elasticity from. As it is now, when scrolling a page that is smaller than the webview, it will make an elasticity effect revealing the background underneath. I would like to remove this.
I have tried doing the following but without success. It finds the WebDynamicScrollBarsView but setting the elasticity of this, does not change anything.
- (void)awakeFromNib
{
NSScrollView *scrollView = [self findScrollViewInSubviews:self.subviews];
scrollView.horizontalScrollElasticity = NSScrollElasticityNone;
scrollView.verticalScrollElasticity = NSScrollElasticityNone;
}
- (NSScrollView *)findScrollViewInSubviews:(NSArray *)subviews
{
for (NSView *view in subviews)
{
if ([view isKindOfClass:[NSScrollView class]])
return (NSScrollView *) view;
else
return [self findScrollViewInSubviews:view.subviews];
}
return nil;
}
Does anyone know how to remove the elasticity effect from a webview?
In my subclass of WebView (or you can just do it directly, with webView.mainFrame.frameView.documentView.enclosingScrollView, but this makes it easier for me, personally)
// return the scroll view that we are currently using, if applicatble
- (NSScrollView *)mainScrollView {
return [[[[self mainFrame] frameView] documentView] enclosingScrollView]; // can be nil
}
Then on finish load, as Anne noted literally as I was typing my answer :) ,
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
NSScrollView *mainScrollView = [sender mainScrollView];
[mainScrollView setVerticalScrollElasticity:NSScrollElasticityNone];
[mainScrollView setHorizontalScrollElasticity:NSScrollElasticityNone];
}
I'm not sure if doing it after the content has loaded is necessary, but I don't have my mac with me to try right now.