অটোলেআউট দিয়ে সমস্ত উপদর্শন ফিট করার জন্য সুপারভিউকে কীভাবে পুনরায় আকার দিন?


142

অটোলেআউট সম্পর্কে আমার বোধগম্যতা হ'ল এটি পর্যবেক্ষণের অবস্থানগুলি গণনা করে এবং বাধা এবং অন্তর্নিহিত আকারের ভিত্তিতে তদারকির আকার নেয় takes

এই প্রক্রিয়াটি বিপরীত করার কোনও উপায় আছে কি? আমি বাধা এবং অভ্যন্তরীণ আকারের বেসে সুপারভিউকে পুনরায় আকার দিতে চাই। এটি অর্জনের সহজ উপায় কী?

আমার কাছে এক্সকোডে ডিজাইন করা ভিউ রয়েছে যা আমি শিরোনাম হিসাবে ব্যবহার করি UITableView। এই দৃশ্যে একটি লেবেল এবং একটি বোতাম অন্তর্ভুক্ত রয়েছে। তথ্যের উপর নির্ভর করে লেবেলের আকার পৃথক হয়। সীমাবদ্ধতার উপর নির্ভর করে লেবেলটি সাফল্যের সাথে বোতামটি নীচে ঠেলে দেয় বা সুপারভাইজারের বোতাম এবং নীচের অংশে যদি কোনও সীমাবদ্ধতা থাকে তবে লেবেলটি সঙ্কুচিত হয়।

আমি কয়েকটি অনুরূপ প্রশ্ন পেয়েছি তবে তাদের ভাল এবং সহজ উত্তর নেই।


28
আপনার নীচের উত্তরগুলির মধ্যে একটি নির্বাচন করা উচিত, অবশ্যই টম সুইফ্ট আপনার প্রশ্নের উত্তর দিয়েছেন। সমস্ত পোস্টার আপনাকে সাহায্য করার জন্য প্রচুর সময় ব্যয় করেছিল, এখন আপনার নিজের অংশটি করা উচিত এবং আপনার পছন্দটি সবচেয়ে ভাল পছন্দ করা উচিত select
ডেভিড এইচ

উত্তর:


149

ব্যবহারের জন্য সঠিক এপিআই হ'ল UIView systemLayoutSizeFittingSize:হয় UILayoutFittingCompressedSizeবা পাস হচ্ছে UILayoutFittingExpandedSize

UIViewঅটোলেআউট ব্যবহারের জন্য সাধারণ হিসাবে এটি যতক্ষণ না আপনার সীমাবদ্ধতাগুলি সঠিক ততক্ষণ কাজ করা উচিত। আপনি যদি এটির UITableViewCell(উদাহরণস্বরূপ সারির উচ্চতা নির্ধারণ করতে) ব্যবহার করতে চান তবে আপনার নিজের ঘরের বিরুদ্ধে এটি কল করতে হবে contentViewএবং উচ্চতাটি দখল করতে হবে।

যদি আপনার দৃষ্টিভঙ্গিতে এক বা একাধিক ইউআইএলবেল একাধিকরেখা থাকে তবে আরও বিবেচনাগুলি বিদ্যমান। এগুলির জন্য এটি নিখুঁত হয় যে preferredMaxLayoutWidthসম্পত্তিটি সঠিকভাবে সেট করা উচিত যাতে লেবেলটি একটি সঠিক সরবরাহ করে intrinsicContentSize, যা systemLayoutSizeFittingSize'sগণনায় ব্যবহৃত হবে ।

সম্পাদনা: অনুরোধ অনুসারে, একটি সারণী দর্শন ঘরের জন্য উচ্চতার গণনার উদাহরণ যুক্ত করুন

টেবিল-সেল উচ্চতার গণনার জন্য অটোলেআউট ব্যবহার অত্যন্ত দক্ষ নয় তবে এটি নিশ্চিত করা সুবিধাজনক, বিশেষত যদি আপনার একটি ঘর জটিল কাঠামোযুক্ত থাকে।

