How to change UIView's position in code? - ios

I created a custom ViewCell class attached with a xib layout file, and I added some labels in xib. But when I want to change the labels' position in code, it doesn't work, can you tell me why?
Here are the the relative codes:
public override void LayoutSubviews ()
{
base.LayoutSubviews ();
var bounds = ContentView.Bounds;
UILabel label_period = RetriveViewByID ("label_period") as UILabel;
label_period.ContentMode = UIViewContentMode.Redraw;
label_period.Text = string.Format ("{0}天之旅", m_travel_data.period); \\Works
label_period.Frame = new RectangleF (20, label_period.Frame.Y, label_period.Frame.Width, label_period.Frame.Height);
}
It is weird, because the Text is actually changed, but Frame not.

There are a few things that you could try here.
First of all try calling
label_period.SetNeedsLayout();
or possibly (but rather unlikely)
label_period.SetNeedsDisplay();
If neither of those work I would check to see that you do not have any unnecessary Layout Constraints declared in your .xib.
Although this was directly part of your question, I recommend instead of using RetrieveViewByID(...) just reference the view by its name.
(If you declared the UIViewController in the .xib you should be able to access the view inside the UIViewController with this.label_period )
Finally, although I do not know the specifics of your situation, I recommend switching over to Storyboards, you will run into issues like these less.

Related

How do I resize my UITableView using Autolayout?

I have a UITableView in a ViewController in a Storyboard (not a UITableViewController). What I want to do is add a custom UIView above the TableView in code. When the View is not there, the TableView's top anchor is anchored to PrimaryNavCollectionViewOutlet. I store this constraint as an outlet, and then if I have to add the View, I can use this outlet to remove the storyboard constraint.
I then constrain the inserted View to be below where the TableView was, and constraint the TableView to be below that.
Here's my code:
if (_viewAboveTableView != null)
{
TableView.RemoveConstraint(TableViewTopConstraint);
View.AddSubview(_viewAboveTableView);
_viewAboveTableView.TranslatesAutoresizingMaskIntoConstraints = false;
TableView.TranslatesAutoresizingMaskIntoConstraints = false;
_viewAboveTableView.LeadingAnchor.ConstraintEqualTo(View.LeadingAnchor).Active = true;
_viewAboveTableView.TopAnchor.ConstraintEqualTo(PrimaryNavCollectionViewOutlet.BottomAnchor).Active = true;
_viewAboveTableView.TrailingAnchor.ConstraintEqualTo(View.TrailingAnchor).Active = true;
_viewAboveTableView.BottomAnchor.ConstraintEqualTo(TableView.TopAnchor).Active = true;
}
Unfortunately, when I run it, I can't see _viewAboveTableView. I feel like I'm missing something easy, but I can't figure out what. I've tried LayoutIfNeeded() and a few other methods on the View, but they don't make it appear. What have I missed?
Add height constraint to _viewAboveTableView.
Or
Make sure _viewAboveTableView has content that will specify intrinsic content size -- it should have properly laid-out subviews with content size (intrinsic or explicit via layouts. e.g. image, label etc.)
My guess is _viewAboveTableView is not getting inflated because it doesn't specify intrinsic content size (height > 0). If it's wrong constraints, you should be able to see it in the console.
It looks like the problem with what I was doing was that TableView.RemoveConstraint(TableViewTopConstraint); didn't have the intended effect.
From this answer I learned that RemoveConstraint is, or is about to be, deprecated in favour of TableViewTopConstraint.Active = false;. This made my code work the way that was intended.

UISplitViewController Master Content Width

