Custom view - a subclass of uiview cannot be resized - ios

It must be a simple thing but I couldn't find a clue,
I am having a imageview and trying a scribbling view which is a subclass of UIview on top of it to allow scribbling. All works fine except for resizing the scribbling view, it occupies the whole screen all the time I couldn't make it smaller.
class ScribbleView: UIView
{
let backgroundLayer1 = CAShapeLayer()
required init()
{
// super.init(frame: CGRect.zero)
super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
layer.addSublayer(backgroundLayer1)
layer.masksToBounds = true
}
class HermiteScribbleView: ScribbleView, Scribblable
{
required init() {
print ("calling parent")
super.init()
}
}
/** main code **/
let hermiteScribbleView = HermiteScribbleView()
hermiteScribbleView.backgroundColor = .red
// imgView.autoresizesSubviews = true
// hermiteScribbleView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
//once this is done, hermieScribbleView is always stretched to full width and height no matter what I try
imgView.addSubview(hermiteScribbleView)

What i guess from above information is that, you might be using the frame of UIImageView to set the frame of UIView. you need to set the size of UIView same as the size of UIImage.
imgImageView.image.size
and secondly set the centre of UIView same as centre of UIImageView.
Hope this will work.

Related

UITextField subclass with bar underneath: bar not going all the way

The objective
I've made my own UITextField subclass, to design it in such a way that there is no border, only a white line under the text. (Subclass code below)
The issue
Unfortunately, the white line does not go to the end of the text field (in terms of width):
I've constrained the text field to have 0 px distance to both the left and right edges of the view, but as the images below show, the line does not meet those constraints.
My investigation and hypotheses
I've put in quite some time to debug, the only thing I can think of is that the dimensions specified in the code (copied below) are exécute before the constraints are applied to the view, which is not something I know how to resolve.
The code
//In a UITextField subclass
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
public override func awakeFromNib() {
super.awakeFromNib()
setup()
}
func setup() {
self.delegate = self
// BORDER
// Remove rectangular border
self.borderStyle = .none
self.layer.borderColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0).cgColor //Make transparent
self.layer.borderWidth = 0 // width = 0
// Add bottom border
bottomBorder = UIView(frame: CGRect(x: 0, y: self.frame.height, width: self.frame.width, height: 1))
bottomBorder.backgroundColor = UIColor.white
self.addSubview(bottomBorder)
}
the only thing I can think of is that the dimensions specified in the code (copied below) are exécute before the constraints are applied to the view,
Perfectly correct! The whole problem is that you are hard-coding the frame of the bar based on the frame of the text field. But the frame of the text field is not yet known during init.
Your entire approach here is wrong. You should be using autolayout constraints to constrain the bar to the text field, not giving the bar a fixed frame. That way, no matter what happens to the size of the text field (as it obeys the constraints you have given it), the size of the bar will follow along in perfect harmony.
You need
override func layoutSubviews() {
super.layoutSubviews()
bottomBorder.frame = CGRect(x: 0, y: self.frame.height - 1, width: self.frame.width, height: 1)
}
As correct frame size of superView is captured inside layoutSubviews not in init / awakeFromNib where the frame is set for current development device dimensions if it's in IB

Bottom Border Width on Swift TextField in TableView