যেমন আমি উপরে বলেছি, আপনি যদি কোনও মাল্টলাইন ব্যবহার করছেন তবে লেবেলের প্রস্থে UILabelসিঙ্ক করা জরুরী preferredMaxLayoutWidth। আমি এটি করতে একটি কাস্টম UILabelসাবক্লাস ব্যবহার করি:

@implementation TSLabel

- (void) layoutSubviews
{
    [super layoutSubviews];

    if ( self.numberOfLines == 0 )
    {
        if ( self.preferredMaxLayoutWidth != self.frame.size.width )
        {
            self.preferredMaxLayoutWidth = self.frame.size.width;
            [self setNeedsUpdateConstraints];
        }
    }
}

- (CGSize) intrinsicContentSize
{
    CGSize s = [super intrinsicContentSize];

    if ( self.numberOfLines == 0 )
    {
        // found out that sometimes intrinsicContentSize is 1pt too short!
        s.height += 1;
    }

    return s;
}

@end

এখানে একটি অনুমোদিত ইউআইটিএবল ভিউকন্ট্রোলার সাবক্লাস উচ্চতা প্রদর্শন করছে রোআউটআইডেক্সপথ:

#import "TSTableViewController.h"
#import "TSTableViewCell.h"

@implementation TSTableViewController

- (NSString*) cellText
{
    return @"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
}

#pragma mark - Table view data source

- (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView
{
    return 1;
}

- (NSInteger) tableView: (UITableView *)tableView numberOfRowsInSection: (NSInteger) section
{
    return 1;
}

- (CGFloat) tableView: (UITableView *) tableView heightForRowAtIndexPath: (NSIndexPath *) indexPath
{
    static TSTableViewCell *sizingCell;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        sizingCell = (TSTableViewCell*)[tableView dequeueReusableCellWithIdentifier: @"TSTableViewCell"];
    });

    // configure the cell
    sizingCell.text = self.cellText;

    // force layout
    [sizingCell setNeedsLayout];
    [sizingCell layoutIfNeeded];

    // get the fitting size
    CGSize s = [sizingCell.contentView systemLayoutSizeFittingSize: UILayoutFittingCompressedSize];
    NSLog( @"fittingSize: %@", NSStringFromCGSize( s ));

    return s.height;
}

