I am subclassing NSTabView to customise the appearance. I want to use an NSMatrix of NSButtonCells to select the tabs. I managed to add the NSMatrix with buttons in the initWithFrame: method of my NSTabView subclass. What I can not get to work is setting the target and action programmatically. Here is what I tried:
define TAB_WIDTH 24.0f
define TAB_HEIGHT 24.0f
- (id)initWithFrame:(NSRect)frameRect
{
self = [super initWithFrame:frameRect];
if (self) {
NSInteger numberOfTabs = 5;
NSInteger tabSpacing = 8;
NSSize cellSize = NSMakeSize(TAB_WIDTH, TAB_HEIGHT);
NSSize interCellSpacing = NSMakeSize(tabSpacing, 0);
CGFloat tabSelectorWidth = TAB_WIDTH * numberOfTabs + tabSpacing * numberOfTabs - 1;
CGFloat xOrigin = (frameRect.size.width - tabSelectorWidth) / 2;
NSRect tabSelectorFrame = NSMakeRect(xOrigin, 0, tabSelectorWidth, TAB_HEIGHT);
NSButtonCell *cellPrototype = [[NSButtonCell alloc] init];
[cellPrototype setBordered:NO];
_tabSelector = [[NSMatrix alloc] initWithFrame:tabSelectorFrame
mode:NSRadioModeMatrix
prototype:cellPrototype
numberOfRows:1
numberOfColumns:5];
[_tabSelector setTarget:self];
[_tabSelector setAction:#selector(selectedTab)];
[_tabSelector setCellSize:cellSize];
[_tabSelector setIntercellSpacing:interCellSpacing];
NSArray *theCells = [_tabSelector cells];
[theCells[0] setImage:[NSImage imageNamed:#"tab1"]];
[theCells[1] setImage:[NSImage imageNamed:#"tab2"]];
[theCells[2] setImage:[NSImage imageNamed:#"tab3"]];
[theCells[3] setImage:[NSImage imageNamed:#"tab4"]];
[theCells[4] setImage:[NSImage imageNamed:#"tab5"]];
[self addSubview:_tabSelector];
[self setDrawsBackground:NO];
[self setTabViewType:NSNoTabsNoBorder];
}
return self;
}
- (void)selectTab:(NSMatrix *)sender
{
NSLog(#"selected tab");
}
The view is drawn as desired, but clicking the buttons does not call the target method.
I have tried to add buttons programmatically to a standard IB view as described here Programatically create and position an NSButton in an OS X app?
That works, but things fall apart in my custom view. Can anybody give me a hint what I am missing?
Martin
Try changing
` [_tabSelector setAction:#selector(selectedTab)];`
To
` [_tabSelector setAction:#selector(selectedTab:)];`
Related
I've subclassed a UINavigationController by adding a UICollectionViewController as a child view controller when I init the subclass:
-(id)init{
self = [super init];
if(self){
UICollectionViewController *collectionViewController = [[UICollectionViewController alloc] initWithCollectionViewLayout:[self getLayout]];
UIBarButtonItem *cancelItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:#selector(cancelPressed)];
collectionViewController.navigationItem.rightBarButtonItem = cancelItem;
[self addChildViewController:collectionViewController];
}
return self;
}
To initialize the layout I call the getLayout method:
- (UICollectionViewFlowLayout *)getLayout {
CGFloat itemWidth = self.view.frame.size.width;
CGSize itemSize = CGSizeMake(itemWidth, itemWidth);
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setItemSize:itemSize];
return flowLayout;
}
However, when I use this code no navigation bar items are shown for iOS 7 apps (iOS 8 still works). However, if I change the line:
CGFloat itemWidth = self.view.frame.size.width;
to
CGFloat itemWidth = [[UIScreen mainScreen] bounds].size.width;
All works well. Both lines return the same value. Any ideas why this is happening?
I am working on an application where i need to zoom a UIView on click of UITapGestureRecognizer and want to perform a IBAction while zooming and after zooming. Is this possible?. Please give me a small example of it.
- (void)viewDidLoad
{
[super viewDidLoad];
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(handleDoubleTap:)];
tapGestureRecognizer.numberOfTapsRequired = 2;
[self.scrollContainer addGestureRecognizer:tapGestureRecognizer];
}
- (void)handleDoubleTap:(UIGestureRecognizer *)recognizer
{
if(isAlreadyZoomed)
{
CGPoint Pointview = [recognizer locationInView:recognizer.view];
CGFloat newZoomscal = 3.0;
CGSize scrollViewSize = self.scrollContainer.bounds.size;
CGFloat width = scrollViewSize.width/newZoomscal;
CGFloat height = scrollViewSize.height /newZoomscal;
CGFloat xPos = Pointview.x-(width/2.0);
CGFloat yPos = Pointview.y-(height/2.0);
CGRect rectTozoom = CGRectMake(xPos, yPos, width, height);
[self.scrollContainer zoomToRect:rectTozoom animated:YES];
[self.scrollContainer setZoomScale:3.0 animated:YES];
isAlreadyZoomed = NO;
}
else
{
[self.scrollContainer setZoomScale:1.0 animated:YES];
isAlreadyZoomed = YES;
}
}
IBAction are just regular methods that returns void and take 0 or 1 argument only.
You can call them in code as you would call any other king of methods.
UITapGestureRecognizer are design to call an IBAction method while triggered. You can set that from InterfaceBuilder or in code
for example the following code
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(handleTap:)];
tap.numberOfTapsRequired = 2;
[imageView addGestureRecognizer:[tap cop]];
will called this methods when the user doubleTap the imageView
- (void) handleTap:(UITapGestureRecognizer *)sender;
in this methods, you can pretty much do whatever you need:
managing your subviews,
calling other methods etc...
However, if you plan an zooming in and out, I would strongly recommand the usage of the UIScrollView class instead of the UIView class.
Cheers
hi i hope below would give u a idea.....
UITapGestureRecognizer *tap2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewDoubleTapped:)];
tap2.numberOfTapsRequired = 2;
[self.view addGestureRecognizer:tap2];
- (void) scrollViewDoubleTapped:(UITapGestureRecognizer *)sender
{
scrollZoomAdjust.zoomScale=2.0f;// set your required Zoom scale
// scrollZoomAdjust is the scroll view that contain the image within it
}
the above code was not tested
In my TestApp, I created a class "Carousel" which should let me create a swipe menu with a UIPageControl in an easy way (by simply creating an instance of the class Carousel).
Carousel is a subclass of UIView
At init, it creates an UIView, containing UIScrollView, UIPageControl
I can add further UIViews to the scroll view
I don't know if this is the proper way to do it, but my example worked quite well in my TestApp. Swiping between pages works perfectly and the display of the current page in the UIPageControl is correct.
If there were not one single problem: The UIPageControl sometimes reacts to clicks/taps (I only tested in Simulator so far!), sometimes it doesn't. Let's say most of the time it doesn't. I couldn't find out yet when it does, for me it's just random...
As you can see below, I added
[pageControl addTarget:self action:#selector(pageChange:) forControlEvents:UIControlEventValueChanged];
to my code. I thought this would do the proper handling of taps? But unfortunately, pageChange doesn't get always called (so the value of the UIPageControl doesn't change every time I click).
I would appreciate any input on this because I couldn't find any solution on this yet.
This is what I have so far:
Carousel.h
#import <UIKit/UIKit.h>
#interface Carousel : UIView {
UIScrollView *scrollView;
UIPageControl *pageControl;
BOOL pageControlBeingUsed;
}
- (void)addView:(UIView *)view;
- (void)setTotalPages:(NSUInteger)pages;
- (void)setCurrentPage:(NSUInteger)current;
- (void)createPageControlAt:(CGRect)cg;
- (void)createScrollViewAt:(CGRect)cg;
#end
Carousel.m
#import "Carousel.h"
#implementation Carousel
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Create a scroll view
scrollView = [[UIScrollView alloc] init];
[self addSubview:scrollView];
scrollView.delegate = (id) self;
// Init Page Control
pageControl = [[UIPageControl alloc] init];
[pageControl addTarget:self action:#selector(pageChange:) forControlEvents:UIControlEventValueChanged];
[self addSubview:pageControl];
}
return self;
}
- (IBAction)pageChange:(id)sender {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * pageControl.currentPage;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:TRUE];
NSLog(#"%i", pageControl.currentPage);
}
- (void)addView:(UIView *)view {
[scrollView addSubview:view];
}
- (void)createPageControlAt:(CGRect)cg {
pageControl.frame = cg;
}
- (void)setTotalPages:(NSUInteger)pages {
pageControl.numberOfPages = pages;
}
- (void)setCurrentPage:(NSUInteger)current {
pageControl.currentPage = current;
}
- (void)createScrollViewAt:(CGRect)cg {
[scrollView setPagingEnabled:TRUE];
scrollView.frame = cg;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width*pageControl.numberOfPages, scrollView.frame.size.height);
[scrollView setShowsHorizontalScrollIndicator:FALSE];
[scrollView setShowsVerticalScrollIndicator:FALSE];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
float frac = scrollView.contentOffset.x / scrollView.frame.size.width;
NSUInteger page = lround(frac);
pageControl.currentPage = page;
}
#end
ViewController.m (somewhere in viewDidLoad)
Carousel *carousel = [[Carousel alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
for (int i=0; i<5; i++) {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(i * 320, 0, 320, 420)];
UIColor *color;
if(i%3==0) color = [UIColor blueColor];
else if(i%3==1) color = [UIColor redColor];
else color = [UIColor purpleColor];
view.backgroundColor = color;
[carousel addView:view];
view = nil;
}
[carousel setTotalPages:5];
[carousel setCurrentPage:0];
[carousel createPageControlAt:CGRectMake(0,420,320,40)];
[carousel createScrollViewAt:CGRectMake(0,0,320,420)];
Your code is correct. Most likely the frame of your pageControl is pretty small, so theres not a lot of area to look for touch events. You would need to increase the size of the height of pageControl in order to make sure taps are recognized all of the time.
I create a custom view in cocoa touch that is superclassed by UIView and in my main controller I initialize it and then add it as a subview to the main view, but when I add it to the main view it calls my initializer method again and causes an infinite loop. Am I going about creating my custom view wrong?
Here is the mainView
- (void)loadView {
UIImage* tempImage = [UIImage imageNamed: #"image1.jpg"];
CustomImageContainer *testImage = [[CustomImageContainer alloc] initWithImage: tempImage andLabel: #"test image" onTop: true atX: 10 atY: 10];
[self.view addSubview: testImage];
}
and the CustomImageContainer
-(CustomImageContainer *) initWithImage: (UIImage *)imageToAdd andLabel: (NSString *)text onTop: (BOOL) top atX: (int) x_cord atY: (int) y_cord{
UIImageView *imageview_to_add = [[UIImageView alloc] initWithImage: imageToAdd];
imageview_to_add.frame = CGRectMake(0, 0, imageToAdd.size.width, imageToAdd.size.height);
UILabel *label_to_add = [[UILabel alloc] init];
label_to_add.text = text;
label_to_add.alpha = 50;
label_to_add.backgroundColor = [UIColor blackColor];
label_to_add.textColor = [UIColor whiteColor];
[self addSubview: imageview_to_add];
self.frame = CGRectMake(x_cord, y_cord, imageToAdd.size.width, imageToAdd.size.height);
if (top) {
label_to_add.frame = CGRectMake(0, 0, imageview_to_add.frame.size.width, imageview_to_add.frame.size.height);
//[self addSubview: label_to_add];
}
else {
label_to_add.frame = CGRectMake(0,.2 * imageview_to_add.frame.size.height, imageview_to_add.frame.size.width, imageview_to_add.frame.size.height);
}
[self addSubview: label_to_add];
[super init];
return self;
}
Why did you put the [super init] statement at the end of the initializer ? When subclassing, you usually put this statement at the start of method.
For UIView subclasses, the designated initializer when creating views in code is initWithFrame:, so you should call it before adding the label and the image. You can use the image to compute the frame needed by the custom view.
-(CustomImageContainer *) initWithImage: (UIImage *)imageToAdd andLabel: (NSString *)text onTop: (BOOL) top atX: (int) x_cord atY: (int) y_cord{
// The view will gets its frame to the size of the image
UIImageView *imageview_to_add = [[UIImageView alloc] initWithImage: imageToAdd];
// Call the designated initializer
CGRect frame = CGRectMake(x_cord, y_cord, imageToAdd.size.width, imageToAdd.size.height);
self = [super initWithFrame:frame];
[self addSubview: imageview_to_add];
UILabel *label_to_add = [[UILabel alloc] init];
label_to_add.text = text;
label_to_add.alpha = 50;
label_to_add.backgroundColor = [UIColor blackColor];
label_to_add.textColor = [UIColor whiteColor];
if (top) {
label_to_add.frame = CGRectMake(0, 0, imageview_to_add.frame.size.width, imageview_to_add.frame.size.height);
}
else {
label_to_add.frame = CGRectMake(0,.2 * imageview_to_add.frame.size.height, imageview_to_add.frame.size.width, imageview_to_add.frame.size.height);
}
[self addSubview: label_to_add];
return self;
}
If you still have an infinite loop, pause the debugger and search for the recurrent method pattern in the stack trace. This pattern will gives you where the code enters the infinite loop.
I have a scroll view that doesn't scroll right, I've simplified the code for below.
It draws the view and some horizontal buttons that i add more stuff to in the real code.
If you drag the whitespace between the buttons the view scrolls. If you happen to put your finger on a button, it won't scroll.
After a related suggestion, I tried to add the delaysContentTouches = YES line, but it doesn't seem to make a difference.
UIScrollview with UIButtons - how to recreate springboard?
What am I doing wrong?
TIA,
Rob
Updated the code
- (void)viewDidLoad {
l = [self landscapeView];
[self.view addSubview:l];
[l release];
}
- (UIScrollView *) landscapeView {
// LANDSCAPE VIEW
UIScrollView *landscapeView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 325)];
landscapeView.backgroundColor = [UIColor whiteColor];
landscapeView.delaysContentTouches = YES;
NSInteger iMargin, runningY, n;
iMargin = 3;
runningY = iMargin;
for (n = 1; n <= 38; n++) {
//add day labels
UIButton *templabel = [[UIButton alloc] initWithFrame:CGRectMake(iMargin,runningY,320 - ( 2 * iMargin),20)];
templabel.backgroundColor = [UIColor grayColor];
[landscapeView addSubview:templabel];
[templabel release];
runningY = runningY + 30;
}
landscapeView.contentSize = CGSizeMake( 320, runningY);
return landscapeView;
}
I can't duplicate your results; when I create a scrollView and add buttons, scrolling is still active even when doing a touch-down on the button. Are you setting any other UIScrollView properties, or subclassing any methods, that aren't showing up in your simplified code?
It looks like one thing that's going on is that the getter for landScapeView is a complicated constructor...so every time you do [self landscapeView] or self.landscapeView, you're creating a whole new UIScrollView and operating on that.
I'd try something like this in your .h file:
#interface MyClass: MyParent {
UIScrollView *landscapeView;
}
#property (retain) UIScrollView *landscapeView;
...and this in your .m file:
#implementation MyClass
#synthesize landscapeView;
(void)viewDidLoad
{ l = [[[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 325)] autorelease];
[self.view addSubview:self.landscapeView]; [l release];
landscapeView.backgroundColor = [UIColor whiteColor];
landscapeView.delaysContentTouches = YES;
NSInteger iMargin, runningY, n;
iMargin = 3;
runningY = iMargin;
for (n = 1; n <= 38; n++) { //add day labels
UIButton *templabel = [[UIButton alloc] initWithFrame:CGRectMake(iMargin,runningY,320 - ( 2 * iMargin),20)];
templabel.backgroundColor = [UIColor grayColor];
[landscapeView addSubview:templabel];
[templabel release]; runningY = runningY + 30;
}
landscapeView.contentSize = CGSizeMake( 320, runningY);
}