I have a UISplitViewController in my application (MvvmCross / Xamarin iOS) and for some reason I cannot get the content to respect the dimensions of the available view areas.
In the situation shown in the screenshot the master view is hosting a UIViewController with a TableView inside. All the layouts are done with constraints and work fine on their own when running in an iPhone emulator.
As soon as I switching to running on an iPad some custom code I have in my presenter shows this same view in the master panel of a UISplitViewController but in this situation the constraints seem to be ignored and I end up with a view that looks like this:
As you can see the right hand side of the table cell is now way off the viewable area of the master panel of the UISplitViewController.
Both the UITableView and the UITableCell both use View.Frame as their initial size (I've tried View.Bounds as well).
How can I get the cells and / or table to respect the bounds of the UISplitViewController available space?
Thanks to Cheesebarons question I found my solution (cause).
I have a set of methods in a helper class that I use to generate my "default" UIViews.
One of these methods creates my default UITableView:
public static UITableView CreateDefaultTableView(CGRect rect, UITableViewStyle style)
{
var tv = new UITableView(rect, style)
{
AutoresizingMask = UIViewAutoresizing.FlexibleHeight,
SeparatorStyle = UITableViewCellSeparatorStyle.SingleLine,
SeparatorColor = IosConstants.DefaultTableSeparatorColor,
BackgroundColor = IosConstants.DefaultViewBackgroundColor
};
return tv;
}
Changing:
AutoresizingMask = UIViewAutoresizing.FlexibleHeight,
To:
AutoresizingMask = UIViewAutoresizing.All,
Schoolboy error!

Set NSLayoutConstraint Constant in iOS7

Ok, I have a super weird issue. I have a UIScrollView that I'm using to update the position of a second UIView. So, when you scroll the UIScrollView, the UIView moves as well at a proportional but slower speed. The code is pretty simple.
Scroller.Scrolled += delegate {
if (Scroller.ContentOffset.Y <= 0) {
filtersTop.Constant = 0;
} else {
filtersTop.Constant = -(Scroller.ContentOffset.Y / 2);
}
}
filtersTop is a NSLayoutConstraint. Scroller is the UIScrollView.
The problem is whenever I set filtersTop.Constant to any value derived from Scroller.Content.Y, it resets Scroll.ContentOffset to 0 in iOS7 only. This code works fine in iOS8+. I've tried a dozen variations on this. If I set filtersTop.Constant to a static number (ie: 123.5), that works.
I've tried saving the value to a variable and forcing the type thinking maybe it was getting cast improperly. If I trace out the value, it works. But, the minute I set it to filtersTop.Constant, it resets Scroller.ConstentOffset.Y again. Scroller and filters are NOT related. They are not nested or associated with each other in any way. So, I have no idea why setting the constant on the constraint on filters would in any way affect Scroller.
Anyone know what is happening here?
Ok I had another look it seems to be resetting the contentSize between calling ViewWillLayoutSubviews and calling ViewDidLayoutSubviews
ContentSize set to: {Width=320, Height=3000}
Called ViewWillLayoutSubviews
ContentSize set to: {Width=320, Height=3000}
ContentSize set to: {Width=0, Height=0}
Called ViewDidLayoutSubviews
I then subclassed the scrollview and forced the contentSize to stay the same like so:
public class CustomScroller : UIScrollView
{
public CustomScroller ()
{
ContentSize = new CGSize(320,3000);
}
public override CGSize ContentSize {
get {
return base.ContentSize;//new CGSize(320,3000);
}
set {
base.ContentSize = new CGSize(320,3000);
Console.WriteLine ("ContentSize set to: "+ContentSize);
}
}
public override void LayoutSubviews ()
{
Console.WriteLine ("LayoutSubviews: "+Frame.Y);
base.LayoutSubviews ();
}
}
You will then have to set the contentSize when the view loads rather than in ViewDidLayoutSubviews.
I have updated the code here with the solution detailed above and here is the result.
Note I changed the scrolled code but it will work with the original too.
This works in iOS7 and iOS8. Hope this helps!

Live Xcode's Interface Builder UIView subclass (IB_DESIGNABLE) without drawRect:

Is it possible to create a UIView subclass that renders live in Xcode (by adding the IB_DESIGNABLE attribute as explained here) but doesn't have a custom drawRect: method?
We have a custom UIView subclass that uses some CAShapeLayers which are added to self.layer for drawing (hence, there's no need to override drawRect:). This class works fine on the App, but won't render on Xcode.
If we replicate the code in drawRect: it works, but we'd prefer to keep the drawing to happen automatically on the layers.
Can this be done?
I also tried doing
- (void)drawRect:(CGRect)rect
{
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(currentContext, self.myLayer1.frame.origin.x, self.myLayer1.frame.origin.y);
[self.myLayer1 renderInContext:currentContext];
CGContextMoveToPoint(currentContext, self.myLayer1.frame.origin.x, self.myLayer1.frame.origin.y);
[self.myLayer2 renderInContext:currentContext];
}
which seems to work on the device but not on Xcode's IB.
You can preview UIView subclasses in IB (with the IB_DESIGNABLE macro) even if the drawRect: isn't overridden.
I added your code in XCode 6.1 and added a OEProgressIndicator into a xib file. Then I debugged it (using menu Editor / Debug Selected View ) by setting a breakpoint in your commonProgressIndicatorInit selector.
Here's why you don't see anything in the preview with your current code: when the commonProgressIndicatorInit is invoked (from the initWithFrame:(CGRect)frame constructor), the frame is equal to CGRectZero (x:0 y:0 width:0 height:0) so that your center variable is actually equal to (0, 0) and radius is -1.
On the device, depending on the way the class is used, you may be directly invoked with the proper frame, that's why it may work on the device but not in IB.
To fix this, I would implement the layoutSubviews selector (override it from UIView) to organise properly the sublayers. This selector is going to be invoked when the frame is going to change from CGRectZero to the proper values set in Interface Builder.
I've been using the method - (void)prepareForInterfaceBuilder in order to tell IB to live render a view.
See here: Creating a Live View of a Custom Object
Also, you guys are right that this feature is also available for Objective-C.
You don't necessarily need to use drawRect, you can try using - (void)layoutSubviews, it seems to work. The problem with leaving code in places like - (void)layoutSubviews just for the sake of live rendering is that it may be less performant, etc (for instance you can do a lot of stuff in - (void)awakeFromNib, but that method does not get called from Live Rendering, so just make sure that you do all your set up in - (void)prepareForInterfaceBuilder as well.
Without seeing all your code, it's hard to see what the source of the problem is, but to answer your question, yes, you can use IBInspectable / IBDesignable without needing to implement any other specific method. I have done this for a view that uses many layers and does not do any drawing (uses the nested layers for that).
For a quick test example snippet with rounded corners:
#IBDesignable
class MyView : UIView {
#IBInspectable var cornerRadius:CGFloat {
get { return self.layer.cornerRadius }
set { self.layer.cornerRadius = newValue }
}
#IBInspectable var borderWidth:CGFloat {
get { return self.layer.borderWidth }
set { self.layer.borderWidth = newValue }
}
#IBInspectable var borderColor:UIColor {
get { return UIColor(CGColor: self.layer.borderColor) }
set { self.layer.borderColor = newValue.CGColor }
}
}
For a simple example that does gradients, see this post.
For an explanation on how to debug the live views, refer to WWDC §411 at about the 22 minute mark.
The only limitation that I have seen so far is that you can add inspectable properties in class extensions, but they only render properly on the device.

