I have two view controllers, both of which contain table views. I would like to reuse the code because they are identical, and would like to keep things clean (as well as keep some data from the view controller). How can I go about doing this? Is it "allowed" so to speak, or is it frowned upon?
CustomTableView.h:
#interface CustomTableView : UITableView
#property (nonatomic, strong) NSString *someCoolString;
#property (nonatomic, strong) UIColor *superDuperColor;
#end
CustomTableView.m:
#import "CustomTableView.h"
#implementation CustomTableView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// Initialization code
self.someCoolString = #"theString";
self.superDuperColor = [UIColor colorWithRed:48.0/255.0 green:32.0/255.0 blue:100.0/255.0 alpha:1.0];
}
return self;
}
#end
You should create a subclass of UITableView.
Related
I have an app where I need to present an overlay for feedback. I started by simply creating the exact thing I wanted within the UIViewController I wanted. However this presents to 2 problems. 1) I can't reuse this in another view (As I need to now) and 2) Because it's an overlay, it covers the entire UIViewController on the storyboard so I can't see the controls beneath it.
I looked at moving to an external UIView .xib file and loading dynamically which worked great, except whatever I did, I couldn't never get a handle on the labels within the nib to update the text.
Then I decided that making it a class and creating a delegate method for it would probably be the best way forward.
I have created a very simply .xib and laid it out as well as a .h and .m file (overlayView) and wired it all in an it looks good, except when trying to present the overlayView I get a exc_bad_access on the line
[window addSubview:self];
And I can't work out why. Full code below:
overlayView.h
#import <UIKit/UIKit.h>
#class overlayView;
#protocol overlayDelegate;
#interface overlayView : UIView
#property (nonatomic, strong) id <overlayDelegate> delagate;
-(instancetype)initWithTitle:(NSString *)title
dateFrom:(NSString *)dateFrom
dateTo:(NSString *)dateTo
description:(NSString *)description;
#property (strong, nonatomic) IBOutlet UILabel *overlayTitleLbl;
#property (strong, nonatomic) IBOutlet UILabel *overlayDateFromLbl;
#property (strong, nonatomic) IBOutlet UILabel *overlayDateToLbl;
#property (strong, nonatomic) IBOutlet UILabel *overlayDescLbl;
#property (strong, nonatomic) IBOutlet UILabel *overlayIcon;
-(void)showOverlay;
-(void)dismissOverlay;
#end
#protocol overlayDelegate <NSObject>
#optional
#end
overlayView.m
#import "overlayView.h"
#import "NSString+FontAwesome.h"
#implementation overlayView
- (instancetype)initWithTitle:(NSString *)title dateFrom:(NSString *)dateFrom dateTo:(NSString *)dateTo description:(NSString *)description {
self.overlayViewTitleLbl.text = title;
self.overlayViewDateFromLbl.text = dateFrom;
self.overlayViewDateToLbl.text = dateTo;
self.overlayViewDescLbl.text = description;
self.overlayViewIcon.text = [NSString fontAwesomeIconStringForIconIdentifier:#"fa-calendar"];
return self;
}
-(void)showOverlay {
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
[window addSubview:self]; <-- Code causing issue
[window makeKeyAndVisible];
}
-(void)dismissOverlay {
// Not wired in yet
}
#end
The being called in my main view controller like:
overlay = [[overlayView alloc] initWithTitle:[tmpDict objectForKeyedSubscript:#"Title"] dateFrom:startDate dateTo:stopDate description:[tmpDict objectForKeyedSubscript:#"Desc"]];
[overlay showOverlay];
Any ideas why this doesn't want to play ball? I have breakpointed the initWithTitle method and all information is being passed correctly so I think I am very close to what I am trying to achieve.
you need to initiate your view first, you're returning self without initiating it
- (instancetype)initWithTitle:(NSString *)title dateFrom:(NSString *)dateFrom dateTo:(NSString *)dateTo description:(NSString *)description {
self = [super init];
if (self) {
self.overlayViewTitleLbl.text = title;
self.overlayViewDateFromLbl.text = dateFrom;
self.overlayViewDateToLbl.text = dateTo;
self.overlayViewDescLbl.text = description;
self.overlayViewIcon.text = [NSString fontAwesomeIconStringForIconIdentifier:#"fa-calendar"];
}
return self;
}
This might be a really bad question, so I apologize in advance. Feel free to tell me if there is a better way to approach this.
I'm using storyboards to layout the initial arrangement of objects. Say that I put a UIView on the storyboard, and I link that view to a property in my ViewController.m file called storyboardView. At runtime, a lot might happen to that UIView, and following the MVC pattern, I'd like the code that governs that behavior to exist in a separate subclass. How can I 'cast' that UIView so that it is now responds to the subclass rather than the ViewController?
I'm thinking of something along these lines, but this doesn't work. It doesn't throw any errors, but the background color isn't turning red, so I know that I am unsuccessful:
#import "ViewController.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UIView *storyboardView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.storyboardView = [[MyView alloc] initWithFrame:self.storyboardView.frame];
}
#end
Subclass Header:
#import <UIKit/UIKit.h>
#interface MyView : UIView
#end
Subclass Implementation:
import "MyView.h"
#implementation MyView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor redColor];
}
return self;
}
#end
In storyboard you can change the UIView to any subclass like this
I'm trying to move a UIView across the screen.
UIGravityBehavior and UIPushBehavior both take things like density, velocity and friction into account. I tried to implement my own dynamic behavior that ignores those physics.
My UIDynamicBehavior subclass and some basic implementation
// MYDynamicBehavior.h
#interface MYDynamicBehavior : UIDynamicBehavior
- (void)addItem:(id<UIDynamicItem>)item;
- (void)removeItem:(id<UIDynamicItem>)item;
#end
// MYDynamicBehavior.m
#interface MYDynamicBehavior ()
#property (strong, nonatomic) NSMutableArray *items;
#end
#implementation MYDynamicBehavior
- (void (^)(void))action
{
__weak MYDynamicBehavior *weakSelf = self;
for (UIView *item in weakSelf.items)
item.center = CGPointMake(item.center.x + 10.0, item.center.y);
}
- (instancetype)init
{
if (self=[super init]) {
__weak MYDynamicBehavior *weakSelf = self;
weakSelf.action = ^{
for (UIView *item in weakSelf.items)
item.center = CGPointMake(item.center.x + 10.0, item.center.y);
};
}
return self;
}
- (void)addItem:(id<UIDynamicItem>)item
{
[self.items addObject:item];
}
#end
// ViewController.m
// #includes
#interface ViewController ()
#property (strong, nonatomic) UIDynamicAnimator *ani;
#property (strong, nonatomic) UIView *kin;
#property (strong, nonatomic) MYDynamicBehavior *skywalk;
#end
#implementation ViewController
…
- (void)viewDidLoad
{
[super viewDidLoad];
self.ani = [[UIDynamicAnimator alloc] init];
self.kin = // some view
self.skywalk = [[MYDynamicBehavior alloc] init];
[self.ani addBehavior:self.skywalk];
[self.skywalk addItem:kin];
}
#end
I'm trying to recreate this from memory, I think the basics are here
Anyway, it's my impression from the documentation that the action property is where I need to implement my animation. It doesn't appear that my action black is ever called, however.
This is the closest I've come to a solution, but I still haven't solved this problem yet.
What am I missing? Why isn't my custom UIDynamicBehavior subclass working?
I haven't found the documentation that states this explicitly, and I can't guess at the underlying reason, but I have found that if my custom UIDynamicBehavior classes call their action blocks only if there is a child behavior added which has at least one item in it.
It's weird enough that I think I'm experiencing a side effect rather than this working as intended, but that does reliably get the action block to fire. Would be really interested if anybody could shed light on the why, though. :1
I am quite new to iOS development and thus new to the concept of storyboard as well.
As this seems to be the 'new thing', everyone should use, I thought I might give it a try as well.
I got a project here, created with a Foo.xib file.
The xib file has several view objects included.
Then I have a class Foo.h and Foo.m class with following content:
Foo.h
#import <UIKit/UIKit.h>
#interface Foo : UIView
#property (strong, nonatomic) IBOutlet UIView *view01;
#property (strong, nonatomic) IBOutlet UIView *view02;
#property (strong, nonatomic) IBOutlet UIView *view03;
#property (strong, nonatomic) IBOutlet UIView *view04;
- (NSUInteger)viewCount;
#end
Foo.m
#import "Foo.h"
#interface Foo()
#property (nonatomic, strong) NSArray *views;
#end
#implementation Foo
- (id)init {
self = [super init];
if (self) {
self.views = [[NSBundle mainBundle] loadNibNamed:#"Foo" owner:self options:nil];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (NSUInteger)viewCount {
return [self.views count];
}
#end
In my ViewController I would then load all the views and make it scrollable, like this:
#import "ViewController.h"
#import "Foo.h"
#interface ViewController ()
#property (nonatomic, strong) UIScrollView *scrollView;
#property (nonatomic, strong) Foo *views;
#end
#implementation ViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.views = [[Foo alloc] init];
CGSize fooSize = self.views.view01.bounds.size;
NSUInteger viewCount = [self.views viewCount];
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, fooSize.width, fooSize.height)];
[self.scrollView setContentSize:CGSizeMake(viewCount*fooSize.width, fooSize.height)];
[self.scrollView setBounces:YES];
[self.scrollView setPagingEnabled:YES];
self.scrollView.delegate = self;
NSArray *views = #[ self.views.view01,
self.views.view02,
self.views.view03,
self.views.view04
];
for (int i=0; i<viewCount; i++) {
UIView *curView = views[i];
CGRect frame = curView.frame;
frame.origin.x = i*fooSize.width;
frame.origin.y = 0;
curView.frame = frame;
[self.scrollView addSubview:curView];
}
[self.view addSubview:self.scrollView];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
However, I have no clue, how to realize this with a storyboard. It seems to me that I have to have a NavigationController which is then linked to the Master View Controller. And now I would have to add a new ViewController for each view? Or is there a way to include all views within one ViewController like I did 'the old way'?
There is a massive mis conception that when using a storyboard it limits you to what you can do. A storyboard is simply like an array of .xib files, it holds many screens in the one file so you can see the entire flow of you app in one place. Inside a storyboard you can create a single viewController and assign a custom viewController class to it and then load / modify what ever you like inside the code of this viewController, as you have done above.
However the benefit of using the storyboard is to have multiple viewController objects so you can design all the screens and navigation there were you can see it, aiding you in debugging and design work.
If you already have the app working without a storyboard and you simply want to use it because its new but keep the old style of coding, you are not going to see much of the benefits. I would suggest following this example from the developer library on how to use a storyboard properly. After you complete this you will see the benefits of the new style and you can decide whether to do it in code or using the interface builder:
http://developer.apple.com/library/ios/#documentation/iPhone/Conceptual/SecondiOSAppTutorial/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011318
I'm making elevator thing. I'm having trouble sending data with different views using presentModalViewController. I got red message "favoriteColorString" property not found. I copied exactly the same but different form names and buttons. The "favoriteColorString" appears an error and unable to send elevator2 data.
I tried two different thing.
Elevator2View.favoriteColorString = [[NSString alloc] initWithFormat:#"Your favorite color is %#", favoriteColorTextField.text];
And
favoriteColorString = [[NSString alloc] initWithFormat:#"Your favorite color is %#", favoriteColorTextField.text];
Here's my code:
ElevatorView.h
#import <UIKit/UIKit.h>
#import "Elevator2View.h"
#interface ElevatorView : UIViewController<PassSecondColor>
{
Elevator2View *Elevator2View;
IBOutlet UITextField *favoriteColorTextField;
IBOutlet UILabel *favoriteColorLabel;
IBOutlet UILabel *secondFavoriteColorLabel;
NSString *secondFavoriteColorString;
}
#property (nonatomic, retain) Elevator2View *Elevator2View;
#property (nonatomic, retain) IBOutlet UITextField *favoriteColorTextField;
#property (nonatomic, retain) IBOutlet UILabel *favoriteColorLabel;
#property (nonatomic, retain) IBOutlet UILabel *secondFavoriteColorLabel;
#property (copy) NSString *secondFavoriteColorString;
#end
ElevatorView.m
#import "ElevatorView.h"
#import "Elevator2View.h"
#implementation ElevatorView
#synthesize Elevator2View, favoriteColorTextField, favoriteColorLabel, secondFavoriteColorLabel;
#synthesize secondFavoriteColorString;
-(IBAction)level1:(id)sender;{
favoriteColorTextField.text = #"1";
Elevator2View.favoriteColorString = [[NSString alloc] initWithFormat:#"Your favorite color is %#", favoriteColorTextField.text];
[self presentModalViewController:[[[Elevator2View alloc] init]
autorelease] animated:NO];
}
Elevator2View.h
#import <UIKit/UIKit.h>
#protocol PassSecondColor <NSObject>
#required
- (void) setSecondFavoriteColor:(NSString *)secondFavoriteColor;
#end
#interface Elevator2View : UIViewController{
IBOutlet UITextField *secondFavoriteColorTextField;
IBOutlet UILabel *favoriteColorLabel;
IBOutlet UILabel *secondFavoriteColorLabel;
NSString *favoriteColorString;
id <PassSecondColor> delegate;
}
#property (copy) NSString *favoriteColorString;
#property (nonatomic, retain) IBOutlet UITextField *secondFavoriteColorTextField;
#property (nonatomic, retain) IBOutlet UILabel *favoriteColorLabel;
#property (nonatomic, retain) IBOutlet UILabel *secondFavoriteColorLabel;
#property (retain) id delegate;
#end
Elevator2View.m
#import "Elevator2View.h"
#interface Elevator2View ()
#end
#implementation Elevator2View
#synthesize secondFavoriteColorTextField, favoriteColorLabel, secondFavoriteColorLabel;
#synthesize favoriteColorString;
#synthesize delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void) viewWillAppear:(BOOL)animated
{
favoriteColorLabel.text = favoriteColorString;
}
- (void) viewWillDisappear:(BOOL) animated
{
// [[self delegate] setSecondFavoriteColor:secondFavoriteColorTextField.text];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
favoriteColorLabel.text = favoriteColorString;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
See http://www.theappcodeblog.com/?p=90
The reason for your "property not found" is that you named your ivar same as class.
Dot notation is just a syntactic sugar: object.property = value is equivalent to [object setProperty:value]. In Objective C classes are also objects, and when you call Elevator2View.favoriteColorString = whatever, Xcode apparently thinks that you are attempting to call class method setFavoriteColorString of class Elevator2View.
Getting rid of this error is easy: just rename your ivar Elevator2View *Elevator2View to something else. In fact, Xcode 4.4 and newer autosynthesizes ivars for your properties: if you have a property propertyName, then Xcode will autosynthesize ivar _propertyName. Your property Elevator2View will have _Elevator2View ivar. So unless you really really need to have ivars with different naming scheme, you can get rid of your #synthesize, and you also don't need to declare ivars for you properties.
(Though I prefer to declare ivars for properties (following Xcode naming scheme), because far too often lldb doesn't show autosynthesized-without-declaring ivars in object inspector.)
That was about properties, ivars and naming conventions. But what are you doing in this code?
-(IBAction)level1:(id)sender;{
favoriteColorTextField.text = #"1";
Elevator2View.favoriteColorString = [[NSString alloc] initWithFormat:#"Your favorite color is %#", favoriteColorTextField.text];
[self presentModalViewController:[[[Elevator2View alloc] init]
autorelease] animated:NO];
}
You set value of Elevator2View's - your instance variable's - property, then create brand new object of Elevator2View class and present that as modal view controller. (By the way, presentModalViewController:animated: is deprecated in iOS 6.0). Of course, this brand new Elevator2View object has no idea what Elevator2View's (your instance variable's) properties are!