- (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath
{
    TSTableViewCell *cell = (TSTableViewCell*)[tableView dequeueReusableCellWithIdentifier: @"TSTableViewCell" ];

    cell.text = self.cellText;

    return cell;
}

@end

একটি সাধারণ কাস্টম সেল:

#import "TSTableViewCell.h"
#import "TSLabel.h"

@implementation TSTableViewCell
{
    IBOutlet TSLabel* _label;
}

- (void) setText: (NSString *) text
{
    _label.text = text;
}

@end

এবং, স্টোরিবোর্ডে সংজ্ঞায়িত প্রতিবন্ধকতার একটি চিত্র এখানে। মনে রাখবেন যে লেবেলে কোনও উচ্চতা / প্রস্থের সীমাবদ্ধতা নেই - সেগুলি লেবেলের থেকে অনুমান করা হয় intrinsicContentSize:

এখানে চিত্র বর্ণনা লিখুন


1
উচ্চতার জন্য প্রয়োগের কী উদাহরণ দেয়? উদাহরণস্বরূপ: এই পদ্ধতিটি মাল্টি-লাইন লেবেলযুক্ত একটি ঘর ব্যবহার করে দেখতে হবে? আমি এটির সাথে বেশ খানিকটা গণ্ডগোল করেছি, এবং এটি কাজ করতে পেলাম না। কীভাবে আপনি একটি সেল পাবেন (বিশেষত যদি কোনও স্টোরবোর্ডে সেল সেটআপ করা থাকে)? এটি কার্যকর করার জন্য আপনার কোন বাধা দরকার?
rdelmar

@ অর্ডেলমার - নিশ্চিত আমি আমার উত্তরে একটি উদাহরণ যুক্ত করেছি।
টমসউফিট

7
সেলটির নীচ থেকে নিম্নতম সাবউভিউয়ের নীচে চূড়ান্ত উল্লম্ব সীমাবদ্ধতা যোগ না করা পর্যন্ত এটি আমার পক্ষে কাজ করছে না। এটি মনে হয় যে উল্লম্ব সীমাবদ্ধতার মধ্যে অবশ্যই কোষের উচ্চতা গণনা সফল হওয়ার জন্য শীর্ষ এবং নীচে উল্লম্ব ব্যবধানটি অবশ্যই সেল এবং এর সামগ্রীর মধ্যে অন্তর্ভুক্ত করতে হবে।
এরিক বাকের

1
এটি কাজ করার জন্য আমার আরও একটি কৌশল প্রয়োজন trick এবং @ এরিকবেকারের টিপ অবশেষে এটি পেরেক দিয়েছিল। লোকটিকে ভাগ করে নেওয়ার জন্য ধন্যবাদ। আমি এখন নন-শূন্য আকার পেয়েছি sizingCellএটির বা এর বিপরীতে contentView
এম কেওয়াল

1
সঠিক উচ্চতা না পেতে যাদের সমস্যা হয় তাদের জন্য নিশ্চিত হয়ে নিন যে আপনার sizingCellপ্রস্থটি আপনার প্রস্থের সাথে মেলে tableView
এমকেভাল

30

এরিক বাকেরের মন্তব্যটি আমাকে মূল ধারণাটি থেকে সরিয়ে নিয়েছে যে তার ধারণার আকারটি তার ভিতরে রাখা বিষয়বস্তু দ্বারা নির্ধারণ করা যেতে পারে, তারপরে এর লিখিত সামগ্রীর উচ্চতাটি চালনার জন্য অবশ্যই ধারণার সাথে একটি সুস্পষ্ট সম্পর্ক থাকতে হবে (বা প্রস্থ) গতিশীল । "সাবউভিউ যুক্ত করুন" এই ধারণাটি তৈরি করে না যেমন আপনি ধরে নিতে পারেন। কোন সাবউইভটি আপনার ধারকটির উচ্চতা এবং / বা প্রস্থকে চালিত করতে হবে তা বেছে নিতে হবে ... আপনি সাধারণত আপনার সামগ্রিক ইউআই এর নীচের ডানদিকে কোণায় যে কোনও ইউআই উপাদান রেখেছেন তা সাধারণত। পয়েন্টটি বর্ণনা করার জন্য এখানে কিছু কোড এবং ইনলাইন মন্তব্য রয়েছে।

দ্রষ্টব্য, আপনি যদি এতে যা কিছু রেখে দেন তার ভিত্তিতে গতিশীলভাবে এটির আকার নির্ধারণ করে এমন একক বিষয়বস্তু দর্শন (যা এটিকে স্ক্রোল ভিউতে যোগাযোগ করে) নির্ধারণ করা সাধারণ কারণ এটি স্ক্রোল দর্শনের সাথে কাজ করার ক্ষেত্রে এটি বিশেষ মূল্যবান হতে পারে। শুভকামনা, আশা করি এটি কারও পক্ষে সাহায্য করবে।

//
//  ViewController.m
//  AutoLayoutDynamicVerticalContainerHeight
//

#import "ViewController.h"

@interface ViewController ()
@property (strong, nonatomic) UIView *contentView;
@property (strong, nonatomic) UILabel *myLabel;
@property (strong, nonatomic) UILabel *myOtherLabel;
@end

@implementation ViewController

- (void)viewDidLoad
{
    // INVOKE SUPER
    [super viewDidLoad];

    // INIT ALL REQUIRED UI ELEMENTS
    self.contentView = [[UIView alloc] init];
    self.myLabel = [[UILabel alloc] init];
    self.myOtherLabel = [[UILabel alloc] init];
    NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_contentView, _myLabel, _myOtherLabel);

    // TURN AUTO LAYOUT ON FOR EACH ONE OF THEM
    self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
    self.myLabel.translatesAutoresizingMaskIntoConstraints = NO;
    self.myOtherLabel.translatesAutoresizingMaskIntoConstraints = NO;

    // ESTABLISH VIEW HIERARCHY
    [self.view addSubview:self.contentView]; // View adds content view
    [self.contentView addSubview:self.myLabel]; // Content view adds my label (and all other UI... what's added here drives the container height (and width))
    [self.contentView addSubview:self.myOtherLabel];

    // LAYOUT

    // Layout CONTENT VIEW (Pinned to left, top. Note, it expects to get its vertical height (and horizontal width) dynamically based on whatever is placed within).
    // Note, if you don't want horizontal width to be driven by content, just pin left AND right to superview.
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_contentView]" options:0 metrics:0 views:viewsDictionary]]; // Only pinned to left, no horizontal width yet
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_contentView]" options:0 metrics:0 views:viewsDictionary]]; // Only pinned to top, no vertical height yet

    /* WHATEVER WE ADD NEXT NEEDS TO EXPLICITLY "PUSH OUT ON" THE CONTAINING CONTENT VIEW SO THAT OUR CONTENT DYNAMICALLY DETERMINES THE SIZE OF THE CONTAINING VIEW */
    // ^To me this is what's weird... but okay once you understand...

    // Layout MY LABEL (Anchor to upper left with default margin, width and height are dynamic based on text, font, etc (i.e. UILabel has an intrinsicContentSize))
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_myLabel]" options:0 metrics:0 views:viewsDictionary]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[_myLabel]" options:0 metrics:0 views:viewsDictionary]];

    // Layout MY OTHER LABEL (Anchored by vertical space to the sibling label that comes before it)
    // Note, this is the view that we are choosing to use to drive the height (and width) of our container...

    // The LAST "|" character is KEY, it's what drives the WIDTH of contentView (red color)
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_myOtherLabel]-|" options:0 metrics:0 views:viewsDictionary]];

    // Again, the LAST "|" character is KEY, it's what drives the HEIGHT of contentView (red color)
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_myLabel]-[_myOtherLabel]-|" options:0 metrics:0 views:viewsDictionary]];

    // COLOR VIEWS
    self.view.backgroundColor = [UIColor purpleColor];
    self.contentView.backgroundColor = [UIColor redColor];
    self.myLabel.backgroundColor = [UIColor orangeColor];
    self.myOtherLabel.backgroundColor = [UIColor greenColor];

    // CONFIGURE VIEWS

    // Configure MY LABEL
    self.myLabel.text = @"HELLO WORLD\nLine 2\nLine 3, yo";
    self.myLabel.numberOfLines = 0; // Let it flow

    // Configure MY OTHER LABEL
    self.myOtherLabel.text = @"My OTHER label... This\nis the UI element I'm\narbitrarily choosing\nto drive the width and height\nof the container (the red view)";
    self.myOtherLabel.numberOfLines = 0;
    self.myOtherLabel.font = [UIFont systemFontOfSize:21];
}

