Add Subview to ViewController with Block and ARC in encapsulated Class - objective-c

i'am searching for a way to add a Subview (in my example a UIPickerView) to a ViewController like MBProgressHUD. My Subview is an UIView which has a UIPickerView and a UIButton on it to select an item.
I use this view in different ViewControllers, so it would be nice to encapsulate it in a own Class. My Problem is that it only works without ARC. With ARC the App crash with EXC_BAD_ACCESS CODE = 1.... So i think i have to set the variables to strong. Can anyone help me?!
The .h File looks as the following:
#
import <Foundation/Foundation.h>
typedef void(^completition)(NSString *result);
#interface CDPickerView : NSObject<UIPickerViewDataSource, UIPickerViewDelegate>
#property(nonatomic, strong) __block UIView *_view;
#property(nonatomic, strong) __block NSMutableArray *_selection;
-(void)addPickerToView:(UIView*)view withSelection:(NSMutableArray*)selection andReturnSelectedUsingBlock:(completition) compBlock;
#end
The .m File:
#import "CDPickerView.h"
#interface CDPickerView (){
// __strong UIView *_view;
// __strong NSMutableArray *_selection;
__strong void (^completitionTest)(NSString* result);
}
#end
#implementation CDPickerView
-(id)init{
if(!self){
self = [self init];
}
return self;
}
-(void)addPickerToView:(UIView *)view withSelection:(NSMutableArray *)selection andReturnSelectedUsingBlock:(completition) compblock{
self._view = view;
self._selection = [[NSMutableArray alloc] initWithArray:selection];
completitionTest = compblock;
dispatch_async(dispatch_get_main_queue(), ^(void){
[self addSelectionView];
});
}
-(void)addPickerToView:(UIView *)view withSelection:(NSMutableArray *)selection{
__view = view;
__selection = [[NSMutableArray alloc] initWithArray:selection];
dispatch_async(dispatch_get_main_queue(), ^(void){
[self addSelectionView];
});
}
-(void)addSelectionView{
__strong UIView *custView = [[UIView alloc]initWithFrame:CGRectMake(5, 5, 310, 300)];
custView.layer.cornerRadius = 10.0;
custView.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.90];
__strong UIPickerView *picker = [[UIPickerView alloc]initWithFrame:CGRectMake(5, 5, 290, 200)];
picker.dataSource = self;
picker.delegate = self;
[custView addSubview:picker];
__strong UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 210, 310, 40)];
[button setTitle:#"Auswählen" forState:UIControlStateNormal];
// button.frame =
button.backgroundColor = [UIColor whiteColor];
button.titleLabel.font = [UIFont systemFontOfSize:20.0];
button.titleLabel.textColor = [UIColor blackColor];
[button addTarget:self action:#selector(choiceButtonTapped) forControlEvents:UIControlEventTouchUpInside];
[custView addSubview:button];
[__view addSubview:custView];
[picker reloadAllComponents];
}
-(void)choiceButtonTapped{
UIView *view = [[__view subviews] lastObject];
UIPickerView *picker = [[view subviews] objectAtIndex:0];
NSString *result = [__selection objectAtIndex:[picker selectedRowInComponent:0]];
completitionTest(result);
[view removeFromSuperview];
}
-(NSString*)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
return [__selection objectAtIndex:row];
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
return __selection.count;
}
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
return 1;
}
#end
To add this view i do the following:
__strong NSMutableArray *selection = [[NSMutableArray alloc]initWithObjects:#"Test1",#"Test2",#"Test3",#"Test4", nil];
__strong CDPickerView *picker = [[CDPickerView alloc]init];
[picker addPickerToView:self.view withSelection:selection andReturnSelectedUsingBlock:^(NSString *result) {
NSLog(#"Test: %#",result);
}];
Thank for your help!

First of all it seems that you should read where __strong and __block should be user.
Change
#property(nonatomic, strong) __block UIView *_view;
#property(nonatomic, strong) __block NSMutableArray *_selection;
to
#property(nonatomic, strong) UIView *_view;
#property(nonatomic, strong) NSMutableArray *_selection;
and
completitionTest = compblock;
to
completitionTest = [compblock copy];
in -(void)addPickerToView:(UIView *)view withSelection:(NSMutableArray *)selection

Related

Change background color on hover on a NSMenuItem with custom NSView

Here is the thing:
I have created a custom NSMenuItem with a custom NSView in it.
Everything works fine except that I can't get the NSMenuItem to get highlighted (= change the background color on mouseover).
I'm trying to do it inside the drawRect method, as shown in other answers posted here.
What am I doing wrong?
The NSView subclass:
#interface customView : NSView
#end
#implementation customView
- (id)initWithFrame:(NSRect)frame
{
NSRect theRect = NSMakeRect(0, 0, 200, 30);
self = [super initWithFrame:theRect];
if (self) {
NSTrackingArea * trackingArea = [[NSTrackingArea alloc] initWithRect:theRect
options: (NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow |NSTrackingActiveAlways)
owner:self userInfo:nil];
[self addTrackingArea:trackingArea];
}
return self;
}
#define menuItem ([self enclosingMenuItem])
- (void) drawRect: (NSRect) rect {
BOOL isHighlighted = [menuItem isHighlighted];
if (isHighlighted) {
//this nslog never happens
NSLog(#"it's highlighted");
}
- (void)mouseUp:(NSEvent*) event {
NSMenuItem* mitem = [self enclosingMenuItem];
NSMenu* m = [mitem menu];
[m cancelTracking];
NSLog(#"you clicked the %ld item",[m indexOfItem: mitem]);
}
#end
The NSMenuItem subclass:
(I add subviews on the custom view here so i can have access to the controls through the NSMenuItem instance)
#interface customItem : NSMenuItem{
}
-(void)setTheText:(NSString*)theString;
#property NSTextField *theLabel;
#end
#import "customItem.h"
#import "customView.h"
#implementation customItem
#synthesize theLabel;
-(id)init{
if (self){
customView *cv = [[customView alloc] init];
theLabel = [[NSTextField alloc] initWithFrame:NSMakeRect(10, 8, 130, 17)];
[theLabel setEditable:NO];
[theLabel setBordered:NO];
NSButton *myButton = [[NSButton alloc] initWithFrame:NSMakeRect(170, 7, 20, 20)];
NSButton *myButton1 = [[NSButton alloc] initWithFrame:NSMakeRect(150, 7, 20, 20)];
[myButton setBezelStyle:NSCircularBezelStyle];
[myButton1 setBezelStyle:NSCircularBezelStyle];
[myButton setTitle:#""];
[myButton1 setTitle:#""];
[cv addSubview:myButton];
[cv addSubview:myButton1];
[cv addSubview:theLabel];
[self setView:cv];
[theLabel setStringValue:#"A Value "];
}
return self;
}
-(void)setTheText:(NSString *)theString{
[theLabel setStringValue:theString];
}
#end
And this is the App Delegate :
#interface AppDelegate : NSObject <NSApplicationDelegate>{
NSStatusItem *statusItem;
IBOutlet NSMenu *theMenu;
}
#property (assign) IBOutlet NSWindow *window;
#end
#import "customItem.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
}
- (void)awakeFromNib{
statusItem = [[NSStatusBar systemStatusBar]
statusItemWithLength:NSSquareStatusItemLength];
NSBundle *bundle = [NSBundle mainBundle];
NSImage *statusImage = [[NSImage alloc] initWithContentsOfFile:[bundle pathForResource:#"barIcon" ofType:#"png"]];
NSImage *highlightImage = [[NSImage alloc] initWithContentsOfFile:[bundle pathForResource:#"barIcon_H" ofType:#"png"]];
[statusItem setImage:statusImage];
[statusItem setAlternateImage:highlightImage];
[statusItem setMenu:theMenu];
[theMenu removeAllItems];
customItem *mi = [[customItem alloc] init];
[theMenu addItem:mi];
customItem *mi2 = [[customItem alloc] init];
[theMenu addItem:mi2];
}
#end
This is what i get:
No need to add Booleans or anything else, you can do it from within your custom NSView which is attached to your NSMenuItem
- (void)drawRect:(NSRect)rect {
[super drawRect:rect];
//Handle the hightlight
if ([[self enclosingMenuItem] isHighlighted])
{
[self.lbl_title setTextColor:[NSColor whiteColor]];
[self.lbl_amount setTextColor:[NSColor colorWithDeviceRed:151.0f/255.0f green:164.0f/255.0f blue:179.0f/255.0f alpha:1.0f]];
[[NSColor selectedMenuItemColor] setFill];
}
else
{
[self.lbl_title setTextColor:[NSColor blackColor]];
[self.lbl_amount setTextColor:[NSColor whiteColor]];
[[self backgroundColor] setFill];
}
NSRectFill(rect);}
OK i think i got it.
I added a public bool variable in the NSView subclass.
Then i used
-(void)mouseEntered:(NSEvent *)theEvent
and
-(void)mouseExited:(NSEvent *)theEvent
to set the variable to YES or to NO. After setting the variable i used
[self setNeedsDisplay:YES]
to call
-(void) drawRect: (NSRect) rect
That's how i got it working :)
Here is the right way of changing the background colour (highlighting the item) without maintaining a local variable and force the view to draw again,
-(void)mouseEntered:(NSEvent *)event {
self.layer.backgroundColor = [[NSColor blueColor] colorWithAlphaComponent:0.3].CGColor;
[theLabel setTextColor:[NSColor whiteColor]];
}
-(void)mouseExited:(NSEvent *)event {
self.layer.backgroundColor = [NSColor clearColor].CGColor;
[theLabel setTextColor:[NSColor blackColor]];
}
Make sure you also set [self setWantsLayer:YES] before changing the layer background colour.

Trying to fix subview flipping and now subview buttons no longer work?

I have a view which includes two subviews. I had it working so that only one subview was shown at a time and each subview had a button and when the button was clicked the subview would flip over and the next subview would appear. The problem was that it appeared as though the entire view was flipping. After reading on this site about how to solve the problem I attempted to add the subviews to a container and flip that instead. However now, although my first subview is showing up when I press the button it no longer flip. It doesn't do anything. I put a log statement in the method which flips the subviews, as well as a breakpoint and as far as I can tell it no longer gets called. I'm very new to xcode and objective c and delegates and I have no idea how to proceed. Any help would be appreciated. Thanks.
I have included the relevant code here:
The header for the ViewController
#interface ExerciseViewController : UIViewController<ExerciseSubViewDelegate>
//stuff for subviews
#property (nonatomic, strong) ExerciseSubViewImage *subViewImage;
#property (nonatomic, strong) ExerciseSubViewText *subViewText;
#property UIView *panel;
#end
This is the code for the ViewController:
#interface ExerciseViewController ()
#end
#implementation ExerciseViewController
#synthesize subViewImage, subViewText;
- (void)viewDidLoad
{
self.subViewImage.delegate = self;
_panel = [[UIView alloc] initWithFrame:CGRectMake(0,0, self.view.bounds.size.width, self.view.bounds.size.height/2)];
_panel.backgroundColor = [UIColor whiteColor];
[self.view addSubview:_panel];
[_panel addSubview:subViewImage];
}
-(ExerciseSubViewImage *)subViewImage
{
if (!subViewImage)
{
CGRect subViewImageFrame = CGRectMake(0,0, _panel.bounds.size.width, _panel.bounds.size.height);
self.subViewImage = [[ExerciseSubViewImage alloc] initWithFrame:subViewImageFrame];
[_panel addSubview:subViewImage];
}
return subViewImage;
}
-(ExerciseSubViewText *)subViewText
{
if (!subViewText)
{
CGRect subViewTextFrame = CGRectMake(0,0, _panel.bounds.size.width, _panel.bounds.size.height);
self.subViewText = [[ExerciseSubViewText alloc] initWithFrame:subViewTextFrame];
self.subViewText.backgroundColor = [UIColor blueColor];
[_panel addSubview:subViewText];
}
return subViewText;
}
-(void)exerciseSubViewImagePressed
{
[UIView transitionWithView:_panel
duration:0.2
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
[subViewImage removeFromSuperview];
[_panel addSubview:subViewText];
}
completion: nil];
//This is how I did it before I added the container
/*[UIView transitionFromView:subViewImage
toView:subViewText
duration:0.2
options:UIViewAnimationOptionTransitionFlipFromRight
completion:nil];
self.subViewText.delegate = self;*/
NSLog(#"Ipushedtheimage");
}
-(void)exerciseSubViewTextPressed
{//I haven't updated this yet
[UIView transitionFromView:subViewText
toView:subViewImage
duration:0.2
options:UIViewAnimationOptionTransitionFlipFromRight
completion:nil];
self.subViewImage.delegate = self;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
subViewImage = nil;
subViewText = nil;
}
#end
This is the code for the delegate
#import
#protocol ExerciseSubViewDelegate <NSObject>
-(void) exerciseSubViewImagePressed;
-(void) exerciseSubViewTextPressed;
#end
I am also added the code for the first subview:
#import
#import "ExerciseSubViewDelegate.h"
#interface ExerciseSubViewImage : UIView
#property (nonatomic, strong) UIButton *button;
#property (nonatomic, assign) id<ExerciseSubViewDelegate>delegate;
#property (strong, nonatomic) UIImageView *exerciseImageView;
#end
#import "ExerciseSubViewImage.h"
#import "UIImage+animatedGIF.h"
#implementation ExerciseSubViewImage
#synthesize button;
#synthesize delegate;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
//Initialization code
self.button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
CGRect buttonFrame = CGRectMake(50,200,100,35);
self.button.frame = buttonFrame;
[self.button setTitle:#"Image"forState:UIControlStateNormal];
[self.button addTarget:self
action:#selector(buttonTouched)
forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.button];
_exerciseImageView = [[UIImageView alloc] initWithFrame:CGRectMake(50,20,160,158)];
NSURL *url = [[NSBundle mainBundle] URLForResource:#"AppleLogo" withExtension:#"gif"];
_exerciseImageView.image = [UIImage animatedImageWithAnimatedGIFURL:url];
[self addSubview:self.exerciseImageView];
}
return self;
}
-(void)buttonTouched
{
NSLog(#"imagebuttonpressed");
[self.delegate exerciseSubViewImagePressed];
}
Again, any help would be appreciate. I know I'm probably just not understanding something simple.
Ok. This took me all weekend but I finally figured it out on my own. I thought I would shere the answer here in case anyone else ever has a similar problem. After trying several other approaches I finally went back to the approach I used here and started inserting a whole bunch of NSLogs to determine the order that every thing was executing in. What I finally ended up doing was changing this: (all in the top ViewController)
self.subViewImage.delegate = self;
_panel = [[UIView alloc] initWithFrame:CGRectMake(0,0, self.view.bounds.size.width, self.view.bounds.size.height/2)];
_panel.backgroundColor = [UIColor whiteColor];
[self.view addSubview:_panel];
[_panel addSubview:subViewImage];
to this:
//create panel
_panel = [[UIView alloc] initWithFrame:CGRectMake(0,0, self.view.bounds.size.width, s self.view.bounds.size.height/2)];
_panel.backgroundColor = [UIColor whiteColor];
[self.view addSubview:_panel];
[_panel addSubview:subViewImage];
//Set the subview delegates
self.subViewImage.delegate = self;
self.subViewText.delegate = self;

iOS: get image label

I have this code:
- (IBAction) checkIt:(id)sender{
NSString *button = [[(UIImageView *)sender label]text];
I want to get the label of the UIImageView. What am I doing wrong? This works with buttons...Not sure I understand how to access attributes correctly.
Subclass UIImageView. Add a string member and property for accessing it. Instead of creating an object of UIImageView, create the object of your class.
In .h
#interface CustomImageView : UIImageView
{
NSString *imageName;
}
#property (nonatomic, retain) NSString *imageName;
#end
in .m
#implementation CustomImageView
#synthesize imageName;
-(void) dealloc
{
[imageName release];
[super dealloc];
}
#end
in the file where you want to use this class
-(void) addCustomImageView
{
CustomImageView *imgView = [[CustomImageView alloc]initWithImage:[UIImage imageNamed:#"abc.png"]];
imgView.frame = CGRectMake(0, 0, 100, 100);
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(imgViewtapped:)];
[imgView addGestureRecognizer:tap];
[tap release];
[self.view addSubview:imgView];
[imgView release];
}
-(NSString *) imgViewtapped:(CustomImageView *)sender
{
return [sender imageName];
}
UIImageViews do not have labels.
https://developer.apple.com/library/ios/#documentation/uikit/reference/UIImageView_Class/Reference/Reference.html

After setting UITextView's text property, screen does not update accordingly

I have view controllers A(FileListViewController) and B(TextFileViewController). A is a UITableViewController. What I am doing now is that after selecting a row in controller A, I load a text file and display that text in a UITextView in controller B.
The following is the header and implementation part(some code is abridged) of my the two controllers.
FileListViewcontroller Interface:
#interface FileListViewController : UITableViewController {
NSMutableArray * fileList;
DBRestClient* restClient;
TextFileViewController *tfvc;
}
#property (nonatomic, retain) NSMutableArray * fileList;
#property (nonatomic, retain) TextFileViewController *tfvc;
#end
FileListViewController Implementation:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
DBMetadata *metaData = [fileList objectAtIndex:indexPath.row];
if(!metaData.isDirectory){
if([Utils isTextFile:metaData.path]){
if(!tfvc){
tfvc = [[TextFileViewController alloc] init];
}
[self restClient].delegate = self;
[[self restClient] loadFile:metaData.path intoPath:filePath];
[self.navigationController pushViewController:tfvc animated:YES];
}
}
- (void)restClient:(DBRestClient*)client loadedFile:(NSString*)destPath {
NSError *err = nil;
NSString *fileContent = [NSString stringWithContentsOfFile:destPath encoding:NSUTF8StringEncoding error:&err];
if(fileContent) {
[tfvc updateText:fileContent];
} else {
NSLog(#"Error reading %#: %#", destPath, err);
}
}
And here is the interface for TextFileViewController:
#interface TextFileViewController : UIViewController {
UITextView * textFileView;
}
#property (nonatomic, retain) IBOutlet UITextView * textFileView;
-(void) updateText:(NSString *) newString;
#end
TextFileViewController implementation:
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(done)] autorelease];
textFileView = [[UITextView alloc] init];
}
- (void) updateText:(NSString *)newString {
NSLog(#"new string has value? %#", newString);
[textFileView setText:[NSString stringWithString:newString]];
NSLog(#"print upddated text of textview: %#", textFileView.text);
[[self textFileView] setNeedsDisplay];
}
(void)restClient: loadedFile: will be call after the loadFile:intoPath: is completed in the disSelectRowAtIndexPath method.
In TextFileViewController's updateText method, from NSLog I see that the text property is updated correctly. But the screen does not update accordingly. I've tried setNeedsDisplay but in vain. Did I miss something?
Thanks in advance.
In -[TextFileViewController viewDidLoad] you're creating a UITextView, but its frame is never set, and it's not added to the view hierarchy.
Try changing this:
textFileView = [[UITextView alloc] init];
to this:
textFileView = [[UITextView alloc] initWithFrame:[[self view] bounds]];
[[self view] addSubview:textFileView];
The problem is that textFileView is created in the viewDidLoad method of TextFileViewController. This method has not yet been called by the time you call updateText (this happens before the TextFileViewController is pushed).
You can fix this by forcing the view to load before you call [[self restClient] loadFile:metaData.path intoPath:filePath];.

Add Background Image behind TextView

Hey guys,
Does anyone know how to make the Text View transparent and add a Background Image behind the Text View? Thanks
In viewDidLoad
textView.backgroundColor = [UIColor clearColor];
UIImageView *imageView = [[[UIImageView alloc] initWithFrame:textView.frame] autorelease];
imageView.image = [UIImage imageNamed:#"myBackgroundImage.png"];
[self.view addSubview:imageView];
[self.view bringSubviewToFront:textView];
Try to make a new class. (Did not test the code, just typed it on the forum, try to use the code otherwise: use the concept)
#interface TextViewWithImage : UIView {
UITextView *textView;
UIImageView *imageView;
}
#property (retain, readonly) UITextView *textView;
#property (retain, readonly) UIImageView *imageView;
#implementation TextViewWithImage
- (id) init {
if (self = [super init]) {
[self setupContentViews];
}
return self;
}
- (id) initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupContentViews];
}
return self;
}
- (void) setupContentViews {
textView = [[UITextView alloc] init];
imageView = [[UIImageView alloc] init];
textView.frame = self.frame;
imageView.frame = self.frame;
textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
- (void) dealloc {
[textView release];
[imageView release];
[super dealloc];
}
#end
UIViews (of which UITextView is a subclass) have a property called backgroundColor.
#property(nonatomic, copy) UIColor *backgroundColor
Try calling [textView setBackgroundColor:[UIColor clearColor]] on the TextView and putting an image view behind it.