i builded a static tableview with more Rowes than the screen has, so the user has to scroll to see all cell.
Every cell has a textfield with the following class to add a bottom border:
class TextFieldWithBottomBorder: UITextField {
let border = CALayer()
let width = CGFloat(1.0)
func addBottomBorder(color: UIColor){
self.border.borderColor = color.cgColor
self.border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height:self.frame.size.height)
self.border.borderWidth = self.width
self.layer.addSublayer(self.border)
self.layer.masksToBounds = true
}
func changeBorderColor(color: UIColor){
self.border.borderColor = color.cgColor
}
}
And i call the method after receiving some data from the server e. g.
self.firstnameTextField.text = firstNameFromDB
self.firstnameTextField.addBottomBorder(color: .blue)
This works fine for every cell is currently displayed. But the cells which are out of the current view the with is shorter than the textfield.
See this screenshot, for "Vorname", means firstName everything looks good, but for email, password etc. the border is to short.
http://share-your-photo.com/34b5e80253
Looks like the size of the UITextField is being resized after you have called addBottomBorder and so the UIView being used at the line is now not wide enough. It's difficult to say why this would be without seeing more code but there are several methods you could use to overcome it.
1) Switch to a UIView instead of a CALayer and use auto layout to keep the view in the correction position.
2) Override layoutSubviews to update the frame of the bottom line.
The simplest for you is probably option 2 (although I would go option 1) and it would look like this:
override func layoutSubviews() {
super.layoutSubviews()
self.border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height:self.frame.size.height)
}
Now whenever the frame/size of the text field changes the frame/size of the border line CALayer will be updated appropriately.
Use this class for bottom line text field
#IBDesignable class BottomTextField: UITextField {
var lineView = UIView()
#IBInspectable var lineViewBgColor:UIColor = UIColor.gray{
didSet {
if !isFirstResponder {
lineView.backgroundColor = lineViewBgColor
}
}
}
required init?(coder aDecoder:NSCoder) {
super.init(coder:aDecoder)!
setup()
}
override init(frame:CGRect) {
super.init(frame:frame)
setup()
}
// MARK:- Private Methods
private func setup() {
lineView.frame = CGRect(x:CGFloat(0), y:self.frame.size.height-2, width:self.frame.size.width, height:CGFloat(1))
lineView.backgroundColor = lineViewBgColor
self.addSubview(lineView)
}
}

getting highly confused by odd behaviour of viewController Method

I have overrided a method as
override func viewDidLayoutSubviews() {
// creating bottom line for textField
let border = CALayer()
let width = CGFloat(1.0)
border.borderColor = UIColor.whiteColor().CGColor
border.frame = CGRect(x: 0, y: emailTextField.frame.size.height - width, width: emailTextField.frame.size.width, height: emailTextField.frame.size.height)
border.borderWidth = width
emailTextField.layer.addSublayer(border)
emailTextField.layer.masksToBounds = true
}
Now whats happening is that when I run my app on Iphone 6, 6+ every thing works fine. But when I run the same code on iphone5 (Simulator + real Device ) viewDidLayoutSubViews is getting called infinite times and my app becoms unresponsive. I solved the problem by using a bool variable. But I do not understand why is this happening. So can someone please explain this to me.
Thanks :-)
As docs says that viewDidLayoutSubviews method need for change layout
Called to notify the view controller that its view has just laid out
its subviews. When the bounds change for a view controller's view,
the view adjusts the positions of its subviews and then the system
calls this method. However, this method being called does not indicate
that the individual layouts of the view's subviews have been
adjusted. Each subview is responsible for adjusting its own layout.
Move your code to viewDidLoad method or to loadView if you are not using xibs or Storyboards
override func viewDidLoad() {
// creating bottom line for textField
let border = CALayer()
let width = CGFloat(1.0)
border.borderColor = UIColor.whiteColor().CGColor
border.frame = CGRect(x: 0, y: emailTextField.frame.size.height - width, width: emailTextField.frame.size.width, height: emailTextField.frame.size.height)
border.borderWidth = width
emailTextField.layer.addSublayer(border)
emailTextField.layer.masksToBounds = true
}
If you would like to change border.frame you can set it to class variable and change it in viewDidLayoutSubviews method
override func viewDidLayoutSubviews() {
self.border.frame = CGRect(x: 0, y: emailTextField.frame.size.height - width, width: emailTextField.frame.size.width, height: emailTextField.frame.size.height)
self.border.borderWidth = width
}
Just replace
emailTextField.layer.addSublayer(border)
with this:
emailTextField.addSublayer(border)
-> you want to add sublayer to a view - as you want to apply masksToBounds property to it either.
Move your implementation inside viewDidLayoutSubviews. I got the same issue. Hope it will work.
override func viewDidLayoutSubviews()
{
//Your CODE
}

Swift: How to set UIScrollView contentSize height to height of contents?

