How to initialize and add a subview to a custom UIView? - ios

I have a custom view inherited from UIView. Now I want to add a UISegmentedControl as a subview to it.
So the first question is: should the property of UISegmentedControl be weak or strong? (With IBOutlets I know that Apple recommends using strong since 2015).
And the second question is where do I initialize it and set its layout. As I understand I shouldn't do this in the drawRect: method. Should it be initialized in initWithFrame: method, added as a subview to my custom view and then its layout to be set in layoutSubviews like so:
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
NSArray *options = #[#"option1", #"option2", #"option3"];
_segmentedControl = [[UISegmentedControl alloc] initWithItems:options];
[_segmentedControl addTarget:self action:#selector(someAction:) forControlEvents:UIControlEventValueChanged];
[self addSubview:_segmentedControl];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGRect segmentedControlFrame = CGRectMake(self.bounds.size.width / 4.0, 50, self.bounds.size.width / 2.0, 30);
self.segmentedControl.frame = segmentedControlFrame;
self.segmentedControl.tintColor = [UIColor blackColor];
[self.segmentedControl setTitleTextAttributes:#{NSForegroundColorAttributeName: [UIColor whiteColor]} forState:UIControlStateSelected];
}
or just do all this in layoutSubviews: method:
- (void)layoutSubviews {
NSArray *options = #[#"option1", #"option2", #"option3"];
self.segmentedControl = [[UISegmentedControl alloc] initWithItems:options];
CGRect segmentedControlFrame = CGRectMake(self.bounds.size.width / 4.0, 50, self.bounds.size.width / 2.0, 30);
segmentedControl.frame = segmentedControlFrame;
segmentedControl.tintColor = [UIColor blackColor];
[segmentedControl setTitleTextAttributes:#{NSForegroundColorAttributeName: [UIColor whiteColor]} forState:UIControlStateSelected];
[segmentedControl addTarget:self action:#selector(someAction:) forControlEvents:UIControlEventValueChanged];
[self addSubview:segmentedControl];
}

You should use strong if your view will keep the segmented control if it is not contained in it's hierarchy. You can use weak when the segmented control is always part of the hierarchy, but you have to keep it with a strong reference until you add it to the hierarchy. In your first code snippet you must use a local (strong) variable, which holds the control:
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
NSArray *options = #[#"option1", #"option2", #"option3"];
UISegmentedControl *control = [[UISegmentedControl alloc] initWithItems:options];
[control addTarget:self action:#selector(someAction:) forControlEvents:UIControlEventValueChanged];
[self addSubview:control]; // By now the view keeps the control
self.segmentedControl = control;
}
return self;
}
The advantage of a weak reference is that it is automatically resetted, if you remove the control from the hierarchy. This may help you to keep the amount memory small.
For the layout you should prefer autolayout contraints. Autosizing is possible, too (take a look at translatesAutoresizingMaskInotConstraints anyway). layoutSubviews shouldn't add the segment to the view, because it is called several times.

Related

UIButton not clickable when adding it programmatically

I'm creating custom UIView that contains two UILabels and UIButton so I created a subclass to make this possible.
Now the process looks like:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
self.backgroundColor = [UIColor whiteColor];
self.settingsButton = [[UIButton alloc] initWithFrame:CGRectMake(frame.size.width - 2 * margin, frame.size.height - titleHeight - dateHeight, buttonSize, buttonSize)];
[self.settingsButton setImage:[UIImage imageNamed:#"settings_icon"] forState:UIControlStateNormal];
[self addSubview:self.settingsButton];
[self.settingsButton addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
return self;
}
Button appears on UIView but it is not clickable. I tried to set self.userInteractionEnabled = YES; and a couple things but still no result :(
This is the most dumb mistake I've ever made. Transparent UINavigationBar was blocking the touch.

Cannot add dznsegmentedcontrol to UIView

I am trying to add the following control to a UIView that is inside a viewcontroller. The control does not seem to appear inside the view.
https://www.cocoacontrols.com/controls/dznsegmentedcontrol
NSArray *items = #[#"Kicks", #"Re-kicks", #"Interested"];
DZNSegmentedControl *control = [[DZNSegmentedControl alloc] initWithItems:items];
control.tintColor = [UIColor blueColor];
control.delegate = self;
control.selectedSegmentIndex = 2;
[control addTarget:self action:#selector(selectedSegment:) forControlEvents:UIControlEventValueChanged];
[cell.segmentView addSubview:control];
I think what you are looking for is to add the controller somehow.
[self.navigationController.navigationBar addSubview:self.control];
or as in the example code givin by dzenbot:
self.tableView.tableHeaderView = self.control;

Changing Only Part of a Custom UITableViewCell Selected Background Color

I have a custom cell that I am controlling the color of when it is selected, ie the example code below:
UIView *selectedBackground = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 600, 600)];
[selectedBackground setBackgroundColor:[UIColor selectedCellColor]];
self.selectedBackgroundView = selectedBackground;
This works, however I would only like to have part of the cell change colors when selected. My custom cell is broken down into many different subviews, and I have it sectioned out where I would be able to define the specific view that I would like to change colors for.
How can I control the selectedBackgroundView, or use a different method, to have the background color change encompass a single subview in my cell?
Ya you are in the rite way,by subclassing the UITableView cell
hear is the sample code that you may find the answer for your question :)
//in subclassed cell class
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
self.frame = CGRectMake(0, 0, 334, 250);
UILabel *aLabel1= [[UILabel alloc]init];
UILabel *aLabel2 = [[UILabel alloc]init];
self.label1 = aLabel1;
self.label2 = aLabel2;
self.label1.text = #"Happy";
self.label2.text = #"coding";
UIImageView *bg1 = [[UIImageView alloc]init];
bg1.tag = 100;
UIImageView *bg2 = [[UIImageView alloc]init];
bg2.tag = 200;
[self addSubview:bg1]; // you must add your background views first
[self addSubview:bg2];
[self addSubview:label1];//then other views
[self addSubview:label2];
[aLabel1 release];
[aLabel2 release];
[bg1 release];
[bg2 release];
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
// Configure the view for the selected state
// hear only you can manage your background views, simply i am adding 2 imageviews by setting different colors
[super setSelected:selected animated:animated];
self.backgroundColor = [UIColor greenColor];
if(selected)
{
self.label1.backgroundColor = [UIColor redColor];
self.label2.backgroundColor = [UIColor brownColor];
UIImageView *bg1 = (UIImageView *)[self viewWithTag:100];
bg1.frame = CGRectMake(0, 0,334/2, 250);
bg1.backgroundColor = [UIColor yellowColor];
}
else
{
self.label1.backgroundColor = [UIColor brownColor];
self.label2.backgroundColor = [UIColor redColor];
UIImageView *bg2 =(UIImageView *) [self viewWithTag:200];
bg2.frame = CGRectMake(35, 0, 334/2, 250);
bg2.backgroundColor = [UIColor lightGrayColor];
}
}
-(void)layoutSubviews
{
//i am setting the frame for each views that i hav added
[super layoutSubviews];
self.label1.frame = CGRectMake(10, 10, 60, 35);
self.label2.frame = CGRectMake(65, 10, 60, 35);
}
hope helps u :)
note: i am using "without ARC"

Drawing a line between two UIlabels when tapped or pressed together

I want to draw a line between two UILabels when i tap or hold both of them at the same time with code, i have no idea how to do this, it will be most appreciated if anyone has an advice , it will be most appreciated
This is how I would do it. My answer is tested btw...
To manage the tappable labels I would create my own label called LabelControl that extends UIControl. This custom control is going to have a UILabel (read-only for simplicity) as a subview. By doing this we will be able to add the Target-Action events UIControlEventTouchDown and UIControlEventTouchUpInside.
Here is the custom label:
The .h file:
#interface LabelControl : UIControl
#property (nonatomic, retain, readonly) UILabel *theLabel;
#end
The .m file:
#implementation LabelControl
#synthesize theLabel = _theLabel;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
_theLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
_theLabel.backgroundColor = [UIColor clearColor];
_theLabel.userInteractionEnabled = NO;
[self addSubview:_theLabel];
}
return self;
}
-(void)dealloc {
[_theLabel release];
[super dealloc];
}
Then for the "line" that you want to draw, I would use a UIView because that way you could just use it's hidden property to hide/show the line. Add this code inside your ViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.label1 = [[[LabelControl alloc] initWithFrame:CGRectMake(60, 50, 200, 40)] autorelease];
self.label1.theLabel.text = #"Hello";
self.label1.userInteractionEnabled = YES;
self.label1.backgroundColor = [UIColor blueColor];
[self.label1 addTarget:self action:#selector(touchDown:) forControlEvents:UIControlEventTouchDown];
[self.label1 addTarget:self action:#selector(touchUp:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.label1];
self.label2 = [[[LabelControl alloc] initWithFrame:CGRectMake(60, 150, 200, 40)] autorelease];
self.label2.theLabel.text = #"World";
self.label2.userInteractionEnabled = YES;
self.label2.backgroundColor = [UIColor purpleColor];
[self.label2 addTarget:self action:#selector(touchDown:) forControlEvents:UIControlEventTouchDown];
[self.label2 addTarget:self action:#selector(touchUp:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.label2];
//the position of the line is hard-coded. You will have to modify this yourself
self.theLine = [[UIView alloc] initWithFrame:CGRectMake(60, 120, 200, 3)];
self.theLine.backgroundColor = [UIColor blueColor];
self.theLine.hidden = YES;
[self.view addSubview:self.theLine];
}
-(void)showOrHideLine {
if (self.label1.selected && self.label2.selected) {
NSLog(#"Both are selected");
self.theLine.hidden = NO;
} else {
self.theLine.hidden = YES;
}
}
-(void)touchDown:(id)sender {
//When any of the custom label gets the touch down event set the `selected` property
//to YES. (This property is inherited form `UIControl`
NSLog(#"Touch down");
LabelControl *labelControl = (LabelControl*)sender;
labelControl.selected = YES;
[self showOrHideLine];
}
-(void)touchUp:(id)sender {
//When any of the custom label gets the touch up event set the `selected` property
//to NO. (This property is inherited form `UIControl`
NSLog(#"Touch Up");
LabelControl *labelControl = (LabelControl*)sender;
labelControl.selected = NO;
[self showOrHideLine];
}
Let me know if you have any questions. Hope this helps!
I am going to outline it and give you some code (untested) but you have some work to make the full effect work.
Programtically, you could subclass UIButton. Inside your new subclass, put your touch recognition methods. Call it something like "customUIButton".
From your main view controller, create two instances of your UIButton and add them as subviews
customUIButton *Label1 = [[customUIButton alloc] init];
[Label1 setTitle:#"Your First Label" forState:UIControlStateNormal];
[Label1 addTarget:self action:#selector(itemClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Label1];
customUIButton *Label2 = [[customUIButton alloc] init];
[Label2 setTitle:#"Your Second Label" forState:UIControlStateNormal];
[Label2 addTarget:self action:#selector(itemClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Label1];
When you push the buttons, it will try to call the method "itemClicked". Make that method and receive (id)sender... - (void)itemClicked: (id)sender { Put some logic in there to indicate that the button is being pushed. You know which button by referring to the sender that was sent.
Also in your view controller, go to the DrawRect method and put in the drawing logic.
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 3.0);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGContextMoveToPoint(context, Label1.center.x, Label1.center.y);
CGContextAddLineToPoint(context, Label2.center.x, Label2.center.y);
CGContextStrokePath(context);
CGContextRestoreGState(context);
Put this in an "if statement" so that if both buttons have focus, it will be drawn (else skip the drawing code).
How about placing a line in IB using an Image View. Hide the Image View in your viewDidLoad method. Then, whenever the user taps one of the the UILabels, unhide the Image View holding your line. It may be tricky depending on your experience with Objective-C to detect touches on a UILabel. If it is, you can place invisible buttons over each label and that should do the trick for you. For more info about detecting touches on UILabels, check this link:
Handling Touch Event in UILabel and hooking it up to an IBAction

Subview of custom UIButton class doesn't trigger click

I'm writing a custom UIButton subclass, to create my own fully customized button
to have total controll. Still, I'm having problems when I try to add a subview
to that UIButton's view. The subview blocks the "touch" events so it won't bubble to
the UIButton itself..
This is a demonstration:
I first create my CustomNavigationButton, with a frame.. It's magenta.
I can see the magenta on the screen so it's there. Secondly, I add a subview
to that CustomNavigationButton (which is green), I can see the green so it's
there, but if I click the green rectangle (subview), the "UIControlEventTouchUpInside"
doesn't get called on my CustomNavigationButton..
In my AppDelete:
CustomNavigationButton* mapBtn = [[CustomNavigationButton alloc] initWithFrame:CGRectMake(0, 0, 100, 25)];
mapBtn.backgroundColor = [UIColor magentaColor];
[mapBtn addTarget:self action:#selector(goMapHandler:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:mapBtn];
And here's my CustomNavigationButton class (which is a subclass of UIButton)
#implementation CustomNavigationButton
#synthesize bg;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// STORE INITIAL FRAME
self.initialFrame = frame;
bg = [[UIView alloc] initWithFrame:CGRectMake(0, 20, 40, 40)];
bg.backgroundColor = [UIColor greenColor];
[bg setUserInteractionEnabled:YES];
[self addSubview:bg];
}
return self;
}
#end
I figured out how to do it!
If "bg" is the subview I'm adding to the UIButton, then you should do:
bg.userInteractionEnabled = NO;
bg.exclusiveTouch = NO;
But keep in mind that, if your subview extends the frame of the UIButton,
a touch will not occur! You can check if your subview exceeds the UIButton
by giving the UIButton a background-color.

Resources