@end

অটোলেআউট.পিএনজি সহ সমস্ত সাবভিউয়ের সাথে ফিট করার জন্য কীভাবে সুপারভিউকে পুনরায় আকার দিন


3
এটি একটি দুর্দান্ত কৌশল এবং সুপরিচিত নয়। পুনরাবৃত্তি করার জন্য: যদি অভ্যন্তরীণ ভিউগুলির অভ্যন্তরীণ উচ্চতা থাকে এবং উপরে এবং নীচে পিন করা থাকে তবে বাইরের দৃশ্যের উচ্চতা নির্দিষ্ট করার প্রয়োজন হয় না এবং প্রকৃতপক্ষে এটির বিষয়বস্তু আলিঙ্গন করে। কাঙ্ক্ষিত ফলাফল পেতে আপনার অভ্যন্তরীণ ভিউগুলির জন্য সামগ্রী সংক্ষেপণ এবং আলিঙ্গন সামঞ্জস্য করতে হতে পারে।
ফ্যাটম্যান

এই টাকা! আমি দেখেছি আরও ভাল সংক্ষিপ্ত ভিএফএল উদাহরণ।
ইভান আর

এটিই আমি খুঁজছিলাম (ধন্যবাদ @ জন) তাই আমি এটির একটি সুইফ্ট সংস্করণ এখানে
জেমস