I have a UIScrollView. Right now I am just setting it to double the height of the screen (frame in this case is just UIScreen.mainScreen().bounds):
class VenueDetailView: UIScrollView {
required init?(coder aDecoder: NSCoder) { fatalError("Storyboard makes me sad.") }
override init(frame: CGRect) {
super.init(frame: frame)
contentSize = CGSize(width: frame.width, height: frame.height*2) <----------------
backgroundColor = UIColor.greenColor()
}
func addBannerImage(imageUrl: NSURL) {
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: UIScreen.mainScreen().bounds.width, height: 300))
// TODO: Make this asynchronous
// Nice to have: cache the image
if let data = NSData(contentsOfURL: imageUrl) {
imageView.image = UIImage(data: data)
}
addSubview(imageView)
}
}
However, I just want it to be the size of all the contents inside it. How would I do this? Does this mean I have to set the contentSize after I add all the subviews?
Does this mean I have to set the contentSize after I add all the subviews?
Basically yes. Your goal should be to make the scroll view small and the content size big. That is what makes a scroll view scrollable: its contentSize is bigger than its own bounds size. So, set the scroll view itself to have a frame that is the same as the bounds of its superview, but then set its contentSize to embrace all its subviews.

trouble creating a scrollable UIView (swift + Interface Builder)

Please do not immediately discredit this a duplicate, I have been on stackoverflow for like 2 days now trying any and every way to do this, read a lot of blog articles (which might have been slightly dated) and still no luck.
Basically, I have a custom UIView which is meant to draw a lot of circles (it's just the starting point) and I cannot get the view to scroll down to the ones apart from the visible circles on initial load. I made the for loop for about 200 of them to be sure there is something to scroll to.
here is my view code:
import UIKit
class Draw2D: UIView {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder);
}
override func drawRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2.0);
let colorSpace = CGColorSpaceCreateDeviceRGB();
let components: [CGFloat] = [0.0, 0.0, 1.0, 1.0];
let color = CGColorCreate(colorSpace, components);
CGContextSetStrokeColorWithColor(context, color);
var ctr = 0;
var ctr2 = 0;
for(var i:Int = 1; i<200; i++){
var ii: CGFloat=CGFloat(10+ctr);
var iii: CGFloat = CGFloat(30+ctr2);
CGContextStrokeEllipseInRect(context, CGRectMake(ii, iii, 33, 33));
if(ctr<200)
{
ctr+=160;
}else{
ctr=0;
ctr2+=50;
}
}
CGContextStrokePath(context);
}
}
In interface builder, I set the view class to Draw2D and the controller to my custom ViewController class. The view controller class is currently a standard one with no additional options added.
I tried dragging a Scroll View on the screen, I tried deleting the standard view, adding the scroll view and then putting my Draw2D view on top of that and it didn't work. I tried changing the size of the scroll view to make it smaller than my content (draw2d) view and nothing. I tried unchecking auto-layout and also changing simulated size to freeform as per certain tutorials I found online.
I also at one point tried creating a scroll view programmatically in the UIView and adding it as a subview which didn't help, maybe I did it wrong, I don't know but I'm really running out of options and sources to generate ideas from.
First, you can inherit from UIScrollView directly:
class Draw2D: UIScrollView {
Next, you need to set the content size of the UIScrollView to the height of the elements you inserted. For instance, let's say you have 4 circles:
let radius: CGFloat = 200
var contentHeight: CGFloat = 0
for i in 0...4 {
// initialize circle here
var circle = UIView()
// customize the view
circle.layer.cornerRadius = radius/2
circle.backgroundColor = UIColor.redColor()
// set the frame of the circle
circle.frame = CGRect(x: 0, y: CGFloat(i)*radius, width: radius, height: radius)
// add to scroll view
self.addSubview(circle)
// keep track of the height of the content
contentHeight += radius
}
At this point you have 4 circles one after the other, summing up to 800 px in height. Thus, you would set the content size of the scroll view as follows:
self.contentSize = CGSize(width: self.frame.width, height: contentHeight)
As far as contentHeight > device screen height then your view will scroll.
Full working code below:
import UIKit
import Foundation
class Draw2D: UIScrollView {
override init(frame: CGRect) {
super.init(frame: frame)
let radius: CGFloat = 200
var contentHeight: CGFloat = 0
for i in 0...4 {
// initialize circle here
var circle = UIView()
// customize the view
circle.layer.cornerRadius = radius/2
circle.backgroundColor = UIColor.redColor()
// set the frame of the circle
circle.frame = CGRect(x: 0, y: CGFloat(i)*radius, width: radius, height: radius)
// add to scroll view
self.addSubview(circle)
// keep track of the height of the content
contentHeight += radius
}
self.contentSize = CGSize(width: self.frame.width, height: contentHeight)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Resources