Update segmented control in ios without change interface

when I update segmented control text, the interface (segment's width) changed and cut some letters.
[segmentedcontoll setTitle:#"test" forSegmentAtIndex:1];
segmentedcontoll.apportionsSegmentWidthsByContent = YES;
How can I solve this ?
EDIT:
It looks like your content has outgrown the dimensions of the standard UISegmentedControl.
If you are okay with smaller font, it's possible to set the entire control to have a smaller font point size, seen here.
Another option is to configure the segments the other supported way.. With images. It's a little bit of a hack, but you can create images on the fly with the UIView Snapshotting API of views/labels configured however you want and set images for each segment instead of using text. This would allow you to create 2 line labels with fixed widths and set images for each section to be images generated from the label as the content changes. More work, but you would still be using the standard class.
The last option, which might work the best for you, is to create some other custom control that does what you would like. After all, UISegmentedControl really is just a nice button container. And it does somewhat seem like you are using the control in a non-standard way - both as a control and an input form section.
Others have gone this route before and created alternatives that you can use.
You can create a separate class as below,
class CustomSegmentedControl: UISegmentedControl {
//code for creating multi line
override func didMoveToSuperview()
{
for segment in subviews
{
for subview in segment.subviews
{
if let segmentLabel = subview as? UILabel
{
segmentLabel.numberOfLines = 0 //just change here the number of lines and check it.
}
}
}
}
}
and create an outlet in your viewcontroller as,
// Initialize
let items = ["Purple", "Green", "New Segment"]
let customSC = CustomSegmentedControl(items: items)
use customSC and do what ever you want to do, similar to segmentedControl object.

Resources