1
"আপনাকে বেছে নিতে হবে যে কোন সাবউভিউটি ধারকটির উচ্চতা এবং / বা প্রস্থকে চালিত করতে চলেছে ... আপনি সাধারণত আপনার সামগ্রিক ইউআইয়ের নীচের ডানদিকে কোণায় যে কোনও ইউআই উপাদান রেখেছেন তা সাধারণভাবে" .. আমি পিউরলআউট ব্যবহার করছি, এবং এটি আমার জন্য মূল বিষয় ছিল। একটি পিতা-মাতার দেখার জন্য, একটি শিশু দর্শন সহ, চাইল্ড ভিউটি নীচে ডানদিকে পিন করতে বসেছিল যা হঠাৎই পিতামাতাকে একটি আকার দিয়েছে। ধন্যবাদ!
স্টিভেন এলিয়ট

1
প্রস্থকে ড্রাইভ করার জন্য একটি ভিউ বেছে নেওয়া হুবহু যা আমি করতে পারি না। কখনও কখনও একটি সাবভিউ বিস্তৃত হয়, কখনও কখনও অন্য সাবউভিউ আরও প্রশস্ত হয়। এই ক্ষেত্রে কোন ধারণা?
হেনিং

3

আপনি এটি একটি সীমাবদ্ধতা তৈরি করে এবং ইন্টারফেস বিল্ডারের মাধ্যমে সংযুক্ত করে এটি করতে পারেন

ব্যাখ্যা দেখুন: অটো_লয়আউট_ কনট্রেন্টস_ইন_ইন্টারফেস_বিল্ডার

রায়ভেন্ডারলিচ শুরুর-অটো-লেআউট

AutolayoutPG নিবন্ধগুলি সীমাবদ্ধ মৌলিক বিষয়গুলি

@interface ViewController : UIViewController {
    IBOutlet NSLayoutConstraint *leadingSpaceConstraint;
    IBOutlet NSLayoutConstraint *topSpaceConstraint;
}
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *leadingSpaceConstraint;

এই সীমাবদ্ধতার আউটলেটটি আপনার উপ দর্শনগুলির সাথে সীমাবদ্ধ করুন বা অতি দৃশ্যের সংযোজনকেও সংযুক্ত করুন এবং আপনার প্রয়োজনীয়তা অনুসারে এটি সেট করুন

 self.leadingSpaceConstraint.constant = 10.0;//whatever you want to assign

আমি আশা করি এটি এটি পরিষ্কার করে দিয়েছে।


সুতরাং উত্স কোড থেকে এক্সকোডে তৈরি প্রতিবন্ধগুলি কনফিগার করা সম্ভব। তবে প্রশ্ন হল কীভাবে তাদের তত্ত্বাবধানের আকার পরিবর্তন করতে কনফিগার করবেন।
ডাক

হ্যাঁ @ ডাক তদারকির বিন্যাস সম্পর্কে নিশ্চিত হন। তত্ত্বাবধানে সীমাবদ্ধ রাখুন এবং উচ্চতার প্রতিবন্ধকতা তৈরি করুন তাই যখনই আপনার দর্শনটি এটি স্বয়ংক্রিয়ভাবে তদারকির পরিমাণ বাড়াতে বাড়ে superv
চন্দন

এটি আমার জন্যও কাজ করেছিল। এটি সাহায্য করে যে আমার কাছে নির্ধারিত আকারের কোষ রয়েছে (60px উচ্চতা)। সুতরাং যখন ভিউটি লোড হয়, আমি আমার আইবিউটলেট'র উচ্চতার সীমাবদ্ধতা 60 * x করতে সেট করি যেখানে এক্স কোষের সংখ্যা। শেষ পর্যন্ত, আপনি সম্ভবত এটি একটি স্ক্রোল ভিউতে চাইবেন যাতে আপনি পুরো জিনিসটি দেখতে পারেন।
স্যান্ডি চ্যাপম্যান

