Create UIViews programmatically in Objective-C and put that in a closure - ios

In Swift I would do this to create a UIView in a kind of closure (I think this is a closure), how can I do this same exact thing in objective-C?
I don't want to set up all the variables and stuff like that in ViewDidLoad().
Thanks for your help.
let myView: UIView = {
let view = UIView()
view.layer.masksToBounds = true
view.layer.cornerRadius = 5
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()

The closest you can get without doing it in init or viewDidLoad is lazy instantiation.
#interface SomeClass: NSObject
#property (nonatomic, strong) UIView *myView;
#end
#implementation SomeClass
- (UIView *)myView
{
if (!_myView) {
_myView = [[UIView alloc] init];
_myView.layer.masksToBounds = YES;
_myView.layer.cornerRadius = 5;
_myView.translatesAutoresizingMaskIntoConstraints = NO;
}
return _myView;
}
#end

You can create nearly identical code in Objective-C:
- (void)viewDidLoad {
[super viewDidLoad];
UIView *myView = ^UIView*() {
UIView *view = [[UIView alloc] initWithFrame: CGRectZero];
view.layer.masksToBounds = TRUE;
view.layer.cornerRadius = 5;
view.translatesAutoresizingMaskIntoConstraints = FALSE;
//Add constraints
return view;
}();
[self.view addSubview: myView];
}
However, that won't create a computed variable like it does in Swift. It just defines a block and calls it in order to initialize a local variable.

Related

Reuse xib in different screens

I have 4 screens that have one almost the same view:
And one screen have the same view but with slightly different UI:
So, my question: Can I use one xib and adapt states (active, inactive) and change ui for different screen? How I can do it?
Here is an example of this kind of class
In Your .m file of custom XIB class if you are using objective-c.
- (void)foo:(NSString*)labelText andButtonText:(NSString*)buttonTitle {
//Do your code here for some screen like change labels and button text
}
- (void)bar:(NSString*)labelText andButtonText:(NSString*)buttonTitle {
//Do your code here for some another screen and change labels and button text
}
In Your .h file of custom XIB class if you are using objective-c.
- (void)foo:(NSString*)labelText andButtonText:(NSString*)buttonTitle;
- (void)bar:(NSString*)labelText andButtonText:(NSString*)buttonTitle;
In your view controller where you want to display the custom UI
Create an instance of your xib or add through interfacebuilder
And On your instance of custom class call the method as required.
Below is a class I've used in one of my project to get a clear understanding.
#import <UIKit/UIKit.h>
#import "DYRateView.h"
#interface LevelAndRankDetails : UIView
#property (nonatomic, strong) IBOutlet UIView* contentView;
#property (nonatomic, strong) IBOutlet UIView* viewContainer;
#property (nonatomic, strong) IBOutlet UILabel* lblLevel;
#property (nonatomic, strong) IBOutlet UILabel* lblRanking;
#property (weak, nonatomic) IBOutlet DYRateView *viewRate;
- (void)setLevel:(NSNumber*)level andRanking:(NSNumber*)ranking;
- (void)setupUI;
#end
.m File
#import "LevelAndRankDetails.h"
#import "AppDelegate.h"
#import "Constants.h"
#implementation LevelAndRankDetails
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]){
[self commonSetup];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self commonSetup];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
}
- (void)viewFromNibForClass {
[[NSBundle mainBundle] loadNibNamed:[[self class] description] owner:self options:nil];
[self addSubview:self.contentView];
self.contentView.frame = self.bounds;
}
- (void)commonSetup {
[self viewFromNibForClass];
//For View's Corner Radius
self.contentView.layer.cornerRadius = 12;
self.contentView.layer.masksToBounds = YES;
self.contentView.backgroundColor = kDefaultBackgroundGreyColor;
self.viewContainer.backgroundColor = kDefaultBackgroundGreyColor;//[UIColor clearColor];
self.backgroundColor = kDefaultBackgroundGreyColor;
//self.viewContainer.backgroundColor = UIColorFromRGB(0xBB9657);//[kLearnFromLightColor colorWithAlphaComponent:0.5];
self.viewRate.rate = 0;
self.viewRate.editable = NO;
self.viewRate.delegate = nil;
self.viewRate.alignment = RateViewAlignmentCenter;
self.viewRate.backgroundColor = [UIColor clearColor];
[self.viewRate setEmptyStarImage:[UIImage imageNamed:#"StarEmpty"]];
UIImage* imageFullStar = [[UIImage imageNamed:#"StarFull"] imageTintedWithColor:kSliderDarkYellowColor];
[self.viewRate setFullStarImage:imageFullStar];
self.lblLevel.textColor = [UIColor whiteColor];
self.lblRanking.textColor = [UIColor whiteColor];
//For Teacher label
}
- (void)setupUI {
self.contentView.layer.cornerRadius = 0;
self.contentView.layer.masksToBounds = YES;
self.contentView.backgroundColor = [UIColor clearColor];
self.viewContainer.backgroundColor = [UIColor clearColor];//[UIColor clearColor];
self.backgroundColor = [UIColor clearColor];
}
- (void)setRanking:(CGFloat)ranking {
self.viewRate.rate = ranking;
}
- (void)setLevel:(NSNumber*)level {
self.lblLevel.text = [NSString stringWithFormat:#"Level : %#",level];
}
- (void)setLevel:(NSNumber*)level andRanking:(NSNumber*)ranking {
if (level.integerValue > 0) {
[self setLevel:level];
}
if (ranking.doubleValue > 0) {
CGFloat rankingConverted = ranking.floatValue;
[self setRanking:rankingConverted];
}
}
#end
And this is how you use it in your view controller
LevelAndRankDetails* toolTipCustomView = [[LevelAndRankDetails alloc] initWithFrame:CGRectMake(0, 0, 250, 66)];
toolTipCustomView.backgroundColor = [UIColor blackColor];
[toolTipCustomView setLevel:#(10) andRanking:#(3)];

How to add headerView like Navigationbar on ASViewController with ASTableNode without NavigationController?

In my application, I do not use the NavigationController, in general.
In UITableView I would do so, would add
[Self.view addSubview: self.myCustomHeaderView];
But I'm using ASTableNode. If I do something, something similar here:
- (instancetype)init {
_tableNode = [[ASTableNode alloc] init];
self = [super initWithNode:_tableNode];
if (self) {
_tableNode.dataSource = self;
_tableNode.delegate = self;
self.navigationItem = //navigationItem implement
UINavigationBar *navbar = [[UINavigationBar alloc]initWithFrame:CGRectMake(0, 0, 320, 50)];
navbar.backgroundColor = [UIColor redColor];
navbar.items = #[self.navigationItem];
ASDisplayNode *navBarNode = [[ASDisplayNode alloc] initWithViewBlock:^UIView * _Nonnull {
return navbar;
}];
[self.node addSubnode:navBarNode];
}
return self;
}
Then this node is added as part of the list and scrolled along with the list, and I need it to be as fixed header.
initWith ASDisplayNode rather than ASTableNode,then add table node on VC's node,for example:
#interface OCTextureTableController : ASViewController<ASDisplayNode *>
#property (nonatomic, strong) ASTableNode *tableNode;
- (instancetype)init
{
self = [super initWithNode:[ASDisplayNode new]];
if (self) {
self.tableNode = [[ASTableNode alloc] init];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.tableNode.backgroundColor = [UIColor yellowColor];
self.tableNode.frame = CGRectMake(0, NAVIGATION_BAR_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT-NAVIGATION_BAR_HEIGHT);
[self.node addSubnode:self.tableNode];
}
(i) take Class A as base Class.
(ii) take Class B as derived Class having type of class A.
Now in class A Add a uiview on top. that will be considered as navigation.(do anything in that view)
To use same navigation bar: you have to take other all classes as having type of class A.
that's it.

Custom Class compiled before ViewController

I need a little help about my code. I want to create a custom bar graph. I did all the coding. When I create the objects in my custom class view it works like a charm. However, I want to make it generic. I would like to create it in ViewController. I also add a view in storyboard and change the class name to my custom class name.
It doesn't work when I try like this.
#implementation ViewController
-(void)viewDidLoad {
[super viewDidLoad];
GCBarGraphRow *first = [[GCBarGraphRow alloc]init];
first.backgroundColor = [UIColor redColor];
first.value = 20;
GCBarGraphRow *second = [[GCBarGraphRow alloc]init];
second.backgroundColor = [UIColor brownColor];
second.value = 40;
GCBarGraphColumn *container = [[GCBarGraphColumn alloc]init];
container.values = #[first,second];
NSLog(#"View Controller %#",container.values);
self.myGraph.array = container;
}
But when I try to create it in my custom view class it works.
#implementation GCBarGraphView
-(id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
barNumber = [array.values count];
//NSLog(#"%#",array.values);
showDifference = NO;
showShadow = NO;
gradientColor = NO;
drawBaseLine = YES;
[self baseClassInit];
}
return self;
}
-(void)baseClassInit {
GCBarGraphRow *first = [[GCBarGraphRow alloc]init];
first.backgroundColor = [UIColor redColor];
first.value = 20;
GCBarGraphRow *second = [[GCBarGraphRow alloc]init];
second.backgroundColor = [UIColor brownColor];
second.value = 40;
GCBarGraphColumn *container = [[GCBarGraphColumn alloc]init];
container.values = #[first,second];
}
If you're creating a view in the storyboard and assign it's class to your CGBarGraphRow, you also need to create an outlet to your view controller.
Then you don't have to initialize it manually from the view controller code. Your constructor initWithCoder will be called automatically.
Update:
Try this. In the storyboard create the following:
- UIView with proper frame and assign custom class to CGBarGraphView
- Insert into that view two more and assign their classes to GCBarGraphRow
In your CGBarGraphView class create two outlets and bind them to the proper views in the storyboard:
#property (nonatomic, weak) IBOutlet GCBarGraphRow *row1;
#property (nonatomic, weak) IBOutlet GCBarGraphRow *row2;
Remove the code you have above for the view controller - it's not needed. Leave something like that in the CGBarGraphView code:
-(id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
}
return self;
}
-(void)viewDidLoad {
GCBarGraphColumn *container = [[GCBarGraphColumn alloc]init];
container.values = #[self.row1,self.row2];
... whatever else you need to initialize
}

Add Subview to main view with function of subview class

I want to add my custom UIView to my main view.
I want to use the function initWithTitle:description: like:
AchievementView *achievement = [[AchievementView alloc] initWithTitle:#"Test" description:#"The Descr"]];
But this doesn't work. tThe initWithTitle:description: function have to be implemented as class method.
AchievementView.h
#interface AchievementView : UIView
#property (strong) NSString *achievementTtl;
#property (strong) NSString *achievementDescr;
#property (strong) UIView *theAchievementView;
- (void)initWithTitle:(NSString*)achievementTitle description:(NSString*)achievementDescription;
- (void)showAchievement;
#end
AchievementView.m
-(void)initWithTitle:(NSString*)achievementTitle description:(NSString*)achievementDescription {
self.achievementTtl = achievementTitle;
self.achievementDescr = achievementDescription;
}
- (void)showAchievement {
// Create view popup
self.theAchievementView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 423)];
self.theAchievementView.layer.cornerRadius = 8;
self.theAchievementView.layer.masksToBounds = YES;
self.theAchievementView.layer.shadowRadius = 5;
self.theAchievementView.layer.shadowOpacity = .15;
[self.theAchievementView setBackgroundColor:[UIColor whiteColor]];
[self addSubview:self.theAchievementView];
}
Call the method in main view:
- (IBAction)share:(id)sender {
AchievementView *achievement = [[AchievementView alloc] init];
achievement.achievementTtl = #"Test";
achievement.achievementDescr = #"TestDEscr";
[achievement showAchievement];
}
I can't add the subview to the main view with this function.
I think the "self" is wrong. What should be there?
[self addSubview:self.theAchievementView];
You have 2 questions: how to initialize the view and how to add it to the screen.
You can use the initWithTitle:description: method, you just have to use it on an instance of the class, not the class itself:
[[AchievementView alloc] initWithTitle:#"Test" description:#"The Descr"]];
You are correct that you have the wrong self in your addSubview call. You need a reference to the parent view. You could pass it into your AchievementView class, but it is cleaner to manage that outside of the class.
I also noticed that AchievementView is a subclass of UIView, but you are creating another UIView inside it. It would be simpler to directly use the AchievementView. Your code should be similar to the below:
AchievementView.h
#interface AchievementView : UIView
#property (strong) NSString *achievementTtl;
#property (strong) NSString *achievementDescr;
- (id)initWithTitle:(NSString*)achievementTitle description:(NSString*)achievementDescription;
#end
AchievementView.m
- (id)initWithTitle:(NSString*)achievementTitle description:(NSString*)achievementDescription {
if (self = [super initWithFrame:CGRectMake(0, 0, 300, 423)]) {
self.achievementTtl = achievementTitle;
self.achievementDescr = achievementDescription;
self.layer.cornerRadius = 8;
self.layer.masksToBounds = YES;
self.layer.shadowRadius = 5;
self.layer.shadowOpacity = .15;
self.backgroundColor = [UIColor whiteColor];
}
return self;
}
Main view
- (IBAction)share:(id)sender {
AchievementView *achievement = [[AchievementView alloc] initWithTitle:#"Test" description:#"TestDEscr"];
[self.view addSubview:achievement];
}
Number one, you are never adding achievement as a subview of your main view. Basically you are creating a view offscreen, adding a view to that view, then moving on with whatever you want to do without ever moving the first view onto the screen.
As a minimum you should do this:
- (IBAction)share:(id)sender {
AchievementView *achievement = [[AchievementView alloc] init];
achievement.achievementTtl = #"Test";
achievement.achievementDescr = #"TestDEscr";
[achievement showAchievement];
[self addSubview:achievement];
}
Along with setting the frame size of achievement which is currently never set (and since most views by default mask to their layer bounds it will mask to (0,0,0,0)).
Then I would seriously reconsider how you are dealing with views and subviews. If you do things properly, your initwithtitle will work just fine.

Programmatically adding a subview to a pre-defined subview in storyboard not working

I have a ViewController in a storyboard consisting of two UIViews and a table at the bottom. the center of the screen contains a UIView defined in the storyboard with an outlet called middleSectionView. I want to programmatically add a subView to middleSectionView. The programmatically added subView is not appearing. Here's my code:
RoundedRect.m:
#import "RoundedRect.h"
#implementation RoundedRect
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
NSLog(#"RoundedRect: initWithFrame: entering");
UIView* roundedView = [[UIView alloc] initWithFrame: frame];
roundedView.layer.cornerRadius = 5.0;
roundedView.layer.masksToBounds = YES;
roundedView.layer.backgroundColor = [UIColor redColor].CGColor;
UIView* shadowView = [[UIView alloc] initWithFrame: frame];
shadowView.layer.shadowColor = [UIColor blackColor].CGColor;
shadowView.layer.shadowRadius = 5.0;
shadowView.layer.shadowOffset = CGSizeMake(3.0, 3.0);
shadowView.layer.opacity = 1.0;
// [shadowView addSubview: roundedView];
}
return self;
}
#end
.h:
...
#property (strong, nonatomic) IBOutlet UIView *middleSectionView;
.m:
...
#import "RoundedRect.h"
...
- (void)viewDidLoad
{
RoundedRect *roundRect= [[RoundedRect alloc] init];
roundRect.layer.masksToBounds = YES;
roundRect.layer.opaque = NO;
[self.middleSectionView addSubview:roundRect]; // This is not working
[self.middleSectionView bringSubviewToFront:roundRect];
// [self.view addSubview:roundRect]; // This didn't work either
// [self.view bringSubviewToFront:roundRect]; // so is commented out
...
}
The reason why you do not see the RoundedRect is that you are calling a wrong initializer: this line
RoundedRect *roundRect= [[RoundedRect alloc] init];
does not invoke the initWithFrame: initializer that does all the work initializing your RoundedRect view. You need to change the call to
RoundedRect *roundRect= [[RoundedRect alloc] initWithFrame:CGRectMake(...)];
and put the desired coordinates of the frame in place of the ... above.

Resources