-1

এটি subviewবৃহত্তর অভ্যন্তরের জন্য স্বাভাবিকের জন্য করা যেতে পারে UIViewতবে এটি স্বয়ংক্রিয়ভাবে কাজ করে না headerViews। একটি উচ্চতা headerViewকি দ্বারা ফিরে হচ্ছে দ্বারা নির্ধারিত হয় tableView:heightForHeaderInSection:তাই আপনি নিরূপণ করা আছে heightউপর ভিত্তি করে heightএর UILabelজন্য প্লাস স্থান UIButtonএবং কোন paddingআপনার যা দরকার। আপনাকে এরকম কিছু করতে হবে:

-(CGFloat)tableView:(UITableView *)tableView 
          heightForHeaderInSection:(NSInteger)section {
    NSString *s = self.headeString[indexPath.section];
    CGSize size = [s sizeWithFont:[UIFont systemFontOfSize:17] 
                constrainedToSize:CGSizeMake(281, CGFLOAT_MAX)
                    lineBreakMode:NSLineBreakByWordWrapping];
    return size.height + 60;
}

এখানে headerStringআপনি পূরণ করতে চান যাই হোক না কেন স্ট্রিং UILabelএবং 281 নম্বর widthএর UILabel(সেটআপ যেমন Interface Builder)


দুর্ভাগ্যক্রমে এটি কাজ করে না। সুপারভিউতে থাকা "কন্টেন্টের সাথে মানানসই আকার" আপনার ভবিষ্যদ্বাণী অনুসারে বাধাটির নীচে সুপারভাইভের সাথে সংযোগ স্থাপন করে এমন সীমাবদ্ধতা সরিয়ে দেয়। রানটাইমের সময় লেবেলটি পুনরায় আকার দেওয়া হয় এবং এটি বোতামটি নীচে চাপ দেয় তবে সুপারভিউ স্বয়ংক্রিয়ভাবে পুনরায় আকার দেওয়া হয় না।
ডাক

@ ডাক, দুঃখিত, সমস্যাটি হ'ল আপনার ভিউটি একটি টেবিল দর্শনের শিরোনাম। আমি আপনার পরিস্থিতি ভুল বুঝেছি। আমি ভাবছিলাম যে বোতামটি এবং লেবেলটি বন্ধ করে দেওয়া ভিউটি অন্য একটি ভিউয়ের অভ্যন্তরে ছিল তবে মনে হচ্ছে আপনি এটি হেডার হিসাবে ব্যবহার করছেন (শিরোনামের ভিতরে ভিউ হওয়ার চেয়ে)। সুতরাং, আমার আসল উত্তরটি কাজ করবে না। আমার যা মনে করা উচিত সেটির জন্য আমার উত্তরটি পরিবর্তন করেছি।
rdelmar

এটি আমার বর্তমান বাস্তবায়নের অনুরূপ তবে আমি আশা করছিলাম যে আরও ভাল উপায় আছে। আমি যখন আমার শিরোনাম পরিবর্তন করি তখন আমি এই কোডটি প্রতিবার আপডেট করা এড়াতে পছন্দ করব।
ডাক

@ ডাক, দুঃখিত, আমি মনে করি না এর চেয়ে ভাল আর কোনও উপায় আছে, কেবলমাত্র টেবিল দর্শনগুলি এইভাবে কাজ করে।
rdelmar

আমার উত্তর দেখুন দয়া করে। "আরও ভাল উপায়" হ'ল ইউআইভিউ সিস্টেমলাইআউটসাইজ ফিটিংসাইজ ব্যবহার করা। এটি বলেছে, কেবল কোনও টেবিল ভিউতে প্রয়োজনীয় উচ্চতা পেতে ভিউ বা সেলটিতে সম্পূর্ণ অটোলেআউট সম্পাদন করা ব্যয়বহুল। আমি জটিল কোষের জন্য এটি করতাম তবে সহজ মামলার জন্য আপনার মতো সমাধানে ফলব্যাক।
টমসউইফট
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.